rip out SHELL_ESCAPE define
[unix-history] / usr / src / usr.bin / more / command.c
CommitLineData
bfe13c81
KB
1/*
2 * Copyright (c) 1988 Mark Nudleman
3 * Copyright (c) 1988 Regents of the University of California.
4 * All rights reserved.
5 *
6 * This code is derived from software contributed to Berkeley by
7 * Mark Nudleman.
8 *
9 * Redistribution and use in source and binary forms are permitted
10 * provided that the above copyright notice and this paragraph are
11 * duplicated in all such forms and that any documentation,
12 * advertising materials, and other materials related to such
13 * distribution and use acknowledge that the software was developed
14 * by the University of California, Berkeley. The name of the
15 * University may not be used to endorse or promote products derived
16 * from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
19 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20 */
21
22#ifndef lint
fc132a88 23static char sccsid[] = "@(#)command.c 5.2 (Berkeley) %G%";
bfe13c81
KB
24#endif /* not lint */
25
26/*
27 * User-level command processor.
28 */
29
30#include "less.h"
31#include "position.h"
32#include "cmd.h"
33
34#define NO_MCA 0
35#define MCA_DONE 1
36#define MCA_MORE 2
37
38extern int erase_char, kill_char;
39extern int ispipe;
40extern int sigs;
41extern int quit_at_eof;
42extern int hit_eof;
43extern int sc_width;
44extern int sc_height;
45extern int sc_window;
46extern int curr_ac;
47extern int ac;
48extern int quitting;
49extern int scroll;
50extern char *first_cmd;
51extern char *every_first_cmd;
52extern char version[];
53extern char *current_file;
54#if EDITOR
55extern char *editor;
56#endif
57extern int screen_trashed; /* The screen has been overwritten */
58
59static char cmdbuf[120]; /* Buffer for holding a multi-char command */
bfe13c81
KB
60static char *cp; /* Pointer into cmdbuf */
61static int cmd_col; /* Current column of the multi-char command */
62static int mca; /* The multicharacter command (action) */
63static int last_mca; /* The previous mca */
64static int number; /* The number typed by the user */
65static int wsearch; /* Search for matches (1) or non-matches (0) */
66
67/*
68 * Reset command buffer (to empty).
69 */
70cmd_reset()
71{
72 cp = cmdbuf;
73}
74
75/*
76 * Backspace in command buffer.
77 */
78 static int
79cmd_erase()
80{
81 if (cp == cmdbuf)
82 /*
83 * Backspace past beginning of the string:
84 * this usually means abort the command.
85 */
86 return (1);
87
88 if (control_char(*--cp))
89 {
90 /*
91 * Erase an extra character, for the carat.
92 */
93 backspace();
94 cmd_col--;
95 }
96 backspace();
97 cmd_col--;
98 return (0);
99}
100
101/*
102 * Set up the display to start a new multi-character command.
103 */
104start_mca(action, prompt)
105 int action;
106 char *prompt;
107{
108 lower_left();
109 clear_eol();
110 putstr(prompt);
111 cmd_col = strlen(prompt);
112 mca = action;
113}
114
115/*
116 * Process a single character of a multi-character command, such as
117 * a number, or the pattern of a search command.
118 */
119 static int
120cmd_char(c)
121 int c;
122{
123 if (c == erase_char)
124 {
125 if (cmd_erase())
126 return (1);
127 } else if (c == kill_char)
128 {
129 /* {{ Could do this faster, but who cares? }} */
130 while (cmd_erase() == 0)
131 ;
132 } else if (cp >= &cmdbuf[sizeof(cmdbuf)-1])
133 {
134 /*
135 * No room in the command buffer.
136 */
137 bell();
138 } else if (cmd_col >= sc_width-3)
139 {
140 /*
141 * No room on the screen.
142 * {{ Could get fancy here; maybe shift the displayed
143 * line and make room for more chars, like ksh. }}
144 */
145 bell();
146 } else
147 {
148 /*
149 * Append the character to the string.
150 */
151 *cp++ = c;
152 if (control_char(c))
153 {
154 putchr('^');
155 cmd_col++;
156 c = carat_char(c);
157 }
158 putchr(c);
159 cmd_col++;
160 }
161 return (0);
162}
163
164/*
165 * Return the number currently in the command buffer.
166 */
167 static int
168cmd_int()
169{
170 *cp = '\0';
171 cp = cmdbuf;
172 return (atoi(cmdbuf));
173}
174
175/*
176 * Move the cursor to lower left before executing a command.
177 * This looks nicer if the command takes a long time before
178 * updating the screen.
179 */
180 static void
181cmd_exec()
182{
183 lower_left();
184 flush();
185}
186
187/*
188 * Display the appropriate prompt.
189 */
190 static void
191prompt()
192{
193 register char *p;
194
195 if (first_cmd != NULL && *first_cmd != '\0')
196 {
197 /*
198 * No prompt necessary if commands are from first_cmd
199 * rather than from the user.
200 */
201 return;
202 }
203
204 /*
205 * If nothing is displayed yet, display starting from line 1.
206 */
207 if (position(TOP) == NULL_POSITION)
208 jump_back(1);
209 else if (screen_trashed)
210 repaint();
211
212 /*
213 * If the -E flag is set and we've hit EOF on the last file, quit.
214 */
215 if (quit_at_eof == 2 && hit_eof && curr_ac + 1 >= ac)
216 quit();
217
218 /*
219 * Select the proper prompt and display it.
220 */
221 lower_left();
222 clear_eol();
223 p = pr_string();
224 if (p == NULL)
225 putchr(':');
226 else
227 {
228 so_enter();
229 putstr(p);
230 so_exit();
231 }
232}
233
234/*
235 * Get command character.
236 * The character normally comes from the keyboard,
237 * but may come from the "first_cmd" string.
238 */
239 static int
240getcc()
241{
242 if (first_cmd == NULL)
243 return (getchr());
244
245 if (*first_cmd == '\0')
246 {
247 /*
248 * Reached end of first_cmd input.
249 */
250 first_cmd = NULL;
251 if (cp > cmdbuf && position(TOP) == NULL_POSITION)
252 {
253 /*
254 * Command is incomplete, so try to complete it.
255 * There are only two cases:
256 * 1. We have "/string" but no newline. Add the \n.
257 * 2. We have a number but no command. Treat as #g.
258 * (This is all pretty hokey.)
259 */
260 if (mca != A_DIGIT)
261 /* Not a number; must be search string */
262 return ('\n');
263 else
264 /* A number; append a 'g' */
265 return ('g');
266 }
267 return (getchr());
268 }
269 return (*first_cmd++);
270}
271
272/*
273 * Execute a multicharacter command.
274 */
275 static void
276exec_mca()
277{
278 register char *p;
279 register int n;
280
281 *cp = '\0';
282 cmd_exec();
283 switch (mca)
284 {
285 case A_F_SEARCH:
286 search(1, cmdbuf, number, wsearch);
287 break;
288 case A_B_SEARCH:
289 search(0, cmdbuf, number, wsearch);
290 break;
291 case A_FIRSTCMD:
292 /*
293 * Skip leading spaces or + signs in the string.
294 */
295 for (p = cmdbuf; *p == '+' || *p == ' '; p++)
296 ;
297 if (every_first_cmd != NULL)
298 free(every_first_cmd);
299 if (*p == '\0')
300 every_first_cmd = NULL;
301 else
302 every_first_cmd = save(p);
303 break;
304 case A_TOGGLE_OPTION:
305 toggle_option(cmdbuf, 1);
306 break;
307 case A_EXAMINE:
308 /*
309 * Ignore leading spaces in the filename.
310 */
311 for (p = cmdbuf; *p == ' '; p++)
312 ;
313 edit(glob(p));
314 break;
bfe13c81
KB
315 }
316}
317
318/*
319 * Add a character to a multi-character command.
320 */
321 static int
322mca_char(c)
323 int c;
324{
325 switch (mca)
326 {
327 case 0:
328 /*
329 * Not in a multicharacter command.
330 */
331 return (NO_MCA);
332
333 case A_PREFIX:
334 /*
335 * In the prefix of a command.
336 */
337 return (NO_MCA);
338
339 case A_DIGIT:
340 /*
341 * Entering digits of a number.
342 * Terminated by a non-digit.
343 */
344 if ((c < '0' || c > '9') &&
345 c != erase_char && c != kill_char)
346 {
347 /*
348 * Not part of the number.
349 * Treat as a normal command character.
350 */
351 number = cmd_int();
352 mca = 0;
353 return (NO_MCA);
354 }
355 break;
356
357 case A_TOGGLE_OPTION:
358 /*
359 * Special case for the TOGGLE_OPTION command.
360 * if the option letter which was entered is a
361 * single-char option, execute the command immediately,
362 * so he doesn't have to hit RETURN.
363 */
364 if (cp == cmdbuf && c != erase_char && c != kill_char &&
365 single_char_option(c))
366 {
367 cmdbuf[0] = c;
368 cmdbuf[1] = '\0';
369 toggle_option(cmdbuf, 1);
370 return (MCA_DONE);
371 }
372 break;
373 }
374
375 /*
376 * Any other multicharacter command
377 * is terminated by a newline.
378 */
379 if (c == '\n' || c == '\r')
380 {
381 /*
382 * Execute the command.
383 */
384 exec_mca();
385 return (MCA_DONE);
386 }
387 /*
388 * Append the char to the command buffer.
389 */
390 if (cmd_char(c))
391 /*
392 * Abort the multi-char command.
393 */
394 return (MCA_DONE);
395 /*
396 * Need another character.
397 */
398 return (MCA_MORE);
399}
400
401/*
402 * Main command processor.
403 * Accept and execute commands until a quit command, then return.
404 */
405 public void
406commands()
407{
408 register int c;
409 register int action;
410
411 last_mca = 0;
412 scroll = (sc_height + 1) / 2;
413
414 for (;;)
415 {
416 mca = 0;
417 number = 0;
418
419 /*
420 * See if any signals need processing.
421 */
422 if (sigs)
423 {
424 psignals();
425 if (quitting)
426 quit();
427 }
428
429 /*
430 * Display prompt and accept a character.
431 */
432 cmd_reset();
433 prompt();
434 noprefix();
435 c = getcc();
436
437 again:
438 if (sigs)
439 continue;
440
441 /*
442 * If we are in a multicharacter command, call mca_char.
443 * Otherwise we call cmd_decode to determine the
444 * action to be performed.
445 */
446 if (mca)
447 switch (mca_char(c))
448 {
449 case MCA_MORE:
450 /*
451 * Need another character.
452 */
453 c = getcc();
454 goto again;
455 case MCA_DONE:
456 /*
457 * Command has been handled by mca_char.
458 * Start clean with a prompt.
459 */
460 continue;
461 case NO_MCA:
462 /*
463 * Not a multi-char command
464 * (at least, not anymore).
465 */
466 break;
467 }
468
469 /*
470 * Decode the command character and decide what to do.
471 */
472 switch (action = cmd_decode(c))
473 {
474 case A_DIGIT:
475 /*
476 * First digit of a number.
477 */
478 start_mca(A_DIGIT, ":");
479 goto again;
480
481 case A_F_SCREEN:
482 /*
483 * Forward one screen.
484 */
485 if (number <= 0)
486 number = sc_window;
487 if (number <= 0)
488 number = sc_height - 1;
489 cmd_exec();
490 forward(number, 1);
491 break;
492
493 case A_B_SCREEN:
494 /*
495 * Backward one screen.
496 */
497 if (number <= 0)
498 number = sc_window;
499 if (number <= 0)
500 number = sc_height - 1;
501 cmd_exec();
502 backward(number, 1);
503 break;
504
505 case A_F_LINE:
506 /*
507 * Forward N (default 1) line.
508 */
509 if (number <= 0)
510 number = 1;
511 cmd_exec();
512 forward(number, 0);
513 break;
514
515 case A_B_LINE:
516 /*
517 * Backward N (default 1) line.
518 */
519 if (number <= 0)
520 number = 1;
521 cmd_exec();
522 backward(number, 0);
523 break;
524
525 case A_F_SCROLL:
526 /*
527 * Forward N lines
528 * (default same as last 'd' or 'u' command).
529 */
530 if (number > 0)
531 scroll = number;
532 cmd_exec();
533 forward(scroll, 0);
534 break;
535
536 case A_B_SCROLL:
537 /*
538 * Forward N lines
539 * (default same as last 'd' or 'u' command).
540 */
541 if (number > 0)
542 scroll = number;
543 cmd_exec();
544 backward(scroll, 0);
545 break;
546
547 case A_FREPAINT:
548 /*
549 * Flush buffers, then repaint screen.
550 * Don't flush the buffers on a pipe!
551 */
552 if (!ispipe)
553 {
554 ch_init(0, 0);
555 clr_linenum();
556 }
557 /* FALLTHRU */
558 case A_REPAINT:
559 /*
560 * Repaint screen.
561 */
562 cmd_exec();
563 repaint();
564 break;
565
566 case A_GOLINE:
567 /*
568 * Go to line N, default beginning of file.
569 */
570 if (number <= 0)
571 number = 1;
572 cmd_exec();
573 jump_back(number);
574 break;
575
576 case A_PERCENT:
577 /*
578 * Go to a specified percentage into the file.
579 */
580 if (number < 0)
581 number = 0;
582 if (number > 100)
583 number = 100;
584 cmd_exec();
585 jump_percent(number);
586 break;
587
588 case A_GOEND:
589 /*
590 * Go to line N, default end of file.
591 */
592 cmd_exec();
593 if (number <= 0)
594 jump_forw();
595 else
596 jump_back(number);
597 break;
598
599 case A_STAT:
600 /*
601 * Print file name, etc.
602 */
603 cmd_exec();
604 error(eq_message());
605 break;
606
607 case A_VERSION:
608 /*
609 * Print version number, without the "@(#)".
610 */
611 cmd_exec();
612 error(version+4);
613 break;
614
615 case A_QUIT:
616 /*
617 * Exit.
618 */
619 quit();
620
621 case A_F_SEARCH:
622 case A_B_SEARCH:
623 /*
624 * Search for a pattern.
625 * Accept chars of the pattern until \n.
626 */
627 if (number <= 0)
628 number = 1;
629 start_mca(action, (action==A_F_SEARCH) ? "/" : "?");
630 last_mca = mca;
631 wsearch = 1;
632 c = getcc();
633 if (c == '!')
634 {
635 /*
636 * Invert the sense of the search.
637 * Set wsearch to 0 and get a new
638 * character for the start of the pattern.
639 */
640 start_mca(action,
641 (action==A_F_SEARCH) ? "!/" : "!?");
642 wsearch = 0;
643 c = getcc();
644 }
645 goto again;
646
647 case A_AGAIN_SEARCH:
648 /*
649 * Repeat previous search.
650 */
651 if (number <= 0)
652 number = 1;
653 if (wsearch)
654 start_mca(last_mca,
655 (last_mca==A_F_SEARCH) ? "/" : "?");
656 else
657 start_mca(last_mca,
658 (last_mca==A_F_SEARCH) ? "!/" : "!?");
659 cmd_exec();
660 search(mca==A_F_SEARCH, (char *)NULL, number, wsearch);
661 break;
662
663 case A_HELP:
664 /*
665 * Help.
666 */
667 lower_left();
668 clear_eol();
669 putstr("help");
670 cmd_exec();
671 help();
672 break;
673
674 case A_EXAMINE:
675 /*
676 * Edit a new file. Get the filename.
677 */
678 cmd_reset();
679 start_mca(A_EXAMINE, "Examine: ");
680 c = getcc();
681 goto again;
682
683 case A_VISUAL:
684 /*
685 * Invoke an editor on the input file.
686 */
687#if EDITOR
688 if (ispipe)
689 {
690 error("Cannot edit standard input");
691 break;
692 }
693 /*
694 * Try to pass the line number to the editor.
695 */
696 cmd_exec();
697 c = currline(MIDDLE);
698 if (c == 0)
699 sprintf(cmdbuf, "%s %s",
700 editor, current_file);
701 else
702 sprintf(cmdbuf, "%s +%d %s",
703 editor, c, current_file);
704 lsystem(cmdbuf);
705 ch_init(0, 0);
706 clr_linenum();
707 break;
708#else
709 error("Command not available");
710 break;
711#endif
712
713 case A_NEXT_FILE:
714 /*
715 * Examine next file.
716 */
717 if (number <= 0)
718 number = 1;
719 next_file(number);
720 break;
721
722 case A_PREV_FILE:
723 /*
724 * Examine previous file.
725 */
726 if (number <= 0)
727 number = 1;
728 prev_file(number);
729 break;
730
731 case A_TOGGLE_OPTION:
732 /*
733 * Toggle a flag setting.
734 */
735 cmd_reset();
736 start_mca(A_TOGGLE_OPTION, "-");
737 c = getcc();
738 goto again;
739
740 case A_DISP_OPTION:
741 /*
742 * Report a flag setting.
743 */
744 cmd_reset();
745 start_mca(A_DISP_OPTION, "_");
746 c = getcc();
747 if (c == erase_char || c == kill_char)
748 break;
749 cmdbuf[0] = c;
750 cmdbuf[1] = '\0';
751 toggle_option(cmdbuf, 0);
752 break;
753
754 case A_FIRSTCMD:
755 /*
756 * Set an initial command for new files.
757 */
758 cmd_reset();
759 start_mca(A_FIRSTCMD, "+");
760 c = getcc();
761 goto again;
762
bfe13c81
KB
763 case A_SETMARK:
764 /*
765 * Set a mark.
766 */
767 lower_left();
768 clear_eol();
769 start_mca(A_SETMARK, "mark: ");
770 c = getcc();
771 if (c == erase_char || c == kill_char)
772 break;
773 setmark(c);
774 break;
775
776 case A_GOMARK:
777 /*
778 * Go to a mark.
779 */
780 lower_left();
781 clear_eol();
782 start_mca(A_GOMARK, "goto mark: ");
783 c = getcc();
784 if (c == erase_char || c == kill_char)
785 break;
786 gomark(c);
787 break;
788
789 case A_PREFIX:
790 /*
791 * The command is incomplete (more chars are needed).
792 * Display the current char so the user knows
793 * what's going on and get another character.
794 */
795 if (mca != A_PREFIX)
796 start_mca(A_PREFIX, "& ");
797 if (control_char(c))
798 {
799 putchr('^');
800 c = carat_char(c);
801 }
802 putchr(c);
803 c = getcc();
804 goto again;
805
806 default:
807 bell();
808 break;
809 }
810 }
811}