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