Added all of the MH sources, including RCS files, in
[mmh] / docs / historical / mh-6.8.5 / miscellany / less-177 / option.c
1 /*
2  * Process command line options.
3  *
4  * Each option is a single letter which controls a program variable.
5  * The options have defaults which may be changed via
6  * the command line option, toggled via the "-" command, 
7  * or queried via the "_" command.
8  */
9
10 #include "less.h"
11 #include "option.h"
12
13 static struct option *pendopt;
14 public int plusoption;
15
16 static char *propt();
17 static char *optstring();
18 static int flip_triple();
19
20 extern int screen_trashed;
21 extern char *every_first_cmd;
22
23 /* 
24  * Scan an argument (either from the command line or from the 
25  * LESS environment variable) and process it.
26  */
27         public void
28 scan_option(s)
29         char *s;
30 {
31         register struct option *o;
32         register int c;
33         char *str;
34         int set_default;
35         PARG parg;
36
37         if (s == NULL)
38                 return;
39
40         /*
41          * If we have a pending string-valued option, handle it now.
42          * This happens if the previous option was, for example, "-P"
43          * without a following string.  In that case, the current
44          * option is simply the string for the previous option.
45          */
46         if (pendopt != NULL)
47         {
48                 (*pendopt->ofunc)(INIT, s);
49                 pendopt = NULL;
50                 return;
51         }
52
53         set_default = 0;
54
55         while (*s != '\0')
56         {
57                 /*
58                  * Check some special cases first.
59                  */
60                 switch (c = *s++)
61                 {
62                 case ' ':
63                 case '\t':
64                 case END_OPTION_STRING:
65                         continue;
66                 case '-':
67                         /*
68                          * "-+" means set these options back to their defaults.
69                          * (They may have been set otherwise by previous 
70                          * options.)
71                          */
72                         if (set_default = (*s == '+'))
73                                 s++;
74                         continue;
75                 case '+':
76                         /*
77                          * An option prefixed by a "+" is ungotten, so 
78                          * that it is interpreted as less commands 
79                          * processed at the start of the first input file.
80                          * "++" means process the commands at the start of
81                          * EVERY input file.
82                          */
83                         plusoption = 1;
84                         if (*s == '+')
85                                 every_first_cmd = save(++s);
86                         else
87                                 ungetsc(s);
88                         s = optstring(s, c);
89                         continue;
90                 case '0':  case '1':  case '2':  case '3':  case '4':
91                 case '5':  case '6':  case '7':  case '8':  case '9':
92                         /*
93                          * Special "more" compatibility form "-<number>"
94                          * instead of -z<number> to set the scrolling 
95                          * window size.
96                          */
97                         s--;
98                         c = 'z';
99                         break;
100                 }
101
102                 /*
103                  * Not a special case.
104                  * Look up the option letter in the option table.
105                  */
106                 o = findopt(c);
107                 if (o == NULL)
108                 {
109                         parg.p_string = propt(c);
110                         error("There is no %s flag (\"less -\\?\" for help)",
111                                 &parg);
112                         quit(1);
113                 }
114
115                 switch (o->otype & OTYPE)
116                 {
117                 case BOOL:
118                         if (set_default)
119                                 *(o->ovar) = o->odefault;
120                         else
121                                 *(o->ovar) = ! o->odefault;
122                         break;
123                 case TRIPLE:
124                         if (set_default)
125                                 *(o->ovar) = o->odefault;
126                         else
127                                 *(o->ovar) = flip_triple(o->odefault,
128                                                 (o->oletter == c));
129                         break;
130                 case STRING:
131                         if (*s == '\0')
132                         {
133                                 /*
134                                  * Set pendopt and return.
135                                  * We will get the string next time
136                                  * scan_option is called.
137                                  */
138                                 pendopt = o;
139                                 return;
140                         }
141                         /*
142                          * Don't do anything here.
143                          * All processing of STRING options is done by 
144                          * the handling function.
145                          */
146                         str = s;
147                         s = optstring(s, c);
148                         break;
149                 case NUMBER:
150                         *(o->ovar) = getnum(&s, c, (int*)NULL);
151                         break;
152                 }
153                 /*
154                  * If the option has a handling function, call it.
155                  */
156                 if (o->ofunc != NULL)
157                         (*o->ofunc)(INIT, str);
158         }
159 }
160
161 /*
162  * Toggle command line flags from within the program.
163  * Used by the "-" and "_" commands.
164  * how_toggle may be:
165  *      OPT_NO_TOGGLE   just report the current setting, without changing it.
166  *      OPT_TOGGLE      invert the current setting
167  *      OPT_UNSET       set to the default value
168  *      OPT_SET         set to the inverse of the default value
169  */
170         public void
171 toggle_option(c, s, how_toggle)
172         int c;
173         char *s;
174         int how_toggle;
175 {
176         register struct option *o;
177         register int num;
178         int err;
179         PARG parg;
180
181         /*
182          * Look up the option letter in the option table.
183          */
184         o = findopt(c);
185         if (o == NULL)
186         {
187                 parg.p_string = propt(c);
188                 error("There is no %s flag", &parg);
189                 return;
190         }
191
192         if (how_toggle == OPT_TOGGLE && (o->otype & NO_TOGGLE))
193         {
194                 parg.p_string = propt(c);
195                 error("Cannot change the %s flag", &parg);
196                 return;
197         } 
198
199         /*
200          * Check for something which appears to be a do_toggle
201          * (because the "-" command was used), but really is not.
202          * This could be a string option with no string, or
203          * a number option with no number.
204          */
205         switch (o->otype & OTYPE)
206         {
207         case STRING:
208         case NUMBER:
209                 if (how_toggle == OPT_TOGGLE && *s == '\0')
210                         how_toggle = OPT_NO_TOGGLE;
211                 break;
212         }
213
214         /*
215          * Now actually toggle (change) the variable.
216          */
217         if (how_toggle != OPT_NO_TOGGLE)
218         {
219                 switch (o->otype & OTYPE)
220                 {
221                 case BOOL:
222                         /*
223                          * Boolean.
224                          */
225                         switch (how_toggle)
226                         {
227                         case OPT_TOGGLE:
228                                 *(o->ovar) = ! *(o->ovar);
229                                 break;
230                         case OPT_UNSET:
231                                 *(o->ovar) = o->odefault;
232                                 break;
233                         case OPT_SET:
234                                 *(o->ovar) = ! o->odefault;
235                                 break;
236                         }
237                         break;
238                 case TRIPLE:
239                         /*
240                          * Triple:
241                          *      If user gave the lower case letter, then switch 
242                          *      to 1 unless already 1, in which case make it 0.
243                          *      If user gave the upper case letter, then switch
244                          *      to 2 unless already 2, in which case make it 0.
245                          */
246                         switch (how_toggle)
247                         {
248                         case OPT_TOGGLE:
249                                 *(o->ovar) = flip_triple(*(o->ovar), 
250                                                 o->oletter == c);
251                                 break;
252                         case OPT_UNSET:
253                                 *(o->ovar) = o->odefault;
254                                 break;
255                         case OPT_SET:
256                                 *(o->ovar) = flip_triple(o->odefault,
257                                                 o->oletter == c);
258                                 break;
259                         }
260                         break;
261                 case STRING:
262                         /*
263                          * String: don't do anything here.
264                          *      The handling function will do everything.
265                          */
266                         switch (how_toggle)
267                         {
268                         case OPT_SET:
269                         case OPT_UNSET:
270                                 error("Can't use \"-+\" or \"--\" for a string flag",
271                                         NULL_PARG);
272                                 return;
273                         }
274                         break;
275                 case NUMBER:
276                         /*
277                          * Number: set the variable to the given number.
278                          */
279                         switch (how_toggle)
280                         {
281                         case OPT_TOGGLE:
282                                 num = getnum(&s, '\0', &err);
283                                 if (!err)
284                                         *(o->ovar) = num;
285                                 break;
286                         case OPT_UNSET:
287                                 *(o->ovar) = o->odefault;
288                                 break;
289                         case OPT_SET:
290                                 error("Can't use \"--\" for a numeric flag",
291                                         NULL_PARG);
292                                 return;
293                         }
294                         break;
295                 }
296         }
297
298         /*
299          * Call the handling function for any special action 
300          * specific to this option.
301          */
302         if (o->ofunc != NULL)
303                 (*o->ofunc)((how_toggle==OPT_NO_TOGGLE) ? QUERY : TOGGLE, s);
304
305         /*
306          * Print a message describing the new setting.
307          */
308         switch (o->otype & OTYPE)
309         {
310         case BOOL:
311         case TRIPLE:
312                 /*
313                  * Print the odesc message.
314                  */
315                 error(o->odesc[*(o->ovar)], NULL_PARG);
316                 break;
317         case NUMBER:
318                 /*
319                  * The message is in odesc[1] and has a %d for 
320                  * the value of the variable.
321                  */
322                 parg.p_int = *(o->ovar);
323                 error(o->odesc[1], &parg);
324                 break;
325         case STRING:
326                 /*
327                  * Message was already printed by the handling function.
328                  */
329                 break;
330         }
331
332         if (how_toggle != OPT_NO_TOGGLE && (o->otype & REPAINT))
333                 screen_trashed = 1;
334 }
335
336 /*
337  * "Toggle" a triple-valued option.
338  */
339         static int
340 flip_triple(val, lc)
341         int val;
342         int lc;
343 {
344         if (lc)
345                 return ((val == 1) ? 0 : 1);
346         else
347                 return ((val == 2) ? 0 : 2);
348 }
349
350 /*
351  * Return a string suitable for printing as the "name" of an option.
352  * For example, if the option letter is 'x', just return "-x".
353  */
354         static char *
355 propt(c)
356         int c;
357 {
358         static char buf[8];
359
360         sprintf(buf, "-%s", prchar(c));
361         return (buf);
362 }
363
364 /*
365  * Determine if an option is a single character option (BOOL or TRIPLE),
366  * or if it a multi-character option (NUMBER).
367  */
368         public int
369 single_char_option(c)
370         int c;
371 {
372         register struct option *o;
373
374         o = findopt(c);
375         if (o == NULL)
376                 return (1);
377         return (o->otype & (BOOL|TRIPLE|NOVAR|NO_TOGGLE));
378 }
379
380 /*
381  * Return the prompt to be used for a given option letter.
382  * Only string and number valued options have prompts.
383  */
384         public char *
385 opt_prompt(c)
386         int c;
387 {
388         register struct option *o;
389
390         o = findopt(c);
391         if (o == NULL || (o->otype & (STRING|NUMBER)) == 0)
392                 return (NULL);
393         return (o->odesc[0]);
394 }
395
396 /*
397  * Return whether or not there is a string option pending;
398  * that is, if the previous option was a string-valued option letter 
399  * (like -P) without a following string.
400  * In that case, the current option is taken to be the string for
401  * the previous option.
402  */
403         public int
404 isoptpending()
405 {
406         return (pendopt != NULL);
407 }
408
409 /*
410  * Print error message about missing string.
411  */
412         static void
413 nostring(c)
414         int c;
415 {
416         PARG parg;
417         parg.p_string = propt(c);
418         error("String is required after %s", &parg);
419 }
420
421 /*
422  * Print error message if a STRING type option is not followed by a string.
423  */
424         public void
425 nopendopt()
426 {
427         nostring(pendopt->oletter);
428 }
429
430 /*
431  * Scan to end of string or to an END_OPTION_STRING character.
432  * In the latter case, replace the char with a null char.
433  * Return a pointer to the remainder of the string, if any.
434  */
435         static char *
436 optstring(s, c)
437         char *s;
438         int c;
439 {
440         register char *p;
441
442         if (*s == '\0')
443         {
444                 nostring(c);
445                 quit(1);
446         }
447         for (p = s;  *p != '\0';  p++)
448                 if (*p == END_OPTION_STRING)
449                 {
450                         *p = '\0';
451                         return (p+1);
452                 }
453         return (p);
454 }
455
456 /*
457  * Translate a string into a number.
458  * Like atoi(), but takes a pointer to a char *, and updates
459  * the char * to point after the translated number.
460  */
461         public int
462 getnum(sp, c, errp)
463         char **sp;
464         int c;
465         int *errp;
466 {
467         register char *s;
468         register int n;
469         register int neg;
470         PARG parg;
471
472         s = skipsp(*sp);
473         neg = 0;
474         if (*s == '-')
475         {
476                 neg = 1;
477                 s++;
478         }
479         if (*s < '0' || *s > '9')
480         {
481                 if (errp != NULL)
482                 {
483                         *errp = 1;
484                         return (-1);
485                 }
486                 parg.p_string = propt(c);
487                 error("Number is required after %s", &parg);
488                 quit(1);
489         }
490
491         n = 0;
492         while (*s >= '0' && *s <= '9')
493                 n = 10 * n + *s++ - '0';
494         *sp = s;
495         if (errp != NULL)
496                 *errp = 0;
497         if (neg)
498                 n = -n;
499         return (n);
500 }