]> diplodocus.org Git - nmh/blob - docs/historical/mh-6.8.5/miscellany/less-177/option.c
ap: Fix write past end of addrs[] array.
[nmh] / 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 }