update copyright notice
[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 *
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 21static 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 */
49public int clean_data; /* Can we assume the data is "clean"?
50 (That is, free of nulls, etc) */
51public int quiet; /* Should we suppress the audible bell? */
52public int how_search; /* Where should forward searches start? */
53public int top_scroll; /* Repaint screen from top?
54 (alternative is scroll from bottom) */
55public int pr_type; /* Type of prompt (short, medium, long) */
56public int bs_mode; /* How to process backspaces */
57public int know_dumb; /* Don't complain about dumb terminals */
58public int quit_at_eof; /* Quit after hitting end of file twice */
59public int squeeze; /* Squeeze multiple blank lines into one */
60public int tabstop; /* Tab settings */
61public int back_scroll; /* Repaint screen on backwards movement */
62public int twiddle; /* Display "~" for lines after EOF */
63public int caseless; /* Do "caseless" searches */
64public int linenums; /* Use line numbers */
65public int cbufs; /* Current number of buffers */
66public int autobuf;
67public int plusoption;
68
69extern char *prproto[];
70extern char *eqproto;
71extern int nbufs;
72extern int sc_window;
73extern int ispipe;
74extern char *first_cmd;
75extern char *every_first_cmd;
bfe13c81
KB
76extern char *tagfile;
77extern char *tagpattern;
78public int tagoption = 0;
bfe13c81
KB
79
80static char *opt_P();
81
82static 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
187init_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
208toggle_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
339single_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 *
360optstring(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
387scan_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 *
481opt_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
512getnum(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}