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