don't force extra keystroke after '=' operators
[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
f5167107 21static char sccsid[] = "@(#)command.c 5.8 (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 }
bfe13c81
KB
422 /*
423 * Display prompt and accept a character.
424 */
425 cmd_reset();
426 prompt();
427 noprefix();
428 c = getcc();
429
f5167107 430again: if (sigs)
bfe13c81
KB
431 continue;
432
433 /*
434 * If we are in a multicharacter command, call mca_char.
435 * Otherwise we call cmd_decode to determine the
436 * action to be performed.
437 */
438 if (mca)
439 switch (mca_char(c))
440 {
441 case MCA_MORE:
442 /*
443 * Need another character.
444 */
445 c = getcc();
446 goto again;
447 case MCA_DONE:
448 /*
449 * Command has been handled by mca_char.
450 * Start clean with a prompt.
451 */
452 continue;
453 case NO_MCA:
454 /*
455 * Not a multi-char command
456 * (at least, not anymore).
457 */
458 break;
459 }
460
461 /*
462 * Decode the command character and decide what to do.
463 */
464 switch (action = cmd_decode(c))
465 {
466 case A_DIGIT:
467 /*
468 * First digit of a number.
469 */
470 start_mca(A_DIGIT, ":");
471 goto again;
472
473 case A_F_SCREEN:
474 /*
475 * Forward one screen.
476 */
477 if (number <= 0)
478 number = sc_window;
479 if (number <= 0)
480 number = sc_height - 1;
481 cmd_exec();
482 forward(number, 1);
483 break;
484
485 case A_B_SCREEN:
486 /*
487 * Backward one screen.
488 */
489 if (number <= 0)
490 number = sc_window;
491 if (number <= 0)
492 number = sc_height - 1;
493 cmd_exec();
494 backward(number, 1);
495 break;
496
497 case A_F_LINE:
498 /*
499 * Forward N (default 1) line.
500 */
501 if (number <= 0)
502 number = 1;
503 cmd_exec();
504 forward(number, 0);
505 break;
506
507 case A_B_LINE:
508 /*
509 * Backward N (default 1) line.
510 */
511 if (number <= 0)
512 number = 1;
513 cmd_exec();
514 backward(number, 0);
515 break;
516
517 case A_F_SCROLL:
518 /*
519 * Forward N lines
520 * (default same as last 'd' or 'u' command).
521 */
522 if (number > 0)
523 scroll = number;
524 cmd_exec();
525 forward(scroll, 0);
526 break;
527
528 case A_B_SCROLL:
529 /*
530 * Forward N lines
531 * (default same as last 'd' or 'u' command).
532 */
533 if (number > 0)
534 scroll = number;
535 cmd_exec();
536 backward(scroll, 0);
537 break;
538
539 case A_FREPAINT:
540 /*
541 * Flush buffers, then repaint screen.
542 * Don't flush the buffers on a pipe!
543 */
544 if (!ispipe)
545 {
546 ch_init(0, 0);
547 clr_linenum();
548 }
549 /* FALLTHRU */
550 case A_REPAINT:
551 /*
552 * Repaint screen.
553 */
554 cmd_exec();
555 repaint();
556 break;
557
558 case A_GOLINE:
559 /*
560 * Go to line N, default beginning of file.
561 */
562 if (number <= 0)
563 number = 1;
564 cmd_exec();
565 jump_back(number);
566 break;
567
568 case A_PERCENT:
569 /*
570 * Go to a specified percentage into the file.
571 */
572 if (number < 0)
573 number = 0;
574 if (number > 100)
575 number = 100;
576 cmd_exec();
577 jump_percent(number);
578 break;
579
580 case A_GOEND:
581 /*
582 * Go to line N, default end of file.
583 */
584 cmd_exec();
585 if (number <= 0)
586 jump_forw();
587 else
588 jump_back(number);
589 break;
590
591 case A_STAT:
592 /*
593 * Print file name, etc.
594 */
595 cmd_exec();
f5167107
KB
596 lower_left();
597 clear_eol();
598 putstr(eq_message());
599 lower_left();
600 c = getcc();
601 goto again;
bfe13c81
KB
602 case A_QUIT:
603 /*
604 * Exit.
605 */
606 quit();
607
608 case A_F_SEARCH:
609 case A_B_SEARCH:
610 /*
611 * Search for a pattern.
612 * Accept chars of the pattern until \n.
613 */
614 if (number <= 0)
615 number = 1;
616 start_mca(action, (action==A_F_SEARCH) ? "/" : "?");
617 last_mca = mca;
618 wsearch = 1;
619 c = getcc();
620 if (c == '!')
621 {
622 /*
623 * Invert the sense of the search.
624 * Set wsearch to 0 and get a new
625 * character for the start of the pattern.
626 */
627 start_mca(action,
628 (action==A_F_SEARCH) ? "!/" : "!?");
629 wsearch = 0;
630 c = getcc();
631 }
632 goto again;
633
634 case A_AGAIN_SEARCH:
635 /*
636 * Repeat previous search.
637 */
638 if (number <= 0)
639 number = 1;
640 if (wsearch)
641 start_mca(last_mca,
642 (last_mca==A_F_SEARCH) ? "/" : "?");
643 else
644 start_mca(last_mca,
645 (last_mca==A_F_SEARCH) ? "!/" : "!?");
646 cmd_exec();
647 search(mca==A_F_SEARCH, (char *)NULL, number, wsearch);
648 break;
649
650 case A_HELP:
651 /*
652 * Help.
653 */
654 lower_left();
655 clear_eol();
656 putstr("help");
657 cmd_exec();
658 help();
659 break;
660
661 case A_EXAMINE:
662 /*
663 * Edit a new file. Get the filename.
664 */
665 cmd_reset();
666 start_mca(A_EXAMINE, "Examine: ");
667 c = getcc();
668 goto again;
669
670 case A_VISUAL:
671 /*
672 * Invoke an editor on the input file.
673 */
bfe13c81
KB
674 if (ispipe)
675 {
676 error("Cannot edit standard input");
677 break;
678 }
679 /*
680 * Try to pass the line number to the editor.
681 */
682 cmd_exec();
683 c = currline(MIDDLE);
684 if (c == 0)
d8cd1060 685 (void)sprintf(cmdbuf, "%s %s",
bfe13c81
KB
686 editor, current_file);
687 else
d8cd1060 688 (void)sprintf(cmdbuf, "%s +%d %s",
bfe13c81
KB
689 editor, c, current_file);
690 lsystem(cmdbuf);
691 ch_init(0, 0);
692 clr_linenum();
693 break;
bfe13c81
KB
694
695 case A_NEXT_FILE:
696 /*
697 * Examine next file.
698 */
699 if (number <= 0)
700 number = 1;
701 next_file(number);
702 break;
703
704 case A_PREV_FILE:
705 /*
706 * Examine previous file.
707 */
708 if (number <= 0)
709 number = 1;
710 prev_file(number);
711 break;
712
713 case A_TOGGLE_OPTION:
714 /*
715 * Toggle a flag setting.
716 */
717 cmd_reset();
718 start_mca(A_TOGGLE_OPTION, "-");
719 c = getcc();
720 goto again;
721
722 case A_DISP_OPTION:
723 /*
724 * Report a flag setting.
725 */
726 cmd_reset();
727 start_mca(A_DISP_OPTION, "_");
728 c = getcc();
729 if (c == erase_char || c == kill_char)
730 break;
731 cmdbuf[0] = c;
732 cmdbuf[1] = '\0';
733 toggle_option(cmdbuf, 0);
734 break;
735
736 case A_FIRSTCMD:
737 /*
738 * Set an initial command for new files.
739 */
740 cmd_reset();
741 start_mca(A_FIRSTCMD, "+");
742 c = getcc();
743 goto again;
744
bfe13c81
KB
745 case A_SETMARK:
746 /*
747 * Set a mark.
748 */
749 lower_left();
750 clear_eol();
751 start_mca(A_SETMARK, "mark: ");
752 c = getcc();
753 if (c == erase_char || c == kill_char)
754 break;
755 setmark(c);
756 break;
757
758 case A_GOMARK:
759 /*
760 * Go to a mark.
761 */
762 lower_left();
763 clear_eol();
764 start_mca(A_GOMARK, "goto mark: ");
765 c = getcc();
766 if (c == erase_char || c == kill_char)
767 break;
768 gomark(c);
769 break;
770
771 case A_PREFIX:
772 /*
773 * The command is incomplete (more chars are needed).
774 * Display the current char so the user knows
775 * what's going on and get another character.
776 */
777 if (mca != A_PREFIX)
778 start_mca(A_PREFIX, "& ");
779 if (control_char(c))
780 {
781 putchr('^');
782 c = carat_char(c);
783 }
784 putchr(c);
785 c = getcc();
786 goto again;
787
788 default:
789 bell();
790 break;
791 }
792 }
793}