home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-385-Vol-1of3.iso / k / ksh48.zip / sh / emacs.c < prev    next >
C/C++ Source or Header  |  1993-01-12  |  39KB  |  1,938 lines

  1. /*
  2.  *  Emacs-like command line editing and history
  3.  *
  4.  *  created by Ron Natalie at BRL
  5.  *  modified by Doug Kingston, Doug Gwyn, and Lou Salkind
  6.  *  adapted to PD ksh by Eric Gisin
  7.  *  Modified Sep 1991 by Kai Uwe Rommel for OS/2:
  8.  *  8 bit support, 3rd meta key (extended keys)
  9.  */
  10.  
  11. #include "config.h"
  12. #ifdef EMACS
  13.  
  14. #ifndef lint
  15. static char *RCSid = "$Id: emacs.c,v 1.4 1992/12/05 13:15:20 sjg Exp $";
  16. #endif
  17.  
  18. #include "stdh.h"
  19. #include <signal.h>
  20. #include <sys/stat.h>
  21. #ifdef OS2
  22. #include <memory.h>
  23. extern char *index_sep(char *);
  24. extern char *rindex_sep(char *);
  25. #endif
  26. #include <dirent.h>
  27. #include <unistd.h>
  28. #include <fcntl.h>
  29. #include <ctype.h>
  30. #include <errno.h>
  31. #include <setjmp.h>
  32. #include "sh.h"
  33. #include "expand.h"
  34. #include "edit.h"
  35.  
  36. #define PUSH_DELETE 1            /* push all deletes of >1 char */
  37.  
  38. static    Area    aedit;
  39. #define    AEDIT    &aedit        /* area for kill ring and macro defns */
  40.  
  41. #undef CTRL            /* _BSD brain damage */
  42. #define    CTRL(x)        ((x) == '?' ? 0x7F : (x) & 0x1F)    /* ASCII */
  43. #define    UNCTRL(x)    ((x) == 0x7F ? '?' : (x) | 0x40)    /* ASCII */
  44.  
  45. #ifndef S_ISDIR
  46. #define S_ISDIR(mode)    (((mode) & S_IFMT) == S_IFDIR)
  47. #endif
  48.  
  49. #ifndef S_ISREG
  50. #define S_ISREG(mode)    (((mode) & S_IFMT) == S_IFREG)
  51. #endif
  52.  
  53. /* values returned by keyboard functions */
  54. #define    KSTD    0
  55. #define    KPREF    1        /* ^[, ^X */
  56. #define    KEOL    2        /* ^M, ^J */
  57. #define    KINTR    3        /* ^G, ^C */
  58. #define    KNULL    4
  59.  
  60. struct    x_ftab  {
  61.     int    (*xf_func)();
  62.     char    *xf_name;
  63.     char    xf_db_tab;
  64.     char    xf_db_char;
  65.     short    xf_flags;
  66. };
  67.  
  68. #define    XF_ALLOC    2
  69. #define    XF_NOBIND    4
  70.  
  71. #define    iscfs(c)    (c == ' ' || c == '\t')    /* Separator for completion */
  72. #define    ismfs(c)    (!(isalnum(c)|| c == '$'))  /* Separator for motion */
  73. #define    BEL        0x07
  74. #define    CMASK        0xFF    /* 7-bit ASCII character mask */
  75.  
  76. #define X_TABS    4            /* number of keydef tables etc */
  77. #define X_TABSZ    256            /* size of keydef tables etc */
  78.  
  79. static    int    x_prefix1 = CTRL('['), x_prefix2 = CTRL('X');
  80. static    int    x_prefix3 = 0xE0;
  81. static    char   **x_histp;    /* history position */
  82. static    char   **x_nextcmdp;    /* for newline-and-next */
  83. static    char    *xmp;        /* mark pointer */
  84. static    int    (*x_last_command)();
  85. static    struct    x_ftab *(*x_tab)[X_TABSZ] = NULL; /* key definition */
  86. static    char    *(*x_atab)[X_TABSZ] = NULL; /* macro definitions */
  87. #define    KILLSIZE    20
  88. static    char    *killstack[KILLSIZE];
  89. static    int    killsp, killtp;
  90. static    int    x_curprefix;
  91. static    char    *macroptr;
  92. static    int    x_maxlen;    /* to determine column width */
  93.  
  94. static int      x_insert    ARGS((int c));
  95. static int      x_ins_string ARGS((int c));
  96. static void     x_ins       ARGS((char *cp));
  97. static int      x_del_back  ARGS((int c));
  98. static int      x_del_char  ARGS((int c));
  99. static void     x_delete    ARGS((int nc));
  100. static int      x_del_bword ARGS((int c));
  101. static int      x_mv_bword  ARGS((int c));
  102. static int      x_mv_fword  ARGS((int c));
  103. static int      x_del_fword ARGS((int c));
  104. static int      x_bword     ARGS((void));
  105. static int      x_fword     ARGS((void));
  106. static void     x_goto      ARGS((char *cp));
  107. static void     x_bs        ARGS((int c));
  108. static int      x_size_str  ARGS((char *cp));
  109. static int      x_size      ARGS((int c));
  110. static void     x_zots      ARGS((char *str));
  111. static void     x_zotc      ARGS((int c));
  112. static int      x_mv_back   ARGS((int c));
  113. static int      x_mv_forw   ARGS((int c));
  114. static int      x_search_char ARGS((int c));
  115. static int      x_newline   ARGS((int c));
  116. static int      x_end_of_text ARGS((int c));
  117. static int      x_beg_hist  ARGS((int c));
  118. static int      x_end_hist  ARGS((int c));
  119. static int      x_prev_com  ARGS((int c));
  120. static int      x_next_com  ARGS((int c));
  121. static void     x_load_hist ARGS((char **hp));
  122. static int      x_nl_next_com ARGS((int c));
  123. static int      x_eot_del   ARGS((int c));
  124. static int      x_search_hist ARGS((int c));
  125. static int      x_search    ARGS((char *pat, int offset));
  126. static int      x_match     ARGS((char *str, char *pat));
  127. static int      x_del_line  ARGS((int c));
  128. static int      x_mv_end    ARGS((int c));
  129. static int      x_mv_begin  ARGS((int c));
  130. static int      x_draw_line ARGS((int c));
  131. static int      x_transpose ARGS((int c));
  132. static int      x_literal   ARGS((int c));
  133. static int      x_meta1     ARGS((int c));
  134. static int      x_meta2     ARGS((int c));
  135. static int      x_meta3     ARGS((int c));
  136. static int      x_kill      ARGS((int c));
  137. static void     x_push      ARGS((int nchars));
  138. static int      x_yank      ARGS((int c));
  139. static int      x_meta_yank ARGS((int c));
  140. static int      x_abort     ARGS((int c));
  141. static int      x_error     ARGS((int c));
  142. static int      x_stuffreset ARGS((int c));
  143. static int      x_stuff     ARGS((int c));
  144. static void     x_mapin     ARGS((char *cp));
  145. static char *   x_mapout    ARGS((int c));
  146. static void     x_print     ARGS((int prefix, int key));
  147. static int      x_set_mark  ARGS((int c));
  148. static int      x_kill_region ARGS((int c));
  149. static int      x_xchg_point_mark ARGS((int c));
  150. static int      x_copy_arg  ARGS((int c));
  151. static int      x_noop      ARGS((int c));
  152. #ifdef SILLY
  153. static int      x_game_of_life ARGS((int c));
  154. #endif
  155. static void     add_stash   ARGS((char *dirnam, char *name));
  156. static void     list_stash  ARGS((void));
  157. static int      x_comp_comm ARGS((int c));
  158. static int      x_list_comm ARGS((int c));
  159. static int      x_complete  ARGS((int c));
  160. static int      x_enumerate ARGS((int c));
  161. static int      x_comp_file ARGS((int c));
  162. static int      x_list_file ARGS((int c));
  163. static int      x_comp_list ARGS((int c));
  164. static void     compl_dec   ARGS((int type));
  165. static void     compl_file  ARGS((int type));
  166. static void     compl_command ARGS((int type));
  167. static int      strmatch    ARGS((char *s1, char *s2));
  168. static int      x_set_arg   ARGS((int c));
  169. static int      x_prev_histword ARGS((void));
  170. static int      x_fold_case ARGS((int c));
  171. static int    x_clear_scr ARGS((int c));
  172. static int    x_list_jobs ARGS((int c));
  173.  
  174.  
  175. static    struct x_ftab x_ftab[] = {
  176.      {x_insert,    "auto-insert",        0,     0,    0 },
  177.     {x_error,    "error",        0,     0,    0 },
  178.      {x_ins_string,    "macro-string",        0,     0,    XF_NOBIND|XF_ALLOC},
  179.     {x_del_back,    "delete-char-backward",    0, CTRL('H'),    0 },
  180.     {x_eot_del,    "eot-or-delete",    0, CTRL('D'),    0 },
  181.     {x_del_bword,    "delete-word-backward",    1, CTRL('H'),    0 },
  182.     {x_mv_bword,    "backward-word",     1,    'b',    0 },
  183.     {x_del_line,    "kill-line",        0, CTRL('U'),    0 },
  184.     {x_abort,    "abort",        0,    0,    0 },
  185.     {x_noop,    "no-op",        0,    0,    0 },
  186. /* Do not move the above! */
  187.     {x_mv_fword,    "forward-word",        1,    'f',    0 },
  188.     {x_del_char,    "delete-char-forward",    0,    0,    0 },
  189.     {x_del_fword,    "delete-word-forward",     1,    'd',    0 },
  190.     {x_mv_back,    "backward-char",    0, CTRL('B'),    0 },
  191.     {x_mv_forw,    "forward-char",        0, CTRL('F'),    0 },
  192.     {x_search_char,    "search-character",    0, CTRL(']'),    0 },
  193.     {x_newline,    "newline",        0, CTRL('M'),    0 },
  194.     {x_newline,    "newline",        0, CTRL('J'),    0 },
  195.     {x_end_of_text,    "eot",            0, CTRL('_'),    0 },
  196.     {x_abort,    "abort",        0, CTRL('G'),    0 },
  197.     {x_prev_com,    "up-history",        0, CTRL('P'),    0},
  198.     {x_next_com,    "down-history",        0, CTRL('N'),    0},
  199.     {x_search_hist,    "search-history",    0, CTRL('R'),    0},
  200.     {x_beg_hist,    "beginning-of-history",    1,    '<',    0},
  201.     {x_end_hist,    "end-of-history",    1,    '>',    0},
  202.     {x_mv_end,    "end-of-line",        0, CTRL('E'),    0 },
  203.     {x_mv_begin,    "beginning-of-line",    0, CTRL('A'),    0 },
  204.     {x_draw_line,    "redraw",        0,     0,    0 },
  205.     {x_clear_scr,    "clear-screen",        0, CTRL('L'),    0 },
  206.     {x_meta1,    "prefix-1",        0, CTRL('['),    0 },
  207.     {x_meta2,    "prefix-2",        0, CTRL('X'),    0 },
  208.     {x_meta3,    "prefix-3",        0, 0xE0,    0 },
  209.     {x_kill,    "kill-to-eol",        0, CTRL('K'),    0 },
  210.     {x_yank,    "yank",            0, CTRL('Y'),    0 },
  211.     {x_meta_yank,    "yank-pop",         1,    'y',    0 },
  212.     {x_literal,    "quote",        0, CTRL('^'),    0 },
  213.     {x_stuffreset,     "stuff-reset",        0,     0,    0 },
  214. #if defined(BRL) && defined(TIOCSTI)
  215.     {x_stuff,     "stuff",        0, CTRL('T'),    0 },
  216.     {x_transpose,    "transpose-chars",    0,     0,    0 },
  217. #else
  218.     {x_stuff,     "stuff",        0,     0,    0 },
  219.     {x_transpose,    "transpose-chars",    0, CTRL('T'),    0 },
  220. #endif
  221.     {x_complete,    "complete",        1, CTRL('['),    0 },
  222.         {x_comp_list,    "complete-list",    1,    '=',    0 },
  223.     {x_enumerate,    "list",            1,    '?',    0 },
  224.     {x_comp_file,    "complete-file",    1, CTRL('X'),    0 },
  225.     {x_comp_comm,    "complete-command",    2, CTRL('['),    0 },
  226.     {x_list_file,    "list-file",        2, CTRL('Y'),    0 },
  227.     {x_list_comm,    "list-command",        2,    '?',    0 },
  228.         {x_list_jobs,   "list-jobs",            2,      'j',    0 },
  229.     {x_nl_next_com,    "newline-and-next",    0, CTRL('O'),    0 },
  230.     {x_set_mark,    "set-mark-command",    1,    ' ',    0 },
  231.     {x_kill_region,    "kill-region",        0, CTRL('W'),    0 },
  232.     {x_xchg_point_mark, "exchange-point-and-mark", 2, CTRL('X'), 0 },
  233. #if 0
  234.     {x_copy_arg,    "copy-last-arg",    1,    '_',    0},
  235. #endif
  236. #ifdef SILLY
  237.     {x_game_of_life, "play-game-of-life",    0,    0,    0 },
  238. #endif
  239. #ifdef DEBUG
  240.         {x_debug_info,    "debug-info",        1, CTRL('H'),    0 },
  241. #endif
  242.     {x_prev_histword, "prev-hist-word",     1,    '.',    0 },
  243.     {x_prev_histword, "prev-hist-word",     1,    '_',    0 },
  244.         {x_set_arg,    "",            1,    '0',    0 },
  245.         {x_set_arg,    "",            1,    '1',    0 },
  246.         {x_set_arg,    "",            1,    '2',    0 },
  247.         {x_set_arg,    "",            1,    '3',    0 },
  248.         {x_set_arg,    "",            1,    '4',    0 },
  249.         {x_set_arg,    "",            1,    '5',    0 },
  250.         {x_set_arg,    "",            1,    '6',    0 },
  251.         {x_set_arg,    "",            1,    '7',    0 },
  252.         {x_set_arg,    "",            1,    '8',    0 },
  253.         {x_set_arg,    "",            1,    '9',    0 },
  254.         {x_fold_case,    "upcase-word",        1,    'U',    0 },
  255.         {x_fold_case,    "downcase-word",    1,    'L',    0 },
  256.         {x_fold_case,    "capitalize-word",    1,    'C',    0 },
  257.         {x_fold_case,    "upcase-word",        1,    'u',    0 },
  258.         {x_fold_case,    "downcase-word",    1,    'l',    0 },
  259.         {x_fold_case,    "capitalize-word",    1,    'c',    0 },
  260.     { 0 }
  261. };
  262.  
  263. #define    xft_insert &x_ftab[0]
  264. #define    xft_error &x_ftab[1]
  265. #define    xft_ins_string &x_ftab[2]
  266. #define    xft_erase &x_ftab[3]
  267. #define    xft_kill &x_ftab[7]
  268. #define    xft_werase &x_ftab[5]
  269. #define xft_intr &x_ftab[8]
  270. #define    xft_quit &x_ftab[9]
  271.  
  272. int
  273. x_emacs(buf, len)
  274.     char *buf;
  275.     size_t len;
  276. {
  277.     int    c;
  278.     int    i;
  279.     int   (*func)();
  280.     extern    x_insert();
  281.  
  282.     xbp = xbuf = buf; xend = buf + len;
  283.     xlp = xcp = xep = buf;
  284.     *xcp = 0;
  285.     xlp_valid = TRUE;
  286.     xmp = NULL;
  287.     x_curprefix = 0;
  288.     macroptr = null;
  289.     x_histp = histptr + 1;
  290.  
  291.     if (x_nextcmdp != NULL) {
  292.         x_load_hist(x_nextcmdp);
  293.         x_nextcmdp = NULL;
  294.     }
  295.  
  296.     x_col = promptlen(prompt);
  297.     x_adj_ok = 1;
  298.     x_displen = x_cols - 2 - x_col;
  299.     x_adj_done = 0;
  300.  
  301.     while (1)  {
  302.         x_flush();
  303.         if (*macroptr)  {
  304.             c = *macroptr++;
  305.             if (*macroptr == 0)
  306.                 macroptr = null;
  307.         }
  308.         else {
  309.             if ((c = x_getc()) < 0)
  310.                 return i;
  311.         }
  312.  
  313.         if (x_curprefix == -1)
  314.             func = x_insert;
  315.         else
  316.             func = x_tab[x_curprefix][c&CMASK]->xf_func;
  317.         if (func == NULL)
  318.             func = x_error;
  319.         i = c | (x_curprefix << 8);
  320.         x_curprefix = 0;
  321.         switch (i = (*func)(i))  {
  322.           case KSTD:
  323.             x_last_command = func;
  324.           case KPREF:
  325.           case KNULL:
  326.             break;
  327.           case KEOL:
  328.             i = xep - xbuf;
  329.             x_last_command = 0;
  330.             return i;
  331.           case KINTR:    /* special case for interrupt */
  332.             errno = EINTR;
  333.             return -1;
  334.         }
  335.     }
  336. }
  337.  
  338. static int
  339. x_insert(c)  {
  340.     char    str[2];
  341.  
  342.     /*
  343.      *  Should allow tab and control chars.
  344.      */
  345.     if (c == 0)  {
  346.         x_putc(BEL);
  347.         return KSTD;
  348.     }
  349.     str[0] = c;
  350.     str[1] = 0;
  351.     x_ins(str);
  352.     return KSTD;
  353. }
  354.  
  355. static int
  356. x_ins_string(c)
  357. {
  358.     if (*macroptr)   {
  359.         x_putc(BEL);
  360.         return KSTD;
  361.     }
  362.     macroptr = x_atab[c>>8][c & CMASK];
  363.     return KSTD;
  364. }
  365.  
  366. static void
  367. x_ins(cp)
  368.     char    *cp;
  369. {
  370.     int    count;
  371.     register int    adj = x_adj_done;
  372.  
  373.     count = strlen(cp);
  374.     if (xep+count >= xend) {
  375.         x_putc(BEL);
  376.         return;
  377.     }
  378.  
  379.     if (xcp != xep)
  380.         memmove(xcp+count, xcp, xep - xcp + 1);
  381.     else
  382.         xcp[count] = 0;
  383.     memmove(xcp, cp, count);
  384.     /*
  385.      * x_zots() may result in a call to x_adjust()
  386.      * we want xcp to reflect the new position.
  387.      */
  388.     cp = xcp;
  389.     xcp += count;
  390.     xep += count;
  391.     xlp_valid = FALSE;
  392.     x_lastcp();
  393.     x_adj_ok = (xcp >= xlp);
  394.     x_zots(cp);
  395.     if (adj == x_adj_done)    /* has x_adjust() been called? */
  396.     {
  397.       /* no */
  398.       for (cp = xlp; cp > xcp; )
  399.         x_bs(*--cp);
  400.     }
  401.  
  402.     x_adj_ok = 1;
  403.     return;
  404. }
  405.  
  406. static int
  407. x_del_back(c)  {
  408.     if (xcp == xbuf)  {
  409.         x_putc(BEL);
  410.         return KSTD;
  411.     }
  412.     x_goto(xcp - 1);
  413.     x_delete(1);
  414.     return KSTD;
  415. }
  416.  
  417. static int
  418. x_del_char(c)  {
  419.     if (xcp == xep)  {
  420.         x_putc(BEL);
  421.         return KSTD;
  422.     }
  423.     x_delete(1);
  424.     return KSTD;
  425. }
  426.  
  427. static void
  428. x_delete(nc)
  429.   int nc;
  430. {
  431.     int    i,j;
  432.     char    *cp;
  433.  
  434.     if (nc == 0)
  435.         return;
  436.     if (xmp != NULL) {
  437.         if (xcp + nc > xmp)
  438.             xmp = xcp;
  439.         else if (xmp > xcp)
  440.             xmp -= nc;
  441.     }
  442. #ifdef PUSH_DELETE
  443.     /*
  444.      * This lets us yank a word we have deleted.
  445.      */
  446.     if (nc > 1)
  447.       x_push(nc);
  448. #endif
  449.     xep -= nc;
  450.     cp = xcp;
  451.     j = 0;
  452.     i = nc;
  453.     while (i--)  {
  454.         j += x_size(*cp++);
  455.     }
  456.     memmove(xcp, xcp+nc, xep - xcp + 1);    /* Copies the null */
  457.     x_adj_ok = 0;            /* don't redraw */
  458.     x_zots(xcp);
  459.     /*
  460.      * if we are already filling the line,
  461.      * there is no need to ' ','\b'.
  462.      * But if we must, make sure we do the minimum.
  463.      */
  464.     if ((i = x_cols - 2 - x_col) > 0)
  465.     {
  466.       j = (j < i) ? j : i;
  467.       i = j;
  468.       while (i--)
  469.         x_putc(' ');
  470.       i = j;
  471.       while (i--)
  472.         x_putc('\b');
  473.     }
  474.     /*x_goto(xcp);*/
  475.     x_adj_ok = 1;
  476.     xlp_valid = FALSE;
  477.     for (cp = x_lastcp(); cp > xcp; )
  478.       x_bs(*--cp);
  479.  
  480.     return;
  481. }
  482.  
  483. static int
  484. x_del_bword(c)  {
  485.     x_delete(x_bword());
  486.     return KSTD;
  487. }
  488.  
  489. static int
  490. x_mv_bword(c)  {
  491.     (void)x_bword();
  492.     return KSTD;
  493. }
  494.  
  495. static int
  496. x_mv_fword(c)  {
  497.     x_goto(xcp + x_fword());
  498.     return KSTD;
  499. }
  500.  
  501. static int
  502. x_del_fword(c)  {
  503.     x_delete(x_fword());
  504.     return KSTD;
  505. }
  506.  
  507. static int
  508. x_bword()  {
  509.     int    nc = 0;
  510.     register char *cp = xcp;
  511.  
  512.     if (cp == xbuf)  {
  513.         x_putc(BEL);
  514.         return 0;
  515.     }
  516.     if (x_last_command != x_set_arg)
  517.       x_arg = 1;
  518.     while (x_arg--)
  519.     {
  520.       while (cp != xbuf && ismfs(cp[-1]))
  521.       {
  522.         cp--;
  523.         nc++;
  524.       }
  525.       while (cp != xbuf && !ismfs(cp[-1]))
  526.       {
  527.         cp--;
  528.         nc++;
  529.       }
  530.     }
  531.     x_goto(cp);
  532.     return nc;
  533. }
  534.  
  535. static int
  536. x_fword()  {
  537.     int    nc = 0;
  538.     register char    *cp = xcp;
  539.  
  540.     if (cp == xep)  {
  541.         x_putc(BEL);
  542.         return 0;
  543.     }
  544.     if (x_last_command != x_set_arg)
  545.       x_arg = 1;
  546.     while (x_arg--)
  547.     {
  548.       while (cp != xep && !ismfs(*cp))
  549.       {
  550.         cp++;
  551.         nc++;
  552.       }
  553.       while (cp != xep && ismfs(*cp))
  554.       {
  555.         cp++;
  556.         nc++;
  557.       }
  558.     }
  559.     return nc;
  560. }
  561.  
  562. static void
  563. x_goto(cp)
  564.     register char *cp;
  565. {
  566.   if (cp < xbp || cp >= (xbp + x_displen))
  567.   {
  568.     /* we are heading off screen */
  569.     xcp = cp;
  570.     x_adjust();
  571.   }
  572.   else
  573.   {
  574.     if (cp < xcp)        /* move back */
  575.     {
  576.       while (cp < xcp)
  577.     x_bs(*--xcp);
  578.     }
  579.     else
  580.     {
  581.       if (cp > xcp)         /* move forward */
  582.       {
  583.     while (cp > xcp)
  584.       x_zotc(*xcp++);
  585.       }
  586.     }
  587.   }
  588. }
  589.  
  590. static void
  591. x_bs(c)  {
  592.     register i;
  593.     i = x_size(c);
  594.     while (i--)
  595.         x_putc('\b');
  596. }
  597.  
  598. static int
  599. x_size_str(cp)
  600.     register char *cp;
  601. {
  602.     register size = 0;
  603.     while (*cp)
  604.         size += x_size(*cp++);
  605.     return size;
  606. }
  607.  
  608. static int
  609. x_size(c)  {
  610.     if (c=='\t')
  611.         return 4;    /* Kludge, tabs are always four spaces. */
  612.     if (c < ' ' || c == 0x7F) /* ASCII control char */
  613.         return 2;
  614.     return 1;
  615. }
  616.  
  617. static void
  618. x_zots(str)
  619.     register char *str;
  620. {
  621.   register int    adj = x_adj_done;
  622.  
  623.   x_lastcp();
  624.   while (*str && str < xlp && adj == x_adj_done)
  625.     x_zotc(*str++);
  626. }
  627.  
  628. static void
  629. x_zotc(c)
  630.     int c;
  631. {
  632.     if (c == '\t')  {
  633.         /*  Kludge, tabs are always four spaces.  */
  634.         x_puts("    ");
  635.     } else if (c < ' ' || c == 0x7F)  { /* ASCII */
  636.         x_putc('^');
  637.         x_putc(UNCTRL(c));
  638.     } else
  639.         x_putc(c);
  640. }
  641.  
  642. static int
  643. x_mv_back(c)  {
  644.     if (xcp == xbuf)  {
  645.         x_putc(BEL);
  646.         return KSTD;
  647.     }
  648.     x_goto(xcp-1);
  649.     return KSTD;
  650. }
  651.  
  652. static int
  653. x_mv_forw(c)  {
  654.     if (xcp == xep)  {
  655.         x_putc(BEL);
  656.         return KSTD;
  657.     }
  658.     x_goto(xcp+1);
  659.     return KSTD;
  660. }
  661.  
  662. static int
  663. x_search_char(c)
  664.     int c;
  665. {
  666.     char *cp;
  667.  
  668.     *xep = '\0';
  669.     if ((c = x_getc()) < 0 ||
  670.         /* we search forward, I don't know what Korn does */
  671.         ((cp = (xcp == xep) ? NULL : strchr(xcp+1, c)) == NULL &&
  672.         (cp = strchr(xbuf, c)) == NULL)) {
  673.         x_putc(BEL);
  674.         return KSTD;
  675.     }
  676.     x_goto(cp);
  677.     return KSTD;
  678. }
  679.  
  680. static int
  681. x_newline(c)  {
  682.     x_putc('\n');
  683.     x_flush();
  684.     *xep++ = '\n';
  685.     return KEOL;
  686. }
  687.  
  688. static int
  689. x_end_of_text(c)  {
  690. #if 0
  691.     x_store_hist();
  692. #endif
  693.     return KEOL;
  694. }
  695.  
  696. static int x_beg_hist(c) {x_load_hist(history); return KSTD;}
  697.  
  698. static int x_end_hist(c) {x_load_hist(histptr); return KSTD;}
  699.  
  700. static int x_prev_com(c) {x_load_hist(x_histp-1); return KSTD;}
  701.  
  702. static int x_next_com(c) {x_load_hist(x_histp+1); return KSTD;}
  703.  
  704. static void
  705. x_load_hist(hp)
  706.     register char **hp;
  707. {
  708.     int    oldsize;
  709.  
  710.     if (hp < history || hp > histptr) {
  711.         x_putc(BEL);
  712.         return;
  713.     }
  714.     x_histp = hp;
  715.     oldsize = x_size_str(xbuf);
  716.     (void)strcpy(xbuf, *hp);
  717.     xbp = xbuf;
  718.     xep = xcp = xbuf + strlen(*hp);
  719.     xlp_valid = FALSE;
  720.     if (xep > x_lastcp())
  721.       x_goto(xep);
  722.     else
  723.       x_redraw(oldsize);
  724. }
  725.  
  726. static int
  727. x_nl_next_com(c)
  728. int    c;
  729. {
  730.     x_nextcmdp = x_histp + 1;
  731.     return (x_newline(c));
  732. }
  733.  
  734. static int
  735. x_eot_del(c)
  736. int    c;
  737. {
  738.     if (xep == xbuf)
  739.         return (x_end_of_text(c));
  740.     else
  741.         return (x_del_char(c));
  742. }
  743.  
  744. static int x_search(), x_match();
  745.  
  746. /* reverse incremental history search */
  747. static int
  748. x_search_hist(c)
  749.     int c;
  750. {
  751.     int offset = -1;    /* offset of match in xbuf, else -1 */
  752.     char pat [256+1];    /* pattern buffer */
  753.     register char *p = pat;
  754.     int (*func)();
  755.  
  756.     *p = 0;
  757.     while (1) {
  758.         if (offset < 0) {
  759.             x_puts("\nI-search: ");
  760.             x_zots(pat);
  761.         }
  762.         x_flush();
  763.         if ((c = x_getc()) < 0)
  764.             return KSTD;
  765.         func = x_tab[0][c&CMASK]->xf_func;
  766.         if (c == CTRL('['))
  767.             break;
  768.         else if (func == x_search_hist)
  769.             offset = x_search(pat, offset);
  770.         else if (func == x_del_back)
  771.             continue;    /* todo */
  772.         else if (func == x_insert) {
  773.             /* add char to pattern */
  774.             *p++ = c, *p = 0;
  775.             if (offset >= 0) {
  776.                 /* already have partial match */
  777.                 offset = x_match(xbuf, pat);
  778.                 if (offset >= 0) {
  779.                     x_goto(xbuf + offset + (p - pat) - (*pat == '^'));
  780.                     continue;
  781.                 }
  782.             }
  783.             offset = x_search(pat, offset);
  784.         } else { /* other command */
  785.             static char push[2];
  786.             push[0] = c;
  787.             macroptr = push; /* push command */
  788.             break;
  789.         }
  790.     }
  791.     if (offset < 0)
  792.         x_redraw(-1);
  793.     return KSTD;
  794. }
  795.  
  796. /* search backward from current line */
  797. static int
  798. x_search(pat, offset)
  799.     char *pat;
  800.     int offset;
  801. {
  802.     register char **hp;
  803.     int i;
  804.  
  805.     for (hp = x_histp; --hp >= history; ) {
  806.         i = x_match(*hp, pat);
  807.         if (i >= 0) {
  808.             if (offset < 0)
  809.                 x_putc('\n');
  810.             x_load_hist(hp);
  811.             x_goto(xbuf + i + strlen(pat) - (*pat == '^'));
  812.             return i;
  813.         }
  814.     }
  815.     x_putc(BEL);
  816.     x_histp = histptr;
  817.     return -1;
  818. }
  819.  
  820. /* return position of first match of pattern in string, else -1 */
  821. static int
  822. x_match(str, pat)
  823.     char *str, *pat;
  824. {
  825.     if (*pat == '^') {
  826.         return (strncmp(str, pat+1, strlen(pat+1)) == 0) ? 0 : -1;
  827.     } else {
  828.         char *q = strstr(str, pat);
  829.         return (q == NULL) ? -1 : q - str;
  830.     }
  831. }
  832.  
  833. static int
  834. x_del_line(c)  {
  835.     int    i, j;
  836.  
  837.     *xep = 0;
  838.     i = xep- xbuf;
  839.     j = x_size_str(xbuf);
  840.     xcp = xbuf;
  841.     x_push(i);
  842.     xlp = xbp = xep = xbuf;
  843.     xlp_valid = TRUE;
  844.     *xcp = 0;
  845.     xmp = NULL;
  846.     if ( c != -1 )
  847.     x_redraw(j);
  848.     return KSTD;
  849. }
  850.  
  851. static int
  852. x_mv_end(c)  {
  853.     x_goto(xep);
  854.     return KSTD;
  855. }
  856.  
  857. static int
  858. x_mv_begin(c)  {
  859.     x_goto(xbuf);
  860.     return KSTD;
  861. }
  862.  
  863. static int
  864. x_draw_line(c)
  865. {
  866.     x_redraw(-1);
  867.     return KSTD;
  868.  
  869. }
  870.  
  871. void
  872. x_redraw(limit)
  873.   int limit;
  874. {
  875.     int    i, j;
  876.     char    *cp;
  877.  
  878.     x_adj_ok = 0;
  879.     if (limit == -1)
  880.         x_putc('\n');
  881.     else
  882.         x_putc('\r');
  883.     x_flush();
  884.     if (xbp == xbuf)
  885.     {
  886.       pprompt(prompt);
  887.       x_col = promptlen(prompt);
  888.     }
  889.     x_displen = x_cols - 2 - x_col;
  890.     xlp_valid = FALSE;
  891.     cp = x_lastcp();
  892.     x_zots(xbp);
  893.     if (xbp != xbuf || xep > xlp)
  894.       limit = x_cols;
  895.     if (limit >= 0)
  896.     {
  897.       if (xep > xlp)
  898.         i = 0;            /* we fill the line */
  899.       else
  900.         i = limit - (xlp - xbp);
  901.  
  902.       for (j = 0; j < i && x_col < (x_cols - 2); j++)
  903.         x_putc(' ');
  904.       i = ' ';
  905.       if (xep > xlp)        /* more off screen */
  906.       {
  907.         if (xbp > xbuf)
  908.           i = '*';
  909.         else
  910.           i = '>';
  911.       }
  912.       else
  913.         if (xbp > xbuf)
  914.           i = '<';
  915.       x_putc(i);
  916.       j++;
  917.       while (j--)
  918.         x_putc('\b');
  919.     }
  920.     for (cp = xlp; cp > xcp; )
  921.       x_bs(*--cp);
  922.     x_adj_ok = 1;
  923.     _D_(x_flush();)
  924.     return;
  925. }
  926.  
  927. static int
  928. x_transpose(c)  {
  929.     char    tmp;
  930.     if (xcp == xbuf) {
  931.         x_putc(BEL);
  932.         return KSTD;
  933.     } else if (xcp == xep) {
  934.         if (xcp - xbuf == 1) {
  935.             x_putc(BEL);
  936.             return KSTD;
  937.         }
  938.         x_bs(xcp[-1]);
  939.         x_bs(xcp[-2]);
  940.         x_zotc(xcp[-1]);
  941.         x_zotc(xcp[-2]);
  942.         tmp = xcp[-1];
  943.         xcp[-1] = xcp[-2];
  944.         xcp[-2] = tmp;
  945.     } else {
  946.         x_bs(xcp[-1]);
  947.         x_zotc(xcp[0]);
  948.         x_zotc(xcp[-1]);
  949.         tmp = xcp[-1];
  950.         xcp[-1] = xcp[0];
  951.         xcp[0] = tmp;
  952.         x_bs(xcp[0]);
  953.     }
  954.     return KSTD;
  955. }
  956.  
  957. static int
  958. x_literal(c)  {
  959.     x_curprefix = -1;
  960.     return KSTD;
  961. }
  962.  
  963. static int
  964. x_meta1(c)  {
  965.     x_curprefix = 1;
  966.     return KPREF;
  967. }
  968.  
  969. static int
  970. x_meta2(c)  {
  971.     x_curprefix = 2;
  972.     return KPREF;
  973. }
  974.  
  975. static int
  976. x_meta3(c) {
  977.         x_curprefix = 3;
  978.         return KPREF;
  979. }
  980.  
  981. static int
  982. x_kill(c)  {
  983.     int    i;
  984.  
  985.     i = xep - xcp;
  986.     xlp = xcp;
  987.     xlp_valid = TRUE;
  988.     x_push(i);
  989.     x_delete(i);
  990.     return KSTD;
  991. }
  992.  
  993. static void
  994. x_push(nchars)  {
  995.     char    *cp;
  996.     cp = alloc((size_t)(nchars+1), AEDIT);
  997.     memmove(cp, xcp, nchars);
  998.     cp[nchars] = 0;
  999.     if (killstack[killsp])
  1000.         afree((void *)killstack[killsp], AEDIT);
  1001.     killstack[killsp] = cp;
  1002.     killsp = (killsp + 1) % KILLSIZE;
  1003. }
  1004.  
  1005. static int
  1006. x_yank(c)  {
  1007.     if (killsp == 0)
  1008.         killtp = KILLSIZE;
  1009.     else
  1010.         killtp = killsp;
  1011.     killtp --;
  1012.     if (killstack[killtp] == 0)  {
  1013.         x_puts("\nnothing to yank");
  1014.         x_redraw(-1);
  1015.         return KSTD;
  1016.     }
  1017.     xmp = xcp;
  1018.     x_ins(killstack[killtp]);
  1019.     return KSTD;
  1020. }
  1021.  
  1022. static int
  1023. x_meta_yank(c)  {
  1024.     int    len;
  1025.     if (x_last_command != x_yank && x_last_command != x_meta_yank)  {
  1026.         x_puts("\nyank something first");
  1027.         x_redraw(-1);
  1028.         return KSTD;
  1029.     }
  1030.     len = strlen(killstack[killtp]);
  1031.     x_goto(xcp - len);
  1032.     x_delete(len);
  1033.     do  {
  1034.         if (killtp == 0)
  1035.             killtp = KILLSIZE - 1;
  1036.         else
  1037.             killtp--;
  1038.     }  while (killstack[killtp] == 0);
  1039.     x_ins(killstack[killtp]);
  1040.     return KSTD;
  1041. }
  1042.  
  1043. static int
  1044. x_abort(c) {
  1045.     /* x_zotc(c); */
  1046.     xlp = xep = xcp = xbp = xbuf;
  1047.     xlp_valid = TRUE;
  1048.     *xcp = 0;
  1049.     x_del_line(-1);
  1050.     return KINTR;
  1051. }
  1052.  
  1053. static int
  1054. x_error(c) {
  1055.     x_putc(BEL);
  1056.     return KSTD;
  1057. }
  1058.  
  1059. static int
  1060. x_stuffreset(c)
  1061. {
  1062. #ifdef TIOCSTI
  1063.     (void)x_stuff(c);
  1064.     return KINTR;
  1065. #else
  1066.     x_zotc(c);
  1067.     xlp = xcp = xep = xbp = xbuf;
  1068.     xlp_valid = TRUE;
  1069.     *xcp = 0;
  1070.     x_redraw(-1);
  1071.     return KSTD;
  1072. #endif
  1073. }
  1074.  
  1075. static int
  1076. x_stuff(c)
  1077. {
  1078. #if 0 || defined TIOCSTI
  1079.     char    ch = c;
  1080.     bool_t    savmode = x_mode(FALSE);
  1081.  
  1082.     (void)ioctl(ttyfd, TIOCSTI, &ch);
  1083.     (void)x_mode(savmode);
  1084.     x_redraw(-1);
  1085. #endif
  1086.     return KSTD;
  1087. }
  1088.  
  1089. static void
  1090. x_mapin(cp)
  1091.     char    *cp;
  1092. {
  1093.     char    *op;
  1094.  
  1095.     op = cp;
  1096.     while (*cp)  {
  1097.         /* XXX -- should handle \^ escape? */
  1098.         if (*cp == '^')  {
  1099.             cp++;
  1100.                     if (*cp == '0')
  1101.                         *op++ = 0xE0;
  1102.                         else if (*cp >= '?')    /* includes '?'; ASCII */
  1103.                 *op++ = CTRL(*cp);
  1104.             else  {
  1105.                 *op++ = '^';
  1106.                 cp--;
  1107.             }
  1108.         } else
  1109.             *op++ = *cp;
  1110.         cp++;
  1111.     }
  1112.     *op = 0;
  1113. }
  1114.  
  1115. static char *
  1116. x_mapout(c)
  1117.     int c;
  1118. {
  1119.     static char buf[8];
  1120.     register char *p = buf;
  1121.  
  1122.     if (c < ' ' || c == 0x7F)  { /* ASCII */
  1123.         *p++ = '^';
  1124.         *p++ = (c == 0x7F) ? '?' : (c | 0x40);
  1125.         }
  1126.         else if (c == 0xE0) {
  1127.             *p++ = '^';
  1128.                 *p++ = '0';
  1129.     } else
  1130.         *p++ = c;
  1131.     *p = 0;
  1132.     return buf;
  1133. }
  1134.  
  1135. static void
  1136. x_print(prefix, key)
  1137.     int prefix, key;
  1138. {
  1139.     if (prefix == 1)
  1140.         shellf("%s", x_mapout(x_prefix1));
  1141.     if (prefix == 2)
  1142.         shellf("%s", x_mapout(x_prefix2));
  1143.         if (prefix == 3)
  1144.               shellf("%s", x_mapout(x_prefix3));
  1145.     shellf("%s = ", x_mapout(key));
  1146.     if (x_tab[prefix][key]->xf_func != x_ins_string)
  1147.         shellf("%s\n", x_tab[prefix][key]->xf_name);
  1148.     else
  1149.         shellf("'%s'\n", x_atab[prefix][key]);
  1150. }
  1151.  
  1152. void
  1153. x_bind(a1, a2, macro)
  1154.     char *a1, *a2;
  1155.     int macro;        /* bind -m */
  1156. {
  1157.     struct x_ftab *fp;
  1158.     int prefix, key;
  1159.     char *sp = NULL;
  1160.  
  1161.     if (x_tab == NULL)
  1162.                 return;
  1163.         /* errorf("cannot bind, not a tty\n"); */
  1164.  
  1165.     if (a1 == NULL) {
  1166.         for (prefix = 0; prefix < X_TABS; prefix++)
  1167.             for (key = 0; key < X_TABSZ; key++) {
  1168.             fp = x_tab[prefix][key];
  1169.             if (fp == NULL ||
  1170.                 fp->xf_func == x_insert || fp->xf_func == x_error)
  1171.                 continue;
  1172.             x_print(prefix, key);
  1173.             }
  1174.         return;
  1175.     }
  1176.  
  1177.     x_mapin(a1);
  1178.     prefix = key = 0;
  1179.     for (;; a1++) {
  1180.         key = *a1;
  1181.         if (x_tab[prefix][key]->xf_func == x_meta1)
  1182.             prefix = 1;
  1183.         else
  1184.         if (x_tab[prefix][key]->xf_func == x_meta2)
  1185.             prefix = 2;
  1186.         else
  1187.                 if (x_tab[prefix][key]->xf_func == x_meta3)
  1188.                      prefix = 3;
  1189.         else
  1190.             break;
  1191.     }
  1192.  
  1193.     if (a2 == NULL) {
  1194.         x_print(prefix, key);
  1195.         return;
  1196.     }
  1197.  
  1198.     if (*a2 == 0)
  1199.         fp = xft_insert;
  1200.     else if (!macro) {
  1201.         for (fp = x_ftab; fp->xf_func; fp++)
  1202.             if (strcmp(fp->xf_name, a2) == 0)
  1203.                 break;
  1204.         if (fp->xf_func == NULL || (fp->xf_flags & XF_NOBIND))
  1205.             errorf("%s: no such function\n", a2);
  1206.         if (fp->xf_func == x_meta1)
  1207.             x_prefix1 = key;
  1208.         if (fp->xf_func == x_meta2)
  1209.             x_prefix2 = key;
  1210.                 if (fp->xf_func == x_meta3)
  1211.                      x_prefix3 = key;
  1212.     } else {
  1213.         fp = xft_ins_string;
  1214.         x_mapin(a2);
  1215.         sp = strsave(a2, AEDIT);
  1216.     }
  1217.  
  1218.     if ((x_tab[prefix][key]->xf_flags & XF_ALLOC) && x_atab[prefix][key])
  1219.         afree((void *)x_atab[prefix][key], AEDIT);
  1220.     x_tab[prefix][key] = fp;
  1221.     x_atab[prefix][key] = sp;
  1222. }
  1223.  
  1224. void
  1225. x_init_emacs()
  1226. {
  1227.     register int i, j;
  1228.     struct x_ftab *fp;
  1229.  
  1230.     ainit(AEDIT);
  1231.  
  1232.     x_tab = (struct x_ftab *(*)[X_TABSZ]) alloc(sizeofN(*x_tab, X_TABS), AEDIT);
  1233.     for (j = 0; j < X_TABSZ; j++)
  1234.         x_tab[0][j] = xft_insert;
  1235.     for (i = 1; i < X_TABS; i++)
  1236.         for (j = 0; j < X_TABSZ; j++)
  1237.             x_tab[i][j] = xft_error;
  1238.     for (fp = x_ftab; fp->xf_func; fp++)
  1239.         if (fp->xf_db_char || fp->xf_db_tab)
  1240.             x_tab[fp->xf_db_tab][fp->xf_db_char] = fp;
  1241.  
  1242.     x_atab = (char *(*)[X_TABSZ]) alloc(sizeofN(*x_atab, X_TABS), AEDIT);
  1243.     for (i = 1; i < X_TABS; i++)
  1244.         for (j = 0; j < X_TABSZ; j++)
  1245.             x_atab[i][j] = NULL;
  1246. }
  1247.  
  1248. static int
  1249. x_clear_scr(c)
  1250. {
  1251.     write(1, "\033[H\033[J", 6);
  1252.     x_redraw(0);
  1253.     return KSTD;
  1254. }
  1255.  
  1256. void
  1257. x_emacs_keys(erase, kill, werase, intr, quit)
  1258.     int erase, kill, werase, intr, quit;
  1259. {
  1260.     x_tab[0][erase] = xft_erase;
  1261.     x_tab[0][kill] = xft_kill;
  1262.     x_tab[0][werase] = xft_werase;
  1263.     x_tab[0][intr] = xft_intr;
  1264.     x_tab[0][quit] = xft_quit;
  1265.     x_tab[1][erase] = xft_werase;
  1266. }
  1267.  
  1268. static int
  1269. x_set_mark(c) {
  1270.     xmp = xcp;
  1271.     return KSTD;
  1272. }
  1273.  
  1274. static int
  1275. x_kill_region(c) {
  1276.     int    rsize;
  1277.     char    *xr;
  1278.  
  1279.     if (xmp == NULL) {
  1280.         x_putc(BEL);
  1281.         return KSTD;
  1282.     }
  1283.     if (xmp > xcp) {
  1284.         rsize = xmp - xcp;
  1285.         xr = xcp;
  1286.     } else {
  1287.         rsize = xcp - xmp;
  1288.         xr = xmp;
  1289.     }
  1290.     x_goto(xr);
  1291.     x_push(rsize);
  1292.     x_delete(rsize);
  1293.     xmp = xr;
  1294.     return KSTD;
  1295. }
  1296.  
  1297. static int
  1298. x_xchg_point_mark(c) {
  1299.     char    *tmp;
  1300.  
  1301.     if (xmp == NULL) {
  1302.         x_putc(BEL);
  1303.         return KSTD;
  1304.     }
  1305.     tmp = xmp;
  1306.     xmp = xcp;
  1307.     x_goto( tmp );
  1308.     return KSTD;
  1309. }
  1310.  
  1311. #if 0
  1312. static int
  1313. x_copy_arg(c)    {
  1314.     char *last;
  1315.     if ((last = strval(local("_"))) && *last)
  1316.         x_ins(last);
  1317.     return KSTD;
  1318. }
  1319. #endif
  1320.  
  1321. static int
  1322. x_noop(c) {
  1323.     return KNULL;
  1324. }
  1325.  
  1326. #ifdef SILLY
  1327. static int
  1328. x_game_of_life(c)  {
  1329.     char    newbuf [256+1];
  1330.     register char *ip, *op;
  1331.     int    i, len;
  1332.  
  1333.     i = xep - xbuf;
  1334.     *xep = 0;
  1335.     len = x_size_str(xbuf);
  1336.     xcp = xbp = xbuf;
  1337.     memmove(newbuf+1, xbuf, i);
  1338.     newbuf[0] = 'A';
  1339.     newbuf[i] = 'A';
  1340.     for (ip = newbuf+1, op = xbuf; --i >= 0; ip++, op++)  {
  1341.         /*  Empty space  */
  1342.         if (*ip < '@' || *ip == '_' || *ip == 0x7F)  {
  1343.             /*  Two adults, make whoopee */
  1344.             if (ip[-1] < '_' && ip[1] < '_')  {
  1345.                 /*  Make kid look like parents.  */
  1346.                 *op = '`' + ((ip[-1] + ip[1])/2)%32;
  1347.                 if (*op == 0x7F) /* Birth defect */
  1348.                     *op = '`';
  1349.             }
  1350.             else
  1351.                 *op = ' ';    /* nothing happens */
  1352.             continue;
  1353.         }
  1354.         /*  Child */
  1355.         if (*ip > '`')  {
  1356.             /*  All alone, dies  */
  1357.             if (ip[-1] == ' ' && ip[1] == ' ')
  1358.                 *op = ' ';
  1359.             else    /*  Gets older */
  1360.                 *op = *ip-'`'+'@';
  1361.             continue;
  1362.         }
  1363.         /*  Adult  */
  1364.         /*  Overcrowded, dies */
  1365.         if (ip[-1] >= '@' && ip[1] >= '@')  {
  1366.             *op = ' ';
  1367.             continue;
  1368.         }
  1369.         *op = *ip;
  1370.     }
  1371.     *op = 0;
  1372.     x_redraw(len);
  1373.     return KSTD;
  1374. }
  1375. #endif
  1376.  
  1377. /*
  1378.  *    File/command name completion routines
  1379.  */
  1380.  
  1381. /* type: 0 for list, 1 for completion */
  1382.  
  1383. static    XPtrV words;
  1384.  
  1385. static void
  1386. add_stash(dirnam, name)
  1387.     char *dirnam;    /* directory name, if file */
  1388.     char *name;
  1389. {
  1390.     char *cp;
  1391.     register int type = 0;    /* '*' if executable, '/' if directory, else 0 */
  1392.     register int len = strlen(name);
  1393.  
  1394.     /* determine file type */
  1395.     if (dirnam)  {
  1396.         struct stat statb;
  1397.         char *buf = alloc((size_t)(strlen(dirnam)+len+2), ATEMP);
  1398.  
  1399.         if (strcmp(dirnam, ".") == 0)
  1400.             *buf = '\0';
  1401.         else if (strcmp(dirnam, DIRSEPSTR) == 0)
  1402.             (void)strcpy(buf, DIRSEPSTR);
  1403.         else
  1404.             (void)strcat(strcpy(buf, dirnam), DIRSEPSTR);
  1405.         (void)strcat(buf, name);
  1406.         if (stat(buf, &statb)==0)
  1407.             if (S_ISDIR(statb.st_mode))
  1408.                 type = '/';
  1409.             else if (S_ISREG(statb.st_mode) && 
  1410.                  (statb.st_mode & S_IEXEC) != 0)
  1411.                 type = '*';
  1412.         if (type)
  1413.             ++len;
  1414.         afree((void *)buf, ATEMP);
  1415.     }
  1416.  
  1417.     if (len > x_maxlen)
  1418.         x_maxlen = len;
  1419.  
  1420.     /* stash name for later sorting */
  1421.     cp = alloc((size_t)(len+1), ATEMP);
  1422.     (void)strcpy(cp = alloc((size_t)(len+1), ATEMP), name);
  1423.     if (dirnam && type)  {    /* append file type indicator */
  1424.         cp[len-1] = type;
  1425.         cp[len] = '\0';
  1426.     }
  1427.     XPput(words, cp);
  1428. }
  1429.  
  1430. static void
  1431. list_stash()
  1432. {
  1433.     register char **array, **record;
  1434.     int items = 0, tabstop, loc, nrows, jump, offset;
  1435.  
  1436.     items = XPsize(words);
  1437.     array = (char**) XPptrv(words);
  1438.     if (items == 0)
  1439.         return;
  1440.     qsortp(XPptrv(words), (size_t)XPsize(words), xstrcmp);
  1441.  
  1442.     /* print names */
  1443.     x_maxlen = (x_maxlen/8 + 1) * 8;    /* column width */
  1444.     nrows = (items-1) / (x_cols/x_maxlen) + 1;
  1445.     for (offset = 0; offset < nrows; ++offset)  {
  1446.         tabstop = loc = 0;
  1447.         x_putc('\n');
  1448.         for (jump = 0; offset+jump < items; jump += nrows)  {
  1449.             if (jump)
  1450.                 while (loc < tabstop)  {
  1451.                     x_putc('\t');
  1452.                     loc = (loc/8 + 1) * 8;
  1453.                 }
  1454.             record = array + (offset + jump);
  1455.             x_puts(*record);
  1456.             loc += strlen(*record);
  1457.             tabstop += x_maxlen;    /* next tab stop */
  1458.             afree((void *)*record, ATEMP);
  1459.         }
  1460.     }
  1461.  
  1462.     afree((void*)array, ATEMP);
  1463.     x_redraw(-1);
  1464. }
  1465.  
  1466. static int
  1467. x_comp_comm(c)  {
  1468.     compl_command(1);
  1469.     return KSTD;
  1470. }
  1471. static int
  1472. x_list_comm(c)  {
  1473.     compl_command(0);
  1474.     return KSTD;
  1475. }
  1476. static int
  1477. x_complete(c)  {
  1478.     compl_dec(1);
  1479.     return KSTD;
  1480. }
  1481. static int
  1482. x_enumerate(c)  {
  1483.     compl_dec(0);
  1484.     return KSTD;
  1485. }
  1486. static int
  1487. x_comp_file(c)   {
  1488.     compl_file(1);
  1489.     return KSTD;
  1490. }
  1491. static int
  1492. x_list_file(c)  {
  1493.     compl_file(0);
  1494.     return KSTD;
  1495. }
  1496.  
  1497. static int
  1498. x_list_jobs(c)
  1499.   int c;
  1500. {
  1501.         j_jobs();
  1502.         x_redraw(-1);
  1503.         return KSTD;
  1504. }
  1505. static int
  1506. x_comp_list(c)   {
  1507.     compl_dec(2);
  1508.     return KSTD;
  1509. }
  1510.  
  1511. static void compl_dec(type) {     char    *cp;     cp = xcp; 
  1512.     while (cp != xbuf && !iscfs(*cp))
  1513.         cp--;
  1514.     if (cp == xbuf && index_sep(cp) == NULL)
  1515.         compl_command(type);
  1516.     else
  1517.         compl_file(type);
  1518. }
  1519.  
  1520. static void
  1521. compl_file(type)
  1522. {
  1523.     char    *str;
  1524.     register char *cp, *xp;
  1525.     char    *lastp;
  1526.     char    *dirnam;
  1527.     char    buf [256+1];
  1528.     char    bug [256+1];
  1529.     DIR    *dirp;
  1530.     struct dirent *dp;
  1531.     long    loc = -1;
  1532.     int    len;
  1533.     int    multi = 0;
  1534.  
  1535.     /* type == 0 for list, 1 for complete and 2 for complete-list */
  1536.     str = xcp;
  1537.     cp = buf;
  1538.     xp = str;
  1539.     while (xp != xbuf)  {
  1540.         --xp;
  1541.         if (iscfs(*xp))  {
  1542.             xp++;
  1543.             break;
  1544.         }
  1545.     }
  1546.     if (digit(*xp) && (xp[1] == '<' || xp[1] == '>'))
  1547.         xp++;
  1548.     while (*xp == '<' || *xp == '>')
  1549.         xp++;
  1550.     if (type) {            /* for complete */
  1551.         while (*xcp && !iscfs(*xcp))
  1552.             x_zotc(*xcp++);
  1553.     }
  1554.     if (type != 1) {        /* for list */
  1555.         x_maxlen = 0;
  1556.         XPinit(words, 16);
  1557.     }
  1558.     while (*xp && !iscfs(*xp))
  1559.         *cp++ = *xp++;
  1560.  
  1561.     *cp = 0;
  1562.     strcpy(buf, cp = substitute(buf, DOTILDE));
  1563.     afree((void*)cp, ATEMP);
  1564.     lastp = rindex_sep(buf);
  1565.     if (lastp)
  1566.         *lastp = 0;
  1567.  
  1568.     dirnam = (lastp == NULL) ? "." : (lastp == buf) ? DIRSEPSTR : buf;
  1569.     dirp = opendir(dirnam);
  1570.     if (dirp == NULL) {
  1571.         x_putc(BEL);
  1572.         return;
  1573.     }
  1574.  
  1575.     if (lastp == NULL)
  1576.         lastp = buf;
  1577.     else
  1578.         lastp++;
  1579.     len = strlen(lastp);
  1580.  
  1581.     while ((dp = readdir(dirp)) != NULL)  {
  1582.         cp = dp->d_name;
  1583.         if (cp[0] == '.' &&
  1584.             (cp[1] == '\0' || (cp[1] == '.' && cp[2] == '\0')))
  1585.             continue;    /* always ignore . and .. */
  1586.         if (strncmp(lastp, cp, len) == 0) {
  1587.             if (type    /* for complete */) {
  1588.                 if (loc == -1)  {
  1589.                     (void)strcpy(bug, cp);
  1590.                     loc = strlen(cp);
  1591.                 } else {
  1592.                     multi = 1;
  1593.                     loc = strmatch(bug, cp);
  1594.                     bug[loc] = 0;
  1595.                 }
  1596.             }
  1597.             if (type != 1) { /* for list */
  1598.                 add_stash(dirnam, cp);
  1599.             }
  1600.         }
  1601.     }
  1602.     (void)closedir(dirp);
  1603.  
  1604.     if (type) {            /* for complete */
  1605.         if (loc < 0 ||
  1606.             (loc == 0 && type != 2))  {
  1607.             x_putc(BEL);
  1608.             return;
  1609.         }
  1610.         cp = bug + len;
  1611.         x_ins(cp);
  1612.         if (!multi)  {
  1613.             struct stat statb;
  1614.             if (lastp == buf)
  1615.                 buf[0] = 0;
  1616.             else if (lastp == buf + 1)  {
  1617.                 buf[1] = 0;
  1618.                 buf[0] = DIRSEP;
  1619.             }  else
  1620.                 (void)strcat(buf, DIRSEPSTR);
  1621.             (void)strcat(buf, bug);
  1622.             if (stat(buf, &statb) == 0 && S_ISDIR(statb.st_mode))
  1623.                 x_ins("/" /* DIRSEPSTR */);
  1624.          /* conflict with \ at end of line in OS/2 mode ! */
  1625.             else
  1626.                 x_ins(" ");
  1627.         }
  1628.     }
  1629.     if (type == 0 ||        /* if list */
  1630.         (type == 2 && multi)) {    /* or complete-list and ambiguous */
  1631.         list_stash();
  1632.     }
  1633. }
  1634.  
  1635. static void
  1636. compl_command(type)
  1637. {
  1638.     register struct tbl *tp;
  1639.     char    *str;
  1640.     char    buf [256+1];
  1641.     char    bug [256+1];
  1642.     char    *xp;
  1643.     char    *cp;
  1644.     int  len;
  1645.     int  multi;
  1646.     int  loc;
  1647.  
  1648.     /* type == 0 for list, 1 for complete and 2 for complete-list */
  1649.     str = xcp;
  1650.     cp = buf;
  1651.     xp = str;
  1652.     while (xp != xbuf)  {
  1653.         --xp;
  1654.         if (iscfs(*xp))  {
  1655.             xp++;
  1656.             break;
  1657.         }
  1658.     }
  1659.     if (type)            /* for complete */
  1660.         while (*xcp && !iscfs(*xcp))
  1661.             x_zotc(*xcp++);
  1662.     if (type != 1) {        /* for list */
  1663.         x_maxlen = 0;
  1664.         XPinit(words, 16);
  1665.     }
  1666.     while (*xp && !iscfs(*xp))
  1667.         *cp++ = *xp++;
  1668.     *cp = 0;
  1669.  
  1670.     len = strlen(buf);
  1671.     loc = -1;
  1672.     multi = 0;
  1673.  
  1674.     for (twalk(&commands); (tp = tnext()) != NULL; ) {
  1675.         int    klen;
  1676.  
  1677.         if (!(tp->flag&ISSET))
  1678.             continue;
  1679.         klen = strlen(tp->name);
  1680.         if (klen < len)
  1681.             continue;
  1682.         if (strncmp(buf, tp->name, len) ==0) {
  1683.             if (type)  {    /* for complete */
  1684.                 if (loc == -1)  {
  1685.                     (void)strcpy(bug, tp->name);
  1686.                     loc = klen;
  1687.                 } else {
  1688.                     multi = 1;
  1689.                     loc = strmatch(bug, tp->name);
  1690.                     bug[loc] = 0;
  1691.                 }
  1692.             }
  1693.             if (type != 1) { /* for list */
  1694.                 add_stash((char *)0, tp->name);
  1695.             }
  1696.         }
  1697.     }
  1698.  
  1699.     if (type)  {            /* for complete */
  1700.         if (loc < 0 ||
  1701.             (loc == 0 && type != 2))  {
  1702.             x_putc(BEL);
  1703.             return;
  1704.         }
  1705.         cp = bug + len;
  1706.         x_ins(cp);
  1707.         if (!multi)
  1708.             x_ins(" ");
  1709.         else if (type == 2)    /* complete and list rest */
  1710.             list_stash();
  1711.     }
  1712.  
  1713.     if (type == 0 ||        /* if list */
  1714.         (type == 2 && multi)) {    /* or complete-list and ambiguous */
  1715.         list_stash();
  1716.     }
  1717. }
  1718.  
  1719. static int
  1720. strmatch(s1, s2)
  1721.     register char *s1, *s2;
  1722. {
  1723.     register char *p;
  1724.  
  1725.     for (p = s1; *p == *s2++ && *p != 0; p++)
  1726.         ;
  1727.     return p - s1;
  1728. }
  1729.  
  1730.  
  1731.  
  1732. /* NAME:
  1733.  *      x_set_arg - set an arg value for next function
  1734.  *
  1735.  * DESCRIPTION:
  1736.  *      This is a simple implementation of M-[0-9].
  1737.  *
  1738.  * RETURN VALUE:
  1739.  *      KSTD
  1740.  */
  1741.  
  1742. static int
  1743. x_set_arg(c)
  1744.   int c;
  1745. {
  1746.   if ((x_arg = (c &= CMASK) - '0') < 0 || x_arg > 9)
  1747.   {
  1748.     x_arg = 1;
  1749.     x_putc(BEL);
  1750.   }
  1751.   return KSTD;
  1752. }
  1753.  
  1754.  
  1755. /* NAME:
  1756.  *      x_prev_histword - recover word from prev command
  1757.  *
  1758.  * DESCRIPTION:
  1759.  *      This function recovers the last word from the previous
  1760.  *      command and inserts it into the current edit line.  If a
  1761.  *      numeric arg is supplied then the n'th word from the
  1762.  *      start of the previous command is used.
  1763.  *
  1764.  *      Bound to M-.
  1765.  *
  1766.  * RETURN VALUE:
  1767.  *      KSTD
  1768.  */
  1769.  
  1770. static int
  1771. x_prev_histword()
  1772. {
  1773.   register char *rcp;
  1774.   char *cp;
  1775.   char **hp;
  1776.  
  1777.   hp = x_histp-1;
  1778.   if (hp < history || hp > histptr)
  1779.   {
  1780.     x_putc(BEL);
  1781.     return KSTD;
  1782.   }
  1783.   cp = *hp;
  1784.   if (x_last_command != x_set_arg)
  1785.   {
  1786.     rcp = &cp[strlen(cp) - 1];
  1787.     /*
  1788.      * ignore white-space after the last word
  1789.      */
  1790.     while (rcp > cp && iscfs(*rcp))
  1791.       rcp--;
  1792.     while (rcp > cp && !iscfs(*rcp))
  1793.       rcp--;
  1794.     if (iscfs(*rcp))
  1795.       rcp++;
  1796.     x_ins(rcp);
  1797.   }
  1798.   else
  1799.   {
  1800.     int c;
  1801.  
  1802.     rcp = cp;
  1803.     /*
  1804.      * ignore white-space at start of line
  1805.      */
  1806.     while (*rcp && iscfs(*rcp))
  1807.       rcp++;
  1808.     while (x_arg-- > 1)
  1809.     {
  1810.       while (*rcp && !iscfs(*rcp))
  1811.     rcp++;
  1812.       while (*rcp && iscfs(*rcp))
  1813.     rcp++;
  1814.     }
  1815.     cp = rcp;
  1816.     while (*rcp && !iscfs(*rcp))
  1817.       rcp++;
  1818.     c = *rcp;
  1819.     *rcp = '\0';
  1820.     x_ins(cp);
  1821.     *rcp = c;
  1822.   }
  1823.   return KSTD;
  1824. }
  1825.  
  1826. /* NAME:
  1827.  *      x_fold_case - convert word to UPPER/lower case
  1828.  *
  1829.  * DESCRIPTION:
  1830.  *      This function is used to implement M-u,M-l and M-c
  1831.  *      to upper case, lower case or Capitalize words.
  1832.  *
  1833.  * RETURN VALUE:
  1834.  *      None
  1835.  */
  1836.  
  1837. static int
  1838. x_fold_case(c)
  1839.   int c;
  1840. {
  1841.   register char    *cp = xcp;
  1842.  
  1843.   if (cp == xep)
  1844.   {
  1845.     x_putc(BEL);
  1846.     return 0;
  1847.   }
  1848.   c &= 0137;                /* strip prefixes and case */
  1849.   if (x_last_command != x_set_arg)
  1850.     x_arg = 1;
  1851.   while (x_arg--)
  1852.   {
  1853.     /*
  1854.      * fisrt skip over any white-space
  1855.      */
  1856.     while (cp != xep && ismfs(*cp))
  1857.     {
  1858.       cp++;
  1859.     }
  1860.     /*
  1861.      * do the first char on its own since it may be
  1862.      * a different action than for the rest.
  1863.      */
  1864.     if (cp != xep)
  1865.     {
  1866.       if (c == 'L')            /* M-l */
  1867.       {
  1868.     if (isupper(*cp))
  1869.       *cp = tolower(*cp);
  1870.       }
  1871.       else                /* M-u or M-c */
  1872.       {
  1873.     if (islower(*cp))
  1874.       *cp = toupper(*cp);
  1875.       }
  1876.       cp++;
  1877.     }
  1878.     /*
  1879.      * now for the rest of the word
  1880.      */
  1881.     while (cp != xep && !ismfs(*cp))
  1882.     {
  1883.       if (c == 'U')            /* M-u */
  1884.       {
  1885.     if (islower(*cp))
  1886.       *cp = toupper(*cp);
  1887.       }
  1888.       else                /* M-l or M-c */
  1889.       {
  1890.     if (isupper(*cp))
  1891.       *cp = tolower(*cp);
  1892.       }
  1893.       cp++;
  1894.     }
  1895.   }
  1896.   x_goto(cp);
  1897.   return 0;
  1898. }
  1899.  
  1900. /* NAME:
  1901.  *      x_lastcp - last visible char
  1902.  *
  1903.  * SYNOPSIS:
  1904.  *      x_lastcp()
  1905.  *
  1906.  * DESCRIPTION:
  1907.  *      This function returns a pointer to that  char in the
  1908.  *      edit buffer that will be the last displayed on the
  1909.  *      screen.  The sequence:
  1910.  *
  1911.  *      for (cp = x_lastcp(); cp > xcp; cp)
  1912.  *        x_bs(*--cp);
  1913.  *
  1914.  *      Will position the cursor correctly on the screen.
  1915.  *
  1916.  * RETURN VALUE:
  1917.  *      cp or NULL
  1918.  */
  1919.  
  1920. char *
  1921. x_lastcp()
  1922. {
  1923.   register char *rcp;
  1924.   register int i;
  1925.  
  1926.   if (!xlp_valid)
  1927.   {
  1928.     for (i = 0, rcp = xbp; rcp < xep && i < x_displen; rcp++)
  1929.       i += x_size(*rcp);
  1930.     xlp = rcp;
  1931.   }
  1932.   xlp_valid = TRUE;
  1933.   return (xlp);
  1934. }
  1935.  
  1936. #endif /* EDIT */
  1937.  
  1938.