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