home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 6 File / 06-File.zip / less373.zip / option.c < prev    next >
C/C++ Source or Header  |  2002-01-14  |  12KB  |  629 lines

  1. /*
  2.  * Copyright (C) 1984-2000  Mark Nudelman
  3.  *
  4.  * You may distribute under the terms of either the GNU General Public
  5.  * License or the Less License, as specified in the README file.
  6.  *
  7.  * For more information about less, or for information on how to 
  8.  * contact the author, see the README file.
  9.  */
  10.  
  11.  
  12. /*
  13.  * Process command line options.
  14.  *
  15.  * Each option is a single letter which controls a program variable.
  16.  * The options have defaults which may be changed via
  17.  * the command line option, toggled via the "-" command, 
  18.  * or queried via the "_" command.
  19.  */
  20.  
  21. #include "less.h"
  22. #include "option.h"
  23.  
  24. static struct option *pendopt;
  25. public int plusoption = FALSE;
  26.  
  27. static char *propt();
  28. static char *optstring();
  29. static int flip_triple();
  30.  
  31. extern int screen_trashed;
  32. extern char *every_first_cmd;
  33.  
  34. /* 
  35.  * Scan an argument (either from the command line or from the 
  36.  * LESS environment variable) and process it.
  37.  */
  38.     public void
  39. scan_option(s)
  40.     char *s;
  41. {
  42.     register struct option *o;
  43.     register int optc;
  44.     char *optname;
  45.     char *printopt;
  46.     char *str;
  47.     int set_default;
  48.     int lc;
  49.     int err;
  50.     PARG parg;
  51.  
  52.     if (s == NULL)
  53.         return;
  54.  
  55.     /*
  56.      * If we have a pending option which requires an argument,
  57.      * handle it now.
  58.      * This happens if the previous option was, for example, "-P"
  59.      * without a following string.  In that case, the current
  60.      * option is simply the argument for the previous option.
  61.      */
  62.     if (pendopt != NULL)
  63.     {
  64.         switch (pendopt->otype & OTYPE)
  65.         {
  66.         case STRING:
  67.             (*pendopt->ofunc)(INIT, s);
  68.             break;
  69.         case NUMBER:
  70.             printopt = propt(pendopt->oletter);
  71.             *(pendopt->ovar) = getnum(&s, printopt, (int*)NULL);
  72.             break;
  73.         }
  74.         pendopt = NULL;
  75.         return;
  76.     }
  77.  
  78.     set_default = FALSE;
  79.     optname = NULL;
  80.  
  81.     while (*s != '\0')
  82.     {
  83.         /*
  84.          * Check some special cases first.
  85.          */
  86.         switch (optc = *s++)
  87.         {
  88.         case ' ':
  89.         case '\t':
  90.         case END_OPTION_STRING:
  91.             continue;
  92.         case '-':
  93.             /*
  94.              * "--" indicates an option name instead of a letter.
  95.              */
  96.             if (*s == '-')
  97.             {
  98.                 optname = ++s;
  99.                 break;
  100.             }
  101.             /*
  102.              * "-+" means set these options back to their defaults.
  103.              * (They may have been set otherwise by previous 
  104.              * options.)
  105.              */
  106.             set_default = (*s == '+');
  107.             if (set_default)
  108.                 s++;
  109.             continue;
  110.         case '+':
  111.             /*
  112.              * An option prefixed by a "+" is ungotten, so 
  113.              * that it is interpreted as less commands 
  114.              * processed at the start of the first input file.
  115.              * "++" means process the commands at the start of
  116.              * EVERY input file.
  117.              */
  118.             plusoption = TRUE;
  119.             s = optstring(s, &str, propt('+'), NULL);
  120.             if (*str == '+')
  121.                 every_first_cmd = save(++str);
  122.             else
  123.                 ungetsc(str);
  124.             continue;
  125.         case '0':  case '1':  case '2':  case '3':  case '4':
  126.         case '5':  case '6':  case '7':  case '8':  case '9':
  127.             /*
  128.              * Special "more" compatibility form "-<number>"
  129.              * instead of -z<number> to set the scrolling 
  130.              * window size.
  131.              */
  132.             s--;
  133.             optc = 'z';
  134.             break;
  135.         }
  136.  
  137.         /*
  138.          * Not a special case.
  139.          * Look up the option letter in the option table.
  140.          */
  141.         err = 0;
  142.         if (optname == NULL)
  143.         {
  144.             printopt = propt(optc);
  145.             lc = SIMPLE_IS_LOWER(optc);
  146.             o = findopt(optc);
  147.         } else
  148.         {
  149.             printopt = optname;
  150.             lc = SIMPLE_IS_LOWER(optname[0]);
  151.             o = findopt_name(&optname, NULL, &err);
  152.             s = optname;
  153.             optname = NULL;
  154.             if (*s == '\0' || *s == ' ')
  155.             {
  156.                 /*
  157.                  * The option name matches exactly.
  158.                  */
  159.                 ;
  160.             } else if (*s == '=')
  161.             {
  162.                 /*
  163.                  * The option name is followed by "=value".
  164.                  */
  165.                 if (o != NULL &&
  166.                     (o->otype & OTYPE) != STRING &&
  167.                     (o->otype & OTYPE) != NUMBER)
  168.                 {
  169.                     parg.p_string = printopt;
  170.                     error("The %s option should not be followed by =",
  171.                         &parg);
  172.                     quit(QUIT_ERROR);
  173.                 }
  174.                 s++;
  175.             } else
  176.             {
  177.                 /*
  178.                  * The specified name is longer than the
  179.                  * real option name.
  180.                  */
  181.                 o = NULL;
  182.             }
  183.         }
  184.         if (o == NULL)
  185.         {
  186.             parg.p_string = printopt;
  187.             if (err == OPT_AMBIG)
  188.                 error("%s is an ambiguous abbreviation (\"less --help\" for help)",
  189.                     &parg);
  190.             else
  191.                 error("There is no %s option (\"less --help\" for help)",
  192.                     &parg);
  193.             quit(QUIT_ERROR);
  194.         }
  195.  
  196.         str = NULL;
  197.         switch (o->otype & OTYPE)
  198.         {
  199.         case BOOL:
  200.             if (set_default)
  201.                 *(o->ovar) = o->odefault;
  202.             else
  203.                 *(o->ovar) = ! o->odefault;
  204.             break;
  205.         case TRIPLE:
  206.             if (set_default)
  207.                 *(o->ovar) = o->odefault;
  208.             else
  209.                 *(o->ovar) = flip_triple(o->odefault, lc);
  210.             break;
  211.         case STRING:
  212.             if (*s == '\0')
  213.             {
  214.                 /*
  215.                  * Set pendopt and return.
  216.                  * We will get the string next time
  217.                  * scan_option is called.
  218.                  */
  219.                 pendopt = o;
  220.                 return;
  221.             }
  222.             /*
  223.              * Don't do anything here.
  224.              * All processing of STRING options is done by 
  225.              * the handling function.
  226.              */
  227.             s = optstring(s, &str, printopt, o->odesc[1]);
  228.             break;
  229.         case NUMBER:
  230.             if (*s == '\0')
  231.             {
  232.                 pendopt = o;
  233.                 return;
  234.             }
  235.             *(o->ovar) = getnum(&s, printopt, (int*)NULL);
  236.             break;
  237.         }
  238.         /*
  239.          * If the option has a handling function, call it.
  240.          */
  241.         if (o->ofunc != NULL)
  242.             (*o->ofunc)(INIT, str);
  243.     }
  244. }
  245.  
  246. /*
  247.  * Toggle command line flags from within the program.
  248.  * Used by the "-" and "_" commands.
  249.  * how_toggle may be:
  250.  *    OPT_NO_TOGGLE    just report the current setting, without changing it.
  251.  *    OPT_TOGGLE    invert the current setting
  252.  *    OPT_UNSET    set to the default value
  253.  *    OPT_SET        set to the inverse of the default value
  254.  */
  255.     public void
  256. toggle_option(c, s, how_toggle)
  257.     int c;
  258.     char *s;
  259.     int how_toggle;
  260. {
  261.     register struct option *o;
  262.     register int num;
  263.     int no_prompt;
  264.     int err;
  265.     PARG parg;
  266.  
  267.     no_prompt = (how_toggle & OPT_NO_PROMPT);
  268.     how_toggle &= ~OPT_NO_PROMPT;
  269.  
  270.     /*
  271.      * Look up the option letter in the option table.
  272.      */
  273.     o = findopt(c);
  274.     if (o == NULL)
  275.     {
  276.         parg.p_string = propt(c);
  277.         error("There is no %s option", &parg);
  278.         return;
  279.     }
  280.  
  281.     if (how_toggle == OPT_TOGGLE && (o->otype & NO_TOGGLE))
  282.     {
  283.         parg.p_string = propt(c);
  284.         error("Cannot change the %s option", &parg);
  285.         return;
  286.     } 
  287.  
  288.     if (how_toggle == OPT_NO_TOGGLE && (o->otype & NO_QUERY))
  289.     {
  290.         parg.p_string = propt(c);
  291.         error("Cannot query the %s option", &parg);
  292.         return;
  293.     } 
  294.  
  295.     /*
  296.      * Check for something which appears to be a do_toggle
  297.      * (because the "-" command was used), but really is not.
  298.      * This could be a string option with no string, or
  299.      * a number option with no number.
  300.      */
  301.     switch (o->otype & OTYPE)
  302.     {
  303.     case STRING:
  304.     case NUMBER:
  305.         if (how_toggle == OPT_TOGGLE && *s == '\0')
  306.             how_toggle = OPT_NO_TOGGLE;
  307.         break;
  308.     }
  309.  
  310. #if HILITE_SEARCH
  311.     if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT))
  312.         repaint_hilite(0);
  313. #endif
  314.  
  315.     /*
  316.      * Now actually toggle (change) the variable.
  317.      */
  318.     if (how_toggle != OPT_NO_TOGGLE)
  319.     {
  320.         switch (o->otype & OTYPE)
  321.         {
  322.         case BOOL:
  323.             /*
  324.              * Boolean.
  325.              */
  326.             switch (how_toggle)
  327.             {
  328.             case OPT_TOGGLE:
  329.                 *(o->ovar) = ! *(o->ovar);
  330.                 break;
  331.             case OPT_UNSET:
  332.                 *(o->ovar) = o->odefault;
  333.                 break;
  334.             case OPT_SET:
  335.                 *(o->ovar) = ! o->odefault;
  336.                 break;
  337.             }
  338.             break;
  339.         case TRIPLE:
  340.             /*
  341.              * Triple:
  342.              *    If user gave the lower case letter, then switch 
  343.              *    to 1 unless already 1, in which case make it 0.
  344.              *    If user gave the upper case letter, then switch
  345.              *    to 2 unless already 2, in which case make it 0.
  346.              */
  347.             switch (how_toggle)
  348.             {
  349.             case OPT_TOGGLE:
  350.                 *(o->ovar) = flip_triple(*(o->ovar), 
  351.                         islower(c));
  352.                 break;
  353.             case OPT_UNSET:
  354.                 *(o->ovar) = o->odefault;
  355.                 break;
  356.             case OPT_SET:
  357.                 *(o->ovar) = flip_triple(o->odefault,
  358.                         islower(c));
  359.                 break;
  360.             }
  361.             break;
  362.         case STRING:
  363.             /*
  364.              * String: don't do anything here.
  365.              *    The handling function will do everything.
  366.              */
  367.             switch (how_toggle)
  368.             {
  369.             case OPT_SET:
  370.             case OPT_UNSET:
  371.                 error("Cannot use \"-+\" or \"--\" for a string option",
  372.                     NULL_PARG);
  373.                 return;
  374.             }
  375.             break;
  376.         case NUMBER:
  377.             /*
  378.              * Number: set the variable to the given number.
  379.              */
  380.             switch (how_toggle)
  381.             {
  382.             case OPT_TOGGLE:
  383.                 num = getnum(&s, '\0', &err);
  384.                 if (!err)
  385.                     *(o->ovar) = num;
  386.                 break;
  387.             case OPT_UNSET:
  388.                 *(o->ovar) = o->odefault;
  389.                 break;
  390.             case OPT_SET:
  391.                 error("Can't use \"-!\" for a numeric option",
  392.                     NULL_PARG);
  393.                 return;
  394.             }
  395.             break;
  396.         }
  397.     }
  398.  
  399.     /*
  400.      * Call the handling function for any special action 
  401.      * specific to this option.
  402.      */
  403.     if (o->ofunc != NULL)
  404.         (*o->ofunc)((how_toggle==OPT_NO_TOGGLE) ? QUERY : TOGGLE, s);
  405.  
  406. #if HILITE_SEARCH
  407.     if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT))
  408.         chg_hilite();
  409. #endif
  410.  
  411.     if (!no_prompt)
  412.     {
  413.         /*
  414.          * Print a message describing the new setting.
  415.          */
  416.         switch (o->otype & OTYPE)
  417.         {
  418.         case BOOL:
  419.         case TRIPLE:
  420.             /*
  421.              * Print the odesc message.
  422.              */
  423.             error(o->odesc[*(o->ovar)], NULL_PARG);
  424.             break;
  425.         case NUMBER:
  426.             /*
  427.              * The message is in odesc[1] and has a %d for 
  428.              * the value of the variable.
  429.              */
  430.             parg.p_int = *(o->ovar);
  431.             error(o->odesc[1], &parg);
  432.             break;
  433.         case STRING:
  434.             /*
  435.              * Message was already printed by the handling function.
  436.              */
  437.             break;
  438.         }
  439.     }
  440.  
  441.     if (how_toggle != OPT_NO_TOGGLE && (o->otype & REPAINT))
  442.         screen_trashed = TRUE;
  443. }
  444.  
  445. /*
  446.  * "Toggle" a triple-valued option.
  447.  */
  448.     static int
  449. flip_triple(val, lc)
  450.     int val;
  451.     int lc;
  452. {
  453.     if (lc)
  454.         return ((val == OPT_ON) ? OPT_OFF : OPT_ON);
  455.     else
  456.         return ((val == OPT_ONPLUS) ? OPT_OFF : OPT_ONPLUS);
  457. }
  458.  
  459. /*
  460.  * Return a string suitable for printing as the "name" of an option.
  461.  * For example, if the option letter is 'x', just return "-x".
  462.  */
  463.     static char *
  464. propt(c)
  465.     int c;
  466. {
  467.     static char buf[8];
  468.  
  469.     sprintf(buf, "-%s", prchar(c));
  470.     return (buf);
  471. }
  472.  
  473. /*
  474.  * Determine if an option is a single character option (BOOL or TRIPLE),
  475.  * or if it a multi-character option (NUMBER).
  476.  */
  477.     public int
  478. single_char_option(c)
  479.     int c;
  480. {
  481.     register struct option *o;
  482.  
  483.     o = findopt(c);
  484.     if (o == NULL)
  485.         return (TRUE);
  486.     return ((o->otype & (BOOL|TRIPLE|NOVAR|NO_TOGGLE)) != 0);
  487. }
  488.  
  489. /*
  490.  * Return the prompt to be used for a given option letter.
  491.  * Only string and number valued options have prompts.
  492.  */
  493.     public char *
  494. opt_prompt(c)
  495.     int c;
  496. {
  497.     register struct option *o;
  498.  
  499.     o = findopt(c);
  500.     if (o == NULL || (o->otype & (STRING|NUMBER)) == 0)
  501.         return (NULL);
  502.     return (o->odesc[0]);
  503. }
  504.  
  505. /*
  506.  * Return whether or not there is a string option pending;
  507.  * that is, if the previous option was a string-valued option letter 
  508.  * (like -P) without a following string.
  509.  * In that case, the current option is taken to be the string for
  510.  * the previous option.
  511.  */
  512.     public int
  513. isoptpending()
  514. {
  515.     return (pendopt != NULL);
  516. }
  517.  
  518. /*
  519.  * Print error message about missing string.
  520.  */
  521.     static void
  522. nostring(printopt)
  523.     char *printopt;
  524. {
  525.     PARG parg;
  526.     parg.p_string = printopt;
  527.     error("Value is required after %s", &parg);
  528. }
  529.  
  530. /*
  531.  * Print error message if a STRING type option is not followed by a string.
  532.  */
  533.     public void
  534. nopendopt()
  535. {
  536.     nostring(propt(pendopt->oletter));
  537. }
  538.  
  539. /*
  540.  * Scan to end of string or to an END_OPTION_STRING character.
  541.  * In the latter case, replace the char with a null char.
  542.  * Return a pointer to the remainder of the string, if any.
  543.  */
  544.     static char *
  545. optstring(s, p_str, printopt, validchars)
  546.     char *s;
  547.     char **p_str;
  548.     char *printopt;
  549.     char *validchars;
  550. {
  551.     register char *p;
  552.  
  553.     if (*s == '\0')
  554.     {
  555.         nostring(printopt);
  556.         quit(QUIT_ERROR);
  557.     }
  558.     *p_str = s;
  559.     for (p = s;  *p != '\0';  p++)
  560.     {
  561.         if (*p == END_OPTION_STRING ||
  562.             (validchars != NULL && strchr(validchars, *p) == NULL))
  563.         {
  564.             switch (*p)
  565.             {
  566.             case END_OPTION_STRING:
  567.             case ' ':  case '\t':  case '-':
  568.                 /* Replace the char with a null to terminate string. */
  569.                 *p++ = '\0';
  570.                 break;
  571.             default:
  572.                 /* Cannot replace char; make a copy of the string. */
  573.                 *p_str = (char *) ecalloc(p-s+1, sizeof(char));
  574.                 strncpy(*p_str, s, p-s);
  575.                 (*p_str)[p-s] = '\0';
  576.                 break;
  577.             }
  578.             break;
  579.         }
  580.     }
  581.     return (p);
  582. }
  583.  
  584. /*
  585.  * Translate a string into a number.
  586.  * Like atoi(), but takes a pointer to a char *, and updates
  587.  * the char * to point after the translated number.
  588.  */
  589.     public int
  590. getnum(sp, printopt, errp)
  591.     char **sp;
  592.     char *printopt;
  593.     int *errp;
  594. {
  595.     register char *s;
  596.     register int n;
  597.     register int neg;
  598.     PARG parg;
  599.  
  600.     s = skipsp(*sp);
  601.     neg = FALSE;
  602.     if (*s == '-')
  603.     {
  604.         neg = TRUE;
  605.         s++;
  606.     }
  607.     if (*s < '0' || *s > '9')
  608.     {
  609.         if (errp != NULL)
  610.         {
  611.             *errp = TRUE;
  612.             return (-1);
  613.         }
  614.         parg.p_string = printopt;
  615.         error("Number is required after %s", &parg);
  616.         quit(QUIT_ERROR);
  617.     }
  618.  
  619.     n = 0;
  620.     while (*s >= '0' && *s <= '9')
  621.         n = 10 * n + *s++ - '0';
  622.     *sp = s;
  623.     if (errp != NULL)
  624.         *errp = FALSE;
  625.     if (neg)
  626.         n = -n;
  627.     return (n);
  628. }
  629.