Annotation of src/usr.bin/less/cmdbuf.c, Revision 1.4
1.1 etheisen 1: /*
1.4 ! millert 2: * Copyright (C) 1984-2002 Mark Nudelman
1.1 etheisen 3: *
1.4 ! millert 4: * You may distribute under the terms of either the GNU General Public
! 5: * License or the Less License, as specified in the README file.
1.1 etheisen 6: *
1.4 ! millert 7: * For more information about less, or for information on how to
! 8: * contact the author, see the README file.
1.1 etheisen 9: */
10:
11:
12: /*
13: * Functions which manipulate the command buffer.
14: * Used only by command() and related functions.
15: */
16:
17: #include "less.h"
18: #include "cmd.h"
19:
20: extern int sc_width;
21:
1.4 ! millert 22: static char cmdbuf[CMDBUF_SIZE]; /* Buffer for holding a multi-char command */
! 23: static int cmd_col; /* Current column of the cursor */
! 24: static int prompt_col; /* Column of cursor just after prompt */
1.1 etheisen 25: static char *cp; /* Pointer into cmdbuf */
1.4 ! millert 26: static int cmd_offset; /* Index into cmdbuf of first displayed char */
! 27: static int literal; /* Next input char should not be interpreted */
1.1 etheisen 28:
29: #if TAB_COMPLETE_FILENAME
30: static int cmd_complete();
31: /*
32: * These variables are statics used by cmd_complete.
33: */
34: static int in_completion = 0;
35: static char *tk_text;
36: static char *tk_original;
37: static char *tk_ipoint;
38: static char *tk_trial;
39: static struct textlist tk_tlist;
40: #endif
41:
1.4 ! millert 42: static int cmd_left();
! 43: static int cmd_right();
! 44:
! 45: #if SPACES_IN_FILENAMES
! 46: public char openquote = '"';
! 47: public char closequote = '"';
! 48: #endif
! 49:
1.1 etheisen 50: #if CMD_HISTORY
51: /*
52: * A mlist structure represents a command history.
53: */
54: struct mlist
55: {
56: struct mlist *next;
57: struct mlist *prev;
58: struct mlist *curr_mp;
59: char *string;
60: };
61:
62: /*
63: * These are the various command histories that exist.
64: */
65: struct mlist mlist_search =
66: { &mlist_search, &mlist_search, &mlist_search, NULL };
1.4 ! millert 67: public void * constant ml_search = (void *) &mlist_search;
! 68:
1.1 etheisen 69: struct mlist mlist_examine =
70: { &mlist_examine, &mlist_examine, &mlist_examine, NULL };
1.4 ! millert 71: public void * constant ml_examine = (void *) &mlist_examine;
! 72:
1.1 etheisen 73: #if SHELL_ESCAPE || PIPEC
74: struct mlist mlist_shell =
75: { &mlist_shell, &mlist_shell, &mlist_shell, NULL };
1.4 ! millert 76: public void * constant ml_shell = (void *) &mlist_shell;
! 77: #endif
! 78:
! 79: #else /* CMD_HISTORY */
! 80:
! 81: /* If CMD_HISTORY is off, these are just flags. */
! 82: public void * constant ml_search = (void *)1;
! 83: public void * constant ml_examine = (void *)2;
! 84: #if SHELL_ESCAPE || PIPEC
! 85: public void * constant ml_shell = (void *)3;
! 86: #endif
! 87:
! 88: #endif /* CMD_HISTORY */
1.1 etheisen 89:
90: /*
91: * History for the current command.
92: */
93: static struct mlist *curr_mlist = NULL;
1.4 ! millert 94: static int curr_cmdflags;
1.1 etheisen 95:
96:
97: /*
98: * Reset command buffer (to empty).
99: */
100: public void
101: cmd_reset()
102: {
103: cp = cmdbuf;
104: *cp = '\0';
105: cmd_col = 0;
1.4 ! millert 106: cmd_offset = 0;
1.1 etheisen 107: literal = 0;
108: }
109:
110: /*
1.4 ! millert 111: * Clear command line on display.
! 112: */
! 113: public void
! 114: clear_cmd()
! 115: {
! 116: clear_bot();
! 117: cmd_col = prompt_col = 0;
! 118: }
! 119:
! 120: /*
! 121: * Display a string, usually as a prompt for input into the command buffer.
! 122: */
! 123: public void
! 124: cmd_putstr(s)
! 125: char *s;
! 126: {
! 127: putstr(s);
! 128: cmd_col += strlen(s);
! 129: prompt_col += strlen(s);
! 130: }
! 131:
! 132: /*
1.1 etheisen 133: * How many characters are in the command buffer?
134: */
135: public int
136: len_cmdbuf()
137: {
138: return (strlen(cmdbuf));
139: }
140:
141: /*
1.4 ! millert 142: * Repaint the line from cp onwards.
! 143: * Then position the cursor just after the char old_cp (a pointer into cmdbuf).
! 144: */
! 145: static void
! 146: cmd_repaint(old_cp)
! 147: char *old_cp;
! 148: {
! 149: char *p;
! 150:
! 151: /*
! 152: * Repaint the line from the current position.
! 153: */
! 154: clear_eol();
! 155: for ( ; *cp != '\0'; cp++)
! 156: {
! 157: p = prchar(*cp);
! 158: if (cmd_col + (int)strlen(p) >= sc_width)
! 159: break;
! 160: putstr(p);
! 161: cmd_col += strlen(p);
! 162: }
! 163:
! 164: /*
! 165: * Back up the cursor to the correct position.
! 166: */
! 167: while (cp > old_cp)
! 168: cmd_left();
! 169: }
! 170:
! 171: /*
! 172: * Put the cursor at "home" (just after the prompt),
! 173: * and set cp to the corresponding char in cmdbuf.
! 174: */
! 175: static void
! 176: cmd_home()
! 177: {
! 178: while (cmd_col > prompt_col)
! 179: {
! 180: putbs();
! 181: cmd_col--;
! 182: }
! 183:
! 184: cp = &cmdbuf[cmd_offset];
! 185: }
! 186:
! 187: /*
! 188: * Shift the cmdbuf display left a half-screen.
! 189: */
! 190: static void
! 191: cmd_lshift()
! 192: {
! 193: char *s;
! 194: char *save_cp;
! 195: int cols;
! 196:
! 197: /*
! 198: * Start at the first displayed char, count how far to the
! 199: * right we'd have to move to reach the center of the screen.
! 200: */
! 201: s = cmdbuf + cmd_offset;
! 202: cols = 0;
! 203: while (cols < (sc_width - prompt_col) / 2 && *s != '\0')
! 204: cols += strlen(prchar(*s++));
! 205:
! 206: cmd_offset = s - cmdbuf;
! 207: save_cp = cp;
! 208: cmd_home();
! 209: cmd_repaint(save_cp);
! 210: }
! 211:
! 212: /*
! 213: * Shift the cmdbuf display right a half-screen.
! 214: */
! 215: static void
! 216: cmd_rshift()
! 217: {
! 218: char *s;
! 219: char *p;
! 220: char *save_cp;
! 221: int cols;
! 222:
! 223: /*
! 224: * Start at the first displayed char, count how far to the
! 225: * left we'd have to move to traverse a half-screen width
! 226: * of displayed characters.
! 227: */
! 228: s = cmdbuf + cmd_offset;
! 229: cols = 0;
! 230: while (cols < (sc_width - prompt_col) / 2 && s > cmdbuf)
! 231: {
! 232: p = prchar(*--s);
! 233: cols += strlen(p);
! 234: }
! 235:
! 236: cmd_offset = s - cmdbuf;
! 237: save_cp = cp;
! 238: cmd_home();
! 239: cmd_repaint(save_cp);
! 240: }
! 241:
! 242: /*
! 243: * Move cursor right one character.
! 244: */
! 245: static int
! 246: cmd_right()
! 247: {
! 248: char *p;
! 249:
! 250: if (*cp == '\0')
! 251: {
! 252: /*
! 253: * Already at the end of the line.
! 254: */
! 255: return (CC_OK);
! 256: }
! 257: p = prchar(*cp);
! 258: if (cmd_col + (int)strlen(p) >= sc_width)
! 259: cmd_lshift();
! 260: else if (cmd_col + (int)strlen(p) == sc_width - 1 && cp[1] != '\0')
! 261: cmd_lshift();
! 262: cp++;
! 263: putstr(p);
! 264: cmd_col += strlen(p);
! 265: return (CC_OK);
! 266: }
! 267:
! 268: /*
! 269: * Move cursor left one character.
! 270: */
! 271: static int
! 272: cmd_left()
! 273: {
! 274: char *p;
! 275:
! 276: if (cp <= cmdbuf)
! 277: {
! 278: /* Already at the beginning of the line */
! 279: return (CC_OK);
! 280: }
! 281: p = prchar(cp[-1]);
! 282: if (cmd_col < prompt_col + (int)strlen(p))
! 283: cmd_rshift();
! 284: cp--;
! 285: cmd_col -= strlen(p);
! 286: while (*p++ != '\0')
! 287: putbs();
! 288: return (CC_OK);
! 289: }
! 290:
! 291: /*
! 292: * Insert a char into the command buffer, at the current position.
! 293: */
! 294: static int
! 295: cmd_ichar(c)
! 296: int c;
! 297: {
! 298: char *s;
! 299:
! 300: if (strlen(cmdbuf) >= sizeof(cmdbuf)-2)
! 301: {
! 302: /*
! 303: * No room in the command buffer for another char.
! 304: */
! 305: bell();
! 306: return (CC_ERROR);
! 307: }
! 308:
! 309: /*
! 310: * Insert the character into the buffer.
! 311: */
! 312: for (s = &cmdbuf[strlen(cmdbuf)]; s >= cp; s--)
! 313: s[1] = s[0];
! 314: *cp = c;
! 315: /*
! 316: * Reprint the tail of the line from the inserted char.
! 317: */
! 318: cmd_repaint(cp);
! 319: cmd_right();
! 320: return (CC_OK);
! 321: }
! 322:
! 323: /*
1.1 etheisen 324: * Backspace in the command buffer.
325: * Delete the char to the left of the cursor.
326: */
327: static int
328: cmd_erase()
329: {
1.4 ! millert 330: register char *s;
1.1 etheisen 331:
332: if (cp == cmdbuf)
333: {
334: /*
335: * Backspace past beginning of the buffer:
336: * this usually means abort the command.
337: */
338: return (CC_QUIT);
339: }
340: /*
1.4 ! millert 341: * Move cursor left (to the char being erased).
1.1 etheisen 342: */
1.4 ! millert 343: cmd_left();
1.1 etheisen 344: /*
1.4 ! millert 345: * Remove the char from the buffer (shift the buffer left).
1.1 etheisen 346: */
347: for (s = cp; *s != '\0'; s++)
348: s[0] = s[1];
349: /*
1.4 ! millert 350: * Repaint the buffer after the erased char.
1.1 etheisen 351: */
1.4 ! millert 352: cmd_repaint(cp);
1.1 etheisen 353:
354: /*
355: * We say that erasing the entire command string causes us
1.4 ! millert 356: * to abort the current command, if CF_QUIT_ON_ERASE is set.
1.1 etheisen 357: */
1.4 ! millert 358: if ((curr_cmdflags & CF_QUIT_ON_ERASE) && cp == cmdbuf && *cp == '\0')
1.1 etheisen 359: return (CC_QUIT);
360: return (CC_OK);
361: }
362:
363: /*
364: * Delete the char under the cursor.
365: */
366: static int
367: cmd_delete()
368: {
369: if (*cp == '\0')
370: {
371: /*
372: * At end of string; there is no char under the cursor.
373: */
374: return (CC_OK);
375: }
376: /*
377: * Move right, then use cmd_erase.
378: */
1.4 ! millert 379: cmd_right();
1.1 etheisen 380: cmd_erase();
381: return (CC_OK);
382: }
383:
384: /*
385: * Delete the "word" to the left of the cursor.
386: */
387: static int
388: cmd_werase()
389: {
390: if (cp > cmdbuf && cp[-1] == ' ')
391: {
392: /*
393: * If the char left of cursor is a space,
394: * erase all the spaces left of cursor (to the first non-space).
395: */
396: while (cp > cmdbuf && cp[-1] == ' ')
397: (void) cmd_erase();
398: } else
399: {
400: /*
401: * If the char left of cursor is not a space,
402: * erase all the nonspaces left of cursor (the whole "word").
403: */
404: while (cp > cmdbuf && cp[-1] != ' ')
405: (void) cmd_erase();
406: }
407: return (CC_OK);
408: }
409:
410: /*
411: * Delete the "word" under the cursor.
412: */
413: static int
414: cmd_wdelete()
415: {
416: if (*cp == ' ')
417: {
418: /*
419: * If the char under the cursor is a space,
420: * delete it and all the spaces right of cursor.
421: */
422: while (*cp == ' ')
423: (void) cmd_delete();
424: } else
425: {
426: /*
427: * If the char under the cursor is not a space,
428: * delete it and all nonspaces right of cursor (the whole word).
429: */
430: while (*cp != ' ' && *cp != '\0')
431: (void) cmd_delete();
432: }
433: return (CC_OK);
434: }
435:
436: /*
437: * Delete all chars in the command buffer.
438: */
439: static int
440: cmd_kill()
441: {
442: if (cmdbuf[0] == '\0')
443: {
444: /*
445: * Buffer is already empty; abort the current command.
446: */
447: return (CC_QUIT);
448: }
1.4 ! millert 449: cmd_offset = 0;
! 450: cmd_home();
1.1 etheisen 451: *cp = '\0';
1.4 ! millert 452: cmd_repaint(cp);
! 453:
1.1 etheisen 454: /*
1.4 ! millert 455: * We say that erasing the entire command string causes us
! 456: * to abort the current command, if CF_QUIT_ON_ERASE is set.
1.1 etheisen 457: */
1.4 ! millert 458: if (curr_cmdflags & CF_QUIT_ON_ERASE)
1.1 etheisen 459: return (CC_QUIT);
460: return (CC_OK);
461: }
462:
463: /*
464: * Select an mlist structure to be the current command history.
465: */
466: public void
1.4 ! millert 467: set_mlist(mlist, cmdflags)
1.1 etheisen 468: void *mlist;
1.4 ! millert 469: int cmdflags;
1.1 etheisen 470: {
471: curr_mlist = (struct mlist *) mlist;
1.4 ! millert 472: curr_cmdflags = cmdflags;
1.1 etheisen 473: }
474:
1.4 ! millert 475: #if CMD_HISTORY
1.1 etheisen 476: /*
477: * Move up or down in the currently selected command history list.
478: */
479: static int
480: cmd_updown(action)
481: int action;
482: {
483: char *s;
484:
485: if (curr_mlist == NULL)
486: {
487: /*
488: * The current command has no history list.
489: */
490: bell();
491: return (CC_OK);
492: }
493: cmd_home();
494: clear_eol();
495: /*
496: * Move curr_mp to the next/prev entry.
497: */
498: if (action == EC_UP)
499: curr_mlist->curr_mp = curr_mlist->curr_mp->prev;
500: else
501: curr_mlist->curr_mp = curr_mlist->curr_mp->next;
502: /*
503: * Copy the entry into cmdbuf and echo it on the screen.
504: */
505: s = curr_mlist->curr_mp->string;
506: if (s == NULL)
507: s = "";
1.4 ! millert 508: for (cp = cmdbuf; *s != '\0'; s++)
1.1 etheisen 509: {
510: *cp = *s;
1.4 ! millert 511: cmd_right();
1.1 etheisen 512: }
513: *cp = '\0';
514: return (CC_OK);
515: }
1.4 ! millert 516: #endif
1.1 etheisen 517:
518: /*
1.4 ! millert 519: * Add a string to a history list.
1.1 etheisen 520: */
521: public void
1.4 ! millert 522: cmd_addhist(mlist, cmd)
! 523: struct mlist *mlist;
! 524: char *cmd;
1.1 etheisen 525: {
1.4 ! millert 526: #if CMD_HISTORY
1.1 etheisen 527: struct mlist *ml;
528:
529: /*
530: * Don't save a trivial command.
531: */
1.4 ! millert 532: if (strlen(cmd) == 0)
1.1 etheisen 533: return;
534: /*
1.4 ! millert 535: * Don't save if a duplicate of a command which is already
! 536: * in the history.
1.1 etheisen 537: * But select the one already in the history to be current.
538: */
1.4 ! millert 539: for (ml = mlist->next; ml != mlist; ml = ml->next)
1.1 etheisen 540: {
1.4 ! millert 541: if (strcmp(ml->string, cmd) == 0)
1.1 etheisen 542: break;
543: }
1.4 ! millert 544: if (ml == mlist)
1.1 etheisen 545: {
546: /*
547: * Did not find command in history.
548: * Save the command and put it at the end of the history list.
549: */
550: ml = (struct mlist *) ecalloc(1, sizeof(struct mlist));
1.4 ! millert 551: ml->string = save(cmd);
! 552: ml->next = mlist;
! 553: ml->prev = mlist->prev;
! 554: mlist->prev->next = ml;
! 555: mlist->prev = ml;
1.1 etheisen 556: }
557: /*
558: * Point to the cmd just after the just-accepted command.
559: * Thus, an UPARROW will always retrieve the previous command.
560: */
1.4 ! millert 561: mlist->curr_mp = ml->next;
! 562: #endif
1.1 etheisen 563: }
1.4 ! millert 564:
! 565: /*
! 566: * Accept the command in the command buffer.
! 567: * Add it to the currently selected history list.
! 568: */
! 569: public void
! 570: cmd_accept()
! 571: {
! 572: #if CMD_HISTORY
! 573: /*
! 574: * Nothing to do if there is no currently selected history list.
! 575: */
! 576: if (curr_mlist == NULL)
! 577: return;
! 578: cmd_addhist(curr_mlist, cmdbuf);
1.1 etheisen 579: #endif
1.4 ! millert 580: }
1.1 etheisen 581:
582: /*
583: * Try to perform a line-edit function on the command buffer,
584: * using a specified char as a line-editing command.
585: * Returns:
586: * CC_PASS The char does not invoke a line edit function.
587: * CC_OK Line edit function done.
588: * CC_QUIT The char requests the current command to be aborted.
589: */
590: static int
591: cmd_edit(c)
592: int c;
593: {
594: int action;
595: int flags;
596:
597: #if TAB_COMPLETE_FILENAME
598: #define not_in_completion() in_completion = 0
599: #else
600: #define not_in_completion()
601: #endif
602:
603: /*
604: * See if the char is indeed a line-editing command.
605: */
606: flags = 0;
1.4 ! millert 607: #if CMD_HISTORY
1.1 etheisen 608: if (curr_mlist == NULL)
609: /*
610: * No current history; don't accept history manipulation cmds.
611: */
612: flags |= EC_NOHISTORY;
1.4 ! millert 613: #endif
! 614: #if TAB_COMPLETE_FILENAME
! 615: if (curr_mlist == ml_search)
1.1 etheisen 616: /*
617: * In a search command; don't accept file-completion cmds.
618: */
619: flags |= EC_NOCOMPLETE;
1.4 ! millert 620: #endif
1.1 etheisen 621:
622: action = editchar(c, flags);
623:
624: switch (action)
625: {
626: case EC_RIGHT:
627: not_in_completion();
628: return (cmd_right());
629: case EC_LEFT:
630: not_in_completion();
631: return (cmd_left());
632: case EC_W_RIGHT:
633: not_in_completion();
634: while (*cp != '\0' && *cp != ' ')
635: cmd_right();
636: while (*cp == ' ')
637: cmd_right();
638: return (CC_OK);
639: case EC_W_LEFT:
640: not_in_completion();
641: while (cp > cmdbuf && cp[-1] == ' ')
642: cmd_left();
643: while (cp > cmdbuf && cp[-1] != ' ')
644: cmd_left();
645: return (CC_OK);
646: case EC_HOME:
647: not_in_completion();
1.4 ! millert 648: cmd_offset = 0;
! 649: cmd_home();
! 650: cmd_repaint(cp);
! 651: return (CC_OK);
1.1 etheisen 652: case EC_END:
653: not_in_completion();
654: while (*cp != '\0')
655: cmd_right();
656: return (CC_OK);
657: case EC_INSERT:
658: not_in_completion();
659: return (CC_OK);
660: case EC_BACKSPACE:
661: not_in_completion();
662: return (cmd_erase());
663: case EC_LINEKILL:
664: not_in_completion();
665: return (cmd_kill());
666: case EC_W_BACKSPACE:
667: not_in_completion();
668: return (cmd_werase());
669: case EC_DELETE:
670: not_in_completion();
671: return (cmd_delete());
672: case EC_W_DELETE:
673: not_in_completion();
674: return (cmd_wdelete());
675: case EC_LITERAL:
676: literal = 1;
677: return (CC_OK);
678: #if CMD_HISTORY
679: case EC_UP:
680: case EC_DOWN:
681: not_in_completion();
682: return (cmd_updown(action));
683: #endif
684: #if TAB_COMPLETE_FILENAME
685: case EC_F_COMPLETE:
686: case EC_B_COMPLETE:
687: case EC_EXPAND:
688: return (cmd_complete(action));
689: #endif
1.4 ! millert 690: case EC_NOACTION:
! 691: return (CC_OK);
1.1 etheisen 692: default:
693: not_in_completion();
694: return (CC_PASS);
695: }
696: }
697:
698: #if TAB_COMPLETE_FILENAME
699: /*
700: * Insert a string into the command buffer, at the current position.
701: */
702: static int
703: cmd_istr(str)
704: char *str;
705: {
706: char *s;
707: int action;
708:
709: for (s = str; *s != '\0'; s++)
710: {
711: action = cmd_ichar(*s);
712: if (action != CC_OK)
713: {
714: bell();
715: return (action);
716: }
717: }
718: return (CC_OK);
719: }
720:
721: /*
722: * Find the beginning and end of the "current" word.
723: * This is the word which the cursor (cp) is inside or at the end of.
724: * Return pointer to the beginning of the word and put the
725: * cursor at the end of the word.
726: */
727: static char *
728: delimit_word()
729: {
730: char *word;
1.4 ! millert 731: #if SPACES_IN_FILENAMES
! 732: char *p;
! 733: int delim_quoted = 0;
! 734: int meta_quoted = 0;
! 735: char *esc = get_meta_escape();
! 736: int esclen = strlen(esc);
! 737: #endif
1.1 etheisen 738:
739: /*
740: * Move cursor to end of word.
741: */
742: if (*cp != ' ' && *cp != '\0')
743: {
744: /*
745: * Cursor is on a nonspace.
746: * Move cursor right to the next space.
747: */
748: while (*cp != ' ' && *cp != '\0')
749: cmd_right();
750: } else if (cp > cmdbuf && cp[-1] != ' ')
751: {
752: /*
753: * Cursor is on a space, and char to the left is a nonspace.
754: * We're already at the end of the word.
755: */
756: ;
1.4 ! millert 757: #if 0
1.1 etheisen 758: } else
759: {
760: /*
761: * Cursor is on a space and char to the left is a space.
762: * Huh? There's no word here.
763: */
764: return (NULL);
1.4 ! millert 765: #endif
1.1 etheisen 766: }
767: /*
1.4 ! millert 768: * Find the beginning of the word which the cursor is in.
1.1 etheisen 769: */
770: if (cp == cmdbuf)
771: return (NULL);
1.4 ! millert 772: #if SPACES_IN_FILENAMES
! 773: /*
! 774: * If we have an unbalanced quote (that is, an open quote
! 775: * without a corresponding close quote), we return everything
! 776: * from the open quote, including spaces.
! 777: */
! 778: for (word = cmdbuf; word < cp; word++)
! 779: if (*word != ' ')
1.1 etheisen 780: break;
1.4 ! millert 781: if (word >= cp)
! 782: return (cp);
! 783: for (p = cmdbuf; p < cp; p++)
! 784: {
! 785: if (meta_quoted)
! 786: {
! 787: meta_quoted = 0;
! 788: } else if (esclen > 0 && p + esclen < cp &&
! 789: strncmp(p, esc, esclen) == 0)
! 790: {
! 791: meta_quoted = 1;
! 792: p += esclen - 1;
! 793: } else if (delim_quoted)
! 794: {
! 795: if (*p == closequote)
! 796: delim_quoted = 0;
! 797: } else /* (!delim_quoted) */
! 798: {
! 799: if (*p == openquote)
! 800: delim_quoted = 1;
! 801: else if (*p == ' ')
! 802: word = p+1;
! 803: }
! 804: }
! 805: #endif
1.1 etheisen 806: return (word);
807: }
808:
809: /*
810: * Set things up to enter completion mode.
811: * Expand the word under the cursor into a list of filenames
812: * which start with that word, and set tk_text to that list.
813: */
814: static void
815: init_compl()
816: {
817: char *word;
818: char c;
819:
820: /*
821: * Get rid of any previous tk_text.
822: */
823: if (tk_text != NULL)
824: {
825: free(tk_text);
826: tk_text = NULL;
827: }
828: /*
829: * Find the original (uncompleted) word in the command buffer.
830: */
831: word = delimit_word();
832: if (word == NULL)
833: return;
834: /*
835: * Set the insertion point to the point in the command buffer
836: * where the original (uncompleted) word now sits.
837: */
838: tk_ipoint = word;
839: /*
840: * Save the original (uncompleted) word
841: */
842: if (tk_original != NULL)
843: free(tk_original);
844: tk_original = (char *) ecalloc(cp-word+1, sizeof(char));
845: strncpy(tk_original, word, cp-word);
846: /*
847: * Get the expanded filename.
848: * This may result in a single filename, or
849: * a blank-separated list of filenames.
850: */
851: c = *cp;
852: *cp = '\0';
1.4 ! millert 853: if (*word != openquote)
! 854: {
! 855: tk_text = fcomplete(word);
! 856: } else
! 857: {
! 858: char *qword = shell_quote(word+1);
! 859: if (qword == NULL)
! 860: tk_text = fcomplete(word+1);
! 861: else
! 862: {
! 863: tk_text = fcomplete(qword);
! 864: free(qword);
! 865: }
! 866: }
1.1 etheisen 867: *cp = c;
868: }
869:
870: /*
871: * Return the next word in the current completion list.
872: */
873: static char *
874: next_compl(action, prev)
1.4 ! millert 875: int action;
1.1 etheisen 876: char *prev;
877: {
878: switch (action)
879: {
880: case EC_F_COMPLETE:
881: return (forw_textlist(&tk_tlist, prev));
882: case EC_B_COMPLETE:
883: return (back_textlist(&tk_tlist, prev));
884: }
1.4 ! millert 885: /* Cannot happen */
! 886: return ("?");
1.1 etheisen 887: }
888:
889: /*
890: * Complete the filename before (or under) the cursor.
891: * cmd_complete may be called multiple times. The global in_completion
892: * remembers whether this call is the first time (create the list),
893: * or a subsequent time (step thru the list).
894: */
895: static int
896: cmd_complete(action)
897: int action;
898: {
1.4 ! millert 899: char *s;
1.1 etheisen 900:
901: if (!in_completion || action == EC_EXPAND)
902: {
903: /*
904: * Expand the word under the cursor and
905: * use the first word in the expansion
906: * (or the entire expansion if we're doing EC_EXPAND).
907: */
908: init_compl();
909: if (tk_text == NULL)
910: {
911: bell();
912: return (CC_OK);
913: }
914: if (action == EC_EXPAND)
915: {
916: /*
917: * Use the whole list.
918: */
919: tk_trial = tk_text;
920: } else
921: {
922: /*
923: * Use the first filename in the list.
924: */
925: in_completion = 1;
926: init_textlist(&tk_tlist, tk_text);
927: tk_trial = next_compl(action, (char*)NULL);
928: }
929: } else
930: {
931: /*
932: * We already have a completion list.
933: * Use the next/previous filename from the list.
934: */
935: tk_trial = next_compl(action, tk_trial);
936: }
937:
938: /*
939: * Remove the original word, or the previous trial completion.
940: */
941: while (cp > tk_ipoint)
942: (void) cmd_erase();
943:
944: if (tk_trial == NULL)
945: {
946: /*
947: * There are no more trial completions.
948: * Insert the original (uncompleted) filename.
949: */
950: in_completion = 0;
951: if (cmd_istr(tk_original) != CC_OK)
952: goto fail;
953: } else
954: {
955: /*
956: * Insert trial completion.
957: */
958: if (cmd_istr(tk_trial) != CC_OK)
959: goto fail;
1.4 ! millert 960: /*
! 961: * If it is a directory, append a slash.
! 962: */
! 963: if (is_dir(tk_trial))
! 964: {
! 965: if (cp > cmdbuf && cp[-1] == closequote)
! 966: (void) cmd_erase();
! 967: s = lgetenv("LESSSEPARATOR");
! 968: if (s == NULL)
! 969: s = PATHNAME_SEP;
! 970: if (cmd_istr(s) != CC_OK)
! 971: goto fail;
! 972: }
1.1 etheisen 973: }
974:
975: return (CC_OK);
976:
977: fail:
978: in_completion = 0;
979: bell();
980: return (CC_OK);
981: }
982:
983: #endif /* TAB_COMPLETE_FILENAME */
984:
985: /*
986: * Process a single character of a multi-character command, such as
987: * a number, or the pattern of a search command.
988: * Returns:
989: * CC_OK The char was accepted.
990: * CC_QUIT The char requests the command to be aborted.
991: * CC_ERROR The char could not be accepted due to an error.
992: */
993: public int
994: cmd_char(c)
995: int c;
996: {
997: int action;
998:
999: if (literal)
1000: {
1001: /*
1002: * Insert the char, even if it is a line-editing char.
1003: */
1004: literal = 0;
1005: return (cmd_ichar(c));
1006: }
1007:
1008: /*
1009: * See if it is a special line-editing character.
1010: */
1011: if (in_mca())
1012: {
1013: action = cmd_edit(c);
1014: switch (action)
1015: {
1016: case CC_OK:
1017: case CC_QUIT:
1018: return (action);
1019: case CC_PASS:
1020: break;
1021: }
1022: }
1023:
1024: /*
1025: * Insert the char into the command buffer.
1026: */
1.4 ! millert 1027: return (cmd_ichar(c));
1.1 etheisen 1028: }
1029:
1030: /*
1031: * Return the number currently in the command buffer.
1032: */
1.4 ! millert 1033: public LINENUM
1.1 etheisen 1034: cmd_int()
1035: {
1.4 ! millert 1036: register char *p;
! 1037: LINENUM n = 0;
1.1 etheisen 1038:
1.4 ! millert 1039: for (p = cmdbuf; *p != '\0'; p++)
! 1040: n = (10 * n) + (*p - '0');
! 1041: return (n);
1.1 etheisen 1042: }
1043:
1044: /*
1045: * Return a pointer to the command buffer.
1046: */
1047: public char *
1048: get_cmdbuf()
1049: {
1050: return (cmdbuf);
1051: }