rip out SHELL_ESCAPE define
[unix-history] / usr / src / usr.bin / more / option.c
CommitLineData
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
23static 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 */
51public int clean_data; /* Can we assume the data is "clean"?
52 (That is, free of nulls, etc) */
53public int quiet; /* Should we suppress the audible bell? */
54public int how_search; /* Where should forward searches start? */
55public int top_scroll; /* Repaint screen from top?
56 (alternative is scroll from bottom) */
57public int pr_type; /* Type of prompt (short, medium, long) */
58public int bs_mode; /* How to process backspaces */
59public int know_dumb; /* Don't complain about dumb terminals */
60public int quit_at_eof; /* Quit after hitting end of file twice */
61public int squeeze; /* Squeeze multiple blank lines into one */
62public int tabstop; /* Tab settings */
63public int back_scroll; /* Repaint screen on backwards movement */
64public int twiddle; /* Display "~" for lines after EOF */
65public int caseless; /* Do "caseless" searches */
66public int linenums; /* Use line numbers */
67public int cbufs; /* Current number of buffers */
68public int autobuf;
69public int plusoption;
70
71extern char *prproto[];
72extern char *eqproto;
73extern int nbufs;
74extern int sc_window;
75extern int ispipe;
76extern char *first_cmd;
77extern char *every_first_cmd;
78#if LOGFILE
79extern char *namelogfile;
80extern int force_logfile;
81extern int logfile;
82#endif
83#if TAGS
84extern char *tagfile;
85extern char *tagpattern;
86public int tagoption = 0;
87#endif
88
89static char *opt_P();
90
91static 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
196init_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
217toggle_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
387single_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 *
414optstring(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
441scan_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 *
546opt_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
577getnum(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}