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 | |
bdd98443 | 23 | static char sccsid[] = "@(#)option.c 5.4 (Berkeley) %G%"; |
bfe13c81 KB |
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; | |
bfe13c81 KB |
78 | extern char *tagfile; |
79 | extern char *tagpattern; | |
80 | public int tagoption = 0; | |
bfe13c81 KB |
81 | |
82 | static char *opt_P(); | |
83 | ||
84 | static struct option | |
85 | { | |
86 | char oletter; /* The controlling letter (a-z) */ | |
87 | char otype; /* Type of the option */ | |
88 | int odefault; /* Default value */ | |
89 | int *ovar; /* Pointer to the associated variable */ | |
90 | char *odesc[3]; /* Description of each value */ | |
91 | } option[] = | |
92 | { | |
93 | { 'a', TRIPLE, 0, &how_search, | |
94 | { "Forward search starts at second REAL line displayed", | |
95 | "Forward search starts at bottom of screen", | |
96 | "Forward search starts at second SCREEN line displayed" | |
97 | } | |
98 | }, | |
99 | { 'b', NUMBER, 10, &cbufs, | |
100 | { "%d buffers", | |
101 | NULL, NULL | |
102 | } | |
103 | }, | |
104 | { 'B', BOOL, 1, &autobuf, | |
105 | { "Don't automatically allocate buffers", | |
106 | "Automatically allocate buffers when needed", | |
107 | NULL | |
108 | } | |
109 | }, | |
110 | { 'c', TRIPLE, 0, &top_scroll, | |
111 | { "Repaint by scrolling from bottom of screen", | |
112 | "Repaint by clearing each line", | |
113 | "Repaint by painting from top of screen" | |
114 | } | |
115 | }, | |
116 | { 'd', BOOL|NO_TOGGLE, 0, &know_dumb, | |
117 | { NULL, NULL, NULL} | |
118 | }, | |
119 | { 'e', TRIPLE, 0, &quit_at_eof, | |
120 | { "Don't quit at end-of-file", | |
121 | "Quit at end-of-file", | |
122 | "Quit immediately at end-of-file" | |
123 | } | |
124 | }, | |
125 | { 'h', NUMBER, -1, &back_scroll, | |
126 | { "Backwards scroll limit is %d lines", | |
127 | NULL, NULL | |
128 | } | |
129 | }, | |
130 | { 'i', BOOL, 0, &caseless, | |
131 | { "Case is significant in searches", | |
132 | "Ignore case in searches", | |
133 | NULL | |
134 | } | |
135 | }, | |
136 | { 'm', TRIPLE, 0, &pr_type, | |
137 | { "Short prompt", | |
138 | "Medium prompt", | |
139 | "Long prompt" | |
140 | } | |
141 | }, | |
142 | { 'n', BOOL, 1, &linenums, | |
143 | { "Don't use line numbers", | |
144 | "Use line numbers", | |
145 | NULL | |
146 | } | |
147 | }, | |
148 | { 'q', TRIPLE, 0, &quiet, | |
149 | { "Ring the bell for errors AND at eof/bof", | |
150 | "Ring the bell for errors but not at eof/bof", | |
151 | "Never ring the bell" | |
152 | } | |
153 | }, | |
154 | { 's', BOOL|REPAINT, 0, &squeeze, | |
155 | { "Don't squeeze multiple blank lines", | |
156 | "Squeeze multiple blank lines", | |
157 | NULL | |
158 | } | |
159 | }, | |
160 | { 'u', TRIPLE|REPAINT, 0, &bs_mode, | |
161 | { "Underlined text displayed in underline mode", | |
162 | "Backspaces cause overstrike", | |
163 | "Backspaces print as ^H" | |
164 | } | |
165 | }, | |
166 | { 'w', BOOL|REPAINT, 1, &twiddle, | |
167 | { "Display nothing for lines after end-of-file", | |
168 | "Display ~ for lines after end-of-file", | |
169 | NULL | |
170 | } | |
171 | }, | |
172 | { 'x', NUMBER|REPAINT, 8, &tabstop, | |
173 | { "Tab stops every %d spaces", | |
174 | NULL, NULL | |
175 | } | |
176 | }, | |
177 | { 'z', NUMBER|REPAINT, -1, &sc_window, | |
178 | { "Scroll window size is %d lines", | |
179 | NULL, NULL | |
180 | } | |
181 | }, | |
182 | { '\0' } | |
183 | }; | |
184 | ||
185 | /* | |
186 | * Initialize each option to its default value. | |
187 | */ | |
188 | public void | |
189 | init_option() | |
190 | { | |
191 | register struct option *o; | |
192 | ||
193 | first_cmd = every_first_cmd = NULL; | |
194 | ||
195 | for (o = option; o->oletter != '\0'; o++) | |
196 | { | |
197 | /* | |
198 | * Set each variable to its default. | |
199 | */ | |
200 | *(o->ovar) = o->odefault; | |
201 | } | |
202 | } | |
203 | ||
204 | /* | |
205 | * Toggle command line flags from within the program. | |
206 | * Used by the "-" and "_" commands. | |
207 | * If do_toggle is zero, just report the current setting, without changing it. | |
208 | */ | |
209 | public void | |
210 | toggle_option(s, do_toggle) | |
211 | char *s; | |
212 | int do_toggle; | |
213 | { | |
214 | int c; | |
215 | register struct option *o; | |
216 | char *msg; | |
217 | int n; | |
218 | int dorepaint; | |
bdd98443 | 219 | char message[100], *strcat(); |
bfe13c81 KB |
220 | |
221 | c = *s++; | |
222 | ||
223 | switch (c) | |
224 | { | |
225 | case 'P': | |
226 | /* | |
227 | * Special case for -P. | |
228 | */ | |
229 | if (*s == '\0') | |
230 | error(prproto[pr_type]); | |
231 | else | |
232 | (void) opt_P(s); | |
233 | return; | |
bfe13c81 KB |
234 | case 't': |
235 | /* | |
236 | * Special case for -t. | |
237 | */ | |
238 | if (*s == '\0') | |
239 | { | |
240 | error("no tag"); | |
241 | return; | |
242 | } | |
243 | findtag(s); | |
244 | if (tagfile != NULL) | |
245 | { | |
246 | edit(tagfile); | |
247 | (void) tagsearch(); | |
248 | } | |
249 | return; | |
bfe13c81 KB |
250 | } |
251 | ||
252 | msg = NULL; | |
253 | for (o = option; o->oletter != '\0'; o++) | |
254 | { | |
255 | if (o->otype & NO_TOGGLE) | |
256 | continue; | |
257 | dorepaint = (o->otype & REPAINT); | |
258 | if ((o->otype & BOOL) && (o->oletter == c)) | |
259 | { | |
260 | /* | |
261 | * Boolean option: | |
262 | * just toggle it. | |
263 | */ | |
264 | if (do_toggle) | |
265 | *(o->ovar) = ! *(o->ovar); | |
266 | } else if ((o->otype & TRIPLE) && (o->oletter == c)) | |
267 | { | |
268 | /* | |
269 | * Triple-valued option with lower case letter: | |
270 | * make it 1 unless already 1, then make it 0. | |
271 | */ | |
272 | if (do_toggle) | |
273 | *(o->ovar) = (*(o->ovar) == 1) ? 0 : 1; | |
274 | } else if ((o->otype & TRIPLE) && (toupper(o->oletter) == c)) | |
275 | { | |
276 | /* | |
277 | * Triple-valued option with upper case letter: | |
278 | * make it 2 unless already 2, then make it 0. | |
279 | */ | |
280 | if (do_toggle) | |
281 | *(o->ovar) = (*(o->ovar) == 2) ? 0 : 2; | |
282 | } else if ((o->otype & NUMBER) && (o->oletter == c)) | |
283 | { | |
284 | n = getnum(&s, '\0'); | |
285 | if (n < 0) | |
286 | { | |
287 | /* | |
288 | * No number; just a query. | |
289 | * No need to repaint screen. | |
290 | */ | |
291 | dorepaint = 0; | |
292 | } else | |
293 | { | |
294 | /* | |
295 | * Number follows the option letter. | |
296 | * Set the variable to that number. | |
297 | */ | |
298 | if (do_toggle) | |
299 | *(o->ovar) = n; | |
300 | } | |
301 | ||
302 | /* | |
303 | * Special case for -b. | |
304 | * Call ch_init to set new number of buffers. | |
305 | */ | |
306 | if (o->ovar == &cbufs) | |
307 | ch_init(cbufs, 1); | |
308 | ||
bdd98443 | 309 | (void)sprintf(message, o->odesc[0], |
bfe13c81 KB |
310 | (o->ovar == &back_scroll) ? |
311 | get_back_scroll() : *(o->ovar)); | |
312 | msg = message; | |
313 | } else | |
314 | continue; | |
315 | ||
316 | /* | |
317 | * Print a message describing the new setting. | |
318 | */ | |
319 | if (msg == NULL) | |
320 | msg = o->odesc[*(o->ovar)]; | |
321 | error(msg); | |
322 | ||
323 | if (do_toggle && dorepaint) | |
324 | repaint(); | |
325 | return; | |
326 | } | |
327 | ||
328 | if (control_char(c)) | |
bdd98443 | 329 | (void)sprintf(message, "-^%c", carat_char(c)); |
bfe13c81 | 330 | else |
bdd98443 KB |
331 | (void)sprintf(message, "-%c", c); |
332 | (void)strcat(message, ": no such flag."); | |
bfe13c81 KB |
333 | error(message); |
334 | } | |
335 | ||
336 | /* | |
337 | * Determine if an option is a single character option (BOOL or TRIPLE), | |
338 | * or if it a multi-character option (NUMBER). | |
339 | */ | |
340 | public int | |
341 | single_char_option(c) | |
342 | int c; | |
343 | { | |
344 | register struct option *o; | |
345 | ||
346 | if (c == 'P') | |
347 | return (0); | |
bfe13c81 KB |
348 | if (c == 't') |
349 | return (0); | |
bfe13c81 KB |
350 | for (o = option; o->oletter != '\0'; o++) |
351 | if (o->oletter == c) | |
352 | return (o->otype & (BOOL|TRIPLE)); | |
353 | return (1); | |
354 | } | |
355 | ||
356 | /* | |
357 | * Scan to end of string or to an END_OPTION_STRING character. | |
358 | * In the latter case, replace the char with a null char. | |
359 | * Return a pointer to the remainder of the string, if any. | |
360 | */ | |
361 | static char * | |
362 | optstring(s, c) | |
363 | char *s; | |
364 | int c; | |
365 | { | |
366 | register char *p; | |
367 | char message[80]; | |
368 | ||
369 | if (*s == '\0') | |
370 | { | |
bdd98443 | 371 | (void)sprintf(message, "string is required after -%c", c); |
bfe13c81 KB |
372 | error(message); |
373 | exit(1); | |
374 | } | |
375 | for (p = s; *p != '\0'; p++) | |
376 | if (*p == END_OPTION_STRING) | |
377 | { | |
378 | *p = '\0'; | |
379 | return (p+1); | |
380 | } | |
381 | return (p); | |
382 | } | |
383 | ||
384 | /* | |
385 | * Scan an argument (either from command line or from LESS environment | |
386 | * variable) and process it. | |
387 | */ | |
388 | public void | |
389 | scan_option(s) | |
390 | char *s; | |
391 | { | |
392 | register struct option *o; | |
393 | register int c; | |
394 | int set_default; | |
395 | char message[80]; | |
396 | ||
397 | if (s == NULL) | |
398 | return; | |
399 | ||
400 | set_default = 0; | |
401 | next: | |
402 | if (*s == '\0') | |
403 | return; | |
404 | switch (c = *s++) | |
405 | { | |
406 | case ' ': | |
407 | case '\t': | |
408 | case END_OPTION_STRING: | |
409 | goto next; | |
410 | case '-': | |
411 | if (set_default = (*s == '+')) | |
412 | s++; | |
413 | goto next; | |
414 | case '+': | |
415 | plusoption = 1; | |
416 | if (*s == '+') | |
417 | every_first_cmd = save(++s); | |
418 | first_cmd = s; | |
419 | s = optstring(s, c); | |
420 | goto next; | |
bfe13c81 KB |
421 | case 't': |
422 | { | |
423 | char *p; | |
424 | tagoption = 1; | |
425 | p = s; | |
426 | s = optstring(s, c); | |
427 | findtag(p); | |
428 | goto next; | |
429 | } | |
bfe13c81 KB |
430 | case 'P': |
431 | s = opt_P(s); | |
432 | goto next; | |
433 | case '0': case '1': case '2': case '3': case '4': | |
434 | case '5': case '6': case '7': case '8': case '9': | |
435 | /* | |
436 | * Handle special "more" compatibility form "-number" | |
437 | * (instead of -znumber) to set the scrolling window size. | |
438 | */ | |
439 | s--; | |
440 | c = 'z'; | |
441 | break; | |
442 | } | |
443 | ||
444 | for (o = option; o->oletter != '\0'; o++) | |
445 | { | |
446 | if ((o->otype & BOOL) && (o->oletter == c)) | |
447 | { | |
448 | if (set_default) | |
449 | *(o->ovar) = o->odefault; | |
450 | else | |
451 | *(o->ovar) = ! o->odefault; | |
452 | goto next; | |
453 | } else if ((o->otype & TRIPLE) && (o->oletter == c)) | |
454 | { | |
455 | if (set_default) | |
456 | *(o->ovar) = o->odefault; | |
457 | else | |
458 | *(o->ovar) = (o->odefault == 1) ? 0 : 1; | |
459 | goto next; | |
460 | } else if ((o->otype & TRIPLE) && (toupper(o->oletter) == c)) | |
461 | { | |
462 | if (set_default) | |
463 | *(o->ovar) = o->odefault; | |
464 | else | |
465 | *(o->ovar) = (o->odefault == 2) ? 0 : 2; | |
466 | goto next; | |
467 | } else if ((o->otype & NUMBER) && (o->oletter == c)) | |
468 | { | |
469 | *(o->ovar) = getnum(&s, c); | |
470 | goto next; | |
471 | } | |
472 | } | |
473 | ||
bdd98443 | 474 | (void)sprintf(message, "\"-%c\": invalid flag", c); |
bfe13c81 KB |
475 | error(message); |
476 | exit(1); | |
477 | } | |
478 | ||
479 | /* | |
480 | * Special case for -P. | |
481 | */ | |
482 | static char * | |
483 | opt_P(s) | |
484 | register char *s; | |
485 | { | |
486 | register char *es; | |
487 | register char **proto; | |
488 | ||
489 | es = optstring(s, 'P'); | |
490 | ||
491 | /* | |
492 | * Figure out which prototype string should be changed. | |
493 | */ | |
494 | switch (*s) | |
495 | { | |
496 | case 'm': proto = &prproto[PR_MEDIUM]; s++; break; | |
497 | case 'M': proto = &prproto[PR_LONG]; s++; break; | |
498 | case '=': proto = &eqproto; s++; break; | |
499 | default: proto = &prproto[pr_type]; break; | |
500 | } | |
501 | ||
502 | free(*proto); | |
503 | *proto = save(s); | |
504 | ||
505 | return (es); | |
506 | } | |
507 | ||
508 | /* | |
509 | * Translate a string into a number. | |
510 | * Like atoi(), but takes a pointer to a char *, and updates | |
511 | * the char * to point after the translated number. | |
512 | */ | |
513 | public int | |
514 | getnum(sp, c) | |
515 | char **sp; | |
516 | int c; | |
517 | { | |
518 | register char *s; | |
519 | register int n; | |
520 | char message[80]; | |
521 | ||
522 | s = *sp; | |
523 | if (*s < '0' || *s > '9') | |
524 | { | |
525 | if (c == '\0') | |
526 | return (-1); | |
bdd98443 | 527 | (void)sprintf(message, "number is required after -%c", c); |
bfe13c81 KB |
528 | error(message); |
529 | exit(1); | |
530 | } | |
531 | ||
532 | n = 0; | |
533 | while (*s >= '0' && *s <= '9') | |
534 | n = 10 * n + *s++ - '0'; | |
535 | *sp = s; | |
536 | return (n); | |
537 | } |