home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume16 / less5 / part03 / option.c < prev    next >
Encoding:
C/C++ Source or Header  |  1988-09-22  |  11.2 KB  |  576 lines

  1. /*
  2.  * Process command line options.
  3.  * Each option is a single letter which controls a program variable.
  4.  * The options have defaults which may be changed via
  5.  * the command line option, or toggled via the "-" command.
  6.  */
  7.  
  8. #include "less.h"
  9.  
  10. #define    toupper(c)    ((c)-'a'+'A')
  11.  
  12. #define    END_OPTION_STRING    ('$')
  13.  
  14. /*
  15.  * Types of options.
  16.  */
  17. #define    BOOL        01    /* Boolean option: 0 or 1 */
  18. #define    TRIPLE        02    /* Triple-valued option: 0, 1 or 2 */
  19. #define    NUMBER        04    /* Numeric option */
  20. #define    REPAINT        040    /* Repaint screen after toggling option */
  21. #define    NO_TOGGLE    0100    /* Option cannot be toggled with "-" cmd */
  22.  
  23. /*
  24.  * Variables controlled by command line options.
  25.  */
  26. public int clean_data;        /* Can we assume the data is "clean"? 
  27.                    (That is, free of nulls, etc) */
  28. public int quiet;        /* Should we suppress the audible bell? */
  29. public int how_search;        /* Where should forward searches start? */
  30. public int top_scroll;        /* Repaint screen from top?
  31.                    (alternative is scroll from bottom) */
  32. public int pr_type;        /* Type of prompt (short, medium, long) */
  33. public int bs_mode;        /* How to process backspaces */
  34. public int know_dumb;        /* Don't complain about dumb terminals */
  35. public int quit_at_eof;        /* Quit after hitting end of file twice */
  36. public int squeeze;        /* Squeeze multiple blank lines into one */
  37. public int tabstop;        /* Tab settings */
  38. public int back_scroll;        /* Repaint screen on backwards movement */
  39. public int twiddle;        /* Display "~" for lines after EOF */
  40. public int caseless;        /* Do "caseless" searches */
  41. public int linenums;        /* Use line numbers */
  42. public int cbufs;        /* Current number of buffers */
  43. public int autobuf;
  44. public int plusoption;
  45.  
  46. extern char *prproto[];
  47. extern char *eqproto;
  48. extern int nbufs;
  49. extern int sc_window;
  50. extern int ispipe;
  51. extern char *first_cmd;
  52. extern char *every_first_cmd;
  53. #if LOGFILE
  54. extern char *namelogfile;
  55. extern int force_logfile;
  56. extern int logfile;
  57. #endif
  58. #if TAGS
  59. extern char *tagfile;
  60. extern char *tagpattern;
  61. public int tagoption = 0;
  62. #endif
  63.  
  64. static char *opt_P();
  65.  
  66. static struct option
  67. {
  68.     char oletter;        /* The controlling letter (a-z) */
  69.     char otype;        /* Type of the option */
  70.     int odefault;        /* Default value */
  71.     int *ovar;        /* Pointer to the associated variable */
  72.     char *odesc[3];        /* Description of each value */
  73. } option[] =
  74. {
  75.     { 'a', TRIPLE, 0, &how_search,
  76.         { "Forward search starts at second REAL line displayed",
  77.           "Forward search starts at bottom of screen",
  78.           "Forward search starts at second SCREEN line displayed"
  79.         }
  80.     },
  81.     { 'b', NUMBER, 10, &cbufs,
  82.         { "%d buffers",
  83.           NULL, NULL
  84.         }
  85.     },
  86.     { 'B', BOOL, 1, &autobuf,
  87.         { "Don't automatically allocate buffers",
  88.           "Automatically allocate buffers when needed",
  89.           NULL
  90.         }
  91.     },
  92.     { 'c', TRIPLE, 0, &top_scroll,
  93.         { "Repaint by scrolling from bottom of screen",
  94.           "Repaint by clearing each line",
  95.           "Repaint by painting from top of screen"
  96.         }
  97.     },
  98.     { 'd', BOOL|NO_TOGGLE, 0, &know_dumb,
  99.         { NULL, NULL, NULL}
  100.     },
  101.     { 'e', TRIPLE, 0, &quit_at_eof,
  102.         { "Don't quit at end-of-file",
  103.           "Quit at end-of-file",
  104.           "Quit immediately at end-of-file"
  105.         }
  106.     },
  107.     { 'h', NUMBER, -1, &back_scroll,
  108.         { "Backwards scroll limit is %d lines",
  109.           NULL, NULL
  110.         }
  111.     },
  112.     { 'i', BOOL, 0, &caseless,
  113.         { "Case is significant in searches",
  114.           "Ignore case in searches",
  115.           NULL
  116.         }
  117.     },
  118.     { 'm', TRIPLE, 0, &pr_type,
  119.         { "Short prompt",
  120.           "Medium prompt",
  121.           "Long prompt"
  122.         }
  123.     },
  124.     { 'n', BOOL, 1, &linenums,
  125.         { "Don't use line numbers",
  126.           "Use line numbers",
  127.           NULL
  128.         }
  129.     },
  130.     { 'q', TRIPLE, 0, &quiet,
  131.         { "Ring the bell for errors AND at eof/bof",
  132.           "Ring the bell for errors but not at eof/bof",
  133.           "Never ring the bell"
  134.         }
  135.     },
  136.     { 's', BOOL|REPAINT, 0, &squeeze,
  137.         { "Don't squeeze multiple blank lines",
  138.           "Squeeze multiple blank lines",
  139.           NULL
  140.         }
  141.     },
  142.     { 'u', TRIPLE|REPAINT, 0, &bs_mode,
  143.         { "Underlined text displayed in underline mode",
  144.           "Backspaces cause overstrike",
  145.           "Backspaces print as ^H"
  146.         }
  147.     },
  148.     { 'w', BOOL|REPAINT, 1, &twiddle,
  149.         { "Display nothing for lines after end-of-file",
  150.           "Display ~ for lines after end-of-file",
  151.           NULL
  152.         }
  153.     },
  154.     { 'x', NUMBER|REPAINT, 8, &tabstop,
  155.         { "Tab stops every %d spaces", 
  156.           NULL, NULL 
  157.         }
  158.     },
  159.     { 'z', NUMBER|REPAINT, -1, &sc_window,
  160.         { "Scroll window size is %d lines",
  161.           NULL, NULL
  162.         }
  163.     },
  164.     { '\0' }
  165. };
  166.  
  167. /*
  168.  * Initialize each option to its default value.
  169.  */
  170.     public void
  171. init_option()
  172. {
  173.     register struct option *o;
  174.  
  175.     first_cmd = every_first_cmd = NULL;
  176.  
  177.     for (o = option;  o->oletter != '\0';  o++)
  178.     {
  179.         /*
  180.          * Set each variable to its default.
  181.          */
  182.         *(o->ovar) = o->odefault;
  183.     }
  184. }
  185.  
  186. /*
  187.  * Toggle command line flags from within the program.
  188.  * Used by the "-" and "_" commands.
  189.  * If do_toggle is zero, just report the current setting, without changing it.
  190.  */
  191.     public void
  192. toggle_option(s, do_toggle)
  193.     char *s;
  194.     int do_toggle;
  195. {
  196.     int c;
  197.     register struct option *o;
  198.     char *msg;
  199.     int n;
  200.     int dorepaint;
  201.     char message[100];
  202.  
  203.     c = *s++;
  204.  
  205.     switch (c)
  206.     {
  207.     case 'P':
  208.         /*
  209.          * Special case for -P.
  210.          */
  211.         if (*s == '\0')
  212.             error(prproto[pr_type]);
  213.         else
  214.             (void) opt_P(s);
  215.         return;
  216. #if TAGS
  217.     case 't':
  218.         /*
  219.          * Special case for -t.
  220.          */
  221.         if (*s == '\0')
  222.         {
  223.             error("no tag");
  224.             return;
  225.         }
  226.         findtag(s);
  227.         if (tagfile != NULL)
  228.         {
  229.             edit(tagfile);
  230.             (void) tagsearch();
  231.         }
  232.         return;
  233. #endif
  234. #if LOGFILE
  235.     case 'L':
  236.         /*
  237.          * Special case for -l and -L.
  238.          */
  239.         force_logfile = 1;
  240.         goto case_l;
  241.     case 'l':
  242.         force_logfile = 0;
  243.     case_l:
  244.         if (*s == '\0')
  245.         {
  246.             if (logfile < 0)
  247.                 error("no log file");
  248.             else
  249.             {
  250.                 sprintf(message, "log file \"%s\"",
  251.                     namelogfile);
  252.                 error(message);
  253.             }
  254.             return;
  255.         }
  256.         if (!ispipe)
  257.         {
  258.             error("input is not a pipe");
  259.             return;
  260.         }
  261.         if (logfile >= 0)
  262.         {
  263.             error("log file is already in use");
  264.             return;
  265.         }
  266.         namelogfile = save(s);
  267.         use_logfile();
  268.         sync_logfile();
  269.         return;
  270. #endif
  271.     }
  272.  
  273.     msg = NULL;
  274.     for (o = option;  o->oletter != '\0';  o++)
  275.     {
  276.         if (o->otype & NO_TOGGLE)
  277.             continue;
  278.         dorepaint = (o->otype & REPAINT);
  279.         if ((o->otype & BOOL) && (o->oletter == c))
  280.         {
  281.             /*
  282.              * Boolean option: 
  283.              * just toggle it.
  284.              */
  285.             if (do_toggle)
  286.                 *(o->ovar) = ! *(o->ovar);
  287.         } else if ((o->otype & TRIPLE) && (o->oletter == c))
  288.         {
  289.             /*
  290.              * Triple-valued option with lower case letter:
  291.              * make it 1 unless already 1, then make it 0.
  292.              */
  293.             if (do_toggle)
  294.                 *(o->ovar) = (*(o->ovar) == 1) ? 0 : 1;
  295.         } else if ((o->otype & TRIPLE) && (toupper(o->oletter) == c))
  296.         {
  297.             /*
  298.              * Triple-valued option with upper case letter:
  299.              * make it 2 unless already 2, then make it 0.
  300.              */
  301.             if (do_toggle)
  302.                 *(o->ovar) = (*(o->ovar) == 2) ? 0 : 2;
  303.         } else if ((o->otype & NUMBER) && (o->oletter == c))
  304.         {
  305.             n = getnum(&s, '\0');
  306.             if (n < 0)
  307.             {
  308.                 /*
  309.                  * No number; just a query.
  310.                  * No need to repaint screen.
  311.                  */
  312.                 dorepaint = 0;
  313.             } else
  314.             {
  315.                 /*
  316.                  * Number follows the option letter.
  317.                  * Set the variable to that number.
  318.                  */
  319.                 if (do_toggle)
  320.                     *(o->ovar) = n;
  321.             }
  322.  
  323.             /*
  324.              * Special case for -b.
  325.              * Call ch_init to set new number of buffers.
  326.              */
  327.             if (o->ovar == &cbufs)
  328.                 ch_init(cbufs, 1);
  329.  
  330.             sprintf(message, o->odesc[0], 
  331.                 (o->ovar == &back_scroll) ? 
  332.                 get_back_scroll() : *(o->ovar));
  333.             msg = message;
  334.         } else
  335.             continue;
  336.  
  337.         /*
  338.          * Print a message describing the new setting.
  339.          */
  340.         if (msg == NULL)
  341.             msg = o->odesc[*(o->ovar)];
  342.         error(msg);
  343.  
  344.         if (do_toggle && dorepaint)
  345.             repaint();
  346.         return;
  347.     }
  348.  
  349.     if (control_char(c))
  350.         sprintf(message, "-^%c", carat_char(c));
  351.     else
  352.         sprintf(message, "-%c", c);
  353.     strcat(message, ": no such flag.");
  354.     error(message);
  355. }
  356.  
  357. /*
  358.  * Determine if an option is a single character option (BOOL or TRIPLE),
  359.  * or if it a multi-character option (NUMBER).
  360.  */
  361.     public int
  362. single_char_option(c)
  363.     int c;
  364. {
  365.     register struct option *o;
  366.  
  367.     if (c == 'P')
  368.         return (0);
  369. #if TAGS
  370.     if (c == 't')
  371.         return (0);
  372. #endif
  373. #if LOGFILE
  374.     if (c == 'l' || c == 'L')
  375.         return (0);
  376. #endif
  377.     for (o = option;  o->oletter != '\0';  o++)
  378.         if (o->oletter == c)
  379.             return (o->otype & (BOOL|TRIPLE));
  380.     return (1);
  381. }
  382.  
  383. /*
  384.  * Scan to end of string or to an END_OPTION_STRING character.
  385.  * In the latter case, replace the char with a null char.
  386.  * Return a pointer to the remainder of the string, if any.
  387.  */
  388.     static char *
  389. optstring(s, c)
  390.     char *s;
  391.     int c;
  392. {
  393.     register char *p;
  394.     char message[80];
  395.  
  396.     if (*s == '\0')
  397.     {
  398.         sprintf(message, "string is required after -%c", c);
  399.         error(message);
  400.         exit(1);
  401.     }
  402.     for (p = s;  *p != '\0';  p++)
  403.         if (*p == END_OPTION_STRING)
  404.         {
  405.             *p = '\0';
  406.             return (p+1);
  407.         }
  408.     return (p);
  409. }
  410.  
  411. /* 
  412.  * Scan an argument (either from command line or from LESS environment 
  413.  * variable) and process it.
  414.  */
  415.     public void
  416. scan_option(s)
  417.     char *s;
  418. {
  419.     register struct option *o;
  420.     register int c;
  421.     int set_default;
  422.     char message[80];
  423.  
  424.     if (s == NULL)
  425.         return;
  426.  
  427.     set_default = 0;
  428.     next:
  429.     if (*s == '\0')
  430.         return;
  431.     switch (c = *s++)
  432.     {
  433.     case ' ':
  434.     case '\t':
  435.     case END_OPTION_STRING:
  436.         goto next;
  437.     case '-':
  438.         if (set_default = (*s == '+'))
  439.             s++;
  440.         goto next;
  441.     case '+':
  442.         plusoption = 1;
  443.         if (*s == '+')
  444.             every_first_cmd = save(++s);
  445.         first_cmd = s;
  446.         s = optstring(s, c);
  447.         goto next;
  448. #if LOGFILE
  449.     case 'L':
  450.         force_logfile = 1;
  451.         /* FALLTHRU */
  452.     case 'l':
  453.         namelogfile = s;
  454.         s = optstring(s, c);
  455.         goto next;
  456. #endif
  457. #if TAGS
  458.     case 't':
  459.     {
  460.         char *p;
  461.         tagoption = 1;
  462.         p = s;
  463.         s = optstring(s, c);
  464.         findtag(p);
  465.         goto next;
  466.     }
  467. #endif
  468.     case 'P':
  469.         s = opt_P(s);
  470.         goto next;
  471.     case '0':  case '1':  case '2':  case '3':  case '4':
  472.     case '5':  case '6':  case '7':  case '8':  case '9':
  473.         /*
  474.          * Handle special "more" compatibility form "-number"
  475.          * (instead of -znumber) to set the scrolling window size.
  476.          */
  477.         s--;
  478.         c = 'z';
  479.         break;
  480.     }
  481.  
  482.     for (o = option;  o->oletter != '\0';  o++)
  483.     {
  484.         if ((o->otype & BOOL) && (o->oletter == c))
  485.         {
  486.             if (set_default)
  487.                 *(o->ovar) = o->odefault;
  488.             else
  489.                 *(o->ovar) = ! o->odefault;
  490.             goto next;
  491.         } else if ((o->otype & TRIPLE) && (o->oletter == c))
  492.         {
  493.             if (set_default)
  494.                 *(o->ovar) = o->odefault;
  495.             else
  496.                 *(o->ovar) = (o->odefault == 1) ? 0 : 1;
  497.             goto next;
  498.         } else if ((o->otype & TRIPLE) && (toupper(o->oletter) == c))
  499.         {
  500.             if (set_default)
  501.                 *(o->ovar) = o->odefault;
  502.             else
  503.                 *(o->ovar) = (o->odefault == 2) ? 0 : 2;
  504.             goto next;
  505.         } else if ((o->otype & NUMBER) && (o->oletter == c))
  506.         {
  507.             *(o->ovar) = getnum(&s, c);
  508.             goto next;
  509.         }
  510.     }
  511.  
  512.     sprintf(message, "\"-%c\": invalid flag", c);
  513.     error(message);
  514.     exit(1);
  515. }
  516.  
  517. /*
  518.  * Special case for -P.
  519.  */
  520.     static char *
  521. opt_P(s)
  522.     register char *s;
  523. {
  524.     register char *es;
  525.     register char **proto;
  526.  
  527.     es = optstring(s, 'P');
  528.  
  529.     /*
  530.      * Figure out which prototype string should be changed.
  531.      */
  532.     switch (*s)
  533.     {
  534.     case 'm':  proto = &prproto[PR_MEDIUM];    s++;    break;
  535.     case 'M':  proto = &prproto[PR_LONG];    s++;    break;
  536.     case '=':  proto = &eqproto;        s++;    break;
  537.     default:   proto = &prproto[pr_type];        break;
  538.     }
  539.  
  540.     free(*proto);
  541.     *proto = save(s);
  542.  
  543.     return (es);
  544. }
  545.  
  546. /*
  547.  * Translate a string into a number.
  548.  * Like atoi(), but takes a pointer to a char *, and updates
  549.  * the char * to point after the translated number.
  550.  */
  551.     public int
  552. getnum(sp, c)
  553.     char **sp;
  554.     int c;
  555. {
  556.     register char *s;
  557.     register int n;
  558.     char message[80];
  559.  
  560.     s = *sp;
  561.     if (*s < '0' || *s > '9')
  562.     {
  563.         if (c == '\0')
  564.             return (-1);
  565.         sprintf(message, "number is required after -%c", c);
  566.         error(message);
  567.         exit(1);
  568.     }
  569.  
  570.     n = 0;
  571.     while (*s >= '0' && *s <= '9')
  572.         n = 10 * n + *s++ - '0';
  573.     *sp = s;
  574.     return (n);
  575. }
  576.