1: /*
2: * User-level command processor.
3: */
4:
5: #include "less.h"
6: #include "position.h"
7: #include <setjmp.h>
8:
9: extern jmp_buf main_loop;
10: extern int erase_char, kill_char;
11: extern int pr_type;
12: extern int sigs;
13: extern int ispipe;
14: extern int quit_at_eof;
15: extern int hit_eof;
16: extern int sc_width, sc_height;
17: extern int sc_window;
18: extern char *first_cmd;
19: extern char *every_first_cmd;
20: extern char version[];
21: extern char current_file[];
22: extern char *editor;
23:
24: static char cmdbuf[90]; /* Buffer for holding a multi-char command */
25: static char *cp; /* Pointer into cmdbuf */
26: static int cmd_col; /* Current column of the multi-char command */
27: static char mcc; /* The multi-char command letter (e.g. '/') */
28: static char last_mcc; /* The previous mcc */
29: static int screen_trashed; /* The screen has been overwritten */
30:
31: /*
32: * Reset command buffer (to empty).
33: */
34: cmd_reset()
35: {
36: cp = cmdbuf;
37: }
38:
39: /*
40: * Backspace in command buffer.
41: */
42: static int
43: cmd_erase()
44: {
45: if (cp == cmdbuf)
46: /*
47: * Backspace past beginning of the string:
48: * this usually means abort the command.
49: */
50: return (1);
51:
52: if (control_char(*--cp))
53: {
54: /*
55: * Erase an extra character, for the carat.
56: */
57: backspace();
58: cmd_col--;
59: }
60: backspace();
61: cmd_col--;
62: return (0);
63: }
64:
65: /*
66: * Set up the display to start a new multi-character command.
67: */
68: start_mcc(c)
69: int c;
70: {
71: mcc = c;
72: lower_left();
73: clear_eol();
74: putc(mcc);
75: cmd_col = 1;
76: }
77:
78: /*
79: * Process a single character of a multi-character command, such as
80: * a number, or the pattern of a search command.
81: */
82: static int
83: cmd_char(c)
84: int c;
85: {
86: if (c == erase_char)
87: {
88: if (cmd_erase())
89: return (1);
90: } else if (c == kill_char)
91: {
92: /* {{ Could do this faster, but who cares? }} */
93: while (cmd_erase() == 0)
94: ;
95: } else
96: {
97: /*
98: * Append the character to the string,
99: * if there is room in the buffer and on the screen.
100: */
101: if (cp < &cmdbuf[sizeof(cmdbuf)-1] && cmd_col < sc_width-3)
102: {
103: *cp++ = c;
104: if (control_char(c))
105: {
106: putc('^');
107: cmd_col++;
108: c = carat_char(c);
109: }
110: putc(c);
111: cmd_col++;
112: } else
113: bell();
114: }
115: return (0);
116: }
117:
118: /*
119: * Return the number currently in the command buffer.
120: */
121: static int
122: cmd_int()
123: {
124: *cp = '\0';
125: cp = cmdbuf;
126: return (atoi(cmdbuf));
127: }
128:
129: /*
130: * Move the cursor to lower left before executing a command.
131: * This looks nicer if the command takes a long time before
132: * updating the screen.
133: */
134: static void
135: cmd_exec()
136: {
137: lower_left();
138: flush();
139: }
140:
141: /*
142: * Display the appropriate prompt.
143: */
144: static void
145: prompt()
146: {
147: register char *p;
148:
149: if (first_cmd != NULL && *first_cmd != '\0')
150: /*
151: * No prompt necessary if commands are from first_cmd
152: * rather than from the user.
153: */
154: return;
155:
156: /*
157: * If nothing is displayed yet, display starting from line 1.
158: */
159: if (position(TOP) == NULL_POSITION)
160: jump_back(1);
161: else if (screen_trashed)
162: repaint();
163: screen_trashed = 0;
164:
165: /*
166: * Select the proper prompt and display it.
167: */
168: lower_left();
169: clear_eol();
170: p = pr_string();
171: if (p == NULL)
172: putc(':');
173: else
174: {
175: so_enter();
176: puts(p);
177: so_exit();
178: }
179: }
180:
181: /*
182: * Get command character.
183: * The character normally comes from the keyboard,
184: * but may come from the "first_cmd" string.
185: */
186: static int
187: getcc()
188: {
189: if (first_cmd == NULL)
190: return (getc());
191:
192: if (*first_cmd == '\0')
193: {
194: /*
195: * Reached end of first_cmd input.
196: */
197: first_cmd = NULL;
198: if (cp > cmdbuf && position(TOP) == NULL_POSITION)
199: {
200: /*
201: * Command is incomplete, so try to complete it.
202: * There are only two cases:
203: * 1. We have "/string" but no newline. Add the \n.
204: * 2. We have a number but no command. Treat as #g.
205: * (This is all pretty hokey.)
206: */
207: if (mcc != ':')
208: /* Not a number; must be search string */
209: return ('\n');
210: else
211: /* A number; append a 'g' */
212: return ('g');
213: }
214: return (getc());
215: }
216: return (*first_cmd++);
217: }
218:
219: /*
220: * Main command processor.
221: * Accept and execute commands until a quit command, then return.
222: */
223: public void
224: commands()
225: {
226: register int c;
227: register int n;
228: register int scroll = 10;
229:
230: last_mcc = 0;
231: setjmp(main_loop);
232: mcc = 0;
233:
234: for (;;)
235: {
236: /*
237: * Display prompt and accept a character.
238: */
239: psignals(); /* See if any signals need processing */
240:
241: if (quit_at_eof && hit_eof > 1)
242: /*
243: * After hitting end-of-file for the second time,
244: * automatically advance to the next file.
245: * If there are no more files, quit.
246: */
247: next_file(1);
248:
249: cmd_reset();
250: prompt();
251: c = getcc();
252:
253: again:
254: if (sigs)
255: continue;
256:
257: if (mcc)
258: {
259: /*
260: * We are in a multi-character command.
261: * All chars until newline go into the command buffer.
262: * (Note that mcc == ':' is a special case that
263: * means a number is being entered.)
264: */
265: if (mcc != ':' && (c == '\n' || c == '\r'))
266: {
267: char *p;
268: static char fcbuf[100];
269:
270: /*
271: * Execute the command.
272: */
273: *cp = '\0';
274: cmd_exec();
275: switch (mcc)
276: {
277: case '/': case '?':
278: search(mcc, cmdbuf, n);
279: break;
280: case '+':
281: for (p = cmdbuf; *p == '+' || *p == ' '; p++) ;
282: if (*p == '\0')
283: every_first_cmd = NULL;
284: else
285: {
286: strtcpy(fcbuf, p, sizeof(fcbuf));
287: every_first_cmd = fcbuf;
288: }
289: break;
290: case 'E':
291: /*
292: * Ignore leading spaces
293: * in the filename.
294: */
295: for (p = cmdbuf; *p == ' '; p++) ;
296: edit(glob(p));
297: break;
298: #if SHELL_ESCAPE
299: case '!':
300: lsystem(cmdbuf);
301: screen_trashed = 1;
302: error("!done");
303: break;
304: #endif
305: }
306: mcc = 0;
307: } else
308: {
309: if (mcc == ':' && (c < '0' || c > '9') &&
310: c != erase_char && c != kill_char)
311: {
312: /*
313: * This is not part of the number
314: * we were entering. Process
315: * it as a regular character.
316: */
317: mcc = 0;
318: goto again;
319: }
320:
321: /*
322: * Append the char to the command buffer.
323: */
324: if (cmd_char(c))
325: {
326: /* Abort the multi-char command. */
327: mcc = 0;
328: continue;
329: }
330: c = getcc();
331: goto again;
332: }
333: } else switch (c)
334: {
335: case '0': case '1': case '2': case '3': case '4':
336: case '5': case '6': case '7': case '8': case '9':
337: /*
338: * First digit of a number.
339: */
340: start_mcc(':');
341: goto again;
342:
343: case 'f':
344: case ' ':
345: case CONTROL('F'):
346: /*
347: * Forward one screen.
348: */
349: n = cmd_int();
350: if (n <= 0)
351: n = sc_window;
352: forward(n, 1);
353: break;
354:
355: case 'b':
356: case CONTROL('B'):
357: /*
358: * Backward one screen.
359: */
360: n = cmd_int();
361: if (n <= 0)
362: n = sc_window;
363: backward(n, 1);
364: break;
365:
366: case 'e':
367: case 'j':
368: case '\r':
369: case '\n':
370: case CONTROL('E'):
371: /*
372: * Forward N (default 1) line.
373: */
374: n = cmd_int();
375: if (n <= 0)
376: n = 1;
377: forward(n, 0);
378: break;
379:
380: case 'y':
381: case 'k':
382: case CONTROL('K'):
383: case CONTROL('Y'):
384: /*
385: * Backward N (default 1) line.
386: */
387: n = cmd_int();
388: if (n <= 0)
389: n = 1;
390: backward(n, 0);
391: break;
392:
393: case 'd':
394: case CONTROL('D'):
395: /*
396: * Forward N lines
397: * (default same as last 'd' or 'u' command).
398: */
399: n = cmd_int();
400: if (n > 0)
401: scroll = n;
402: forward(scroll, 0);
403: break;
404:
405: case 'u':
406: case CONTROL('U'):
407: /*
408: * Forward N lines
409: * (default same as last 'd' or 'u' command).
410: */
411: n = cmd_int();
412: if (n > 0)
413: scroll = n;
414: backward(scroll, 0);
415: break;
416:
417: case 'R':
418: /*
419: * Flush buffers, then repaint screen.
420: * Don't flush the buffers on a pipe!
421: */
422: if (!ispipe)
423: ch_init(0);
424: /* Fall thru */
425: case 'r':
426: case CONTROL('R'):
427: case CONTROL('L'):
428: /*
429: * Repaint screen.
430: */
431: repaint();
432: break;
433:
434: case 'g':
435: /*
436: * Go to line N, default beginning of file.
437: */
438: n = cmd_int();
439: if (n <= 0)
440: n = 1;
441: cmd_exec();
442: jump_back(n);
443: break;
444:
445: case 'p':
446: case '%':
447: /*
448: * Go to a specified percentage into the file.
449: */
450: n = cmd_int();
451: if (n < 0)
452: n = 0;
453: if (n > 100)
454: n = 100;
455: cmd_exec();
456: jump_percent(n);
457: break;
458:
459: case 'G':
460: /*
461: * Go to line N, default end of file.
462: */
463: n = cmd_int();
464: cmd_exec();
465: if (n <= 0)
466: jump_forw();
467: else
468: jump_back(n);
469: break;
470:
471: case '=':
472: case CONTROL('G'):
473: /*
474: * Print file name, etc.
475: */
476: error(eq_message());
477: break;
478:
479: case 'V':
480: /*
481: * Print version number, without the "@(#)".
482: */
483: error(version+4);
484: break;
485:
486: case 'q':
487: /*
488: * Exit.
489: */
490: /*setjmp(main_loop);*/
491: quit();
492:
493: case '/':
494: case '?':
495: /*
496: * Search for a pattern.
497: * Accept chars of the pattern until \n.
498: */
499: n = cmd_int();
500: if (n <= 0)
501: n = 1;
502: start_mcc(c);
503: last_mcc = c;
504: c = getcc();
505: goto again;
506:
507: case 'n':
508: /*
509: * Repeat previous search.
510: */
511: n = cmd_int();
512: if (n <= 0)
513: n = 1;
514: start_mcc(last_mcc);
515: cmd_exec();
516: search(mcc, (char *)NULL, n);
517: mcc = 0;
518: break;
519:
520: case 'h':
521: /*
522: * Help.
523: */
524: lower_left();
525: clear_eol();
526: puts("help");
527: cmd_exec();
528: help();
529: screen_trashed = 1;
530: break;
531:
532: case 'E':
533: /*
534: * Edit a new file. Get the filename.
535: */
536: cmd_reset();
537: start_mcc('E');
538: puts("xamine: "); /* This looks nicer */
539: cmd_col += 8;
540: c = getcc();
541: goto again;
542:
543: case '!':
544: #if SHELL_ESCAPE
545: /*
546: * Shell escape.
547: */
548: cmd_reset();
549: start_mcc('!');
550: c = getcc();
551: goto again;
552: #else
553: error("Command not available");
554: break;
555: #endif
556:
557: case 'v':
558: #if EDITOR
559: if (ispipe)
560: {
561: error("Cannot edit standard input");
562: break;
563: }
564: sprintf(cmdbuf, "%s %s", editor, current_file);
565: lsystem(cmdbuf);
566: ch_init(0);
567: screen_trashed = 1;
568: break;
569: #else
570: error("Command not available");
571: break;
572: #endif
573:
574: case 'N':
575: /*
576: * Examine next file.
577: */
578: n = cmd_int();
579: if (n <= 0)
580: n = 1;
581: next_file(n);
582: break;
583:
584: case 'P':
585: /*
586: * Examine previous file.
587: */
588: n = cmd_int();
589: if (n <= 0)
590: n = 1;
591: prev_file(n);
592: break;
593:
594: case '-':
595: /*
596: * Toggle a flag setting.
597: */
598: start_mcc('-');
599: c = getcc();
600: mcc = 0;
601: if (c == erase_char || c == kill_char)
602: break;
603: toggle_option(c);
604: break;
605:
606: case '+':
607: cmd_reset();
608: start_mcc('+');
609: c = getcc();
610: goto again;
611:
612: case 'm':
613: /*
614: * Set a mark.
615: */
616: lower_left();
617: clear_eol();
618: puts("mark: ");
619: c = getcc();
620: if (c == erase_char || c == kill_char)
621: break;
622: setmark(c);
623: break;
624:
625: case '\'':
626: /*
627: * Go to a mark.
628: */
629: lower_left();
630: clear_eol();
631: puts("goto mark: ");
632: c = getcc();
633: if (c == erase_char || c == kill_char)
634: break;
635: gomark(c);
636: break;
637:
638: default:
639: bell();
640: break;
641: }
642: }
643: }
Defined functions
cmd_int
defined in line
121; used 13 times
- in line 349,
360,
374,
387,
399,
411,
438,
450,
463,
499,
511,
578,
588
Defined variables
cmdbuf
defined in line
24; used 13 times
cp
defined in line
25; used 9 times
mcc
defined in line
27; used 15 times
- in line 71-74(2),
207,
232,
257,
265,
275-278(2),
306-309(2),
317,
327,
516-517(2),
600