Annotation of src/usr.bin/less/command.c, Revision 1.2
1.1 etheisen 1: /*
2: * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
3: * All rights reserved.
4: *
5: * Redistribution and use in source and binary forms, with or without
6: * modification, are permitted provided that the following conditions
7: * are met:
8: * 1. Redistributions of source code must retain the above copyright
9: * notice, this list of conditions and the following disclaimer.
10: * 2. Redistributions in binary form must reproduce the above copyright
11: * notice in the documentation and/or other materials provided with
12: * the distribution.
13: *
14: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
15: * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
18: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
20: * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
21: * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
23: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
24: * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25: */
26:
27:
28: /*
29: * User-level command processor.
30: */
31:
32: #include "less.h"
33: #include "position.h"
34: #include "option.h"
35: #include "cmd.h"
36:
37: extern int erase_char, kill_char;
38: extern int sigs;
39: extern int quit_at_eof;
40: extern int hit_eof;
41: extern int sc_width;
42: extern int sc_height;
43: extern int swindow;
44: extern int jump_sline;
45: extern int quitting;
46: extern int wscroll;
47: extern int nohelp;
48: extern int top_scroll;
49: extern int ignore_eoi;
50: extern char *every_first_cmd;
51: extern char *curr_altfilename;
52: extern char version[];
53: extern struct scrpos initial_scrpos;
54: extern IFILE curr_ifile;
55: #if CMD_HISTORY
56: extern void *ml_search;
57: extern void *ml_examine;
58: #if SHELL_ESCAPE || PIPEC
59: extern void *ml_shell;
60: #endif
61: #else
62: /* No CMD_HISTORY */
63: #define ml_search NULL
64: #define ml_examine NULL
65: #define ml_shell NULL
66: #endif
67: #if EDITOR
68: extern char *editor;
69: extern char *editproto;
70: #endif
71: extern int screen_trashed; /* The screen has been overwritten */
1.2 ! etheisen 72: extern int be_helpful;
! 73:
! 74: public int helpprompt;
1.1 etheisen 75:
76: static char ungot[100];
77: static char *ungotp = NULL;
78: #if SHELL_ESCAPE
79: static char *shellcmd = NULL; /* For holding last shell command for "!!" */
80: #endif
81: static int mca; /* The multicharacter command (action) */
82: static int search_type; /* The previous type of search */
83: static int number; /* The number typed by the user */
84: static char optchar;
85: static int optflag;
86: #if PIPEC
87: static char pipec;
88: #endif
89:
90: static void multi_search();
91:
92: /*
93: * Move the cursor to lower left before executing a command.
94: * This looks nicer if the command takes a long time before
95: * updating the screen.
96: */
97: static void
98: cmd_exec()
99: {
100: lower_left();
101: flush();
102: }
103:
104: /*
105: * Set up the display to start a new multi-character command.
106: */
107: static void
108: start_mca(action, prompt, mlist)
109: int action;
110: char *prompt;
111: void *mlist;
112: {
113: mca = action;
114: clear_bot();
115: cmd_putstr(prompt);
116: #if CMD_HISTORY
117: set_mlist(mlist);
118: #endif
119: }
120:
121: public int
122: in_mca()
123: {
124: return (mca != 0 && mca != A_PREFIX);
125: }
126:
127: /*
128: * Set up the display to start a new search command.
129: */
130: static void
131: mca_search()
132: {
133: if (search_type & SRCH_FORW)
134: mca = A_F_SEARCH;
135: else
136: mca = A_B_SEARCH;
137:
138: clear_bot();
139:
140: if (search_type & SRCH_FIRST_FILE)
141: cmd_putstr("@");
142:
143: if (search_type & SRCH_PAST_EOF)
144: cmd_putstr("*");
145:
146: if (search_type & SRCH_NOMATCH)
147: cmd_putstr("!");
148:
149: if (search_type & SRCH_FORW)
150: cmd_putstr("/");
151: else
152: cmd_putstr("?");
153: #if CMD_HISTORY
154: set_mlist(ml_search);
155: #endif
156: }
157:
158: /*
159: * Execute a multicharacter command.
160: */
161: static void
162: exec_mca()
163: {
164: register char *cbuf;
165:
166: cmd_exec();
167: cbuf = get_cmdbuf();
168:
169: switch (mca)
170: {
171: case A_F_SEARCH:
172: case A_B_SEARCH:
173: multi_search(cbuf, number);
174: break;
175: case A_FIRSTCMD:
176: /*
177: * Skip leading spaces or + signs in the string.
178: */
179: while (*cbuf == '+' || *cbuf == ' ')
180: cbuf++;
181: if (every_first_cmd != NULL)
182: free(every_first_cmd);
183: if (*cbuf == '\0')
184: every_first_cmd = NULL;
185: else
186: every_first_cmd = save(cbuf);
187: break;
188: case A_OPT_TOGGLE:
189: toggle_option(optchar, cbuf, optflag);
190: optchar = '\0';
191: break;
192: case A_F_BRACKET:
193: match_brac(cbuf[0], cbuf[1], 1, number);
194: break;
195: case A_B_BRACKET:
196: match_brac(cbuf[1], cbuf[0], 0, number);
197: break;
198: #if EXAMINE
199: case A_EXAMINE:
200: edit_list(cbuf);
201: break;
202: #endif
203: #if SHELL_ESCAPE
204: case A_SHELL:
205: /*
206: * !! just uses whatever is in shellcmd.
207: * Otherwise, copy cmdbuf to shellcmd,
208: * expanding any special characters ("%" or "#").
209: */
210: if (*cbuf != '!')
211: {
212: if (shellcmd != NULL)
213: free(shellcmd);
214: shellcmd = fexpand(cbuf);
215: }
216:
217: if (shellcmd == NULL)
218: lsystem("");
219: else
220: lsystem(shellcmd);
221: error("!done", NULL_PARG);
222: break;
223: #endif
224: #if PIPEC
225: case A_PIPE:
226: (void) pipe_mark(pipec, cbuf);
227: error("|done", NULL_PARG);
228: break;
229: #endif
230: }
231: }
232:
233: /*
234: * Add a character to a multi-character command.
235: */
236: static int
237: mca_char(c)
238: int c;
239: {
240: char *p;
241: int flag;
242: char buf[3];
243:
244: switch (mca)
245: {
246: case 0:
247: /*
248: * Not in a multicharacter command.
249: */
250: return (NO_MCA);
251:
252: case A_PREFIX:
253: /*
254: * In the prefix of a command.
255: * This not considered a multichar command
256: * (even tho it uses cmdbuf, etc.).
257: * It is handled in the commands() switch.
258: */
259: return (NO_MCA);
260:
261: case A_DIGIT:
262: /*
263: * Entering digits of a number.
264: * Terminated by a non-digit.
265: */
266: if ((c < '0' || c > '9') &&
267: editchar(c, EC_PEEK|EC_NOHISTORY|EC_NOCOMPLETE) == A_INVALID)
268: {
269: /*
270: * Not part of the number.
271: * Treat as a normal command character.
272: */
273: number = cmd_int();
274: mca = 0;
275: cmd_accept();
276: return (NO_MCA);
277: }
278: break;
279:
280: case A_OPT_TOGGLE:
281: /*
282: * Special case for the TOGGLE_OPTION command.
283: * If the option letter which was entered is a
284: * single-char option, execute the command immediately,
285: * so user doesn't have to hit RETURN.
286: * If the first char is + or -, this indicates
287: * OPT_UNSET or OPT_SET respectively, instead of OPT_TOGGLE.
288: */
289: if (c == erase_char || c == kill_char)
290: break;
291: if (optchar != '\0' && optchar != '+' && optchar != '-')
292: /*
293: * We already have the option letter.
294: */
295: break;
296: switch (c)
297: {
298: case '+':
299: optflag = OPT_UNSET;
300: break;
301: case '-':
302: optflag = OPT_SET;
303: break;
304: default:
305: optchar = c;
306: if (optflag != OPT_TOGGLE || single_char_option(c))
307: {
308: toggle_option(c, "", optflag);
309: return (MCA_DONE);
310: }
311: break;
312: }
313: if (optchar == '+' || optchar == '-')
314: {
315: optchar = c;
316: break;
317: }
318: /*
319: * Display a prompt appropriate for the option letter.
320: */
321: if ((p = opt_prompt(c)) == NULL)
322: {
323: buf[0] = '-';
324: buf[1] = c;
325: buf[2] = '\0';
326: p = buf;
327: }
328: start_mca(A_OPT_TOGGLE, p, (void*)NULL);
329: return (MCA_MORE);
330:
331: case A_F_SEARCH:
332: case A_B_SEARCH:
333: /*
334: * Special case for search commands.
335: * Certain characters as the first char of
336: * the pattern have special meaning:
337: * ! Toggle the NOMATCH flag
338: * * Toggle the PAST_EOF flag
339: * @ Toggle the FIRST_FILE flag
340: */
341: if (len_cmdbuf() > 0)
342: /*
343: * Only works for the first char of the pattern.
344: */
345: break;
346:
347: flag = 0;
348: switch (c)
349: {
350: case '!':
351: flag = SRCH_NOMATCH;
352: break;
353: case '@':
354: flag = SRCH_FIRST_FILE;
355: break;
356: case '*':
357: flag = SRCH_PAST_EOF;
358: break;
359: }
360: if (flag != 0)
361: {
362: search_type ^= flag;
363: mca_search();
364: return (MCA_MORE);
365: }
366: break;
367: }
368:
369: /*
370: * Any other multicharacter command
371: * is terminated by a newline.
372: */
373: if (c == '\n' || c == '\r')
374: {
375: /*
376: * Execute the command.
377: */
378: exec_mca();
379: return (MCA_DONE);
380: }
381: /*
382: * Append the char to the command buffer.
383: */
384: if (cmd_char(c) == CC_QUIT)
385: /*
386: * Abort the multi-char command.
387: */
388: return (MCA_DONE);
389:
390: if ((mca == A_F_BRACKET || mca == A_B_BRACKET) && len_cmdbuf() >= 2)
391: {
392: /*
393: * Special case for the bracket-matching commands.
394: * Execute the command after getting exactly two
395: * characters from the user.
396: */
397: exec_mca();
398: return (MCA_DONE);
399: }
400:
401: /*
402: * Need another character.
403: */
404: return (MCA_MORE);
405: }
406:
407: /*
408: * Display the appropriate prompt.
409: */
410: static void
411: prompt()
412: {
413: register char *p;
414:
415: if (ungotp != NULL && ungotp > ungot)
416: {
417: /*
418: * No prompt necessary if commands are from
419: * ungotten chars rather than from the user.
420: */
421: return;
422: }
423:
424: /*
425: * If nothing is displayed yet, display starting from initial_scrpos.
426: */
427: if (empty_screen())
428: {
429: if (initial_scrpos.pos == NULL_POSITION)
430: /*
431: * {{ Maybe this should be:
432: * jump_loc(ch_zero(), jump_sline);
433: * but this behavior seems rather unexpected
434: * on the first screen. }}
435: */
436: jump_loc(ch_zero(), 1);
437: else
438: jump_loc(initial_scrpos.pos, initial_scrpos.ln);
439: } else if (screen_trashed)
440: {
441: int save_top_scroll;
442: save_top_scroll = top_scroll;
443: top_scroll = 1;
444: repaint();
445: top_scroll = save_top_scroll;
446: }
447:
448: /*
449: * If the -E flag is set and we've hit EOF on the last file, quit.
450: */
451: if (quit_at_eof == OPT_ONPLUS && hit_eof &&
452: next_ifile(curr_ifile) == NULL_IFILE)
453: quit(QUIT_OK);
454:
455: /*
456: * Select the proper prompt and display it.
457: */
458: clear_bot();
1.2 ! etheisen 459: if (helpprompt) {
1.1 etheisen 460: so_enter();
1.2 ! etheisen 461: putstr("[Press 'h' for instructions.]");
1.1 etheisen 462: so_exit();
1.2 ! etheisen 463: helpprompt = 0;
! 464: } else {
! 465: p = pr_string();
! 466: if (p == NULL)
! 467: putchr(':');
! 468: else
! 469: {
! 470: so_enter();
! 471: putstr(p);
! 472: if (be_helpful)
! 473: putstr(" [Press space to continue, 'q' to quit.]");
! 474: so_exit();
! 475: }
1.1 etheisen 476: }
477: }
478:
479: public void
480: dispversion()
481: {
482: PARG parg;
483:
484: parg.p_string = version;
485: error("less version %s", &parg);
486: }
487:
488: /*
489: * Get command character.
490: * The character normally comes from the keyboard,
491: * but may come from ungotten characters
492: * (characters previously given to ungetcc or ungetsc).
493: */
494: public int
495: getcc()
496: {
497: if (ungotp == NULL)
498: /*
499: * Normal case: no ungotten chars, so get one from the user.
500: */
501: return (getchr());
502:
503: if (ungotp > ungot)
504: /*
505: * Return the next ungotten char.
506: */
507: return (*--ungotp);
508:
509: /*
510: * We have just run out of ungotten chars.
511: */
512: ungotp = NULL;
513: if (len_cmdbuf() == 0 || !empty_screen())
514: return (getchr());
515: /*
516: * Command is incomplete, so try to complete it.
517: */
518: switch (mca)
519: {
520: case A_DIGIT:
521: /*
522: * We have a number but no command. Treat as #g.
523: */
524: return ('g');
525:
526: case A_F_SEARCH:
527: case A_B_SEARCH:
528: /*
529: * We have "/string" but no newline. Add the \n.
530: */
531: return ('\n');
532:
533: default:
534: /*
535: * Some other incomplete command. Let user complete it.
536: */
537: return (getchr());
538: }
539: }
540:
541: /*
542: * "Unget" a command character.
543: * The next getcc() will return this character.
544: */
545: public void
546: ungetcc(c)
547: int c;
548: {
549: if (ungotp == NULL)
550: ungotp = ungot;
551: if (ungotp >= ungot + sizeof(ungot))
552: {
553: error("ungetcc overflow", NULL_PARG);
554: quit(QUIT_ERROR);
555: }
556: *ungotp++ = c;
557: }
558:
559: /*
560: * Unget a whole string of command characters.
561: * The next sequence of getcc()'s will return this string.
562: */
563: public void
564: ungetsc(s)
565: char *s;
566: {
567: register char *p;
568:
569: for (p = s + strlen(s) - 1; p >= s; p--)
570: ungetcc(*p);
571: }
572:
573: /*
574: * Search for a pattern, possibly in multiple files.
575: * If SRCH_FIRST_FILE is set, begin searching at the first file.
576: * If SRCH_PAST_EOF is set, continue the search thru multiple files.
577: */
578: static void
579: multi_search(pattern, n)
580: char *pattern;
581: int n;
582: {
583: register int nomore;
584: IFILE save_ifile;
585: int changed_file;
586:
587: changed_file = 0;
588: save_ifile = curr_ifile;
589:
590: if (search_type & SRCH_FIRST_FILE)
591: {
592: /*
593: * Start at the first (or last) file
594: * in the command line list.
595: */
596: if (search_type & SRCH_FORW)
597: nomore = edit_first();
598: else
599: nomore = edit_last();
600: if (nomore)
601: return;
602: changed_file = 1;
603: search_type &= ~SRCH_FIRST_FILE;
604: }
605:
606: for (;;)
607: {
608: if ((n = search(search_type, pattern, n)) == 0)
609: /*
610: * Found it.
611: */
612: return;
613:
614: if (n < 0)
615: /*
616: * Some kind of error in the search.
617: * Error message has been printed by search().
618: */
619: break;
620:
621: if ((search_type & SRCH_PAST_EOF) == 0)
622: /*
623: * We didn't find a match, but we're
624: * supposed to search only one file.
625: */
626: break;
627: /*
628: * Move on to the next file.
629: */
630: if (search_type & SRCH_FORW)
631: nomore = edit_next(1);
632: else
633: nomore = edit_prev(1);
634: if (nomore)
635: break;
636: changed_file = 1;
637: }
638:
639: /*
640: * Didn't find it.
641: * Print an error message if we haven't already.
642: */
643: if (n > 0)
644: error("Pattern not found", NULL_PARG);
645:
646: if (changed_file)
647: {
648: /*
649: * Restore the file we were originally viewing.
650: */
651: if (edit_ifile(save_ifile))
652: quit(QUIT_ERROR);
653: }
654: }
655:
656: /*
657: * Main command processor.
658: * Accept and execute commands until a quit command.
659: */
660: public void
661: commands()
662: {
663: register int c;
664: register int action;
665: register char *cbuf;
666: int save_search_type;
667: char *s;
668: char tbuf[2];
669: PARG parg;
670:
671: search_type = SRCH_FORW;
672: wscroll = (sc_height + 1) / 2;
673:
674: for (;;)
675: {
676: mca = 0;
677: cmd_accept();
678: number = 0;
679: optchar = '\0';
680:
681: /*
682: * See if any signals need processing.
683: */
684: if (sigs)
685: {
686: psignals();
687: if (quitting)
688: quit(QUIT_SAVED_STATUS);
689: }
690:
691: /*
692: * Display prompt and accept a character.
693: */
694: cmd_reset();
695: prompt();
696: if (sigs)
697: continue;
698: c = getcc();
699:
700: again:
701: if (sigs)
702: continue;
703:
704: /*
705: * If we are in a multicharacter command, call mca_char.
706: * Otherwise we call fcmd_decode to determine the
707: * action to be performed.
708: */
709: if (mca)
710: switch (mca_char(c))
711: {
712: case MCA_MORE:
713: /*
714: * Need another character.
715: */
716: c = getcc();
717: goto again;
718: case MCA_DONE:
719: /*
720: * Command has been handled by mca_char.
721: * Start clean with a prompt.
722: */
723: continue;
724: case NO_MCA:
725: /*
726: * Not a multi-char command
727: * (at least, not anymore).
728: */
729: break;
730: }
731:
732: /*
733: * Decode the command character and decide what to do.
734: */
735: if (mca)
736: {
737: /*
738: * We're in a multichar command.
739: * Add the character to the command buffer
740: * and display it on the screen.
741: * If the user backspaces past the start
742: * of the line, abort the command.
743: */
744: if (cmd_char(c) == CC_QUIT || len_cmdbuf() == 0)
745: continue;
746: cbuf = get_cmdbuf();
747: } else
748: {
749: /*
750: * Don't use cmd_char if we're starting fresh
751: * at the beginning of a command, because we
752: * don't want to echo the command until we know
753: * it is a multichar command. We also don't
754: * want erase_char/kill_char to be treated
755: * as line editing characters.
756: */
757: tbuf[0] = c;
758: tbuf[1] = '\0';
759: cbuf = tbuf;
760: }
761: s = NULL;
762: action = fcmd_decode(cbuf, &s);
763: /*
764: * If an "extra" string was returned,
765: * process it as a string of command characters.
766: */
767: if (s != NULL)
768: ungetsc(s);
769: /*
770: * Clear the cmdbuf string.
771: * (But not if we're in the prefix of a command,
772: * because the partial command string is kept there.)
773: */
774: if (action != A_PREFIX)
775: cmd_reset();
776:
777: switch (action)
778: {
779: case A_DIGIT:
780: /*
781: * First digit of a number.
782: */
783: start_mca(A_DIGIT, ":", (void*)NULL);
784: goto again;
785:
786: case A_F_WINDOW:
787: /*
788: * Forward one window (and set the window size).
789: */
790: if (number > 0)
791: swindow = number;
792: /* FALLTHRU */
793: case A_F_SCREEN:
794: /*
795: * Forward one screen.
796: */
797: if (number <= 0)
798: number = get_swindow();
799: cmd_exec();
800: forward(number, 0, 1);
801: break;
802:
803: case A_B_WINDOW:
804: /*
805: * Backward one window (and set the window size).
806: */
807: if (number > 0)
808: swindow = number;
809: /* FALLTHRU */
810: case A_B_SCREEN:
811: /*
812: * Backward one screen.
813: */
814: if (number <= 0)
815: number = get_swindow();
816: cmd_exec();
817: backward(number, 0, 1);
818: break;
819:
820: case A_F_LINE:
821: /*
822: * Forward N (default 1) line.
823: */
824: if (number <= 0)
825: number = 1;
826: cmd_exec();
827: forward(number, 0, 0);
828: break;
829:
830: case A_B_LINE:
831: /*
832: * Backward N (default 1) line.
833: */
834: if (number <= 0)
835: number = 1;
836: cmd_exec();
837: backward(number, 0, 0);
838: break;
839:
840: case A_FF_LINE:
841: /*
842: * Force forward N (default 1) line.
843: */
844: if (number <= 0)
845: number = 1;
846: cmd_exec();
847: forward(number, 1, 0);
848: break;
849:
850: case A_BF_LINE:
851: /*
852: * Force backward N (default 1) line.
853: */
854: if (number <= 0)
855: number = 1;
856: cmd_exec();
857: backward(number, 1, 0);
858: break;
859:
860: case A_F_FOREVER:
861: /*
862: * Forward forever, ignoring EOF.
863: */
864: cmd_exec();
865: jump_forw();
866: ignore_eoi = 1;
867: hit_eof = 0;
868: while (!ABORT_SIGS())
869: forward(1, 0, 0);
870: ignore_eoi = 0;
871: break;
872:
873: case A_F_SCROLL:
874: /*
875: * Forward N lines
876: * (default same as last 'd' or 'u' command).
877: */
878: if (number > 0)
879: wscroll = number;
880: cmd_exec();
881: forward(wscroll, 0, 0);
882: break;
883:
884: case A_B_SCROLL:
885: /*
886: * Forward N lines
887: * (default same as last 'd' or 'u' command).
888: */
889: if (number > 0)
890: wscroll = number;
891: cmd_exec();
892: backward(wscroll, 0, 0);
893: break;
894:
895: case A_FREPAINT:
896: /*
897: * Flush buffers, then repaint screen.
898: * Don't flush the buffers on a pipe!
899: */
900: if (ch_getflags() & CH_CANSEEK)
901: {
902: ch_flush();
903: clr_linenum();
904: }
905: /* FALLTHRU */
906: case A_REPAINT:
907: /*
908: * Repaint screen.
909: */
910: cmd_exec();
911: repaint();
912: break;
913:
914: case A_GOLINE:
915: /*
916: * Go to line N, default beginning of file.
917: */
918: if (number <= 0)
919: number = 1;
920: cmd_exec();
921: jump_back(number);
922: break;
923:
924: case A_PERCENT:
925: /*
926: * Go to a specified percentage into the file.
927: */
928: if (number < 0)
929: number = 0;
930: if (number > 100)
931: number = 100;
932: cmd_exec();
933: jump_percent(number);
934: break;
935:
936: case A_GOEND:
937: /*
938: * Go to line N, default end of file.
939: */
940: cmd_exec();
941: if (number <= 0)
942: jump_forw();
943: else
944: jump_back(number);
945: break;
946:
947: case A_GOPOS:
948: /*
949: * Go to a specified byte position in the file.
950: */
951: cmd_exec();
952: if (number < 0)
953: number = 0;
954: jump_line_loc((POSITION)number, jump_sline);
955: break;
956:
957: case A_STAT:
958: /*
959: * Print file name, etc.
960: */
961: cmd_exec();
962: parg.p_string = eq_message();
963: error("%s", &parg);
964: break;
965:
966: case A_VERSION:
967: /*
968: * Print version number, without the "@(#)".
969: */
970: cmd_exec();
971: dispversion();
972: break;
973:
974: case A_QUIT:
975: /*
976: * Exit.
977: */
978: quit(QUIT_OK);
979:
980: /*
981: * Define abbreviation for a commonly used sequence below.
982: */
983: #define DO_SEARCH() if (number <= 0) number = 1; \
984: mca_search(); \
985: cmd_exec(); \
986: multi_search((char *)NULL, number);
987:
988:
989: case A_F_SEARCH:
990: /*
991: * Search forward for a pattern.
992: * Get the first char of the pattern.
993: */
994: search_type = SRCH_FORW;
995: if (number <= 0)
996: number = 1;
997: mca_search();
998: c = getcc();
999: goto again;
1000:
1001: case A_B_SEARCH:
1002: /*
1003: * Search backward for a pattern.
1004: * Get the first char of the pattern.
1005: */
1006: search_type = SRCH_BACK;
1007: if (number <= 0)
1008: number = 1;
1009: mca_search();
1010: c = getcc();
1011: goto again;
1012:
1013: case A_AGAIN_SEARCH:
1014: /*
1015: * Repeat previous search.
1016: */
1017: DO_SEARCH();
1018: break;
1019:
1020: case A_T_AGAIN_SEARCH:
1021: /*
1022: * Repeat previous search, multiple files.
1023: */
1024: search_type |= SRCH_PAST_EOF;
1025: DO_SEARCH();
1026: break;
1027:
1028: case A_REVERSE_SEARCH:
1029: /*
1030: * Repeat previous search, in reverse direction.
1031: */
1032: save_search_type = search_type;
1033: search_type = SRCH_REVERSE(search_type);
1034: DO_SEARCH();
1035: search_type = save_search_type;
1036: break;
1037:
1038: case A_T_REVERSE_SEARCH:
1039: /*
1040: * Repeat previous search,
1041: * multiple files in reverse direction.
1042: */
1043: save_search_type = search_type;
1044: search_type = SRCH_REVERSE(search_type);
1045: search_type |= SRCH_PAST_EOF;
1046: DO_SEARCH();
1047: search_type = save_search_type;
1048: break;
1049:
1050: case A_UNDO_SEARCH:
1051: undo_search();
1052: break;
1053:
1054: case A_HELP:
1055: /*
1056: * Help.
1057: */
1058: if (nohelp)
1059: {
1060: bell();
1061: break;
1062: }
1063: clear_bot();
1064: putstr(" help");
1065: cmd_exec();
1066: help(0);
1067: break;
1068:
1069: case A_EXAMINE:
1070: #if EXAMINE
1071: /*
1072: * Edit a new file. Get the filename.
1073: */
1074: start_mca(A_EXAMINE, "Examine: ", ml_examine);
1075: c = getcc();
1076: goto again;
1077: #else
1078: error("Command not available", NULL_PARG);
1079: break;
1080: #endif
1081:
1082: case A_VISUAL:
1083: /*
1084: * Invoke an editor on the input file.
1085: */
1086: #if EDITOR
1087: if (strcmp(get_filename(curr_ifile), "-") == 0)
1088: {
1089: error("Cannot edit standard input", NULL_PARG);
1090: break;
1091: }
1092: if (curr_altfilename != NULL)
1093: {
1094: error("Cannot edit file processed with LESSOPEN",
1095: NULL_PARG);
1096: break;
1097: }
1098: /*
1099: * Expand the editor prototype string
1100: * and pass it to the system to execute.
1101: */
1102: cmd_exec();
1103: lsystem(pr_expand(editproto, 0));
1104: /*
1105: * Re-edit the file, since data may have changed.
1106: * Some editors even recreate the file, so flushing
1107: * buffers is not sufficient.
1108: */
1109: if (edit_ifile(curr_ifile))
1110: quit(QUIT_ERROR);
1111: break;
1112: #else
1113: error("Command not available", NULL_PARG);
1114: break;
1115: #endif
1116:
1117: case A_NEXT_FILE:
1118: /*
1119: * Examine next file.
1120: */
1121: if (number <= 0)
1122: number = 1;
1123: if (edit_next(number))
1124: {
1125: if (quit_at_eof && hit_eof)
1126: quit(QUIT_OK);
1127: parg.p_string = (number > 1) ? "(N-th) " : "";
1128: error("No %snext file", &parg);
1129: }
1130: break;
1131:
1132: case A_PREV_FILE:
1133: /*
1134: * Examine previous file.
1135: */
1136: if (number <= 0)
1137: number = 1;
1138: if (edit_prev(number))
1139: {
1140: parg.p_string = (number > 1) ? "(N-th) " : "";
1141: error("No %sprevious file", &parg);
1142: }
1143: break;
1144:
1145: case A_INDEX_FILE:
1146: /*
1147: * Examine a particular file.
1148: */
1149: if (number <= 0)
1150: number = 1;
1151: if (edit_index(number))
1152: error("No such file", NULL_PARG);
1153: break;
1154:
1155: case A_OPT_TOGGLE:
1156: start_mca(A_OPT_TOGGLE, "-", (void*)NULL);
1157: optflag = OPT_TOGGLE;
1158: c = getcc();
1159: goto again;
1160:
1161: case A_DISP_OPTION:
1162: /*
1163: * Report a flag setting.
1164: */
1165: start_mca(A_DISP_OPTION, "_", (void*)NULL);
1166: c = getcc();
1167: if (c == erase_char || c == kill_char)
1168: break;
1169: toggle_option(c, "", OPT_NO_TOGGLE);
1170: break;
1171:
1172: case A_FIRSTCMD:
1173: /*
1174: * Set an initial command for new files.
1175: */
1176: start_mca(A_FIRSTCMD, "+", (void*)NULL);
1177: c = getcc();
1178: goto again;
1179:
1180: case A_SHELL:
1181: /*
1182: * Shell escape.
1183: */
1184: #if SHELL_ESCAPE
1185: start_mca(A_SHELL, "!", ml_shell);
1186: c = getcc();
1187: goto again;
1188: #else
1189: error("Command not available", NULL_PARG);
1190: break;
1191: #endif
1192:
1193: case A_SETMARK:
1194: /*
1195: * Set a mark.
1196: */
1197: start_mca(A_SETMARK, "mark: ", (void*)NULL);
1198: c = getcc();
1199: if (c == erase_char || c == kill_char ||
1200: c == '\n' || c == '\r')
1201: break;
1202: setmark(c);
1203: break;
1204:
1205: case A_GOMARK:
1206: /*
1207: * Go to a mark.
1208: */
1209: start_mca(A_GOMARK, "goto mark: ", (void*)NULL);
1210: c = getcc();
1211: if (c == erase_char || c == kill_char ||
1212: c == '\n' || c == '\r')
1213: break;
1214: gomark(c);
1215: break;
1216:
1217: case A_PIPE:
1218: #if PIPEC
1219: start_mca(A_PIPE, "|mark: ", (void*)NULL);
1220: c = getcc();
1221: if (c == erase_char || c == kill_char)
1222: break;
1223: if (c == '\n' || c == '\r')
1224: c = '.';
1225: if (badmark(c))
1226: break;
1227: pipec = c;
1228: start_mca(A_PIPE, "!", ml_shell);
1229: c = getcc();
1230: goto again;
1231: #else
1232: error("Command not available", NULL_PARG);
1233: break;
1234: #endif
1235:
1236: case A_B_BRACKET:
1237: case A_F_BRACKET:
1238: start_mca(action, "Brackets: ", (void*)NULL);
1239: c = getcc();
1240: goto again;
1241:
1242: case A_PREFIX:
1243: /*
1244: * The command is incomplete (more chars are needed).
1245: * Display the current char, so the user knows
1246: * what's going on, and get another character.
1247: */
1248: if (mca != A_PREFIX)
1249: {
1250: start_mca(A_PREFIX, " ", (void*)NULL);
1251: cmd_reset();
1252: (void) cmd_char(c);
1253: }
1254: c = getcc();
1255: goto again;
1256:
1257: case A_NOACTION:
1258: break;
1259:
1260: default:
1.2 ! etheisen 1261: if (be_helpful)
! 1262: helpprompt = 1;
! 1263: else
! 1264: bell();
1.1 etheisen 1265: break;
1266: }
1267: }
1268: }