Update to less-418:
[dragonfly.git] / contrib / less-394 / option.c
1 /*
2  * Copyright (C) 1984-2004  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 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 loption *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 = ASCII_IS_LOWER(optc);
146                         o = findopt(optc);
147                 } else
148                 {
149                         printopt = optname;
150                         lc = ASCII_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                         while (*s == ' ')
228                                 s++;
229                         s = optstring(s, &str, printopt, o->odesc[1]);
230                         break;
231                 case NUMBER:
232                         if (*s == '\0')
233                         {
234                                 pendopt = o;
235                                 return;
236                         }
237                         *(o->ovar) = getnum(&s, printopt, (int*)NULL);
238                         break;
239                 }
240                 /*
241                  * If the option has a handling function, call it.
242                  */
243                 if (o->ofunc != NULL)
244                         (*o->ofunc)(INIT, str);
245         }
246 }
247
248 /*
249  * Toggle command line flags from within the program.
250  * Used by the "-" and "_" commands.
251  * how_toggle may be:
252  *      OPT_NO_TOGGLE   just report the current setting, without changing it.
253  *      OPT_TOGGLE      invert the current setting
254  *      OPT_UNSET       set to the default value
255  *      OPT_SET         set to the inverse of the default value
256  */
257         public void
258 toggle_option(c, s, how_toggle)
259         int c;
260         char *s;
261         int how_toggle;
262 {
263         register struct loption *o;
264         register int num;
265         int no_prompt;
266         int err;
267         PARG parg;
268
269         no_prompt = (how_toggle & OPT_NO_PROMPT);
270         how_toggle &= ~OPT_NO_PROMPT;
271
272         /*
273          * Look up the option letter in the option table.
274          */
275         o = findopt(c);
276         if (o == NULL)
277         {
278                 parg.p_string = propt(c);
279                 error("There is no %s option", &parg);
280                 return;
281         }
282
283         if (how_toggle == OPT_TOGGLE && (o->otype & NO_TOGGLE))
284         {
285                 parg.p_string = propt(c);
286                 error("Cannot change the %s option", &parg);
287                 return;
288         } 
289
290         if (how_toggle == OPT_NO_TOGGLE && (o->otype & NO_QUERY))
291         {
292                 parg.p_string = propt(c);
293                 error("Cannot query the %s option", &parg);
294                 return;
295         } 
296
297         /*
298          * Check for something which appears to be a do_toggle
299          * (because the "-" command was used), but really is not.
300          * This could be a string option with no string, or
301          * a number option with no number.
302          */
303         switch (o->otype & OTYPE)
304         {
305         case STRING:
306         case NUMBER:
307                 if (how_toggle == OPT_TOGGLE && *s == '\0')
308                         how_toggle = OPT_NO_TOGGLE;
309                 break;
310         }
311
312 #if HILITE_SEARCH
313         if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT))
314                 repaint_hilite(0);
315 #endif
316
317         /*
318          * Now actually toggle (change) the variable.
319          */
320         if (how_toggle != OPT_NO_TOGGLE)
321         {
322                 switch (o->otype & OTYPE)
323                 {
324                 case BOOL:
325                         /*
326                          * Boolean.
327                          */
328                         switch (how_toggle)
329                         {
330                         case OPT_TOGGLE:
331                                 *(o->ovar) = ! *(o->ovar);
332                                 break;
333                         case OPT_UNSET:
334                                 *(o->ovar) = o->odefault;
335                                 break;
336                         case OPT_SET:
337                                 *(o->ovar) = ! o->odefault;
338                                 break;
339                         }
340                         break;
341                 case TRIPLE:
342                         /*
343                          * Triple:
344                          *      If user gave the lower case letter, then switch 
345                          *      to 1 unless already 1, in which case make it 0.
346                          *      If user gave the upper case letter, then switch
347                          *      to 2 unless already 2, in which case make it 0.
348                          */
349                         switch (how_toggle)
350                         {
351                         case OPT_TOGGLE:
352                                 *(o->ovar) = flip_triple(*(o->ovar), 
353                                                 ASCII_IS_LOWER(c));
354                                 break;
355                         case OPT_UNSET:
356                                 *(o->ovar) = o->odefault;
357                                 break;
358                         case OPT_SET:
359                                 *(o->ovar) = flip_triple(o->odefault,
360                                                 ASCII_IS_LOWER(c));
361                                 break;
362                         }
363                         break;
364                 case STRING:
365                         /*
366                          * String: don't do anything here.
367                          *      The handling function will do everything.
368                          */
369                         switch (how_toggle)
370                         {
371                         case OPT_SET:
372                         case OPT_UNSET:
373                                 error("Cannot use \"-+\" or \"--\" for a string option",
374                                         NULL_PARG);
375                                 return;
376                         }
377                         break;
378                 case NUMBER:
379                         /*
380                          * Number: set the variable to the given number.
381                          */
382                         switch (how_toggle)
383                         {
384                         case OPT_TOGGLE:
385                                 num = getnum(&s, NULL, &err);
386                                 if (!err)
387                                         *(o->ovar) = num;
388                                 break;
389                         case OPT_UNSET:
390                                 *(o->ovar) = o->odefault;
391                                 break;
392                         case OPT_SET:
393                                 error("Can't use \"-!\" for a numeric option",
394                                         NULL_PARG);
395                                 return;
396                         }
397                         break;
398                 }
399         }
400
401         /*
402          * Call the handling function for any special action 
403          * specific to this option.
404          */
405         if (o->ofunc != NULL)
406                 (*o->ofunc)((how_toggle==OPT_NO_TOGGLE) ? QUERY : TOGGLE, s);
407
408 #if HILITE_SEARCH
409         if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT))
410                 chg_hilite();
411 #endif
412
413         if (!no_prompt)
414         {
415                 /*
416                  * Print a message describing the new setting.
417                  */
418                 switch (o->otype & OTYPE)
419                 {
420                 case BOOL:
421                 case TRIPLE:
422                         /*
423                          * Print the odesc message.
424                          */
425                         error(o->odesc[*(o->ovar)], NULL_PARG);
426                         break;
427                 case NUMBER:
428                         /*
429                          * The message is in odesc[1] and has a %d for 
430                          * the value of the variable.
431                          */
432                         parg.p_int = *(o->ovar);
433                         error(o->odesc[1], &parg);
434                         break;
435                 case STRING:
436                         /*
437                          * Message was already printed by the handling function.
438                          */
439                         break;
440                 }
441         }
442
443         if (how_toggle != OPT_NO_TOGGLE && (o->otype & REPAINT))
444                 screen_trashed = TRUE;
445 }
446
447 /*
448  * "Toggle" a triple-valued option.
449  */
450         static int
451 flip_triple(val, lc)
452         int val;
453         int lc;
454 {
455         if (lc)
456                 return ((val == OPT_ON) ? OPT_OFF : OPT_ON);
457         else
458                 return ((val == OPT_ONPLUS) ? OPT_OFF : OPT_ONPLUS);
459 }
460
461 /*
462  * Return a string suitable for printing as the "name" of an option.
463  * For example, if the option letter is 'x', just return "-x".
464  */
465         static char *
466 propt(c)
467         int c;
468 {
469         static char buf[8];
470
471         sprintf(buf, "-%s", prchar(c));
472         return (buf);
473 }
474
475 /*
476  * Determine if an option is a single character option (BOOL or TRIPLE),
477  * or if it a multi-character option (NUMBER).
478  */
479         public int
480 single_char_option(c)
481         int c;
482 {
483         register struct loption *o;
484
485         o = findopt(c);
486         if (o == NULL)
487                 return (TRUE);
488         return ((o->otype & (BOOL|TRIPLE|NOVAR|NO_TOGGLE)) != 0);
489 }
490
491 /*
492  * Return the prompt to be used for a given option letter.
493  * Only string and number valued options have prompts.
494  */
495         public char *
496 opt_prompt(c)
497         int c;
498 {
499         register struct loption *o;
500
501         o = findopt(c);
502         if (o == NULL || (o->otype & (STRING|NUMBER)) == 0)
503                 return (NULL);
504         return (o->odesc[0]);
505 }
506
507 /*
508  * Return whether or not there is a string option pending;
509  * that is, if the previous option was a string-valued option letter 
510  * (like -P) without a following string.
511  * In that case, the current option is taken to be the string for
512  * the previous option.
513  */
514         public int
515 isoptpending()
516 {
517         return (pendopt != NULL);
518 }
519
520 /*
521  * Print error message about missing string.
522  */
523         static void
524 nostring(printopt)
525         char *printopt;
526 {
527         PARG parg;
528         parg.p_string = printopt;
529         error("Value is required after %s", &parg);
530 }
531
532 /*
533  * Print error message if a STRING type option is not followed by a string.
534  */
535         public void
536 nopendopt()
537 {
538         nostring(propt(pendopt->oletter));
539 }
540
541 /*
542  * Scan to end of string or to an END_OPTION_STRING character.
543  * In the latter case, replace the char with a null char.
544  * Return a pointer to the remainder of the string, if any.
545  */
546         static char *
547 optstring(s, p_str, printopt, validchars)
548         char *s;
549         char **p_str;
550         char *printopt;
551         char *validchars;
552 {
553         register char *p;
554
555         if (*s == '\0')
556         {
557                 nostring(printopt);
558                 quit(QUIT_ERROR);
559         }
560         *p_str = s;
561         for (p = s;  *p != '\0';  p++)
562         {
563                 if (*p == END_OPTION_STRING ||
564                     (validchars != NULL && strchr(validchars, *p) == NULL))
565                 {
566                         switch (*p)
567                         {
568                         case END_OPTION_STRING:
569                         case ' ':  case '\t':  case '-':
570                                 /* Replace the char with a null to terminate string. */
571                                 *p++ = '\0';
572                                 break;
573                         default:
574                                 /* Cannot replace char; make a copy of the string. */
575                                 *p_str = (char *) ecalloc(p-s+1, sizeof(char));
576                                 strncpy(*p_str, s, p-s);
577                                 (*p_str)[p-s] = '\0';
578                                 break;
579                         }
580                         break;
581                 }
582         }
583         return (p);
584 }
585
586 /*
587  * Translate a string into a number.
588  * Like atoi(), but takes a pointer to a char *, and updates
589  * the char * to point after the translated number.
590  */
591         public int
592 getnum(sp, printopt, errp)
593         char **sp;
594         char *printopt;
595         int *errp;
596 {
597         register char *s;
598         register int n;
599         register int neg;
600         PARG parg;
601
602         s = skipsp(*sp);
603         neg = FALSE;
604         if (*s == '-')
605         {
606                 neg = TRUE;
607                 s++;
608         }
609         if (*s < '0' || *s > '9')
610         {
611                 if (errp != NULL)
612                 {
613                         *errp = TRUE;
614                         return (-1);
615                 }
616                 if (printopt != NULL)
617                 {
618                         parg.p_string = printopt;
619                         error("Number is required after %s", &parg);
620                 }
621                 quit(QUIT_ERROR);
622         }
623
624         n = 0;
625         while (*s >= '0' && *s <= '9')
626                 n = 10 * n + *s++ - '0';
627         *sp = s;
628         if (errp != NULL)
629                 *errp = FALSE;
630         if (neg)
631                 n = -n;
632         return (n);
633 }