lint cleanup
[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
00896020 23static char sccsid[] = "@(#)command.c 5.4 (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;
bfe13c81
KB
277
278 *cp = '\0';
279 cmd_exec();
280 switch (mca)
281 {
282 case A_F_SEARCH:
283 search(1, cmdbuf, number, wsearch);
284 break;
285 case A_B_SEARCH:
286 search(0, cmdbuf, number, wsearch);
287 break;
288 case A_FIRSTCMD:
289 /*
290 * Skip leading spaces or + signs in the string.
291 */
292 for (p = cmdbuf; *p == '+' || *p == ' '; p++)
293 ;
294 if (every_first_cmd != NULL)
295 free(every_first_cmd);
296 if (*p == '\0')
297 every_first_cmd = NULL;
298 else
299 every_first_cmd = save(p);
300 break;
301 case A_TOGGLE_OPTION:
302 toggle_option(cmdbuf, 1);
303 break;
304 case A_EXAMINE:
305 /*
306 * Ignore leading spaces in the filename.
307 */
308 for (p = cmdbuf; *p == ' '; p++)
309 ;
310 edit(glob(p));
311 break;
bfe13c81
KB
312 }
313}
314
315/*
316 * Add a character to a multi-character command.
317 */
318 static int
319mca_char(c)
320 int c;
321{
322 switch (mca)
323 {
324 case 0:
325 /*
326 * Not in a multicharacter command.
327 */
328 return (NO_MCA);
329
330 case A_PREFIX:
331 /*
332 * In the prefix of a command.
333 */
334 return (NO_MCA);
335
336 case A_DIGIT:
337 /*
338 * Entering digits of a number.
339 * Terminated by a non-digit.
340 */
341 if ((c < '0' || c > '9') &&
342 c != erase_char && c != kill_char)
343 {
344 /*
345 * Not part of the number.
346 * Treat as a normal command character.
347 */
348 number = cmd_int();
349 mca = 0;
350 return (NO_MCA);
351 }
352 break;
353
354 case A_TOGGLE_OPTION:
355 /*
356 * Special case for the TOGGLE_OPTION command.
357 * if the option letter which was entered is a
358 * single-char option, execute the command immediately,
359 * so he doesn't have to hit RETURN.
360 */
361 if (cp == cmdbuf && c != erase_char && c != kill_char &&
362 single_char_option(c))
363 {
364 cmdbuf[0] = c;
365 cmdbuf[1] = '\0';
366 toggle_option(cmdbuf, 1);
367 return (MCA_DONE);
368 }
369 break;
370 }
371
372 /*
373 * Any other multicharacter command
374 * is terminated by a newline.
375 */
376 if (c == '\n' || c == '\r')
377 {
378 /*
379 * Execute the command.
380 */
381 exec_mca();
382 return (MCA_DONE);
383 }
384 /*
385 * Append the char to the command buffer.
386 */
387 if (cmd_char(c))
388 /*
389 * Abort the multi-char command.
390 */
391 return (MCA_DONE);
392 /*
393 * Need another character.
394 */
395 return (MCA_MORE);
396}
397
398/*
399 * Main command processor.
400 * Accept and execute commands until a quit command, then return.
401 */
402 public void
403commands()
404{
405 register int c;
406 register int action;
407
408 last_mca = 0;
409 scroll = (sc_height + 1) / 2;
410
411 for (;;)
412 {
413 mca = 0;
414 number = 0;
415
416 /*
417 * See if any signals need processing.
418 */
419 if (sigs)
420 {
421 psignals();
422 if (quitting)
423 quit();
424 }
425
426 /*
427 * Display prompt and accept a character.
428 */
429 cmd_reset();
430 prompt();
431 noprefix();
432 c = getcc();
433
434 again:
435 if (sigs)
436 continue;
437
438 /*
439 * If we are in a multicharacter command, call mca_char.
440 * Otherwise we call cmd_decode to determine the
441 * action to be performed.
442 */
443 if (mca)
444 switch (mca_char(c))
445 {
446 case MCA_MORE:
447 /*
448 * Need another character.
449 */
450 c = getcc();
451 goto again;
452 case MCA_DONE:
453 /*
454 * Command has been handled by mca_char.
455 * Start clean with a prompt.
456 */
457 continue;
458 case NO_MCA:
459 /*
460 * Not a multi-char command
461 * (at least, not anymore).
462 */
463 break;
464 }
465
466 /*
467 * Decode the command character and decide what to do.
468 */
469 switch (action = cmd_decode(c))
470 {
471 case A_DIGIT:
472 /*
473 * First digit of a number.
474 */
475 start_mca(A_DIGIT, ":");
476 goto again;
477
478 case A_F_SCREEN:
479 /*
480 * Forward one screen.
481 */
482 if (number <= 0)
483 number = sc_window;
484 if (number <= 0)
485 number = sc_height - 1;
486 cmd_exec();
487 forward(number, 1);
488 break;
489
490 case A_B_SCREEN:
491 /*
492 * Backward one screen.
493 */
494 if (number <= 0)
495 number = sc_window;
496 if (number <= 0)
497 number = sc_height - 1;
498 cmd_exec();
499 backward(number, 1);
500 break;
501
502 case A_F_LINE:
503 /*
504 * Forward N (default 1) line.
505 */
506 if (number <= 0)
507 number = 1;
508 cmd_exec();
509 forward(number, 0);
510 break;
511
512 case A_B_LINE:
513 /*
514 * Backward N (default 1) line.
515 */
516 if (number <= 0)
517 number = 1;
518 cmd_exec();
519 backward(number, 0);
520 break;
521
522 case A_F_SCROLL:
523 /*
524 * Forward N lines
525 * (default same as last 'd' or 'u' command).
526 */
527 if (number > 0)
528 scroll = number;
529 cmd_exec();
530 forward(scroll, 0);
531 break;
532
533 case A_B_SCROLL:
534 /*
535 * Forward N lines
536 * (default same as last 'd' or 'u' command).
537 */
538 if (number > 0)
539 scroll = number;
540 cmd_exec();
541 backward(scroll, 0);
542 break;
543
544 case A_FREPAINT:
545 /*
546 * Flush buffers, then repaint screen.
547 * Don't flush the buffers on a pipe!
548 */
549 if (!ispipe)
550 {
551 ch_init(0, 0);
552 clr_linenum();
553 }
554 /* FALLTHRU */
555 case A_REPAINT:
556 /*
557 * Repaint screen.
558 */
559 cmd_exec();
560 repaint();
561 break;
562
563 case A_GOLINE:
564 /*
565 * Go to line N, default beginning of file.
566 */
567 if (number <= 0)
568 number = 1;
569 cmd_exec();
570 jump_back(number);
571 break;
572
573 case A_PERCENT:
574 /*
575 * Go to a specified percentage into the file.
576 */
577 if (number < 0)
578 number = 0;
579 if (number > 100)
580 number = 100;
581 cmd_exec();
582 jump_percent(number);
583 break;
584
585 case A_GOEND:
586 /*
587 * Go to line N, default end of file.
588 */
589 cmd_exec();
590 if (number <= 0)
591 jump_forw();
592 else
593 jump_back(number);
594 break;
595
596 case A_STAT:
597 /*
598 * Print file name, etc.
599 */
600 cmd_exec();
601 error(eq_message());
602 break;
603
604 case A_VERSION:
605 /*
606 * Print version number, without the "@(#)".
607 */
608 cmd_exec();
609 error(version+4);
610 break;
611
612 case A_QUIT:
613 /*
614 * Exit.
615 */
616 quit();
617
618 case A_F_SEARCH:
619 case A_B_SEARCH:
620 /*
621 * Search for a pattern.
622 * Accept chars of the pattern until \n.
623 */
624 if (number <= 0)
625 number = 1;
626 start_mca(action, (action==A_F_SEARCH) ? "/" : "?");
627 last_mca = mca;
628 wsearch = 1;
629 c = getcc();
630 if (c == '!')
631 {
632 /*
633 * Invert the sense of the search.
634 * Set wsearch to 0 and get a new
635 * character for the start of the pattern.
636 */
637 start_mca(action,
638 (action==A_F_SEARCH) ? "!/" : "!?");
639 wsearch = 0;
640 c = getcc();
641 }
642 goto again;
643
644 case A_AGAIN_SEARCH:
645 /*
646 * Repeat previous search.
647 */
648 if (number <= 0)
649 number = 1;
650 if (wsearch)
651 start_mca(last_mca,
652 (last_mca==A_F_SEARCH) ? "/" : "?");
653 else
654 start_mca(last_mca,
655 (last_mca==A_F_SEARCH) ? "!/" : "!?");
656 cmd_exec();
657 search(mca==A_F_SEARCH, (char *)NULL, number, wsearch);
658 break;
659
660 case A_HELP:
661 /*
662 * Help.
663 */
664 lower_left();
665 clear_eol();
666 putstr("help");
667 cmd_exec();
668 help();
669 break;
670
671 case A_EXAMINE:
672 /*
673 * Edit a new file. Get the filename.
674 */
675 cmd_reset();
676 start_mca(A_EXAMINE, "Examine: ");
677 c = getcc();
678 goto again;
679
680 case A_VISUAL:
681 /*
682 * Invoke an editor on the input file.
683 */
bfe13c81
KB
684 if (ispipe)
685 {
686 error("Cannot edit standard input");
687 break;
688 }
689 /*
690 * Try to pass the line number to the editor.
691 */
692 cmd_exec();
693 c = currline(MIDDLE);
694 if (c == 0)
695 sprintf(cmdbuf, "%s %s",
696 editor, current_file);
697 else
698 sprintf(cmdbuf, "%s +%d %s",
699 editor, c, current_file);
700 lsystem(cmdbuf);
701 ch_init(0, 0);
702 clr_linenum();
703 break;
bfe13c81
KB
704
705 case A_NEXT_FILE:
706 /*
707 * Examine next file.
708 */
709 if (number <= 0)
710 number = 1;
711 next_file(number);
712 break;
713
714 case A_PREV_FILE:
715 /*
716 * Examine previous file.
717 */
718 if (number <= 0)
719 number = 1;
720 prev_file(number);
721 break;
722
723 case A_TOGGLE_OPTION:
724 /*
725 * Toggle a flag setting.
726 */
727 cmd_reset();
728 start_mca(A_TOGGLE_OPTION, "-");
729 c = getcc();
730 goto again;
731
732 case A_DISP_OPTION:
733 /*
734 * Report a flag setting.
735 */
736 cmd_reset();
737 start_mca(A_DISP_OPTION, "_");
738 c = getcc();
739 if (c == erase_char || c == kill_char)
740 break;
741 cmdbuf[0] = c;
742 cmdbuf[1] = '\0';
743 toggle_option(cmdbuf, 0);
744 break;
745
746 case A_FIRSTCMD:
747 /*
748 * Set an initial command for new files.
749 */
750 cmd_reset();
751 start_mca(A_FIRSTCMD, "+");
752 c = getcc();
753 goto again;
754
bfe13c81
KB
755 case A_SETMARK:
756 /*
757 * Set a mark.
758 */
759 lower_left();
760 clear_eol();
761 start_mca(A_SETMARK, "mark: ");
762 c = getcc();
763 if (c == erase_char || c == kill_char)
764 break;
765 setmark(c);
766 break;
767
768 case A_GOMARK:
769 /*
770 * Go to a mark.
771 */
772 lower_left();
773 clear_eol();
774 start_mca(A_GOMARK, "goto mark: ");
775 c = getcc();
776 if (c == erase_char || c == kill_char)
777 break;
778 gomark(c);
779 break;
780
781 case A_PREFIX:
782 /*
783 * The command is incomplete (more chars are needed).
784 * Display the current char so the user knows
785 * what's going on and get another character.
786 */
787 if (mca != A_PREFIX)
788 start_mca(A_PREFIX, "& ");
789 if (control_char(c))
790 {
791 putchr('^');
792 c = carat_char(c);
793 }
794 putchr(c);
795 c = getcc();
796 goto again;
797
798 default:
799 bell();
800 break;
801 }
802 }
803}