Annotation of src/usr.bin/less/command.c, Revision 1.7
1.1 etheisen 1: /*
1.6 millert 2: * Copyright (C) 1984-2002 Mark Nudelman
1.1 etheisen 3: *
1.6 millert 4: * You may distribute under the terms of either the GNU General Public
5: * License or the Less License, as specified in the README file.
1.1 etheisen 6: *
1.6 millert 7: * For more information about less, or for information on how to
8: * contact the author, see the README file.
1.1 etheisen 9: */
10:
11:
12: /*
13: * User-level command processor.
14: */
15:
16: #include "less.h"
1.6 millert 17: #if MSDOS_COMPILER==WIN32C
18: #include <windows.h>
19: #endif
1.1 etheisen 20: #include "position.h"
21: #include "option.h"
22: #include "cmd.h"
23:
24: extern int erase_char, kill_char;
25: extern int sigs;
26: extern int quit_at_eof;
1.6 millert 27: extern int quit_if_one_screen;
28: extern int squished;
1.1 etheisen 29: extern int hit_eof;
30: extern int sc_width;
31: extern int sc_height;
32: extern int swindow;
33: extern int jump_sline;
34: extern int quitting;
35: extern int wscroll;
1.7 ! millert 36: extern int nohelp;
1.1 etheisen 37: extern int top_scroll;
38: extern int ignore_eoi;
1.6 millert 39: extern int secure;
40: extern int hshift;
41: extern int show_attn;
1.1 etheisen 42: extern char *every_first_cmd;
43: extern char *curr_altfilename;
44: extern char version[];
45: extern struct scrpos initial_scrpos;
46: extern IFILE curr_ifile;
1.6 millert 47: extern void constant *ml_search;
48: extern void constant *ml_examine;
1.1 etheisen 49: #if SHELL_ESCAPE || PIPEC
1.6 millert 50: extern void constant *ml_shell;
1.1 etheisen 51: #endif
52: #if EDITOR
53: extern char *editor;
54: extern char *editproto;
55: #endif
56: extern int screen_trashed; /* The screen has been overwritten */
1.6 millert 57: extern int shift_count;
1.2 etheisen 58: extern int be_helpful;
59:
1.6 millert 60: static char ungot[UNGOT_SIZE];
1.1 etheisen 61: static char *ungotp = NULL;
62: #if SHELL_ESCAPE
63: static char *shellcmd = NULL; /* For holding last shell command for "!!" */
64: #endif
65: static int mca; /* The multicharacter command (action) */
66: static int search_type; /* The previous type of search */
1.6 millert 67: static LINENUM number; /* The number typed by the user */
1.1 etheisen 68: static char optchar;
69: static int optflag;
1.6 millert 70: static int optgetname;
71: static POSITION bottompos;
72: static char *help_prompt;
1.1 etheisen 73: #if PIPEC
74: static char pipec;
75: #endif
76:
77: static void multi_search();
78:
79: /*
80: * Move the cursor to lower left before executing a command.
81: * This looks nicer if the command takes a long time before
82: * updating the screen.
83: */
84: static void
85: cmd_exec()
86: {
1.6 millert 87: clear_attn();
1.1 etheisen 88: lower_left();
89: flush();
90: }
91:
92: /*
93: * Set up the display to start a new multi-character command.
94: */
95: static void
1.6 millert 96: start_mca(action, prompt, mlist, cmdflags)
1.1 etheisen 97: int action;
98: char *prompt;
99: void *mlist;
1.6 millert 100: int cmdflags;
1.1 etheisen 101: {
102: mca = action;
1.6 millert 103: clear_cmd();
1.1 etheisen 104: cmd_putstr(prompt);
1.6 millert 105: set_mlist(mlist, cmdflags);
1.1 etheisen 106: }
107:
108: public int
109: in_mca()
110: {
111: return (mca != 0 && mca != A_PREFIX);
112: }
113:
114: /*
115: * Set up the display to start a new search command.
116: */
117: static void
118: mca_search()
119: {
120: if (search_type & SRCH_FORW)
121: mca = A_F_SEARCH;
122: else
123: mca = A_B_SEARCH;
124:
1.6 millert 125: clear_cmd();
1.1 etheisen 126:
1.6 millert 127: if (search_type & SRCH_NO_MATCH)
128: cmd_putstr("Non-match ");
1.1 etheisen 129: if (search_type & SRCH_FIRST_FILE)
1.6 millert 130: cmd_putstr("First-file ");
1.1 etheisen 131: if (search_type & SRCH_PAST_EOF)
1.6 millert 132: cmd_putstr("EOF-ignore ");
133: if (search_type & SRCH_NO_MOVE)
134: cmd_putstr("Keep-pos ");
135: if (search_type & SRCH_NO_REGEX)
136: cmd_putstr("Regex-off ");
1.1 etheisen 137:
138: if (search_type & SRCH_FORW)
139: cmd_putstr("/");
140: else
141: cmd_putstr("?");
1.6 millert 142: set_mlist(ml_search, 0);
143: }
144:
145: /*
146: * Set up the display to start a new toggle-option command.
147: */
148: static void
149: mca_opt_toggle()
150: {
151: int no_prompt;
152: int flag;
153: char *dash;
154:
155: no_prompt = (optflag & OPT_NO_PROMPT);
156: flag = (optflag & ~OPT_NO_PROMPT);
157: dash = (flag == OPT_NO_TOGGLE) ? "_" : "-";
158:
159: mca = A_OPT_TOGGLE;
160: clear_cmd();
161: cmd_putstr(dash);
162: if (optgetname)
163: cmd_putstr(dash);
164: if (no_prompt)
165: cmd_putstr("(P)");
166: switch (flag)
167: {
168: case OPT_UNSET:
169: cmd_putstr("+");
170: break;
171: case OPT_SET:
172: cmd_putstr("!");
173: break;
174: }
175: set_mlist(NULL, 0);
1.1 etheisen 176: }
177:
178: /*
179: * Execute a multicharacter command.
180: */
181: static void
182: exec_mca()
183: {
1.6 millert 184: register char *cbuf;
1.1 etheisen 185:
186: cmd_exec();
187: cbuf = get_cmdbuf();
188:
189: switch (mca)
190: {
191: case A_F_SEARCH:
192: case A_B_SEARCH:
1.6 millert 193: multi_search(cbuf, (int) number);
1.1 etheisen 194: break;
195: case A_FIRSTCMD:
196: /*
197: * Skip leading spaces or + signs in the string.
198: */
199: while (*cbuf == '+' || *cbuf == ' ')
200: cbuf++;
201: if (every_first_cmd != NULL)
202: free(every_first_cmd);
203: if (*cbuf == '\0')
204: every_first_cmd = NULL;
205: else
206: every_first_cmd = save(cbuf);
207: break;
208: case A_OPT_TOGGLE:
209: toggle_option(optchar, cbuf, optflag);
210: optchar = '\0';
211: break;
212: case A_F_BRACKET:
1.6 millert 213: match_brac(cbuf[0], cbuf[1], 1, (int) number);
1.1 etheisen 214: break;
215: case A_B_BRACKET:
1.6 millert 216: match_brac(cbuf[1], cbuf[0], 0, (int) number);
1.1 etheisen 217: break;
218: #if EXAMINE
219: case A_EXAMINE:
1.6 millert 220: if (secure)
221: break;
1.1 etheisen 222: edit_list(cbuf);
1.6 millert 223: #if TAGS
224: /* If tag structure is loaded then clean it up. */
225: cleantags();
226: #endif
1.1 etheisen 227: break;
228: #endif
229: #if SHELL_ESCAPE
230: case A_SHELL:
231: /*
232: * !! just uses whatever is in shellcmd.
233: * Otherwise, copy cmdbuf to shellcmd,
234: * expanding any special characters ("%" or "#").
235: */
236: if (*cbuf != '!')
237: {
238: if (shellcmd != NULL)
239: free(shellcmd);
240: shellcmd = fexpand(cbuf);
241: }
242:
1.6 millert 243: if (secure)
244: break;
1.1 etheisen 245: if (shellcmd == NULL)
1.6 millert 246: lsystem("", "!done");
1.1 etheisen 247: else
1.6 millert 248: lsystem(shellcmd, "!done");
1.1 etheisen 249: break;
250: #endif
251: #if PIPEC
252: case A_PIPE:
1.6 millert 253: if (secure)
254: break;
1.1 etheisen 255: (void) pipe_mark(pipec, cbuf);
256: error("|done", NULL_PARG);
257: break;
258: #endif
259: }
260: }
261:
262: /*
263: * Add a character to a multi-character command.
264: */
265: static int
266: mca_char(c)
267: int c;
268: {
269: char *p;
270: int flag;
271: char buf[3];
1.6 millert 272: PARG parg;
1.1 etheisen 273:
274: switch (mca)
275: {
276: case 0:
277: /*
278: * Not in a multicharacter command.
279: */
280: return (NO_MCA);
281:
282: case A_PREFIX:
283: /*
284: * In the prefix of a command.
285: * This not considered a multichar command
286: * (even tho it uses cmdbuf, etc.).
287: * It is handled in the commands() switch.
288: */
289: return (NO_MCA);
290:
291: case A_DIGIT:
292: /*
293: * Entering digits of a number.
294: * Terminated by a non-digit.
295: */
296: if ((c < '0' || c > '9') &&
1.6 millert 297: editchar(c, EC_PEEK|EC_NOHISTORY|EC_NOCOMPLETE|EC_NORIGHTLEFT) == A_INVALID)
1.1 etheisen 298: {
299: /*
300: * Not part of the number.
301: * Treat as a normal command character.
302: */
303: number = cmd_int();
304: mca = 0;
305: cmd_accept();
306: return (NO_MCA);
307: }
308: break;
309:
310: case A_OPT_TOGGLE:
311: /*
312: * Special case for the TOGGLE_OPTION command.
313: * If the option letter which was entered is a
314: * single-char option, execute the command immediately,
315: * so user doesn't have to hit RETURN.
316: * If the first char is + or -, this indicates
317: * OPT_UNSET or OPT_SET respectively, instead of OPT_TOGGLE.
1.6 millert 318: * "--" begins inputting a long option name.
1.1 etheisen 319: */
1.6 millert 320: if (optchar == '\0' && len_cmdbuf() == 0)
321: {
322: flag = (optflag & ~OPT_NO_PROMPT);
323: if (flag == OPT_NO_TOGGLE)
324: {
325: switch (c)
326: {
327: case '_':
328: /* "__" = long option name. */
329: optgetname = TRUE;
330: mca_opt_toggle();
331: return (MCA_MORE);
332: }
333: } else
334: {
335: switch (c)
336: {
337: case '+':
338: /* "-+" = UNSET. */
339: optflag = (flag == OPT_UNSET) ?
340: OPT_TOGGLE : OPT_UNSET;
341: mca_opt_toggle();
342: return (MCA_MORE);
343: case '!':
344: /* "-!" = SET */
345: optflag = (flag == OPT_SET) ?
346: OPT_TOGGLE : OPT_SET;
347: mca_opt_toggle();
348: return (MCA_MORE);
349: case CONTROL('P'):
350: optflag ^= OPT_NO_PROMPT;
351: mca_opt_toggle();
352: return (MCA_MORE);
353: case '-':
354: /* "--" = long option name. */
355: optgetname = TRUE;
356: mca_opt_toggle();
357: return (MCA_MORE);
358: }
359: }
360: }
361: if (optgetname)
362: {
1.1 etheisen 363: /*
1.6 millert 364: * We're getting a long option name.
365: * See if we've matched an option name yet.
366: * If so, display the complete name and stop
367: * accepting chars until user hits RETURN.
368: */
369: struct loption *o;
370: char *oname;
371: int lc;
372:
373: if (c == '\n' || c == '\r')
374: {
375: /*
376: * When the user hits RETURN, make sure
377: * we've matched an option name, then
378: * pretend he just entered the equivalent
379: * option letter.
380: */
381: if (optchar == '\0')
382: {
383: parg.p_string = get_cmdbuf();
384: error("There is no --%s option", &parg);
385: return (MCA_DONE);
386: }
387: optgetname = FALSE;
388: cmd_reset();
389: c = optchar;
390: } else
1.1 etheisen 391: {
1.6 millert 392: if (optchar != '\0')
393: {
394: /*
395: * Already have a match for the name.
396: * Don't accept anything but erase/kill.
397: */
398: if (c == erase_char || c == kill_char)
399: return (MCA_DONE);
400: return (MCA_MORE);
401: }
402: /*
403: * Add char to cmd buffer and try to match
404: * the option name.
405: */
406: if (cmd_char(c) == CC_QUIT)
407: return (MCA_DONE);
408: p = get_cmdbuf();
409: lc = islower(p[0]);
410: o = findopt_name(&p, &oname, NULL);
411: if (o != NULL)
412: {
413: /*
414: * Got a match.
415: * Remember the option letter and
416: * display the full option name.
417: */
418: optchar = o->oletter;
419: if (!lc && islower(optchar))
420: optchar = toupper(optchar);
421: cmd_reset();
422: mca_opt_toggle();
423: for (p = oname; *p != '\0'; p++)
424: {
425: c = *p;
426: if (!lc && islower(c))
427: c = toupper(c);
428: if (cmd_char(c) != CC_OK)
429: return (MCA_DONE);
430: }
431: }
432: return (MCA_MORE);
1.1 etheisen 433: }
1.6 millert 434: } else
435: {
436: if (c == erase_char || c == kill_char)
437: break;
438: if (optchar != '\0')
439: /* We already have the option letter. */
440: break;
1.1 etheisen 441: }
1.6 millert 442:
443: optchar = c;
444: if ((optflag & ~OPT_NO_PROMPT) != OPT_TOGGLE ||
445: single_char_option(c))
1.1 etheisen 446: {
1.6 millert 447: toggle_option(c, "", optflag);
448: return (MCA_DONE);
1.1 etheisen 449: }
450: /*
451: * Display a prompt appropriate for the option letter.
452: */
453: if ((p = opt_prompt(c)) == NULL)
454: {
455: buf[0] = '-';
456: buf[1] = c;
457: buf[2] = '\0';
458: p = buf;
459: }
1.6 millert 460: start_mca(A_OPT_TOGGLE, p, (void*)NULL, 0);
1.1 etheisen 461: return (MCA_MORE);
462:
463: case A_F_SEARCH:
464: case A_B_SEARCH:
465: /*
466: * Special case for search commands.
467: * Certain characters as the first char of
468: * the pattern have special meaning:
1.6 millert 469: * ! Toggle the NO_MATCH flag
1.1 etheisen 470: * * Toggle the PAST_EOF flag
471: * @ Toggle the FIRST_FILE flag
472: */
473: if (len_cmdbuf() > 0)
474: /*
475: * Only works for the first char of the pattern.
476: */
477: break;
478:
479: flag = 0;
480: switch (c)
481: {
1.6 millert 482: case CONTROL('E'): /* ignore END of file */
483: case '*':
484: flag = SRCH_PAST_EOF;
1.1 etheisen 485: break;
1.6 millert 486: case CONTROL('F'): /* FIRST file */
1.1 etheisen 487: case '@':
488: flag = SRCH_FIRST_FILE;
489: break;
1.6 millert 490: case CONTROL('K'): /* KEEP position */
491: flag = SRCH_NO_MOVE;
492: break;
493: case CONTROL('R'): /* Don't use REGULAR EXPRESSIONS */
494: flag = SRCH_NO_REGEX;
495: break;
496: case CONTROL('N'): /* NOT match */
497: case '!':
498: flag = SRCH_NO_MATCH;
1.1 etheisen 499: break;
500: }
501: if (flag != 0)
502: {
503: search_type ^= flag;
504: mca_search();
505: return (MCA_MORE);
506: }
507: break;
508: }
509:
510: /*
511: * Any other multicharacter command
512: * is terminated by a newline.
513: */
514: if (c == '\n' || c == '\r')
515: {
516: /*
517: * Execute the command.
518: */
519: exec_mca();
520: return (MCA_DONE);
521: }
1.6 millert 522:
1.1 etheisen 523: /*
524: * Append the char to the command buffer.
525: */
526: if (cmd_char(c) == CC_QUIT)
527: /*
528: * Abort the multi-char command.
529: */
530: return (MCA_DONE);
531:
532: if ((mca == A_F_BRACKET || mca == A_B_BRACKET) && len_cmdbuf() >= 2)
533: {
534: /*
535: * Special case for the bracket-matching commands.
536: * Execute the command after getting exactly two
537: * characters from the user.
538: */
539: exec_mca();
540: return (MCA_DONE);
541: }
542:
543: /*
544: * Need another character.
545: */
546: return (MCA_MORE);
547: }
548:
549: /*
1.6 millert 550: * Make sure the screen is displayed.
1.1 etheisen 551: */
552: static void
1.6 millert 553: make_display()
1.1 etheisen 554: {
555: /*
556: * If nothing is displayed yet, display starting from initial_scrpos.
557: */
558: if (empty_screen())
559: {
560: if (initial_scrpos.pos == NULL_POSITION)
561: /*
562: * {{ Maybe this should be:
563: * jump_loc(ch_zero(), jump_sline);
564: * but this behavior seems rather unexpected
565: * on the first screen. }}
566: */
567: jump_loc(ch_zero(), 1);
568: else
569: jump_loc(initial_scrpos.pos, initial_scrpos.ln);
570: } else if (screen_trashed)
571: {
572: int save_top_scroll;
573: save_top_scroll = top_scroll;
574: top_scroll = 1;
575: repaint();
576: top_scroll = save_top_scroll;
577: }
1.6 millert 578: }
579:
580: /*
581: * Display the appropriate prompt.
582: */
583: static void
584: prompt()
585: {
586: register char *p;
587:
588: if (ungotp != NULL && ungotp > ungot)
589: {
590: /*
591: * No prompt necessary if commands are from
592: * ungotten chars rather than from the user.
593: */
594: return;
595: }
596:
597: /*
598: * Make sure the screen is displayed.
599: */
600: make_display();
601: bottompos = position(BOTTOM_PLUS_ONE);
1.1 etheisen 602:
603: /*
604: * If the -E flag is set and we've hit EOF on the last file, quit.
605: */
1.6 millert 606: if ((quit_at_eof == OPT_ONPLUS || quit_if_one_screen) &&
1.7 ! millert 607: hit_eof && next_ifile(curr_ifile) == NULL_IFILE)
1.1 etheisen 608: quit(QUIT_OK);
1.6 millert 609: quit_if_one_screen = FALSE;
610: #if 0 /* This doesn't work well because some "te"s clear the screen. */
611: /*
612: * If the -e flag is set and we've hit EOF on the last file,
613: * and the file is squished (shorter than the screen), quit.
614: */
615: if (quit_at_eof && squished &&
616: next_ifile(curr_ifile) == NULL_IFILE)
617: quit(QUIT_OK);
618: #endif
1.1 etheisen 619:
620: /*
621: * Select the proper prompt and display it.
622: */
1.6 millert 623: clear_cmd();
624: p = help_prompt ? help_prompt : pr_string();
625: if (p == NULL)
626: putchr(':');
627: else
628: {
1.1 etheisen 629: so_enter();
1.6 millert 630: putstr(p);
631: if (be_helpful && !help_prompt && strlen(p) + 40 < sc_width)
632: putstr(" [Press space to continue, 'q' to quit.]");
1.1 etheisen 633: so_exit();
634: }
1.6 millert 635: help_prompt = NULL;
1.1 etheisen 636: }
637:
1.6 millert 638: /*
639: * Display the less version message.
640: */
1.1 etheisen 641: public void
642: dispversion()
643: {
644: PARG parg;
645:
646: parg.p_string = version;
1.6 millert 647: error("less %s", &parg);
1.1 etheisen 648: }
649:
650: /*
651: * Get command character.
652: * The character normally comes from the keyboard,
653: * but may come from ungotten characters
654: * (characters previously given to ungetcc or ungetsc).
655: */
656: public int
657: getcc()
658: {
659: if (ungotp == NULL)
660: /*
661: * Normal case: no ungotten chars, so get one from the user.
662: */
663: return (getchr());
664:
665: if (ungotp > ungot)
666: /*
667: * Return the next ungotten char.
668: */
669: return (*--ungotp);
670:
671: /*
672: * We have just run out of ungotten chars.
673: */
674: ungotp = NULL;
675: if (len_cmdbuf() == 0 || !empty_screen())
676: return (getchr());
677: /*
678: * Command is incomplete, so try to complete it.
679: */
680: switch (mca)
681: {
682: case A_DIGIT:
683: /*
684: * We have a number but no command. Treat as #g.
685: */
686: return ('g');
687:
688: case A_F_SEARCH:
689: case A_B_SEARCH:
690: /*
691: * We have "/string" but no newline. Add the \n.
692: */
693: return ('\n');
694:
695: default:
696: /*
697: * Some other incomplete command. Let user complete it.
698: */
699: return (getchr());
700: }
701: }
702:
703: /*
704: * "Unget" a command character.
705: * The next getcc() will return this character.
706: */
707: public void
708: ungetcc(c)
709: int c;
710: {
711: if (ungotp == NULL)
712: ungotp = ungot;
713: if (ungotp >= ungot + sizeof(ungot))
714: {
715: error("ungetcc overflow", NULL_PARG);
716: quit(QUIT_ERROR);
717: }
718: *ungotp++ = c;
719: }
720:
721: /*
722: * Unget a whole string of command characters.
723: * The next sequence of getcc()'s will return this string.
724: */
725: public void
726: ungetsc(s)
727: char *s;
728: {
1.6 millert 729: register char *p;
1.1 etheisen 730:
731: for (p = s + strlen(s) - 1; p >= s; p--)
732: ungetcc(*p);
733: }
734:
735: /*
736: * Search for a pattern, possibly in multiple files.
737: * If SRCH_FIRST_FILE is set, begin searching at the first file.
738: * If SRCH_PAST_EOF is set, continue the search thru multiple files.
739: */
740: static void
741: multi_search(pattern, n)
742: char *pattern;
743: int n;
744: {
1.6 millert 745: register int nomore;
1.1 etheisen 746: IFILE save_ifile;
747: int changed_file;
748:
749: changed_file = 0;
1.6 millert 750: save_ifile = save_curr_ifile();
1.1 etheisen 751:
752: if (search_type & SRCH_FIRST_FILE)
753: {
754: /*
755: * Start at the first (or last) file
756: * in the command line list.
757: */
758: if (search_type & SRCH_FORW)
759: nomore = edit_first();
760: else
761: nomore = edit_last();
762: if (nomore)
1.6 millert 763: {
764: unsave_ifile(save_ifile);
1.1 etheisen 765: return;
1.6 millert 766: }
1.1 etheisen 767: changed_file = 1;
768: search_type &= ~SRCH_FIRST_FILE;
769: }
770:
771: for (;;)
772: {
1.6 millert 773: n = search(search_type, pattern, n);
774: /*
775: * The SRCH_NO_MOVE flag doesn't "stick": it gets cleared
776: * after being used once. This allows "n" to work after
777: * using a /@@ search.
778: */
779: search_type &= ~SRCH_NO_MOVE;
780: if (n == 0)
781: {
1.1 etheisen 782: /*
783: * Found it.
784: */
1.6 millert 785: unsave_ifile(save_ifile);
1.1 etheisen 786: return;
1.6 millert 787: }
1.1 etheisen 788:
789: if (n < 0)
790: /*
791: * Some kind of error in the search.
792: * Error message has been printed by search().
793: */
794: break;
795:
796: if ((search_type & SRCH_PAST_EOF) == 0)
797: /*
798: * We didn't find a match, but we're
799: * supposed to search only one file.
800: */
801: break;
802: /*
803: * Move on to the next file.
804: */
805: if (search_type & SRCH_FORW)
806: nomore = edit_next(1);
807: else
808: nomore = edit_prev(1);
809: if (nomore)
810: break;
811: changed_file = 1;
812: }
813:
814: /*
815: * Didn't find it.
816: * Print an error message if we haven't already.
817: */
818: if (n > 0)
819: error("Pattern not found", NULL_PARG);
820:
821: if (changed_file)
822: {
823: /*
824: * Restore the file we were originally viewing.
825: */
1.6 millert 826: reedit_ifile(save_ifile);
1.1 etheisen 827: }
828: }
829:
830: /*
831: * Main command processor.
832: * Accept and execute commands until a quit command.
833: */
834: public void
835: commands()
836: {
1.6 millert 837: register int c;
838: register int action;
839: register char *cbuf;
840: int newaction;
1.1 etheisen 841: int save_search_type;
1.6 millert 842: char *extra;
1.1 etheisen 843: char tbuf[2];
844: PARG parg;
1.6 millert 845: IFILE old_ifile;
846: IFILE new_ifile;
847: char *tagfile;
1.1 etheisen 848:
849: search_type = SRCH_FORW;
850: wscroll = (sc_height + 1) / 2;
1.6 millert 851: newaction = A_NOACTION;
1.1 etheisen 852:
853: for (;;)
854: {
855: mca = 0;
856: cmd_accept();
857: number = 0;
858: optchar = '\0';
859:
860: /*
861: * See if any signals need processing.
862: */
863: if (sigs)
864: {
865: psignals();
866: if (quitting)
867: quit(QUIT_SAVED_STATUS);
868: }
1.6 millert 869:
870: /*
871: * See if window size changed, for systems that don't
872: * generate SIGWINCH.
873: */
874: check_winch();
875:
1.1 etheisen 876: /*
877: * Display prompt and accept a character.
878: */
879: cmd_reset();
880: prompt();
881: if (sigs)
882: continue;
1.6 millert 883: if (newaction == A_NOACTION)
884: c = getcc();
1.1 etheisen 885:
886: again:
887: if (sigs)
888: continue;
889:
1.6 millert 890: if (newaction != A_NOACTION)
891: {
892: action = newaction;
893: newaction = A_NOACTION;
894: } else
895: {
896: /*
897: * If we are in a multicharacter command, call mca_char.
898: * Otherwise we call fcmd_decode to determine the
899: * action to be performed.
900: */
901: if (mca)
902: switch (mca_char(c))
903: {
904: case MCA_MORE:
905: /*
906: * Need another character.
907: */
908: c = getcc();
909: goto again;
910: case MCA_DONE:
911: /*
912: * Command has been handled by mca_char.
913: * Start clean with a prompt.
914: */
915: continue;
916: case NO_MCA:
917: /*
918: * Not a multi-char command
919: * (at least, not anymore).
920: */
921: break;
922: }
923:
924: /*
925: * Decode the command character and decide what to do.
926: */
927: if (mca)
1.1 etheisen 928: {
929: /*
1.6 millert 930: * We're in a multichar command.
931: * Add the character to the command buffer
932: * and display it on the screen.
933: * If the user backspaces past the start
934: * of the line, abort the command.
1.1 etheisen 935: */
1.6 millert 936: if (cmd_char(c) == CC_QUIT || len_cmdbuf() == 0)
937: continue;
938: cbuf = get_cmdbuf();
939: } else
940: {
1.1 etheisen 941: /*
1.6 millert 942: * Don't use cmd_char if we're starting fresh
943: * at the beginning of a command, because we
944: * don't want to echo the command until we know
945: * it is a multichar command. We also don't
946: * want erase_char/kill_char to be treated
947: * as line editing characters.
1.1 etheisen 948: */
1.6 millert 949: tbuf[0] = c;
950: tbuf[1] = '\0';
951: cbuf = tbuf;
1.1 etheisen 952: }
1.6 millert 953: extra = NULL;
954: action = fcmd_decode(cbuf, &extra);
1.1 etheisen 955: /*
1.6 millert 956: * If an "extra" string was returned,
957: * process it as a string of command characters.
958: */
959: if (extra != NULL)
960: ungetsc(extra);
1.1 etheisen 961: }
962: /*
963: * Clear the cmdbuf string.
964: * (But not if we're in the prefix of a command,
965: * because the partial command string is kept there.)
966: */
967: if (action != A_PREFIX)
968: cmd_reset();
969:
970: switch (action)
971: {
972: case A_DIGIT:
973: /*
974: * First digit of a number.
975: */
1.6 millert 976: start_mca(A_DIGIT, ":", (void*)NULL, CF_QUIT_ON_ERASE);
1.1 etheisen 977: goto again;
978:
979: case A_F_WINDOW:
980: /*
981: * Forward one window (and set the window size).
982: */
983: if (number > 0)
1.6 millert 984: swindow = (int) number;
1.1 etheisen 985: /* FALLTHRU */
986: case A_F_SCREEN:
987: /*
988: * Forward one screen.
989: */
990: if (number <= 0)
991: number = get_swindow();
992: cmd_exec();
1.6 millert 993: if (show_attn)
994: set_attnpos(bottompos);
995: forward((int) number, 0, 1);
1.1 etheisen 996: break;
997:
998: case A_B_WINDOW:
999: /*
1000: * Backward one window (and set the window size).
1001: */
1002: if (number > 0)
1.6 millert 1003: swindow = (int) number;
1.1 etheisen 1004: /* FALLTHRU */
1005: case A_B_SCREEN:
1006: /*
1007: * Backward one screen.
1008: */
1009: if (number <= 0)
1010: number = get_swindow();
1011: cmd_exec();
1.6 millert 1012: backward((int) number, 0, 1);
1.1 etheisen 1013: break;
1014:
1015: case A_F_LINE:
1016: /*
1017: * Forward N (default 1) line.
1018: */
1019: if (number <= 0)
1020: number = 1;
1021: cmd_exec();
1.6 millert 1022: if (show_attn == OPT_ONPLUS && number > 1)
1023: set_attnpos(bottompos);
1024: forward((int) number, 0, 0);
1.1 etheisen 1025: break;
1026:
1027: case A_B_LINE:
1028: /*
1029: * Backward N (default 1) line.
1030: */
1031: if (number <= 0)
1032: number = 1;
1033: cmd_exec();
1.6 millert 1034: backward((int) number, 0, 0);
1.1 etheisen 1035: break;
1036:
1037: case A_FF_LINE:
1038: /*
1039: * Force forward N (default 1) line.
1040: */
1041: if (number <= 0)
1042: number = 1;
1043: cmd_exec();
1.6 millert 1044: if (show_attn == OPT_ONPLUS && number > 1)
1045: set_attnpos(bottompos);
1046: forward((int) number, 1, 0);
1.1 etheisen 1047: break;
1048:
1049: case A_BF_LINE:
1050: /*
1051: * Force backward N (default 1) line.
1052: */
1053: if (number <= 0)
1054: number = 1;
1055: cmd_exec();
1.6 millert 1056: backward((int) number, 1, 0);
1.1 etheisen 1057: break;
1058:
1.6 millert 1059: case A_FF_SCREEN:
1060: /*
1061: * Force forward one screen.
1062: */
1063: if (number <= 0)
1064: number = get_swindow();
1065: cmd_exec();
1066: if (show_attn == OPT_ONPLUS)
1067: set_attnpos(bottompos);
1068: forward((int) number, 1, 0);
1069: break;
1070:
1.1 etheisen 1071: case A_F_FOREVER:
1072: /*
1073: * Forward forever, ignoring EOF.
1074: */
1075: cmd_exec();
1076: jump_forw();
1077: ignore_eoi = 1;
1078: hit_eof = 0;
1.6 millert 1079: while (!sigs)
1.1 etheisen 1080: forward(1, 0, 0);
1081: ignore_eoi = 0;
1.6 millert 1082: /*
1083: * This gets us back in "F mode" after processing
1084: * a non-abort signal (e.g. window-change).
1085: */
1086: if (sigs && !ABORT_SIGS())
1087: newaction = A_F_FOREVER;
1.1 etheisen 1088: break;
1089:
1090: case A_F_SCROLL:
1091: /*
1092: * Forward N lines
1093: * (default same as last 'd' or 'u' command).
1094: */
1095: if (number > 0)
1.6 millert 1096: wscroll = (int) number;
1.1 etheisen 1097: cmd_exec();
1.6 millert 1098: if (show_attn == OPT_ONPLUS)
1099: set_attnpos(bottompos);
1.1 etheisen 1100: forward(wscroll, 0, 0);
1101: break;
1102:
1103: case A_B_SCROLL:
1104: /*
1105: * Forward N lines
1106: * (default same as last 'd' or 'u' command).
1107: */
1108: if (number > 0)
1.6 millert 1109: wscroll = (int) number;
1.1 etheisen 1110: cmd_exec();
1111: backward(wscroll, 0, 0);
1112: break;
1113:
1114: case A_FREPAINT:
1115: /*
1116: * Flush buffers, then repaint screen.
1117: * Don't flush the buffers on a pipe!
1118: */
1119: if (ch_getflags() & CH_CANSEEK)
1120: {
1121: ch_flush();
1122: clr_linenum();
1.6 millert 1123: #if HILITE_SEARCH
1124: clr_hilite();
1125: #endif
1.1 etheisen 1126: }
1127: /* FALLTHRU */
1128: case A_REPAINT:
1129: /*
1130: * Repaint screen.
1131: */
1132: cmd_exec();
1133: repaint();
1134: break;
1135:
1136: case A_GOLINE:
1137: /*
1138: * Go to line N, default beginning of file.
1139: */
1140: if (number <= 0)
1141: number = 1;
1142: cmd_exec();
1143: jump_back(number);
1144: break;
1145:
1146: case A_PERCENT:
1147: /*
1148: * Go to a specified percentage into the file.
1149: */
1150: if (number < 0)
1151: number = 0;
1152: if (number > 100)
1153: number = 100;
1154: cmd_exec();
1.6 millert 1155: jump_percent((int) number);
1.1 etheisen 1156: break;
1157:
1158: case A_GOEND:
1159: /*
1160: * Go to line N, default end of file.
1161: */
1162: cmd_exec();
1163: if (number <= 0)
1164: jump_forw();
1165: else
1166: jump_back(number);
1167: break;
1168:
1169: case A_GOPOS:
1170: /*
1171: * Go to a specified byte position in the file.
1172: */
1173: cmd_exec();
1174: if (number < 0)
1175: number = 0;
1.6 millert 1176: jump_line_loc((POSITION) number, jump_sline);
1.1 etheisen 1177: break;
1178:
1179: case A_STAT:
1180: /*
1181: * Print file name, etc.
1182: */
1183: cmd_exec();
1184: parg.p_string = eq_message();
1185: error("%s", &parg);
1186: break;
1.6 millert 1187:
1.1 etheisen 1188: case A_VERSION:
1189: /*
1190: * Print version number, without the "@(#)".
1191: */
1192: cmd_exec();
1193: dispversion();
1194: break;
1195:
1196: case A_QUIT:
1197: /*
1198: * Exit.
1199: */
1.6 millert 1200: if (extra != NULL)
1201: quit(*extra);
1.1 etheisen 1202: quit(QUIT_OK);
1.6 millert 1203: break;
1.1 etheisen 1204:
1205: /*
1206: * Define abbreviation for a commonly used sequence below.
1207: */
1208: #define DO_SEARCH() if (number <= 0) number = 1; \
1209: mca_search(); \
1210: cmd_exec(); \
1.6 millert 1211: multi_search((char *)NULL, (int) number);
1.1 etheisen 1212:
1213:
1214: case A_F_SEARCH:
1215: /*
1216: * Search forward for a pattern.
1217: * Get the first char of the pattern.
1218: */
1219: search_type = SRCH_FORW;
1220: if (number <= 0)
1221: number = 1;
1222: mca_search();
1223: c = getcc();
1224: goto again;
1225:
1226: case A_B_SEARCH:
1227: /*
1228: * Search backward for a pattern.
1229: * Get the first char of the pattern.
1230: */
1231: search_type = SRCH_BACK;
1232: if (number <= 0)
1233: number = 1;
1234: mca_search();
1235: c = getcc();
1236: goto again;
1237:
1238: case A_AGAIN_SEARCH:
1239: /*
1240: * Repeat previous search.
1241: */
1242: DO_SEARCH();
1243: break;
1244:
1245: case A_T_AGAIN_SEARCH:
1246: /*
1247: * Repeat previous search, multiple files.
1248: */
1249: search_type |= SRCH_PAST_EOF;
1250: DO_SEARCH();
1251: break;
1252:
1253: case A_REVERSE_SEARCH:
1254: /*
1255: * Repeat previous search, in reverse direction.
1256: */
1257: save_search_type = search_type;
1258: search_type = SRCH_REVERSE(search_type);
1259: DO_SEARCH();
1260: search_type = save_search_type;
1261: break;
1262:
1263: case A_T_REVERSE_SEARCH:
1264: /*
1265: * Repeat previous search,
1266: * multiple files in reverse direction.
1267: */
1268: save_search_type = search_type;
1269: search_type = SRCH_REVERSE(search_type);
1270: search_type |= SRCH_PAST_EOF;
1271: DO_SEARCH();
1272: search_type = save_search_type;
1273: break;
1274:
1275: case A_UNDO_SEARCH:
1276: undo_search();
1277: break;
1278:
1279: case A_HELP:
1280: /*
1281: * Help.
1282: */
1.7 ! millert 1283: if (nohelp)
! 1284: {
! 1285: bell();
1.1 etheisen 1286: break;
1.7 ! millert 1287: }
! 1288: clear_bot();
! 1289: putstr(" help");
1.1 etheisen 1290: cmd_exec();
1.7 ! millert 1291: help(0);
1.1 etheisen 1292: break;
1293:
1294: case A_EXAMINE:
1295: #if EXAMINE
1296: /*
1297: * Edit a new file. Get the filename.
1298: */
1.6 millert 1299: if (secure)
1300: {
1301: error("Command not available", NULL_PARG);
1302: break;
1303: }
1304: start_mca(A_EXAMINE, "Examine: ", ml_examine, 0);
1.1 etheisen 1305: c = getcc();
1306: goto again;
1307: #else
1308: error("Command not available", NULL_PARG);
1309: break;
1310: #endif
1311:
1312: case A_VISUAL:
1313: /*
1314: * Invoke an editor on the input file.
1315: */
1316: #if EDITOR
1.6 millert 1317: if (secure)
1318: {
1319: error("Command not available", NULL_PARG);
1320: break;
1321: }
1.1 etheisen 1322: if (strcmp(get_filename(curr_ifile), "-") == 0)
1323: {
1324: error("Cannot edit standard input", NULL_PARG);
1325: break;
1326: }
1327: if (curr_altfilename != NULL)
1328: {
1329: error("Cannot edit file processed with LESSOPEN",
1330: NULL_PARG);
1331: break;
1332: }
1.6 millert 1333: start_mca(A_SHELL, "!", ml_shell, 0);
1.1 etheisen 1334: /*
1335: * Expand the editor prototype string
1336: * and pass it to the system to execute.
1.6 millert 1337: * (Make sure the screen is displayed so the
1338: * expansion of "+%lm" works.)
1.1 etheisen 1339: */
1.6 millert 1340: make_display();
1.1 etheisen 1341: cmd_exec();
1.6 millert 1342: lsystem(pr_expand(editproto, 0), (char*)NULL);
1.1 etheisen 1343: break;
1344: #else
1345: error("Command not available", NULL_PARG);
1346: break;
1347: #endif
1348:
1349: case A_NEXT_FILE:
1350: /*
1351: * Examine next file.
1352: */
1.6 millert 1353: #if TAGS
1354: if (ntags())
1355: {
1356: error("No next file", NULL_PARG);
1357: break;
1358: }
1359: #endif
1.1 etheisen 1360: if (number <= 0)
1361: number = 1;
1.6 millert 1362: if (edit_next((int) number))
1.1 etheisen 1363: {
1.7 ! millert 1364: if (quit_at_eof && hit_eof)
1.1 etheisen 1365: quit(QUIT_OK);
1366: parg.p_string = (number > 1) ? "(N-th) " : "";
1367: error("No %snext file", &parg);
1368: }
1369: break;
1370:
1371: case A_PREV_FILE:
1372: /*
1373: * Examine previous file.
1374: */
1.6 millert 1375: #if TAGS
1376: if (ntags())
1377: {
1378: error("No previous file", NULL_PARG);
1379: break;
1380: }
1381: #endif
1.1 etheisen 1382: if (number <= 0)
1383: number = 1;
1.6 millert 1384: if (edit_prev((int) number))
1.1 etheisen 1385: {
1386: parg.p_string = (number > 1) ? "(N-th) " : "";
1387: error("No %sprevious file", &parg);
1388: }
1389: break;
1390:
1.6 millert 1391: case A_NEXT_TAG:
1392: #if TAGS
1393: if (number <= 0)
1394: number = 1;
1395: tagfile = nexttag((int) number);
1396: if (tagfile == NULL)
1397: {
1398: error("No next tag", NULL_PARG);
1399: break;
1400: }
1401: if (edit(tagfile) == 0)
1402: {
1403: POSITION pos = tagsearch();
1404: if (pos != NULL_POSITION)
1405: jump_loc(pos, jump_sline);
1406: }
1407: #else
1408: error("Command not available", NULL_PARG);
1409: #endif
1410: break;
1411:
1412: case A_PREV_TAG:
1413: #if TAGS
1414: if (number <= 0)
1415: number = 1;
1416: tagfile = prevtag((int) number);
1417: if (tagfile == NULL)
1418: {
1419: error("No previous tag", NULL_PARG);
1420: break;
1421: }
1422: if (edit(tagfile) == 0)
1423: {
1424: POSITION pos = tagsearch();
1425: if (pos != NULL_POSITION)
1426: jump_loc(pos, jump_sline);
1427: }
1428: #else
1429: error("Command not available", NULL_PARG);
1430: #endif
1431: break;
1432:
1.1 etheisen 1433: case A_INDEX_FILE:
1434: /*
1435: * Examine a particular file.
1436: */
1437: if (number <= 0)
1438: number = 1;
1.6 millert 1439: if (edit_index((int) number))
1.1 etheisen 1440: error("No such file", NULL_PARG);
1441: break;
1442:
1.6 millert 1443: case A_REMOVE_FILE:
1444: old_ifile = curr_ifile;
1445: new_ifile = getoff_ifile(curr_ifile);
1446: if (new_ifile == NULL_IFILE)
1447: {
1448: bell();
1449: break;
1450: }
1451: if (edit_ifile(new_ifile) != 0)
1452: {
1453: reedit_ifile(old_ifile);
1454: break;
1455: }
1456: del_ifile(old_ifile);
1457: break;
1458:
1.1 etheisen 1459: case A_OPT_TOGGLE:
1460: optflag = OPT_TOGGLE;
1.6 millert 1461: optgetname = FALSE;
1462: mca_opt_toggle();
1.1 etheisen 1463: c = getcc();
1464: goto again;
1465:
1466: case A_DISP_OPTION:
1467: /*
1468: * Report a flag setting.
1469: */
1.6 millert 1470: optflag = OPT_NO_TOGGLE;
1471: optgetname = FALSE;
1472: mca_opt_toggle();
1.1 etheisen 1473: c = getcc();
1.6 millert 1474: goto again;
1.1 etheisen 1475:
1476: case A_FIRSTCMD:
1477: /*
1478: * Set an initial command for new files.
1479: */
1.6 millert 1480: start_mca(A_FIRSTCMD, "+", (void*)NULL, 0);
1.1 etheisen 1481: c = getcc();
1482: goto again;
1483:
1484: case A_SHELL:
1485: /*
1486: * Shell escape.
1487: */
1488: #if SHELL_ESCAPE
1.6 millert 1489: if (secure)
1490: {
1491: error("Command not available", NULL_PARG);
1492: break;
1493: }
1494: start_mca(A_SHELL, "!", ml_shell, 0);
1.1 etheisen 1495: c = getcc();
1496: goto again;
1497: #else
1498: error("Command not available", NULL_PARG);
1499: break;
1500: #endif
1501:
1502: case A_SETMARK:
1503: /*
1504: * Set a mark.
1505: */
1.6 millert 1506: start_mca(A_SETMARK, "mark: ", (void*)NULL, 0);
1.1 etheisen 1507: c = getcc();
1508: if (c == erase_char || c == kill_char ||
1509: c == '\n' || c == '\r')
1510: break;
1511: setmark(c);
1512: break;
1513:
1514: case A_GOMARK:
1515: /*
1516: * Go to a mark.
1517: */
1.6 millert 1518: start_mca(A_GOMARK, "goto mark: ", (void*)NULL, 0);
1.1 etheisen 1519: c = getcc();
1520: if (c == erase_char || c == kill_char ||
1521: c == '\n' || c == '\r')
1522: break;
1523: gomark(c);
1524: break;
1525:
1526: case A_PIPE:
1527: #if PIPEC
1.6 millert 1528: if (secure)
1529: {
1530: error("Command not available", NULL_PARG);
1531: break;
1532: }
1533: start_mca(A_PIPE, "|mark: ", (void*)NULL, 0);
1.1 etheisen 1534: c = getcc();
1535: if (c == erase_char || c == kill_char)
1536: break;
1537: if (c == '\n' || c == '\r')
1538: c = '.';
1539: if (badmark(c))
1540: break;
1541: pipec = c;
1.6 millert 1542: start_mca(A_PIPE, "!", ml_shell, 0);
1.1 etheisen 1543: c = getcc();
1544: goto again;
1545: #else
1546: error("Command not available", NULL_PARG);
1547: break;
1548: #endif
1549:
1550: case A_B_BRACKET:
1551: case A_F_BRACKET:
1.6 millert 1552: start_mca(action, "Brackets: ", (void*)NULL, 0);
1.1 etheisen 1553: c = getcc();
1554: goto again;
1555:
1.6 millert 1556: case A_LSHIFT:
1557: if (number > 0)
1558: shift_count = number;
1559: else
1560: number = (shift_count > 0) ?
1561: shift_count : sc_width / 2;
1562: if (number > hshift)
1563: number = hshift;
1564: hshift -= number;
1565: screen_trashed = 1;
1566: break;
1567:
1568: case A_RSHIFT:
1569: if (number > 0)
1570: shift_count = number;
1571: else
1572: number = (shift_count > 0) ?
1573: shift_count : sc_width / 2;
1574: hshift += number;
1575: screen_trashed = 1;
1576: break;
1577:
1.1 etheisen 1578: case A_PREFIX:
1579: /*
1580: * The command is incomplete (more chars are needed).
1581: * Display the current char, so the user knows
1582: * what's going on, and get another character.
1583: */
1584: if (mca != A_PREFIX)
1585: {
1586: cmd_reset();
1.6 millert 1587: start_mca(A_PREFIX, " ", (void*)NULL,
1588: CF_QUIT_ON_ERASE);
1.1 etheisen 1589: (void) cmd_char(c);
1590: }
1591: c = getcc();
1592: goto again;
1593:
1594: case A_NOACTION:
1595: break;
1596:
1597: default:
1.2 etheisen 1598: if (be_helpful)
1.6 millert 1599: help_prompt = "[Press 'h' for instructions.]";
1.2 etheisen 1600: else
1601: bell();
1.1 etheisen 1602: break;
1603: }
1604: }
1605: }