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