|
|
1.1 ! root 1: /* ! 2: * User-level command processor. ! 3: */ ! 4: ! 5: #include "less.h" ! 6: #include "position.h" ! 7: #include <setjmp.h> ! 8: ! 9: extern jmp_buf main_loop; ! 10: extern int erase_char, kill_char; ! 11: extern int pr_type; ! 12: extern int sigs; ! 13: extern int ispipe; ! 14: extern int quit_at_eof; ! 15: extern int hit_eof; ! 16: extern int sc_width, sc_height; ! 17: extern char *first_cmd; ! 18: extern char version[]; ! 19: extern char current_file[]; ! 20: extern char *editor; ! 21: ! 22: static char cmdbuf[90]; /* Buffer for holding a multi-char command */ ! 23: static char *cp; /* Pointer into cmdbuf */ ! 24: static int cmd_col; /* Current column of the multi-char command */ ! 25: static char mcc; /* The multi-char command letter (e.g. '/') */ ! 26: static char last_mcc; /* The previous mcc */ ! 27: ! 28: /* ! 29: * Reset command buffer (to empty). ! 30: */ ! 31: cmd_reset() ! 32: { ! 33: cp = cmdbuf; ! 34: } ! 35: ! 36: /* ! 37: * Backspace in command buffer. ! 38: */ ! 39: static int ! 40: cmd_erase() ! 41: { ! 42: if (cp == cmdbuf) ! 43: /* ! 44: * Backspace past beginning of the string: ! 45: * this usually means abort the command. ! 46: */ ! 47: return (1); ! 48: ! 49: if (control_char(*--cp)) ! 50: { ! 51: /* ! 52: * Erase an extra character, for the carat. ! 53: */ ! 54: backspace(); ! 55: cmd_col--; ! 56: } ! 57: backspace(); ! 58: cmd_col--; ! 59: return (0); ! 60: } ! 61: ! 62: /* ! 63: * Set up the display to start a new multi-character command. ! 64: */ ! 65: start_mcc() ! 66: { ! 67: lower_left(); ! 68: clear_eol(); ! 69: putc(mcc); ! 70: cmd_col = 1; ! 71: } ! 72: ! 73: /* ! 74: * Process a single character of a multi-character command, such as ! 75: * a number, or the pattern of a search command. ! 76: */ ! 77: static int ! 78: cmd_char(c) ! 79: int c; ! 80: { ! 81: if (c == erase_char) ! 82: { ! 83: if (cmd_erase()) ! 84: return (1); ! 85: } else if (c == kill_char) ! 86: { ! 87: /* {{ Could do this faster, but who cares? }} */ ! 88: while (cmd_erase() == 0) ! 89: ; ! 90: } else ! 91: { ! 92: /* ! 93: * Append the character to the string, ! 94: * if there is room in the buffer and on the screen. ! 95: */ ! 96: if (cp < &cmdbuf[sizeof(cmdbuf)-1] && cmd_col < sc_width-3) ! 97: { ! 98: *cp++ = c; ! 99: if (control_char(c)) ! 100: { ! 101: putc('^'); ! 102: cmd_col++; ! 103: c = carat_char(c); ! 104: } ! 105: putc(c); ! 106: cmd_col++; ! 107: } else ! 108: bell(); ! 109: } ! 110: return (0); ! 111: } ! 112: ! 113: /* ! 114: * Return the number currently in the command buffer. ! 115: */ ! 116: static int ! 117: cmd_int() ! 118: { ! 119: *cp = '\0'; ! 120: cp = cmdbuf; ! 121: return (atoi(cmdbuf)); ! 122: } ! 123: ! 124: /* ! 125: * Move the cursor to lower left before executing a command. ! 126: * This looks nicer if the command takes a long time before ! 127: * updating the screen. ! 128: */ ! 129: static void ! 130: cmd_exec() ! 131: { ! 132: lower_left(); ! 133: flush(); ! 134: } ! 135: ! 136: /* ! 137: * Display the appropriate prompt. ! 138: */ ! 139: static void ! 140: prompt() ! 141: { ! 142: register char *p; ! 143: ! 144: if (first_cmd != NULL && *first_cmd != '\0') ! 145: /* ! 146: * No prompt necessary if commands are from first_cmd ! 147: * rather than from the user. ! 148: */ ! 149: return; ! 150: ! 151: /* ! 152: * Select the proper prompt and display it. ! 153: */ ! 154: p = pr_string(); ! 155: if (p == NULL) ! 156: putc(':'); ! 157: else ! 158: { ! 159: so_enter(); ! 160: puts(p); ! 161: so_exit(); ! 162: } ! 163: } ! 164: ! 165: /* ! 166: * Get command character. ! 167: * The character normally comes from the keyboard, ! 168: * but may come from the "first_cmd" string. ! 169: */ ! 170: static int ! 171: getcc() ! 172: { ! 173: if (first_cmd == NULL) ! 174: return (getc()); ! 175: ! 176: if (*first_cmd == '\0') ! 177: { ! 178: /* ! 179: * Reached end of first_cmd input. ! 180: */ ! 181: first_cmd = NULL; ! 182: if (cp > cmdbuf && position(TOP) == NULL_POSITION) ! 183: { ! 184: /* ! 185: * Command is incomplete, so try to complete it. ! 186: * There are only two cases: ! 187: * 1. We have "/string" but no newline. Add the \n. ! 188: * 2. We have a number but no command. Treat as #g. ! 189: * (This is all pretty hokey.) ! 190: */ ! 191: if (mcc != ':') ! 192: return ('\n'); ! 193: else ! 194: return ('g'); ! 195: } ! 196: return (getc()); ! 197: } ! 198: return (*first_cmd++); ! 199: } ! 200: ! 201: /* ! 202: * Main command processor. ! 203: * Accept and execute commands until a quit command, then return. ! 204: */ ! 205: public void ! 206: commands() ! 207: { ! 208: register int c; ! 209: register int n; ! 210: register int scroll = 10; ! 211: ! 212: mcc = last_mcc = 0; ! 213: ! 214: setjmp(main_loop); ! 215: for (;;) ! 216: { ! 217: /* ! 218: * Display prompt and accept a character. ! 219: */ ! 220: psignals(); /* See if any signals need processing */ ! 221: ! 222: if (quit_at_eof && hit_eof > 1) ! 223: /* ! 224: * After hitting end-of-file for the second time, ! 225: * automatically advance to the next file. ! 226: * If there are no more files, quit. ! 227: */ ! 228: next_file(1); ! 229: ! 230: cmd_reset(); ! 231: lower_left(); ! 232: clear_eol(); ! 233: prompt(); ! 234: c = getcc(); ! 235: ! 236: again: ! 237: if (sigs) ! 238: continue; ! 239: ! 240: if (mcc) ! 241: { ! 242: /* ! 243: * We are in a multi-character command. ! 244: * All chars until newline go into the command buffer. ! 245: * (Note that mcc == ':' is a special case that ! 246: * means a number is being entered.) ! 247: */ ! 248: if (mcc != ':' && (c == '\n' || c == '\r')) ! 249: { ! 250: /* ! 251: * Execute the command. ! 252: */ ! 253: *cp = '\0'; ! 254: cmd_exec(); ! 255: if (mcc == 'E') ! 256: { ! 257: char *p; ! 258: /* ! 259: * Ignore leading spaces ! 260: * in the filename. ! 261: */ ! 262: for (p = cmdbuf; *p == ' '; p++) ; ! 263: edit(p); ! 264: #if SHELL_ESCAPE ! 265: } else if (mcc == '!') ! 266: { ! 267: lsystem(cmdbuf); ! 268: error("!done"); ! 269: first_cmd = "r"; /* Repaint */ ! 270: #endif ! 271: } else ! 272: search(mcc, cmdbuf, n); ! 273: mcc = 0; ! 274: } else ! 275: { ! 276: if (mcc == ':' && (c < '0' || c > '9') && ! 277: c != erase_char && c != kill_char) ! 278: { ! 279: /* ! 280: * This is not part of the number ! 281: * we were entering. Process ! 282: * it as a regular character. ! 283: */ ! 284: mcc = 0; ! 285: goto again; ! 286: } ! 287: ! 288: /* ! 289: * Append the char to the command buffer. ! 290: */ ! 291: if (cmd_char(c)) ! 292: { ! 293: /* Abort the multi-char command. */ ! 294: mcc = 0; ! 295: continue; ! 296: } ! 297: c = getcc(); ! 298: goto again; ! 299: } ! 300: } else switch (c) ! 301: { ! 302: case '0': case '1': case '2': case '3': case '4': ! 303: case '5': case '6': case '7': case '8': case '9': ! 304: /* ! 305: * First digit of a number. ! 306: */ ! 307: mcc = ':'; ! 308: start_mcc(); ! 309: goto again; ! 310: ! 311: case 'f': ! 312: case ' ': ! 313: case CONTROL('F'): ! 314: /* ! 315: * Forward one screen. ! 316: */ ! 317: n = cmd_int(); ! 318: if (n <= 0) ! 319: n = sc_height - 1; ! 320: forward(n, 1); ! 321: break; ! 322: ! 323: case 'b': ! 324: case CONTROL('B'): ! 325: /* ! 326: * Backward one screen. ! 327: */ ! 328: n = cmd_int(); ! 329: if (n <= 0) ! 330: n = sc_height - 1; ! 331: backward(n, 1); ! 332: break; ! 333: ! 334: case 'e': ! 335: case 'j': ! 336: case '\r': ! 337: case '\n': ! 338: case CONTROL('E'): ! 339: /* ! 340: * Forward N (default 1) line. ! 341: */ ! 342: n = cmd_int(); ! 343: if (n <= 0) ! 344: n = 1; ! 345: forward(n, 0); ! 346: break; ! 347: ! 348: case 'y': ! 349: case 'k': ! 350: case CONTROL('K'): ! 351: case CONTROL('Y'): ! 352: /* ! 353: * Backward N (default 1) line. ! 354: */ ! 355: n = cmd_int(); ! 356: if (n <= 0) ! 357: n = 1; ! 358: backward(n, 0); ! 359: break; ! 360: ! 361: case 'd': ! 362: case CONTROL('D'): ! 363: /* ! 364: * Forward N lines ! 365: * (default same as last 'd' or 'u' command). ! 366: */ ! 367: n = cmd_int(); ! 368: if (n > 0) ! 369: scroll = n; ! 370: forward(scroll, 0); ! 371: break; ! 372: ! 373: case 'u': ! 374: case CONTROL('U'): ! 375: /* ! 376: * Forward N lines ! 377: * (default same as last 'd' or 'u' command). ! 378: */ ! 379: n = cmd_int(); ! 380: if (n > 0) ! 381: scroll = n; ! 382: backward(scroll, 0); ! 383: break; ! 384: ! 385: case 'R': ! 386: /* ! 387: * Flush buffers, then repaint screen. ! 388: */ ! 389: ch_init(0); ! 390: /* Fall thru */ ! 391: case 'r': ! 392: case CONTROL('R'): ! 393: case CONTROL('L'): ! 394: /* ! 395: * Repaint screen. ! 396: */ ! 397: repaint(); ! 398: break; ! 399: ! 400: case 'g': ! 401: /* ! 402: * Go to line N, default beginning of file. ! 403: */ ! 404: n = cmd_int(); ! 405: if (n <= 0) ! 406: n = 1; ! 407: cmd_exec(); ! 408: jump_back(n); ! 409: break; ! 410: ! 411: case 'p': ! 412: case '%': ! 413: /* ! 414: * Go to a specified percentage into the file. ! 415: */ ! 416: n = cmd_int(); ! 417: if (n < 0) ! 418: n = 0; ! 419: if (n > 100) ! 420: n = 100; ! 421: cmd_exec(); ! 422: jump_percent(n); ! 423: break; ! 424: ! 425: case 'G': ! 426: /* ! 427: * Go to line N, default end of file. ! 428: */ ! 429: n = cmd_int(); ! 430: cmd_exec(); ! 431: if (n <= 0) ! 432: jump_forw(); ! 433: else ! 434: jump_back(n); ! 435: break; ! 436: ! 437: case '=': ! 438: case CONTROL('G'): ! 439: /* ! 440: * Print file name, etc. ! 441: */ ! 442: error(eq_message()); ! 443: break; ! 444: ! 445: case 'V': ! 446: /* ! 447: * Print version number, without the "@(#)". ! 448: */ ! 449: error(version+4); ! 450: break; ! 451: ! 452: case 'q': ! 453: /* ! 454: * Exit. ! 455: */ ! 456: return; ! 457: ! 458: case '/': ! 459: case '?': ! 460: /* ! 461: * Search for a pattern. ! 462: * Accept chars of the pattern until \n. ! 463: */ ! 464: n = cmd_int(); ! 465: if (n <= 0) ! 466: n = 1; ! 467: mcc = last_mcc = c; ! 468: start_mcc(); ! 469: c = getcc(); ! 470: goto again; ! 471: ! 472: case 'n': ! 473: /* ! 474: * Repeat previous search. ! 475: */ ! 476: n = cmd_int(); ! 477: if (n <= 0) ! 478: n = 1; ! 479: mcc = last_mcc; ! 480: start_mcc(); ! 481: cmd_exec(); ! 482: search(mcc, (char *)NULL, n); ! 483: mcc = 0; ! 484: break; ! 485: ! 486: case 'h': ! 487: /* ! 488: * Help. ! 489: */ ! 490: help(); ! 491: repaint(); ! 492: break; ! 493: ! 494: case 'E': ! 495: /* ! 496: * Edit a new file. Get the filename. ! 497: */ ! 498: cmd_reset(); ! 499: mcc = 'E'; ! 500: start_mcc(); ! 501: puts("dit: "); /* This looks nicer */ ! 502: cmd_col += 5; ! 503: c = getcc(); ! 504: goto again; ! 505: ! 506: #if SHELL_ESCAPE ! 507: case '!': ! 508: /* ! 509: * Shell escape. ! 510: */ ! 511: cmd_reset(); ! 512: mcc = '!'; ! 513: start_mcc(); ! 514: c = getcc(); ! 515: goto again; ! 516: #endif ! 517: ! 518: #if EDITOR ! 519: case 'v': ! 520: if (ispipe) ! 521: { ! 522: error("Cannot edit standard input"); ! 523: break; ! 524: } ! 525: sprintf(cmdbuf, "%s %s", editor, current_file); ! 526: lsystem(cmdbuf); ! 527: first_cmd = "R"; ! 528: break; ! 529: #endif ! 530: ! 531: case 'N': ! 532: /* ! 533: * Examine next file. ! 534: */ ! 535: n = cmd_int(); ! 536: if (n <= 0) ! 537: n = 1; ! 538: next_file(n); ! 539: break; ! 540: ! 541: case 'P': ! 542: /* ! 543: * Examine previous file. ! 544: */ ! 545: n = cmd_int(); ! 546: if (n <= 0) ! 547: n = 1; ! 548: prev_file(n); ! 549: break; ! 550: ! 551: case '-': ! 552: /* ! 553: * Toggle a flag setting. ! 554: */ ! 555: mcc = '-'; ! 556: start_mcc(); ! 557: c = getcc(); ! 558: mcc = 0; ! 559: if (c == erase_char || c == kill_char) ! 560: break; ! 561: toggle_option(c); ! 562: break; ! 563: ! 564: case 'm': ! 565: /* ! 566: * Set a mark. ! 567: */ ! 568: lower_left(); ! 569: clear_eol(); ! 570: puts("mark: "); ! 571: c = getcc(); ! 572: if (c == erase_char || c == kill_char) ! 573: break; ! 574: setmark(c); ! 575: break; ! 576: ! 577: case '\'': ! 578: /* ! 579: * Go to a mark. ! 580: */ ! 581: lower_left(); ! 582: clear_eol(); ! 583: puts("goto mark: "); ! 584: c = getcc(); ! 585: if (c == erase_char || c == kill_char) ! 586: break; ! 587: gomark(c); ! 588: break; ! 589: ! 590: default: ! 591: bell(); ! 592: break; ! 593: } ! 594: } ! 595: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.