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