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