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