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