Annotation of src/usr.bin/more/command.c, Revision 1.1
1.1 ! deraadt 1: /*
! 2: * Copyright (c) 1988 Mark Nudleman
! 3: * Copyright (c) 1988 Regents of the University of California.
! 4: * All rights reserved.
! 5: *
! 6: * Redistribution and use in source and binary forms, with or without
! 7: * modification, are permitted provided that the following conditions
! 8: * are met:
! 9: * 1. Redistributions of source code must retain the above copyright
! 10: * notice, this list of conditions and the following disclaimer.
! 11: * 2. Redistributions in binary form must reproduce the above copyright
! 12: * notice, this list of conditions and the following disclaimer in the
! 13: * documentation and/or other materials provided with the distribution.
! 14: * 3. All advertising materials mentioning features or use of this software
! 15: * must display the following acknowledgement:
! 16: * This product includes software developed by the University of
! 17: * California, Berkeley and its contributors.
! 18: * 4. Neither the name of the University nor the names of its contributors
! 19: * may be used to endorse or promote products derived from this software
! 20: * without specific prior written permission.
! 21: *
! 22: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
! 23: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 24: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 25: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
! 26: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 27: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 28: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 29: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 30: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 31: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 32: * SUCH DAMAGE.
! 33: */
! 34:
! 35: #ifndef lint
! 36: /* from: static char sccsid[] = "@(#)command.c 5.22 (Berkeley) 6/21/92"; */
! 37: static char *rcsid = "$Id: command.c,v 1.6 1995/08/06 09:22:30 ghudson Exp $";
! 38: #endif /* not lint */
! 39:
! 40: #include <sys/param.h>
! 41: #include <stdio.h>
! 42: #include <string.h>
! 43: #include <ctype.h>
! 44: #include <less.h>
! 45: #include "pathnames.h"
! 46:
! 47: #define NO_MCA 0
! 48: #define MCA_DONE 1
! 49: #define MCA_MORE 2
! 50:
! 51: extern int erase_char, kill_char, werase_char;
! 52: extern int ispipe;
! 53: extern int sigs;
! 54: extern int quit_at_eof;
! 55: extern int hit_eof;
! 56: extern int sc_width;
! 57: extern int sc_height;
! 58: extern int sc_window;
! 59: extern int curr_ac;
! 60: extern int ac;
! 61: extern int quitting;
! 62: extern int scroll;
! 63: extern int screen_trashed; /* The screen has been overwritten */
! 64: extern int novice;
! 65:
! 66: static char cmdbuf[120]; /* Buffer for holding a multi-char command */
! 67: static char *cp; /* Pointer into cmdbuf */
! 68: static int cmd_col; /* Current column of the multi-char command */
! 69: static int longprompt; /* if stat command instead of prompt */
! 70: static int helpprompt; /* If help directions instead of prompt */
! 71: static int mca; /* The multicharacter command (action) */
! 72: static int last_mca; /* The previous mca */
! 73: static int number; /* The number typed by the user */
! 74: static char *shellcmd = NULL; /* Pointer to a shell command */
! 75: static int wsearch; /* Search for matches (1) or non-matches (0) */
! 76:
! 77: #define CMD_RESET cp = cmdbuf /* reset command buffer to empty */
! 78: #define CMD_EXEC lower_left(); flush()
! 79:
! 80: /* backspace in command buffer. */
! 81: static
! 82: cmd_erase()
! 83: {
! 84: /*
! 85: * backspace past beginning of the string: this usually means
! 86: * abort the command.
! 87: */
! 88: if (cp == cmdbuf)
! 89: return(1);
! 90:
! 91: /* erase an extra character, for the carat. */
! 92: if (CONTROL_CHAR(*--cp)) {
! 93: backspace();
! 94: --cmd_col;
! 95: }
! 96:
! 97: backspace();
! 98: --cmd_col;
! 99: return(0);
! 100: }
! 101:
! 102: /* set up the display to start a new multi-character command. */
! 103: start_mca(action, prompt)
! 104: int action;
! 105: char *prompt;
! 106: {
! 107: lower_left();
! 108: clear_eol();
! 109: putstr(prompt);
! 110: cmd_col = strlen(prompt);
! 111: mca = action;
! 112: }
! 113:
! 114: /*
! 115: * process a single character of a multi-character command, such as
! 116: * a number, or the pattern of a search command.
! 117: */
! 118: static
! 119: cmd_char(c)
! 120: int c;
! 121: {
! 122: if (c == erase_char)
! 123: return(cmd_erase());
! 124: /* in this order, in case werase == erase_char */
! 125: if (c == werase_char) {
! 126: if (cp > cmdbuf) {
! 127: while (isspace(cp[-1]) && !cmd_erase());
! 128: while (!isspace(cp[-1]) && !cmd_erase());
! 129: while (isspace(cp[-1]) && !cmd_erase());
! 130: }
! 131: return(cp == cmdbuf);
! 132: }
! 133: if (c == kill_char) {
! 134: while (!cmd_erase());
! 135: return(1);
! 136: }
! 137: /*
! 138: * No room in the command buffer, or no room on the screen;
! 139: * {{ Could get fancy here; maybe shift the displayed line
! 140: * and make room for more chars, like ksh. }}
! 141: */
! 142: if (cp >= &cmdbuf[sizeof(cmdbuf)-1] || cmd_col >= sc_width-3)
! 143: bell();
! 144: else {
! 145: *cp++ = c;
! 146: if (CONTROL_CHAR(c)) {
! 147: putchr('^');
! 148: cmd_col++;
! 149: c = CARAT_CHAR(c);
! 150: }
! 151: putchr(c);
! 152: cmd_col++;
! 153: }
! 154: return(0);
! 155: }
! 156:
! 157: off_t position();
! 158:
! 159: prompt()
! 160: {
! 161: extern int linenums, short_file;
! 162: extern char *current_name, *firstsearch, *next_name;
! 163: off_t len, pos, ch_length(), forw_line();
! 164: char pbuf[40];
! 165:
! 166: /*
! 167: * if nothing is displayed yet, display starting from line 1;
! 168: * if search string provided, go there instead.
! 169: */
! 170: if (position(TOP) == NULL_POSITION) {
! 171: if (forw_line((off_t)0) == NULL_POSITION)
! 172: return(0);
! 173: if (!firstsearch || !search(1, firstsearch, 1, 1))
! 174: jump_back(1);
! 175: }
! 176: else if (screen_trashed)
! 177: repaint();
! 178:
! 179: /* if no -e flag and we've hit EOF on the last file, quit. */
! 180: if ((!quit_at_eof || short_file) && hit_eof && curr_ac + 1 >= ac)
! 181: quit();
! 182:
! 183: /* select the proper prompt and display it. */
! 184: lower_left();
! 185: clear_eol();
! 186: if (longprompt) {
! 187: so_enter();
! 188: putstr(current_name);
! 189: putstr(":");
! 190: if (!ispipe) {
! 191: (void)sprintf(pbuf, " file %d/%d", curr_ac + 1, ac);
! 192: putstr(pbuf);
! 193: }
! 194: if (linenums) {
! 195: (void)sprintf(pbuf, " line %d", currline(BOTTOM));
! 196: putstr(pbuf);
! 197: }
! 198: if ((pos = position(BOTTOM)) != NULL_POSITION) {
! 199: (void)sprintf(pbuf, " byte %qd", pos);
! 200: putstr(pbuf);
! 201: if (!ispipe && (len = ch_length())) {
! 202: (void)sprintf(pbuf, "/%qd pct %qd%%",
! 203: len, ((100 * pos) / len));
! 204: putstr(pbuf);
! 205: }
! 206: }
! 207: so_exit();
! 208: longprompt = 0;
! 209: }
! 210: else if (helpprompt) {
! 211: so_enter();
! 212: putstr("[Press 'h' for instructions.]");
! 213: so_exit();
! 214: helpprompt = 0;
! 215: }
! 216: else {
! 217: so_enter();
! 218: putstr(current_name);
! 219: if (hit_eof)
! 220: if (next_name) {
! 221: putstr(": END (next file: ");
! 222: putstr(next_name);
! 223: putstr(")");
! 224: }
! 225: else
! 226: putstr(": END");
! 227: else if (!ispipe &&
! 228: (pos = position(BOTTOM)) != NULL_POSITION &&
! 229: (len = ch_length())) {
! 230: (void)sprintf(pbuf, " (%qd%%)", ((100 * pos) / len));
! 231: putstr(pbuf);
! 232: }
! 233: if (novice)
! 234: putstr(" [Press space to continue, 'q' to quit.]");
! 235: so_exit();
! 236: }
! 237: return(1);
! 238: }
! 239:
! 240: /* get command character. */
! 241: static
! 242: getcc()
! 243: {
! 244: extern int cmdstack;
! 245: int ch;
! 246:
! 247: /* left over from error() routine. */
! 248: if (cmdstack) {
! 249: ch = cmdstack;
! 250: cmdstack = NULL;
! 251: return(ch);
! 252: }
! 253: if (cp > cmdbuf && position(TOP) == NULL_POSITION) {
! 254: /*
! 255: * Command is incomplete, so try to complete it.
! 256: * There are only two cases:
! 257: * 1. We have "/string" but no newline. Add the \n.
! 258: * 2. We have a number but no command. Treat as #g.
! 259: * (This is all pretty hokey.)
! 260: */
! 261: if (mca != A_DIGIT)
! 262: /* Not a number; must be search string */
! 263: return('\n');
! 264: else
! 265: /* A number; append a 'g' */
! 266: return('g');
! 267: }
! 268: return(getchr());
! 269: }
! 270:
! 271: /* execute a multicharacter command. */
! 272: static
! 273: exec_mca()
! 274: {
! 275: extern int file;
! 276: extern char *tagfile;
! 277: register char *p;
! 278: char *glob(), *fexpand();
! 279:
! 280: *cp = '\0';
! 281: CMD_EXEC;
! 282: switch (mca) {
! 283: case A_F_SEARCH:
! 284: (void)search(1, cmdbuf, number, wsearch);
! 285: break;
! 286: case A_B_SEARCH:
! 287: (void)search(0, cmdbuf, number, wsearch);
! 288: break;
! 289: case A_EXAMINE:
! 290: for (p = cmdbuf; isspace(*p); ++p);
! 291: (void)edit(glob(p));
! 292: break;
! 293: case A_SHELL:
! 294: /*
! 295: * Copy cmdbuf to shellcmd,
! 296: * expanding any special characters ("%" or "#"
! 297: * or an initial !).
! 298: */
! 299: if ((p = fexpand(cmdbuf, shellcmd)) == NULL)
! 300: break;
! 301: else if (shellcmd != NULL)
! 302: free(shellcmd);
! 303: lsystem(shellcmd = p);
! 304: error("!done");
! 305: break;
! 306: case A_TAGFILE:
! 307: for (p = cmdbuf; isspace(*p); ++p);
! 308: findtag(p);
! 309: if (tagfile == NULL)
! 310: break;
! 311: if (edit(tagfile))
! 312: (void)tagsearch();
! 313: break;
! 314: }
! 315: }
! 316:
! 317: /* add a character to a multi-character command. */
! 318: static
! 319: mca_char(c)
! 320: int c;
! 321: {
! 322: switch (mca) {
! 323: case 0: /* not in a multicharacter command. */
! 324: case A_PREFIX: /* in the prefix of a command. */
! 325: return(NO_MCA);
! 326: case A_DIGIT:
! 327: /*
! 328: * Entering digits of a number.
! 329: * Terminated by a non-digit.
! 330: */
! 331: if (!isascii(c) || !isdigit(c) &&
! 332: c != erase_char && c != kill_char && c != werase_char) {
! 333: /*
! 334: * Not part of the number.
! 335: * Treat as a normal command character.
! 336: */
! 337: *cp = '\0';
! 338: number = atoi(cmdbuf);
! 339: CMD_RESET;
! 340: mca = 0;
! 341: return(NO_MCA);
! 342: }
! 343: break;
! 344: }
! 345:
! 346: /*
! 347: * Any other multicharacter command
! 348: * is terminated by a newline.
! 349: */
! 350: if (c == '\n' || c == '\r') {
! 351: exec_mca();
! 352: return(MCA_DONE);
! 353: }
! 354:
! 355: /* append the char to the command buffer. */
! 356: if (cmd_char(c))
! 357: return(MCA_DONE);
! 358:
! 359: return(MCA_MORE);
! 360: }
! 361:
! 362: /*
! 363: * Main command processor.
! 364: * Accept and execute commands until a quit command, then return.
! 365: */
! 366: commands()
! 367: {
! 368: register int c;
! 369: register int action;
! 370:
! 371: last_mca = 0;
! 372: scroll = (sc_height + 1) / 2;
! 373:
! 374: for (;;) {
! 375: mca = 0;
! 376: number = 0;
! 377:
! 378: /*
! 379: * See if any signals need processing.
! 380: */
! 381: if (sigs) {
! 382: psignals();
! 383: if (quitting)
! 384: quit();
! 385: }
! 386: /*
! 387: * Display prompt and accept a character.
! 388: */
! 389: CMD_RESET;
! 390: if (!prompt()) {
! 391: next_file(1);
! 392: continue;
! 393: }
! 394: noprefix();
! 395: c = getcc();
! 396:
! 397: again: if (sigs)
! 398: continue;
! 399:
! 400: /*
! 401: * If we are in a multicharacter command, call mca_char.
! 402: * Otherwise we call cmd_decode to determine the
! 403: * action to be performed.
! 404: */
! 405: if (mca)
! 406: switch (mca_char(c)) {
! 407: case MCA_MORE:
! 408: /*
! 409: * Need another character.
! 410: */
! 411: c = getcc();
! 412: goto again;
! 413: case MCA_DONE:
! 414: /*
! 415: * Command has been handled by mca_char.
! 416: * Start clean with a prompt.
! 417: */
! 418: continue;
! 419: case NO_MCA:
! 420: /*
! 421: * Not a multi-char command
! 422: * (at least, not anymore).
! 423: */
! 424: break;
! 425: }
! 426:
! 427: /* decode the command character and decide what to do. */
! 428: switch (action = cmd_decode(c)) {
! 429: case A_DIGIT: /* first digit of a number */
! 430: start_mca(A_DIGIT, ":");
! 431: goto again;
! 432: case A_F_SCREEN: /* forward one screen */
! 433: CMD_EXEC;
! 434: if (number <= 0 && (number = sc_window) <= 0)
! 435: number = sc_height - 1;
! 436: forward(number, 1);
! 437: break;
! 438: case A_B_SCREEN: /* backward one screen */
! 439: CMD_EXEC;
! 440: if (number <= 0 && (number = sc_window) <= 0)
! 441: number = sc_height - 1;
! 442: backward(number, 1);
! 443: break;
! 444: case A_F_LINE: /* forward N (default 1) line */
! 445: CMD_EXEC;
! 446: forward(number <= 0 ? 1 : number, 0);
! 447: break;
! 448: case A_B_LINE: /* backward N (default 1) line */
! 449: CMD_EXEC;
! 450: backward(number <= 0 ? 1 : number, 0);
! 451: break;
! 452: case A_F_SCROLL: /* forward N lines */
! 453: CMD_EXEC;
! 454: if (number > 0)
! 455: scroll = number;
! 456: forward(scroll, 0);
! 457: break;
! 458: case A_B_SCROLL: /* backward N lines */
! 459: CMD_EXEC;
! 460: if (number > 0)
! 461: scroll = number;
! 462: backward(scroll, 0);
! 463: break;
! 464: case A_FREPAINT: /* flush buffers and repaint */
! 465: if (!ispipe) {
! 466: ch_init(0, 0);
! 467: clr_linenum();
! 468: }
! 469: /* FALLTHROUGH */
! 470: case A_REPAINT: /* repaint the screen */
! 471: CMD_EXEC;
! 472: repaint();
! 473: break;
! 474: case A_GOLINE: /* go to line N, default 1 */
! 475: CMD_EXEC;
! 476: if (number <= 0)
! 477: number = 1;
! 478: jump_back(number);
! 479: break;
! 480: case A_PERCENT: /* go to percent of file */
! 481: CMD_EXEC;
! 482: if (number < 0)
! 483: number = 0;
! 484: else if (number > 100)
! 485: number = 100;
! 486: jump_percent(number);
! 487: break;
! 488: case A_GOEND: /* go to line N, default end */
! 489: CMD_EXEC;
! 490: if (number <= 0)
! 491: jump_forw();
! 492: else
! 493: jump_back(number);
! 494: break;
! 495: case A_STAT: /* print file name, etc. */
! 496: longprompt = 1;
! 497: continue;
! 498: case A_QUIT: /* exit */
! 499: quit();
! 500: case A_F_SEARCH: /* search for a pattern */
! 501: case A_B_SEARCH:
! 502: if (number <= 0)
! 503: number = 1;
! 504: start_mca(action, (action==A_F_SEARCH) ? "/" : "?");
! 505: last_mca = mca;
! 506: wsearch = 1;
! 507: c = getcc();
! 508: if (c == '!') {
! 509: /*
! 510: * Invert the sense of the search; set wsearch
! 511: * to 0 and get a new character for the start
! 512: * of the pattern.
! 513: */
! 514: start_mca(action,
! 515: (action == A_F_SEARCH) ? "!/" : "!?");
! 516: wsearch = 0;
! 517: c = getcc();
! 518: }
! 519: goto again;
! 520: case A_AGAIN_SEARCH: /* repeat previous search */
! 521: if (number <= 0)
! 522: number = 1;
! 523: if (wsearch)
! 524: start_mca(last_mca,
! 525: (last_mca == A_F_SEARCH) ? "/" : "?");
! 526: else
! 527: start_mca(last_mca,
! 528: (last_mca == A_F_SEARCH) ? "!/" : "!?");
! 529: CMD_EXEC;
! 530: (void)search(mca == A_F_SEARCH, (char *)NULL,
! 531: number, wsearch);
! 532: break;
! 533: case A_HELP: /* help */
! 534: lower_left();
! 535: clear_eol();
! 536: putstr("help");
! 537: CMD_EXEC;
! 538: help();
! 539: break;
! 540: case A_TAGFILE: /* tag a new file */
! 541: CMD_RESET;
! 542: start_mca(A_TAGFILE, "Tag: ");
! 543: c = getcc();
! 544: goto again;
! 545: case A_FILE_LIST: /* show list of file names */
! 546: CMD_EXEC;
! 547: showlist();
! 548: repaint();
! 549: break;
! 550: case A_EXAMINE: /* edit a new file */
! 551: CMD_RESET;
! 552: start_mca(A_EXAMINE, "Examine: ");
! 553: c = getcc();
! 554: goto again;
! 555: case A_VISUAL: /* invoke the editor */
! 556: if (ispipe) {
! 557: error("Cannot edit standard input");
! 558: break;
! 559: }
! 560: CMD_EXEC;
! 561: editfile();
! 562: ch_init(0, 0);
! 563: clr_linenum();
! 564: break;
! 565: case A_NEXT_FILE: /* examine next file */
! 566: if (number <= 0)
! 567: number = 1;
! 568: next_file(number);
! 569: break;
! 570: case A_PREV_FILE: /* examine previous file */
! 571: if (number <= 0)
! 572: number = 1;
! 573: prev_file(number);
! 574: break;
! 575: case A_SETMARK: /* set a mark */
! 576: lower_left();
! 577: clear_eol();
! 578: start_mca(A_SETMARK, "mark: ");
! 579: c = getcc();
! 580: if (c == erase_char || c == kill_char)
! 581: break;
! 582: setmark(c);
! 583: break;
! 584: case A_GOMARK: /* go to mark */
! 585: lower_left();
! 586: clear_eol();
! 587: start_mca(A_GOMARK, "goto mark: ");
! 588: c = getcc();
! 589: if (c == erase_char || c == kill_char)
! 590: break;
! 591: gomark(c);
! 592: break;
! 593: case A_SHELL:
! 594: /*
! 595: * Shell escape.
! 596: */
! 597: start_mca(A_SHELL, "!");
! 598: c = getcc();
! 599: goto again;
! 600: case A_PREFIX:
! 601: /*
! 602: * The command is incomplete (more chars are needed).
! 603: * Display the current char so the user knows what's
! 604: * going on and get another character.
! 605: */
! 606: if (mca != A_PREFIX)
! 607: start_mca(A_PREFIX, "");
! 608: if (CONTROL_CHAR(c)) {
! 609: putchr('^');
! 610: c = CARAT_CHAR(c);
! 611: }
! 612: putchr(c);
! 613: c = getcc();
! 614: goto again;
! 615: default:
! 616: if (novice)
! 617: helpprompt = 1;
! 618: else
! 619: bell();
! 620: break;
! 621: }
! 622: }
! 623: }
! 624:
! 625: editfile()
! 626: {
! 627: extern char *current_file;
! 628: static int dolinenumber;
! 629: static char *editor;
! 630: int c;
! 631: char buf[MAXPATHLEN * 2 + 20], *getenv();
! 632:
! 633: if (editor == NULL) {
! 634: editor = getenv("EDITOR");
! 635: /* pass the line number to vi */
! 636: if (editor == NULL || *editor == '\0') {
! 637: editor = _PATH_VI;
! 638: dolinenumber = 1;
! 639: }
! 640: else
! 641: dolinenumber = 0;
! 642: }
! 643: if (dolinenumber && (c = currline(MIDDLE)))
! 644: (void)sprintf(buf, "%s +%d %s", editor, c, current_file);
! 645: else
! 646: (void)sprintf(buf, "%s %s", editor, current_file);
! 647: lsystem(buf);
! 648: }
! 649:
! 650: showlist()
! 651: {
! 652: extern int sc_width;
! 653: extern char **av;
! 654: register int indx, width;
! 655: int len;
! 656: char *p;
! 657:
! 658: if (ac <= 0) {
! 659: error("No files provided as arguments.");
! 660: return;
! 661: }
! 662: for (width = indx = 0; indx < ac;) {
! 663: p = strcmp(av[indx], "-") ? av[indx] : "stdin";
! 664: len = strlen(p) + 1;
! 665: if (curr_ac == indx)
! 666: len += 2;
! 667: if (width + len + 1 >= sc_width) {
! 668: if (!width) {
! 669: if (curr_ac == indx)
! 670: putchr('[');
! 671: putstr(p);
! 672: if (curr_ac == indx)
! 673: putchr(']');
! 674: ++indx;
! 675: }
! 676: width = 0;
! 677: putchr('\n');
! 678: continue;
! 679: }
! 680: if (width)
! 681: putchr(' ');
! 682: if (curr_ac == indx)
! 683: putchr('[');
! 684: putstr(p);
! 685: if (curr_ac == indx)
! 686: putchr(']');
! 687: width += len;
! 688: ++indx;
! 689: }
! 690: putchr('\n');
! 691: error((char *)NULL);
! 692: }