Commit | Line | Data |
---|---|---|
bfe13c81 KB |
1 | /* |
2 | * Copyright (c) 1988 Mark Nudleman | |
3 | * Copyright (c) 1988 Regents of the University of California. | |
4 | * All rights reserved. | |
5 | * | |
6 | * This code is derived from software contributed to Berkeley by | |
7 | * Mark Nudleman. | |
8 | * | |
9 | * Redistribution and use in source and binary forms are permitted | |
10 | * provided that the above copyright notice and this paragraph are | |
11 | * duplicated in all such forms and that any documentation, | |
12 | * advertising materials, and other materials related to such | |
13 | * distribution and use acknowledge that the software was developed | |
14 | * by the University of California, Berkeley. The name of the | |
15 | * University may not be used to endorse or promote products derived | |
16 | * from this software without specific prior written permission. | |
17 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR | |
18 | * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED | |
19 | * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. | |
20 | */ | |
21 | ||
22 | #ifndef lint | |
23 | static char sccsid[] = "@(#)option.c 5.1 (Berkeley) %G%"; | |
24 | #endif /* not lint */ | |
25 | ||
26 | /* | |
27 | * Process command line options. | |
28 | * Each option is a single letter which controls a program variable. | |
29 | * The options have defaults which may be changed via | |
30 | * the command line option, or toggled via the "-" command. | |
31 | */ | |
32 | ||
33 | #include "less.h" | |
34 | ||
35 | #define toupper(c) ((c)-'a'+'A') | |
36 | ||
37 | #define END_OPTION_STRING ('$') | |
38 | ||
39 | /* | |
40 | * Types of options. | |
41 | */ | |
42 | #define BOOL 01 /* Boolean option: 0 or 1 */ | |
43 | #define TRIPLE 02 /* Triple-valued option: 0, 1 or 2 */ | |
44 | #define NUMBER 04 /* Numeric option */ | |
45 | #define REPAINT 040 /* Repaint screen after toggling option */ | |
46 | #define NO_TOGGLE 0100 /* Option cannot be toggled with "-" cmd */ | |
47 | ||
48 | /* | |
49 | * Variables controlled by command line options. | |
50 | */ | |
51 | public int clean_data; /* Can we assume the data is "clean"? | |
52 | (That is, free of nulls, etc) */ | |
53 | public int quiet; /* Should we suppress the audible bell? */ | |
54 | public int how_search; /* Where should forward searches start? */ | |
55 | public int top_scroll; /* Repaint screen from top? | |
56 | (alternative is scroll from bottom) */ | |
57 | public int pr_type; /* Type of prompt (short, medium, long) */ | |
58 | public int bs_mode; /* How to process backspaces */ | |
59 | public int know_dumb; /* Don't complain about dumb terminals */ | |
60 | public int quit_at_eof; /* Quit after hitting end of file twice */ | |
61 | public int squeeze; /* Squeeze multiple blank lines into one */ | |
62 | public int tabstop; /* Tab settings */ | |
63 | public int back_scroll; /* Repaint screen on backwards movement */ | |
64 | public int twiddle; /* Display "~" for lines after EOF */ | |
65 | public int caseless; /* Do "caseless" searches */ | |
66 | public int linenums; /* Use line numbers */ | |
67 | public int cbufs; /* Current number of buffers */ | |
68 | public int autobuf; | |
69 | public int plusoption; | |
70 | ||
71 | extern char *prproto[]; | |
72 | extern char *eqproto; | |
73 | extern int nbufs; | |
74 | extern int sc_window; | |
75 | extern int ispipe; | |
76 | extern char *first_cmd; | |
77 | extern char *every_first_cmd; | |
78 | #if LOGFILE | |
79 | extern char *namelogfile; | |
80 | extern int force_logfile; | |
81 | extern int logfile; | |
82 | #endif | |
83 | #if TAGS | |
84 | extern char *tagfile; | |
85 | extern char *tagpattern; | |
86 | public int tagoption = 0; | |
87 | #endif | |
88 | ||
89 | static char *opt_P(); | |
90 | ||
91 | static struct option | |
92 | { | |
93 | char oletter; /* The controlling letter (a-z) */ | |
94 | char otype; /* Type of the option */ | |
95 | int odefault; /* Default value */ | |
96 | int *ovar; /* Pointer to the associated variable */ | |
97 | char *odesc[3]; /* Description of each value */ | |
98 | } option[] = | |
99 | { | |
100 | { 'a', TRIPLE, 0, &how_search, | |
101 | { "Forward search starts at second REAL line displayed", | |
102 | "Forward search starts at bottom of screen", | |
103 | "Forward search starts at second SCREEN line displayed" | |
104 | } | |
105 | }, | |
106 | { 'b', NUMBER, 10, &cbufs, | |
107 | { "%d buffers", | |
108 | NULL, NULL | |
109 | } | |
110 | }, | |
111 | { 'B', BOOL, 1, &autobuf, | |
112 | { "Don't automatically allocate buffers", | |
113 | "Automatically allocate buffers when needed", | |
114 | NULL | |
115 | } | |
116 | }, | |
117 | { 'c', TRIPLE, 0, &top_scroll, | |
118 | { "Repaint by scrolling from bottom of screen", | |
119 | "Repaint by clearing each line", | |
120 | "Repaint by painting from top of screen" | |
121 | } | |
122 | }, | |
123 | { 'd', BOOL|NO_TOGGLE, 0, &know_dumb, | |
124 | { NULL, NULL, NULL} | |
125 | }, | |
126 | { 'e', TRIPLE, 0, &quit_at_eof, | |
127 | { "Don't quit at end-of-file", | |
128 | "Quit at end-of-file", | |
129 | "Quit immediately at end-of-file" | |
130 | } | |
131 | }, | |
132 | { 'h', NUMBER, -1, &back_scroll, | |
133 | { "Backwards scroll limit is %d lines", | |
134 | NULL, NULL | |
135 | } | |
136 | }, | |
137 | { 'i', BOOL, 0, &caseless, | |
138 | { "Case is significant in searches", | |
139 | "Ignore case in searches", | |
140 | NULL | |
141 | } | |
142 | }, | |
143 | { 'm', TRIPLE, 0, &pr_type, | |
144 | { "Short prompt", | |
145 | "Medium prompt", | |
146 | "Long prompt" | |
147 | } | |
148 | }, | |
149 | { 'n', BOOL, 1, &linenums, | |
150 | { "Don't use line numbers", | |
151 | "Use line numbers", | |
152 | NULL | |
153 | } | |
154 | }, | |
155 | { 'q', TRIPLE, 0, &quiet, | |
156 | { "Ring the bell for errors AND at eof/bof", | |
157 | "Ring the bell for errors but not at eof/bof", | |
158 | "Never ring the bell" | |
159 | } | |
160 | }, | |
161 | { 's', BOOL|REPAINT, 0, &squeeze, | |
162 | { "Don't squeeze multiple blank lines", | |
163 | "Squeeze multiple blank lines", | |
164 | NULL | |
165 | } | |
166 | }, | |
167 | { 'u', TRIPLE|REPAINT, 0, &bs_mode, | |
168 | { "Underlined text displayed in underline mode", | |
169 | "Backspaces cause overstrike", | |
170 | "Backspaces print as ^H" | |
171 | } | |
172 | }, | |
173 | { 'w', BOOL|REPAINT, 1, &twiddle, | |
174 | { "Display nothing for lines after end-of-file", | |
175 | "Display ~ for lines after end-of-file", | |
176 | NULL | |
177 | } | |
178 | }, | |
179 | { 'x', NUMBER|REPAINT, 8, &tabstop, | |
180 | { "Tab stops every %d spaces", | |
181 | NULL, NULL | |
182 | } | |
183 | }, | |
184 | { 'z', NUMBER|REPAINT, -1, &sc_window, | |
185 | { "Scroll window size is %d lines", | |
186 | NULL, NULL | |
187 | } | |
188 | }, | |
189 | { '\0' } | |
190 | }; | |
191 | ||
192 | /* | |
193 | * Initialize each option to its default value. | |
194 | */ | |
195 | public void | |
196 | init_option() | |
197 | { | |
198 | register struct option *o; | |
199 | ||
200 | first_cmd = every_first_cmd = NULL; | |
201 | ||
202 | for (o = option; o->oletter != '\0'; o++) | |
203 | { | |
204 | /* | |
205 | * Set each variable to its default. | |
206 | */ | |
207 | *(o->ovar) = o->odefault; | |
208 | } | |
209 | } | |
210 | ||
211 | /* | |
212 | * Toggle command line flags from within the program. | |
213 | * Used by the "-" and "_" commands. | |
214 | * If do_toggle is zero, just report the current setting, without changing it. | |
215 | */ | |
216 | public void | |
217 | toggle_option(s, do_toggle) | |
218 | char *s; | |
219 | int do_toggle; | |
220 | { | |
221 | int c; | |
222 | register struct option *o; | |
223 | char *msg; | |
224 | int n; | |
225 | int dorepaint; | |
226 | char message[100]; | |
227 | ||
228 | c = *s++; | |
229 | ||
230 | switch (c) | |
231 | { | |
232 | case 'P': | |
233 | /* | |
234 | * Special case for -P. | |
235 | */ | |
236 | if (*s == '\0') | |
237 | error(prproto[pr_type]); | |
238 | else | |
239 | (void) opt_P(s); | |
240 | return; | |
241 | #if TAGS | |
242 | case 't': | |
243 | /* | |
244 | * Special case for -t. | |
245 | */ | |
246 | if (*s == '\0') | |
247 | { | |
248 | error("no tag"); | |
249 | return; | |
250 | } | |
251 | findtag(s); | |
252 | if (tagfile != NULL) | |
253 | { | |
254 | edit(tagfile); | |
255 | (void) tagsearch(); | |
256 | } | |
257 | return; | |
258 | #endif | |
259 | #if LOGFILE | |
260 | case 'L': | |
261 | /* | |
262 | * Special case for -l and -L. | |
263 | */ | |
264 | force_logfile = 1; | |
265 | goto case_l; | |
266 | case 'l': | |
267 | force_logfile = 0; | |
268 | case_l: | |
269 | if (*s == '\0') | |
270 | { | |
271 | if (logfile < 0) | |
272 | error("no log file"); | |
273 | else | |
274 | { | |
275 | sprintf(message, "log file \"%s\"", | |
276 | namelogfile); | |
277 | error(message); | |
278 | } | |
279 | return; | |
280 | } | |
281 | if (!ispipe) | |
282 | { | |
283 | error("input is not a pipe"); | |
284 | return; | |
285 | } | |
286 | if (logfile >= 0) | |
287 | { | |
288 | error("log file is already in use"); | |
289 | return; | |
290 | } | |
291 | namelogfile = save(s); | |
292 | use_logfile(); | |
293 | sync_logfile(); | |
294 | return; | |
295 | #endif | |
296 | } | |
297 | ||
298 | msg = NULL; | |
299 | for (o = option; o->oletter != '\0'; o++) | |
300 | { | |
301 | if (o->otype & NO_TOGGLE) | |
302 | continue; | |
303 | dorepaint = (o->otype & REPAINT); | |
304 | if ((o->otype & BOOL) && (o->oletter == c)) | |
305 | { | |
306 | /* | |
307 | * Boolean option: | |
308 | * just toggle it. | |
309 | */ | |
310 | if (do_toggle) | |
311 | *(o->ovar) = ! *(o->ovar); | |
312 | } else if ((o->otype & TRIPLE) && (o->oletter == c)) | |
313 | { | |
314 | /* | |
315 | * Triple-valued option with lower case letter: | |
316 | * make it 1 unless already 1, then make it 0. | |
317 | */ | |
318 | if (do_toggle) | |
319 | *(o->ovar) = (*(o->ovar) == 1) ? 0 : 1; | |
320 | } else if ((o->otype & TRIPLE) && (toupper(o->oletter) == c)) | |
321 | { | |
322 | /* | |
323 | * Triple-valued option with upper case letter: | |
324 | * make it 2 unless already 2, then make it 0. | |
325 | */ | |
326 | if (do_toggle) | |
327 | *(o->ovar) = (*(o->ovar) == 2) ? 0 : 2; | |
328 | } else if ((o->otype & NUMBER) && (o->oletter == c)) | |
329 | { | |
330 | n = getnum(&s, '\0'); | |
331 | if (n < 0) | |
332 | { | |
333 | /* | |
334 | * No number; just a query. | |
335 | * No need to repaint screen. | |
336 | */ | |
337 | dorepaint = 0; | |
338 | } else | |
339 | { | |
340 | /* | |
341 | * Number follows the option letter. | |
342 | * Set the variable to that number. | |
343 | */ | |
344 | if (do_toggle) | |
345 | *(o->ovar) = n; | |
346 | } | |
347 | ||
348 | /* | |
349 | * Special case for -b. | |
350 | * Call ch_init to set new number of buffers. | |
351 | */ | |
352 | if (o->ovar == &cbufs) | |
353 | ch_init(cbufs, 1); | |
354 | ||
355 | sprintf(message, o->odesc[0], | |
356 | (o->ovar == &back_scroll) ? | |
357 | get_back_scroll() : *(o->ovar)); | |
358 | msg = message; | |
359 | } else | |
360 | continue; | |
361 | ||
362 | /* | |
363 | * Print a message describing the new setting. | |
364 | */ | |
365 | if (msg == NULL) | |
366 | msg = o->odesc[*(o->ovar)]; | |
367 | error(msg); | |
368 | ||
369 | if (do_toggle && dorepaint) | |
370 | repaint(); | |
371 | return; | |
372 | } | |
373 | ||
374 | if (control_char(c)) | |
375 | sprintf(message, "-^%c", carat_char(c)); | |
376 | else | |
377 | sprintf(message, "-%c", c); | |
378 | strcat(message, ": no such flag."); | |
379 | error(message); | |
380 | } | |
381 | ||
382 | /* | |
383 | * Determine if an option is a single character option (BOOL or TRIPLE), | |
384 | * or if it a multi-character option (NUMBER). | |
385 | */ | |
386 | public int | |
387 | single_char_option(c) | |
388 | int c; | |
389 | { | |
390 | register struct option *o; | |
391 | ||
392 | if (c == 'P') | |
393 | return (0); | |
394 | #if TAGS | |
395 | if (c == 't') | |
396 | return (0); | |
397 | #endif | |
398 | #if LOGFILE | |
399 | if (c == 'l' || c == 'L') | |
400 | return (0); | |
401 | #endif | |
402 | for (o = option; o->oletter != '\0'; o++) | |
403 | if (o->oletter == c) | |
404 | return (o->otype & (BOOL|TRIPLE)); | |
405 | return (1); | |
406 | } | |
407 | ||
408 | /* | |
409 | * Scan to end of string or to an END_OPTION_STRING character. | |
410 | * In the latter case, replace the char with a null char. | |
411 | * Return a pointer to the remainder of the string, if any. | |
412 | */ | |
413 | static char * | |
414 | optstring(s, c) | |
415 | char *s; | |
416 | int c; | |
417 | { | |
418 | register char *p; | |
419 | char message[80]; | |
420 | ||
421 | if (*s == '\0') | |
422 | { | |
423 | sprintf(message, "string is required after -%c", c); | |
424 | error(message); | |
425 | exit(1); | |
426 | } | |
427 | for (p = s; *p != '\0'; p++) | |
428 | if (*p == END_OPTION_STRING) | |
429 | { | |
430 | *p = '\0'; | |
431 | return (p+1); | |
432 | } | |
433 | return (p); | |
434 | } | |
435 | ||
436 | /* | |
437 | * Scan an argument (either from command line or from LESS environment | |
438 | * variable) and process it. | |
439 | */ | |
440 | public void | |
441 | scan_option(s) | |
442 | char *s; | |
443 | { | |
444 | register struct option *o; | |
445 | register int c; | |
446 | int set_default; | |
447 | char message[80]; | |
448 | ||
449 | if (s == NULL) | |
450 | return; | |
451 | ||
452 | set_default = 0; | |
453 | next: | |
454 | if (*s == '\0') | |
455 | return; | |
456 | switch (c = *s++) | |
457 | { | |
458 | case ' ': | |
459 | case '\t': | |
460 | case END_OPTION_STRING: | |
461 | goto next; | |
462 | case '-': | |
463 | if (set_default = (*s == '+')) | |
464 | s++; | |
465 | goto next; | |
466 | case '+': | |
467 | plusoption = 1; | |
468 | if (*s == '+') | |
469 | every_first_cmd = save(++s); | |
470 | first_cmd = s; | |
471 | s = optstring(s, c); | |
472 | goto next; | |
473 | #if LOGFILE | |
474 | case 'L': | |
475 | force_logfile = 1; | |
476 | /* FALLTHRU */ | |
477 | case 'l': | |
478 | namelogfile = s; | |
479 | s = optstring(s, c); | |
480 | goto next; | |
481 | #endif | |
482 | #if TAGS | |
483 | case 't': | |
484 | { | |
485 | char *p; | |
486 | tagoption = 1; | |
487 | p = s; | |
488 | s = optstring(s, c); | |
489 | findtag(p); | |
490 | goto next; | |
491 | } | |
492 | #endif | |
493 | case 'P': | |
494 | s = opt_P(s); | |
495 | goto next; | |
496 | case '0': case '1': case '2': case '3': case '4': | |
497 | case '5': case '6': case '7': case '8': case '9': | |
498 | /* | |
499 | * Handle special "more" compatibility form "-number" | |
500 | * (instead of -znumber) to set the scrolling window size. | |
501 | */ | |
502 | s--; | |
503 | c = 'z'; | |
504 | break; | |
505 | } | |
506 | ||
507 | for (o = option; o->oletter != '\0'; o++) | |
508 | { | |
509 | if ((o->otype & BOOL) && (o->oletter == c)) | |
510 | { | |
511 | if (set_default) | |
512 | *(o->ovar) = o->odefault; | |
513 | else | |
514 | *(o->ovar) = ! o->odefault; | |
515 | goto next; | |
516 | } else if ((o->otype & TRIPLE) && (o->oletter == c)) | |
517 | { | |
518 | if (set_default) | |
519 | *(o->ovar) = o->odefault; | |
520 | else | |
521 | *(o->ovar) = (o->odefault == 1) ? 0 : 1; | |
522 | goto next; | |
523 | } else if ((o->otype & TRIPLE) && (toupper(o->oletter) == c)) | |
524 | { | |
525 | if (set_default) | |
526 | *(o->ovar) = o->odefault; | |
527 | else | |
528 | *(o->ovar) = (o->odefault == 2) ? 0 : 2; | |
529 | goto next; | |
530 | } else if ((o->otype & NUMBER) && (o->oletter == c)) | |
531 | { | |
532 | *(o->ovar) = getnum(&s, c); | |
533 | goto next; | |
534 | } | |
535 | } | |
536 | ||
537 | sprintf(message, "\"-%c\": invalid flag", c); | |
538 | error(message); | |
539 | exit(1); | |
540 | } | |
541 | ||
542 | /* | |
543 | * Special case for -P. | |
544 | */ | |
545 | static char * | |
546 | opt_P(s) | |
547 | register char *s; | |
548 | { | |
549 | register char *es; | |
550 | register char **proto; | |
551 | ||
552 | es = optstring(s, 'P'); | |
553 | ||
554 | /* | |
555 | * Figure out which prototype string should be changed. | |
556 | */ | |
557 | switch (*s) | |
558 | { | |
559 | case 'm': proto = &prproto[PR_MEDIUM]; s++; break; | |
560 | case 'M': proto = &prproto[PR_LONG]; s++; break; | |
561 | case '=': proto = &eqproto; s++; break; | |
562 | default: proto = &prproto[pr_type]; break; | |
563 | } | |
564 | ||
565 | free(*proto); | |
566 | *proto = save(s); | |
567 | ||
568 | return (es); | |
569 | } | |
570 | ||
571 | /* | |
572 | * Translate a string into a number. | |
573 | * Like atoi(), but takes a pointer to a char *, and updates | |
574 | * the char * to point after the translated number. | |
575 | */ | |
576 | public int | |
577 | getnum(sp, c) | |
578 | char **sp; | |
579 | int c; | |
580 | { | |
581 | register char *s; | |
582 | register int n; | |
583 | char message[80]; | |
584 | ||
585 | s = *sp; | |
586 | if (*s < '0' || *s > '9') | |
587 | { | |
588 | if (c == '\0') | |
589 | return (-1); | |
590 | sprintf(message, "number is required after -%c", c); | |
591 | error(message); | |
592 | exit(1); | |
593 | } | |
594 | ||
595 | n = 0; | |
596 | while (*s >= '0' && *s <= '9') | |
597 | n = 10 * n + *s++ - '0'; | |
598 | *sp = s; | |
599 | return (n); | |
600 | } |