1: /* 2: * Process command line options. 3: * Each option is a single letter which controls a program variable. 4: * The options have defaults which may be changed via 5: * the command line option, or toggled via the "-" command. 6: */ 7: 8: #include "less.h" 9: 10: #define toupper(c) ((c)-'a'+'A') 11: 12: #define END_OPTION_STRING ('$') 13: 14: /* 15: * Types of options. 16: */ 17: #define BOOL 01 /* Boolean option: 0 or 1 */ 18: #define TRIPLE 02 /* Triple-valued option: 0, 1 or 2 */ 19: #define NUMBER 04 /* Numeric option */ 20: #define REPAINT 040 /* Repaint screen after toggling option */ 21: #define NO_TOGGLE 0100 /* Option cannot be toggled with "-" cmd */ 22: 23: /* 24: * Variables controlled by command line options. 25: */ 26: public int p_nbufs, f_nbufs; /* Number of buffers. There are two values, 27: one used for input from a pipe and 28: the other for input from a file. */ 29: public int clean_data; /* Can we assume the data is "clean"? 30: (That is, free of nulls, etc) */ 31: public int quiet; /* Should we suppress the audible bell? */ 32: public int top_search; /* Should forward searches start at the top 33: of the screen? (alternative is bottom) */ 34: public int top_scroll; /* Repaint screen from top? 35: (alternative is scroll from bottom) */ 36: public int pr_type; /* Type of prompt (short, medium, long) */ 37: public int bs_mode; /* How to process backspaces */ 38: public int know_dumb; /* Don't complain about dumb terminals */ 39: public int quit_at_eof; /* Quit after hitting end of file twice */ 40: public int squeeze; /* Squeeze multiple blank lines into one */ 41: public int tabstop; /* Tab settings */ 42: public int back_scroll; /* Repaint screen on backwards movement */ 43: public int twiddle; /* Display "~" for lines after EOF */ 44: 45: extern char *prproto[]; 46: extern int nbufs; 47: extern int sc_window; 48: extern char *first_cmd; 49: extern char *every_first_cmd; 50: #if LOGFILE 51: extern char *namelogfile; 52: #endif 53: 54: #define DEF_F_NBUFS 5 /* Default for f_nbufs */ 55: #define DEF_P_NBUFS 12 /* Default for p_nbufs */ 56: 57: static struct option 58: { 59: char oletter; /* The controlling letter (a-z) */ 60: char otype; /* Type of the option */ 61: int odefault; /* Default value */ 62: int *ovar; /* Pointer to the associated variable */ 63: char *odesc[3]; /* Description of each value */ 64: } option[] = 65: { 66: { 'c', BOOL, 0, &clean_data, 67: { "Don't assume data is clean", 68: "Assume data is clean", 69: NULL 70: } 71: }, 72: { 'd', BOOL|NO_TOGGLE, 0, &know_dumb, 73: { NULL, NULL, NULL} 74: }, 75: { 'e', BOOL, 0, &quit_at_eof, 76: { "Don't quit at end-of-file", 77: "Quit at end-of-file", 78: NULL 79: } 80: }, 81: { 'h', NUMBER, -1, &back_scroll, 82: { "Backwards scroll limit is %d lines", 83: NULL, NULL 84: } 85: }, 86: { 'p', BOOL, 0, &top_scroll, 87: { "Repaint by scrolling from bottom of screen", 88: "Repaint by painting from top of screen", 89: NULL 90: } 91: }, 92: { 'x', NUMBER, 8, &tabstop, 93: { "Tab stops every %d spaces", 94: NULL, NULL 95: } 96: }, 97: { 's', BOOL|REPAINT, 0, &squeeze, 98: { "Don't squeeze multiple blank lines", 99: "Squeeze multiple blank lines", 100: NULL 101: } 102: }, 103: { 't', BOOL, 1, &top_search, 104: { "Forward search starts from bottom of screen", 105: "Forward search starts from top of screen", 106: NULL 107: } 108: }, 109: { 'w', BOOL|REPAINT, 1, &twiddle, 110: { "Display nothing for lines after end-of-file", 111: "Display ~ for lines after end-of-file", 112: NULL 113: } 114: }, 115: { 'm', TRIPLE, 0, &pr_type, 116: { "Short prompt", 117: "Medium prompt", 118: "Long prompt" 119: } 120: }, 121: { 'q', TRIPLE, 0, &quiet, 122: { "Ring the bell for errors AND at eof/bof", 123: "Ring the bell for errors but not at eof/bof", 124: "Never ring the bell" 125: } 126: }, 127: { 'u', TRIPLE|REPAINT, 0, &bs_mode, 128: { "Underlined text displayed in underline mode", 129: "Backspaces cause overstrike", 130: "Backspaces print as ^H" 131: } 132: }, 133: { 'z', NUMBER, 24, &sc_window, 134: { "Scroll window size is %d lines", 135: NULL, NULL 136: } 137: }, 138: { '\0' } 139: }; 140: 141: public char all_options[64]; /* List of all valid options */ 142: 143: /* 144: * Initialize each option to its default value. 145: */ 146: public void 147: init_option() 148: { 149: register struct option *o; 150: register char *p; 151: 152: /* 153: * First do special cases, not in option table. 154: */ 155: first_cmd = every_first_cmd = NULL; 156: f_nbufs = DEF_F_NBUFS; /* -bf */ 157: p_nbufs = DEF_P_NBUFS; /* -bp */ 158: 159: p = all_options; 160: *p++ = 'b'; 161: 162: for (o = option; o->oletter != '\0'; o++) 163: { 164: /* 165: * Set each variable to its default. 166: * Also make a list of all options, in "all_options". 167: */ 168: *(o->ovar) = o->odefault; 169: *p++ = o->oletter; 170: if (o->otype & TRIPLE) 171: *p++ = toupper(o->oletter); 172: } 173: *p = '\0'; 174: } 175: 176: /* 177: * Toggle command line flags from within the program. 178: * Used by the "-" command. 179: */ 180: public void 181: toggle_option(c) 182: int c; 183: { 184: register struct option *o; 185: char message[100]; 186: char buf[5]; 187: 188: /* 189: * First check for special cases not handled by the option table. 190: */ 191: switch (c) 192: { 193: case 'b': 194: sprintf(message, "%d buffers", nbufs); 195: error(message); 196: return; 197: } 198: 199: 200: for (o = option; o->oletter != '\0'; o++) 201: { 202: if (o->otype & NO_TOGGLE) 203: continue; 204: if ((o->otype & BOOL) && (o->oletter == c)) 205: { 206: /* 207: * Boolean option: 208: * just toggle it. 209: */ 210: *(o->ovar) = ! *(o->ovar); 211: } else if ((o->otype & TRIPLE) && (o->oletter == c)) 212: { 213: /* 214: * Triple-valued option with lower case letter: 215: * make it 1 unless already 1, then make it 0. 216: */ 217: *(o->ovar) = (*(o->ovar) == 1) ? 0 : 1; 218: } else if ((o->otype & TRIPLE) && (toupper(o->oletter) == c)) 219: { 220: /* 221: * Triple-valued option with upper case letter: 222: * make it 2 unless already 2, then make it 0. 223: */ 224: *(o->ovar) = (*(o->ovar) == 2) ? 0 : 2; 225: } else if ((o->otype & NUMBER) && (o->oletter == c)) 226: { 227: sprintf(message, o->odesc[0], 228: (o->ovar == &back_scroll) ? 229: get_back_scroll() : *(o->ovar)); 230: error(message); 231: return; 232: } else 233: continue; 234: 235: if (o->otype & REPAINT) 236: repaint(); 237: error(o->odesc[*(o->ovar)]); 238: return; 239: } 240: 241: if (control_char(c)) 242: sprintf(buf, "^%c", carat_char(c)); 243: else 244: sprintf(buf, "%c", c); 245: sprintf(message, "\"-%s\": no such flag. Use one of \"%s\"", 246: buf, all_options); 247: error(message); 248: } 249: 250: /* 251: * Scan to end of string or to an END_OPTION_STRING character. 252: * In the latter case, replace the char with a null char. 253: * Return a pointer to the remainder of the string, if any. 254: */ 255: static char * 256: optstring(s) 257: char *s; 258: { 259: register char *p; 260: 261: for (p = s; *p != '\0'; p++) 262: if (*p == END_OPTION_STRING) 263: { 264: *p = '\0'; 265: return (p+1); 266: } 267: return (p); 268: } 269: 270: /* 271: * Scan an argument (either from command line or from LESS environment 272: * variable) and process it. 273: */ 274: public void 275: scan_option(s) 276: char *s; 277: { 278: register struct option *o; 279: register int c; 280: char message[80]; 281: 282: if (s == NULL) 283: return; 284: 285: next: 286: if (*s == '\0') 287: return; 288: switch (c = *s++) 289: { 290: case '-': 291: case ' ': 292: case '\t': 293: case END_OPTION_STRING: 294: goto next; 295: case '+': 296: if (*s == '+') 297: every_first_cmd = ++s; 298: first_cmd = s; 299: s = optstring(s); 300: goto next; 301: case 'P': 302: switch (*s) 303: { 304: case 'm': prproto[PR_MEDIUM] = ++s; break; 305: case 'M': prproto[PR_LONG] = ++s; break; 306: default: prproto[PR_SHORT] = s; break; 307: } 308: s = optstring(s); 309: goto next; 310: #if LOGFILE 311: case 'l': 312: namelogfile = s; 313: s = optstring(s); 314: goto next; 315: #endif 316: case 'b': 317: switch (*s) 318: { 319: case 'f': 320: s++; 321: f_nbufs = getnum(&s, 'b'); 322: break; 323: case 'p': 324: s++; 325: p_nbufs = getnum(&s, 'b'); 326: break; 327: default: 328: f_nbufs = p_nbufs = getnum(&s, 'b'); 329: break; 330: } 331: goto next; 332: case '0': case '1': case '2': case '3': case '4': 333: case '5': case '6': case '7': case '8': case '9': 334: { 335: /* 336: * Handle special "more" compatibility form "-number" 337: * to set the scrolling window size. 338: */ 339: s--; 340: sc_window = getnum(&s, '-'); 341: goto next; 342: } 343: } 344: 345: for (o = option; o->oletter != '\0'; o++) 346: { 347: if ((o->otype & BOOL) && (o->oletter == c)) 348: { 349: *(o->ovar) = ! o->odefault; 350: goto next; 351: } else if ((o->otype & TRIPLE) && (o->oletter == c)) 352: { 353: *(o->ovar) = (o->odefault == 1) ? 0 : 1; 354: goto next; 355: } else if ((o->otype & TRIPLE) && (toupper(o->oletter) == c)) 356: { 357: *(o->ovar) = (o->odefault == 2) ? 0 : 2; 358: goto next; 359: } else if ((o->otype & NUMBER) && (o->oletter == c)) 360: { 361: *(o->ovar) = getnum(&s, c); 362: goto next; 363: } 364: } 365: 366: sprintf(message, "\"-%c\": invalid flag", c); 367: error(message); 368: exit(1); 369: } 370: 371: /* 372: * Translate a string into a number. 373: * Like atoi(), but takes a pointer to a char *, and updates 374: * the char * to point after the translated number. 375: */ 376: static int 377: getnum(sp, c) 378: char **sp; 379: int c; 380: { 381: register char *s; 382: register int n; 383: char message[80]; 384: 385: s = *sp; 386: if (*s < '0' || *s > '9') 387: { 388: sprintf(message, "number is required after -%c", c); 389: error(message); 390: exit(1); 391: } 392: 393: n = 0; 394: while (*s >= '0' && *s <= '9') 395: n = 10 * n + *s++ - '0'; 396: *sp = s; 397: return (n); 398: }