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