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