home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume19 / atty / part02 < prev    next >
Text File  |  1989-05-30  |  50KB  |  2,185 lines

  1. Subject:  v19i011:  A command-line editor (for BSD), Part02/04
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: ka@june.cs.washington.edu (Kenneth Almquist)
  7. Posting-number: Volume 19, Issue 11
  8. Archive-name: atty/part02
  9.  
  10. # This is part 2 of atty.  To unpack, feed it into the shell (not csh).
  11. # The atty distribution consists of four pieces.  After you unpack everyting,
  12. # read the file README.
  13.  
  14. echo extracting ed.c
  15. cat > ed.c <<\EOF
  16. /*
  17.  * Input line editing for atty.
  18.  *
  19.  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  20.  * This file is part of atty, which is distributed under the terms specified
  21.  * by the Atty General Public License.  See the file named LICENSE.
  22.  */
  23.  
  24. #include <stdio.h>
  25. #include "attyed.h"
  26. #include "ed.h"
  27. #include "bind.h"
  28. #include "regex.h"
  29. #include <ctype.h>
  30. #include <signal.h>
  31.  
  32.  
  33. #define HISTFILLER -1        /* indicates blank record in history file */
  34. #define HISTSIZE 8192        /* size of history; MUST be a power of 2 */
  35. #define LOCMASK (HISTSIZE - 1)    /* mask to get offset in history area */
  36.  
  37. /* flags to histadvance */
  38. #define SKIPEMPTY 01        /* skip empty history lines */
  39. #define SAMEPRGM 02        /* skip history lines read by other programs */
  40. #define HSEARCH 04        /* skip history lines not matching pattern */
  41.  
  42.  
  43. struct histhdr {
  44.       short len;        /* length of history line */
  45.       short prevlen;        /* length of preceding line */
  46.       long prgmid;        /* identifies program */
  47.       /* text of line goes here */
  48. };
  49.  
  50.  
  51. struct histline {
  52.       short len;        /* length of history line */
  53.       short prevlen;        /* length of preceding line */
  54.       long prgmid;        /* identifies program */
  55.       char *text;        /* text of line */
  56. };
  57.  
  58.  
  59. char curline[MAXLLEN+1];    /* line being edited */
  60. char *point;            /* location of point */
  61. char *mark;            /* location of mark */
  62. char *endcurline;        /* end of line */
  63. char histarea[HISTSIZE];    /* history file */
  64. long endhist;            /* end of history file */
  65. long curhist;            /* location in history file */
  66. char lasthist[MAXLLEN];        /* line after end of history file */
  67. int lasthistlen;        /* length of lasthist */
  68. int histprevlen;        /* length of last entry in history file */
  69. char killbuffer[MAXLLEN];    /* killed text goes here */
  70. int killsize;            /* length of killed text */
  71. int prefixarg;            /* if set, evaluating prefix arg */
  72. int prefixval;            /* value of prefix argument */
  73. int prefixsign;            /* sign of prefix argument */
  74. char cmdcharacter;        /* character that invoked this command */
  75. unsigned char *curmode;        /* base key table for current mode */
  76. unsigned char *curtable;    /* current command parsing table */
  77. unsigned char *keytabs;        /* key tables */
  78. int keyhash;            /* DEBUG */
  79. char syntaxtbl[NSYNTAX][16];    /* syntax tables */
  80. char *edstrings;        /* string table */
  81. char *abbrevtbl;        /* abbreviation table */
  82. extern int version;        /* version of .bindc format */
  83. extern int (*edfunc[])();    /* table of editor functions */
  84. int (*edrecurse)();        /* function to call when recursive edit done */
  85. int (*ednextc)();        /* function to call on next character */
  86. struct re_pattern_buffer pbuf;    /* buffer containing last search pattern */
  87. int savedpattern;        /* true if current pattern set */
  88. char savecurline[MAXLLEN];    /* saved copy of current line */
  89. int savecurlinelen;        /* length of savecurline */
  90. char *savepoint;        /* point saved for recursive edit */
  91. char *savemark;            /* point saved for recursive edit */
  92. int saveprefixval;        /* prefixval saved here during recursive edit */
  93. char editprompt;        /* printed during recursive edit */
  94.  
  95.  
  96. #ifdef __STDC__
  97. int getsh(FILE *);
  98. int runfmatch(int);
  99. void zapline(void);
  100. void delregion(char *, int);
  101. int dosearch(int);
  102. int extractword(char *, int, int);
  103. int histadvance(int, int);
  104. void addhist(void);
  105. void loadhistline(long);
  106. void gethist(long, struct histline *);
  107. char *getenv(char *);
  108. char *malloc(unsigned);
  109. #else
  110. int getsh();
  111. int runfmatch();
  112. void zapline();
  113. void delregion();
  114. int dosearch();
  115. int extractword();
  116. int histadvance();
  117. void addhist();
  118. void loadhistline();
  119. void gethist();
  120. char *getenv();
  121. char *malloc();
  122. #endif
  123.  
  124.  
  125.  
  126. void
  127. edinit(file)
  128.       char *file;
  129.       {
  130.       FILE *fp;
  131.       int ktsize;
  132.       int stringsize;
  133.       int abbrevsize;
  134.       char fname[256];
  135.       char *home;
  136.       extern char dftbind[];
  137.  
  138.       if (file == NULL) {
  139.         if ((home = getenv("HOME")) != NULL) {
  140.           strcpy(fname, home);
  141.           strcat(fname, "/.bindc");
  142.           if ((fp = fopen(fname, "r")) == NULL)
  143.             file = dftbind;
  144.         } else {
  145.           file = dftbind;
  146.         }
  147.       }
  148.       if (file != NULL) {
  149.         if ((fp = fopen(file, "r")) == NULL) {
  150.           perror(file);
  151.           badinit("Can't read bindings file");
  152.         }
  153.       }
  154.       if (getsh(fp) != BINDMAGIC) {
  155.         badinit("Bad magic number on key binding file");
  156.       }
  157.       if (getc(fp) != version) {
  158.         badinit(".bindc file needs to be recompiled");
  159.       }
  160.       ktsize = getc(fp) << 8;
  161.       stringsize = getsh(fp);
  162.       abbrevsize = getsh(fp);
  163.       if (feof(fp) || ferror(fp))
  164.         badinit("Key binding file truncated");
  165.       if ((keytabs = (unsigned char *)malloc(ktsize)) == NULL)
  166.         badinit("Out of space");
  167.       fread((char *)keytabs, ktsize, 1, fp);
  168.       keyhash = genkeyhash();    /*DEBUG*/
  169.       fread((char *)syntaxtbl, sizeof syntaxtbl, 1, fp);
  170.       if (stringsize != 0) {
  171.         if ((edstrings = malloc(stringsize)) == NULL)
  172.           badinit("Out of space");
  173.         fread(edstrings, stringsize, 1, fp);
  174.       }
  175.       if (abbrevsize != 0) {
  176.         if ((abbrevtbl = malloc(abbrevsize + 1)) == NULL)
  177.           badinit("Out of space");
  178.         fread(abbrevtbl, abbrevsize, 1, fp);
  179.         abbrevtbl[abbrevsize] = '\0';    /* mark end of abbrevs */
  180.       }
  181.       if (feof(fp) || ferror(fp))
  182.         badinit("Key binding file truncated");
  183.       fclose(fp);
  184.       endcurline = curline;
  185.       point = curline;
  186.       curtable = curmode = keytabs;
  187.       gettermcap();
  188. }
  189.  
  190.  
  191.  
  192. /*
  193.  * Read a short integer from a file.
  194.  */
  195.  
  196. int
  197. getsh(fp)
  198.       FILE *fp;
  199.       {
  200.       int c;
  201.  
  202.       c = getc(fp);
  203.       return c | getc(fp) << 8;
  204. }
  205.  
  206.  
  207. void
  208. newttychars() {
  209.       /* nothing to do */
  210. }
  211.  
  212.  
  213. void
  214. edflush() {
  215.       zapline();
  216.       /* also reset state to start of command */
  217.       curtable = keytabs;
  218.       prefixarg = 0;
  219. }
  220.  
  221.  
  222. void
  223. inchar(c)
  224.       char c;
  225.       {
  226.       int cmd;
  227.       int index;
  228.       int status;
  229.       int pfx;
  230.       int (*func)();
  231.  
  232.       c &= 0177;        /* just in case */
  233.       if (ednextc) {
  234.         func = ednextc;
  235.         ednextc = NULL;
  236.         if ((*func)(c) == 1)
  237.           needbeep++;
  238.         goto out;
  239.       }
  240.       if (prefixarg && c >= '0' && c <= '9') {
  241.         if (prefixarg >= 2)
  242.           prefixval = 0;
  243.         prefixarg = 1;
  244.         prefixval = prefixval * 10 + c - '0';
  245.         goto out;
  246.       }
  247.       cmd = curtable[c];
  248.       index = curtable[c + 128];
  249.       if (cmd == C_FUNC || cmd == C_INSERT) {
  250.         if (prefixarg == 0)
  251.           pfx = 1;
  252.         else if (prefixarg == 3)
  253.           pfx = -1;
  254.         else if (prefixsign)
  255.           pfx = - prefixval;
  256.         else
  257.           pfx = prefixval;
  258.         cmdcharacter = c;
  259.         if (cmd == C_FUNC)
  260.           status = (*edfunc[index])(pfx);
  261.         else
  262.           status = insert_string(index, pfx);
  263.         if (status == 1)
  264.           needbeep++;
  265.         if (status != 2)
  266.           prefixarg = 0;
  267.         curtable = curmode;
  268.       } else if (cmd == C_PFXTBL) {
  269.         curtable = keytabs + index * 256;
  270.       } else {
  271.         needbeep++;
  272.         curtable = curmode;
  273.       }
  274. out:;
  275. }
  276.  
  277.  
  278. void
  279. zapline() {
  280.       point = curline;
  281.       endcurline = curline;
  282.       mark = NULL;
  283. }
  284.  
  285.  
  286.  
  287. universal_argument(n) {
  288.       if (prefixarg == 0) {
  289.         prefixval = 1;
  290.         prefixsign = 0;
  291.       }
  292.       prefixarg = 2;
  293.       prefixval *= 4;
  294.       return 2;
  295. }
  296.  
  297.  
  298. digit_argument(n) {
  299.       prefixarg = 1;
  300.       prefixval = cmdcharacter - '0';
  301.       if (prefixval > 9 || prefixval < 0)
  302.         prefixval = 0;
  303.       prefixsign = 0;
  304.       return 2;
  305. }
  306.  
  307.  
  308. negative_argument(n) {
  309.       prefixarg = 3;
  310.       prefixval = 0;
  311.       prefixsign = 1;
  312.       return 2;
  313. }
  314.  
  315.  
  316. mode_0(n) {
  317.       curmode = keytabs;
  318.       return 0;
  319. }
  320.  
  321.  
  322. mode_1(n) {
  323.       curmode = keytabs + 256;
  324.       return 0;
  325. }
  326.  
  327.  
  328. newline(n) {
  329.       if (edrecurse)
  330.         return (*edrecurse)(0);
  331.       if (point == endcurline && expandabbrev() < 0)
  332.         return 1;
  333.       addhist();
  334.       refresh();
  335.       *endcurline++ = '\n';
  336.       senddata(curline, endcurline - curline);
  337.       movetoend();
  338.       curhist = endhist;
  339.       zapline();
  340.       curmode = keytabs;
  341.       return 0;
  342. }
  343.  
  344.  
  345. end_of_file(n) {
  346.       if (edrecurse)
  347.         return (*edrecurse)(1);
  348.       if (point == endcurline && expandabbrev() < 0)
  349.         return 1;
  350.       addhist();
  351.       senddata(curline, endcurline - curline);
  352.       freezedisp(1);
  353.       curhist = endhist;
  354.       zapline();
  355.       curmode = keytabs;
  356.       return 0;
  357. }
  358.  
  359.  
  360. eof_or_delete_char(n) {
  361.       if (endcurline == curline)
  362.         return end_of_file(n);
  363.       else
  364.         return delete_char(n);
  365. }
  366.  
  367.  
  368. newline_and_insert(n) {
  369.       if (edrecurse)
  370.         return 1;
  371.       if (point == endcurline && expandabbrev() < 0)
  372.         return 1;
  373.       if (curhist == endhist) {
  374.         lasthistlen = endcurline - curline;
  375.         bcopy(curline, lasthist, lasthistlen);
  376.       }
  377.       addhist();
  378.       refresh();
  379.       *endcurline++ = '\n';
  380.       senddata(curline, endcurline - curline);
  381.       movetoend();
  382.       zapline();
  383.       curmode = keytabs;
  384.       if (curhist < endhist - HISTSIZE) {
  385.         curhist = endhist;
  386.         return 1;
  387.       }
  388.       return histadvance(1, 0);
  389. }
  390.  
  391.  
  392. file_complete(n) {
  393.       return runfmatch('c');
  394. }
  395.  
  396.  
  397. list_file_completions(n) {
  398.       return runfmatch('l');
  399. }
  400.  
  401.  
  402. runfmatch(type) {
  403.       char saveline[MAXLLEN];
  404.       register char *p;
  405.       char *savemark;
  406.       char *savepoint;
  407.       int savelen;
  408.       int status;
  409.  
  410.       p = point;
  411.       while (p < endcurline && infname(*p))
  412.         p++;
  413.       if (p == curline || ! infname(p[-1]))
  414.         return 1;
  415.       savelen = endcurline - curline;
  416.       bcopy(curline, saveline, savelen);
  417.       savemark = mark;
  418.       savepoint = p;
  419.       if (promptset && type == 'l') {
  420.         refresh();
  421.         movetoend();
  422.         promptset = 1;        /* retain prompt */
  423.       }
  424.       point = p;
  425.       delregion(endcurline, 0);
  426.       while (p > curline && infname(p[-1]))
  427.         p--;
  428.       point = curline;
  429.       delregion(p, 0);
  430.       if (makespace(10)) {
  431.         status = 1;
  432.         goto out;
  433.       }
  434.       bcopy("fmatch -x ", curline, 10);
  435.       curline[8] = type;
  436.       if (promptset) {
  437.         if (makespace(1)) {
  438.           status = 1;
  439.           goto out;
  440.         }
  441.         curline[0] = '\034';
  442.       } else {
  443.         refresh();
  444.         movetoend();
  445.       }
  446. #ifdef notdef /* no longer add to history file */
  447.       atend = (curhist == endhist);
  448.       addhist();
  449.       if (atend)
  450.         curhist = endhist;
  451. #endif
  452.       *endcurline++ = '\n';
  453.       senddata(curline, endcurline - curline);
  454.       status = 0;
  455. out:
  456.       endcurline = curline + savelen;
  457.       bcopy(saveline, curline, savelen);
  458.       mark = savemark;
  459.       point = savepoint;
  460.       return status;
  461. }
  462.  
  463.       
  464. tty_intr(n) {
  465.       if (edrecurse)
  466.         return (*edrecurse)(1);
  467.       sendsig(SIGINT);
  468.       return 0;
  469. }
  470.  
  471.  
  472. tty_quit(n) {
  473.       sendsig(SIGQUIT);
  474.       return 0;
  475. }
  476.  
  477.  
  478. tty_susp(n) {
  479.       sendsig(SIGTSTP);
  480.       return 0;
  481. }
  482.  
  483.  
  484. do_quoted_insert(c) {
  485.       cmdcharacter = c;
  486.       return self_insert(prefixval);
  487. }
  488.  
  489.  
  490. quoted_insert(n) {
  491.       ednextc = do_quoted_insert;
  492.       prefixval = n;
  493. }
  494.  
  495.  
  496. /*
  497.  * Delete the characters between the point and "end".  If "kill" is true,
  498.  * copy to kill buffer.  Exception:  we don't copy to kill if the region
  499.  * is empty.
  500.  */
  501.  
  502. void
  503. delregion(end, kill)
  504.       char *end;
  505.       {
  506.       register char *p, *q;
  507.       register char *k;
  508.  
  509.       if (end > endcurline)
  510.         end = endcurline;
  511.       if (end < curline)
  512.         end = curline;
  513.       if (end > point) {
  514.         p = point;
  515.         q = end;
  516.       } else {
  517.         p = end;
  518.         q = point;
  519.       }
  520.       if (kill && p != q) {
  521.         end = p;
  522.         k = killbuffer;
  523.         while (p < q)
  524.           *k++ = *p++;
  525.         killsize = k - killbuffer;
  526.         p = end;
  527.       }
  528.       point = p;
  529.       if (mark && mark > p) {
  530.         if (mark > q)
  531.           mark -= q - p;
  532.         else
  533.           mark = p;
  534.       }
  535.       while (q < endcurline)
  536.         *p++ = *q++;
  537.       endcurline = p;
  538. }
  539.  
  540.  
  541. delete_backward_char(n) {
  542.       delregion(point - n, prefixarg);
  543. }
  544.  
  545.  
  546. delete_char(n) {
  547.       delregion(point + n, prefixarg);
  548. }
  549.  
  550.  
  551. kill_word(n) {
  552.       char *save = point;
  553.  
  554.       forward_word(n);
  555.       delregion(save, 1);
  556.       return 0;
  557. }
  558.  
  559.  
  560. backward_kill_word(n) {
  561.       char *save = point;
  562.  
  563.       backward_word(n);
  564.       delregion(save, 1);
  565.       return 0;
  566. }
  567.  
  568.  
  569. kill_region(n) {
  570.       if (mark == NULL)
  571.         return 1;
  572.       delregion(mark, 1);
  573.       return 0;
  574. }
  575.  
  576.  
  577. kill_line(n) {
  578.       delregion(n <= 0? curline : endcurline, 1);
  579.       return 0;
  580. }
  581.  
  582.  
  583. kill_input(n) {
  584.       point = curline;
  585.       delregion(endcurline, 1);
  586.       return 0;
  587. }
  588.  
  589.  
  590. copy_region_as_kill(n) {
  591.       char *savepoint = point;
  592.       char *savemark = mark;
  593.       delregion(mark, 1);
  594.       yank(1);
  595.       point = savepoint;
  596.       mark = savemark;
  597. }
  598.  
  599.  
  600. forward_char(n) {
  601.       int left;
  602.       left = endcurline - point;
  603.       if (n > left)
  604.         n = left;
  605.       left = curline - point;
  606.       if (n < left)
  607.         n = left;
  608.       point += n;
  609.       return 0;
  610. }
  611.  
  612.  
  613. backward_char(n) {
  614.       return forward_char(-n);
  615. }
  616.  
  617.  
  618. int
  619. inword(c)
  620.       char c;
  621.       {
  622.       c &= 0177;
  623.       return syntaxtbl[0][c >> 3] & 1 << (c & 07);
  624. }
  625.  
  626.  
  627. int
  628. infname(c)
  629.       char c;
  630.       {
  631.       c &= 0177;
  632.       return syntaxtbl[1][c >> 3] & 1 << (c & 07);
  633. }
  634.  
  635.  
  636. int
  637. inabbrev(c)
  638.       char c;
  639.       {
  640.       c &= 0177;
  641.       return syntaxtbl[2][c >> 3] & 1 << (c & 07);
  642. }
  643.  
  644.  
  645. forward_word(n) {
  646.       if (n < 0)
  647.         return backward_word(-n);
  648.       while (--n >= 0) {
  649.         while (point < endcurline && ! inword(*point))
  650.           point++;
  651.         while (point < endcurline && inword(*point))
  652.           point++;
  653.       }
  654.       return 0;
  655. }
  656.  
  657.  
  658. backward_word(n) {
  659.       if (n < 0)
  660.         return forward_word(-n);
  661.       while (--n >= 0) {
  662.         while (--point >= curline && ! inword(*point));
  663.         point++;
  664.         while (--point >= curline && inword(*point));
  665.         point++;
  666.       }
  667.       return 0;
  668. }
  669.  
  670.  
  671. end_of_line(n) {
  672.       point = endcurline;
  673.       return 0;
  674. }
  675.  
  676.  
  677. beginning_of_line(n) {
  678.       point = curline;
  679.       return 0;
  680. }
  681.  
  682.  
  683.  
  684. /*
  685.  * Make space for n characters after the point.  Return 1 on failure.
  686.  */
  687.  
  688. int
  689. makespace(n) {
  690.       char *p;
  691.  
  692.       if (endcurline - curline + n > MAXLLEN)
  693.         return 1;
  694.       for (p = endcurline ; --p >= point ; )
  695.         *(p + n) = *p;
  696.       endcurline += n;
  697.       if (mark != NULL && mark > point)
  698.         mark += n;
  699.       return 0;
  700. }
  701.  
  702.  
  703. void
  704. insertchars(p, n)
  705.       char *p;
  706.       {
  707.       if (makespace(n) != 0) {
  708.         needbeep++;
  709.         return;
  710.       }
  711.       bcopy(p, point, n);
  712.       point += n;
  713. }
  714.  
  715.  
  716. self_insert(n) {
  717.       if (abbrevtbl && ! inabbrev(cmdcharacter)) {
  718.         if (expandabbrev() < 0)
  719.           return 1;
  720.       }
  721.       while (--n >= 0) {
  722.         if (point == endcurline) {
  723.           if (endcurline == &curline[MAXLLEN])
  724.             return 1;
  725.           endcurline++;
  726.         } else {
  727.           if (makespace(1) != 0)
  728.             return 1;
  729.         }
  730.         *point++ = cmdcharacter;
  731.       }
  732.       return 0;
  733. }
  734.  
  735.  
  736. yank(n) {
  737.       if (makespace(killsize) != 0)
  738.         return 1;
  739.       bcopy(killbuffer, point, killsize);
  740.       mark = point +  killsize;
  741.       if (! prefixarg)
  742.         exchange_point_and_mark(0);
  743.       return 0;
  744. }
  745.  
  746.  
  747. insert_string(stringnum, n) {
  748.       char *p;
  749.       int len;
  750.  
  751.       p = edstrings;
  752.       while (--stringnum >= 0)
  753.         p = p + *p + 1;
  754.       if (abbrevtbl && *p != 0 &&! inabbrev(p[1])) {
  755.         if (expandabbrev() < 0)
  756.           return 1;
  757.       }
  758.       while (--n >= 0) {
  759.         if (makespace(len = *p) != 0) {
  760.           return 1;
  761.         }
  762.         bcopy(p + 1, point, len);
  763.         point += len;
  764.       }
  765.       return 0;
  766. }
  767.  
  768.  
  769. set_mark(n) {
  770.       mark = point;
  771.       return 0;
  772. }
  773.  
  774.  
  775. exchange_point_and_mark(n) {
  776.       char *p;
  777.  
  778.       if (mark == NULL)
  779.         return 1;
  780.       p = mark;
  781.       mark = point;
  782.       point = p;
  783.       return 0;
  784. }
  785.  
  786.  
  787. /*
  788.  * Convert characters between the point and the specified location to
  789.  * upper case.
  790.  */
  791.  
  792. void
  793. upcase(end)
  794.       char *end;
  795.       {
  796.       char *p;
  797.       int len;
  798.  
  799.       if (end > point) {
  800.         p = point;
  801.         len = end - point;
  802.       } else {
  803.         p = end;
  804.         len = point - end;
  805.       }
  806.       while (--len >= 0) {
  807.         if (islower(*p))
  808.           *p = toupper(*p);
  809.         p++;
  810.       }
  811. }
  812.  
  813.  
  814. upcase_char(n) {
  815.       char *save = point;
  816.  
  817.       forward_char(n);
  818.       upcase(save);
  819.       return 0;
  820. }
  821.  
  822.  
  823. upcase_word(n) {
  824.       char *save = point;
  825.  
  826.       forward_word(n);
  827.       upcase(save);
  828.       return 0;
  829. }
  830.  
  831.  
  832. upcase_region(n) {
  833.       upcase(mark);
  834.       return 0;
  835. }
  836.  
  837.  
  838. gosling_transpose_chars(n) {
  839.       char c;
  840.  
  841.       if (prefixarg)
  842.         return transpose_chars(n);
  843.       if (point < curline + 2)
  844.         return 1;
  845.       c = point[-1];
  846.       point[-1] = point[-2];
  847.       point[-2] = c;
  848.       return 0;
  849. }
  850.  
  851.  
  852. transpose_chars(n) {
  853.       char c;
  854.  
  855.       if (point == endcurline && point >= curline + 2 && ! prefixarg)
  856.         point--;
  857.       if (point <= curline || n > 0 && point >= endcurline)
  858.         return 1;
  859.       c = *(point - 1);
  860.       delregion(point - 1, 0);
  861.       forward_char(n);
  862.       makespace(1);
  863.       *point++ = c;
  864.       return 0;
  865. }
  866.  
  867.  
  868. transpose_words(n) {
  869.       char *save = point;
  870.       char *start;
  871.       char *loc;
  872.       int end1, start2, end2;
  873.       char buf[MAXLLEN];
  874.  
  875.       if (point == endcurline)
  876.         backward_word(1);
  877.       loc = point;
  878.       backward_word(1);
  879.       start = point;
  880.       if (start >= loc || ! inword(*start))
  881.         goto fail;
  882.       forward_word(1);
  883.       if (point > loc)
  884.         point = loc;
  885.       end1 = point - start;
  886.       forward_word(1);
  887.       end2 = point - start;
  888.       backward_word(1);
  889.       if (point < loc) {
  890.         point = loc;
  891.         if (! inword(*loc))
  892.           goto fail;
  893.       }
  894.       start2 = point - start;
  895.       bcopy(start, buf, end2);
  896.       point = start + start2;
  897.       delregion(start + end2, 0);
  898.       point = start;
  899.       delregion(start + end1, 0);
  900.       makespace(end2 - start2);
  901.       bcopy(buf + start2, start, end2 - start2);
  902.       point = start + end2 - end1;
  903.       makespace(end1);
  904.       bcopy(buf, point, end1);
  905.       point += end1;
  906.       if (save == endcurline)
  907.         point = save;
  908.       return 0;
  909.  
  910. fail:
  911.       point = save;
  912.       return 1;
  913. }
  914.  
  915.  
  916. re_search_backward(n) {
  917.       return re_search_forward(-n);
  918. }
  919.  
  920.  
  921. re_search_forward(n) {
  922.       if (edrecurse)
  923.         return 1;
  924.       edrecurse = dosearch;
  925.       savecurlinelen = endcurline - curline;
  926.       bcopy(curline, savecurline, savecurlinelen);
  927.       savepoint = point;
  928.       savemark = mark;
  929.       saveprefixval = n;
  930.       zapline();
  931.       editprompt = cmdcharacter? cmdcharacter : '/';
  932.       curmode = keytabs;
  933.       return 0;
  934. }
  935.  
  936.  
  937. dosearch(flag) {
  938.       edrecurse = NULL;
  939.       editprompt = '\0';
  940.       if (flag)
  941.         goto fail;
  942.       if (endcurline == curline) {
  943.         if (! savedpattern)
  944.           goto fail;
  945.       } else if (re_compile_pattern(curline, endcurline - curline, &pbuf)) {
  946.         savedpattern = 0;
  947.         goto fail;
  948.       }
  949.       savedpattern = 1;
  950.       bcopy(savecurline, curline, savecurlinelen);
  951.       endcurline = curline + savecurlinelen;
  952.       point = savepoint;
  953.       mark = savemark;
  954.       return histadvance(saveprefixval, HSEARCH);
  955.  
  956. fail:
  957.       bcopy(savecurline, curline, savecurlinelen);
  958.       endcurline = curline + savecurlinelen;
  959.       point = savepoint;
  960.       mark = savemark;
  961.       return 1;
  962. }
  963.  
  964.  
  965. get_history_word(n) {
  966.       struct histline hline;
  967.  
  968.       if (endhist == 0)
  969.         return 1;
  970.       gethist(endhist - histprevlen - sizeof (struct histhdr), &hline);
  971.       if (! prefixarg)
  972.         n = -1;
  973.       return extractword(hline.text, hline.len, n);
  974. }
  975.  
  976.  
  977. last_output_line(n) {
  978.       if (! prefixarg)
  979.         n = 0;
  980.       return extractword(lastoutline, lastoutlinelen, n);
  981. }
  982.  
  983.  
  984. /*
  985.  * Insert the contents of word n of the line.  If n is zero, insert the
  986.  * entire line; if it is negative, count words from the end.  In this
  987.  * routine, words are always delimited by spaces and tabs.
  988.  */
  989.  
  990. int
  991. extractword(line, len, n)
  992.       char *line;
  993.       {
  994.       char *p;
  995.       char *q;
  996.       char *endline = line + len;
  997.       int wordlen;
  998.  
  999.       if (n > 0) {
  1000.         p = line;
  1001.         for (;;) {
  1002.           while (p < endline && ! infname(*p))
  1003.             p++;
  1004.           q = p;
  1005.           while (q < endline && infname(*q))
  1006.             q++;
  1007.           if (--n == 0)
  1008.             break;
  1009.           p = q;
  1010.         }
  1011.       } else if (n < 0) {
  1012.         q = endline;
  1013.         for (;;) {
  1014.           while (q > line && ! infname(q[-1]))
  1015.             q--;
  1016.           p = q;
  1017.           while (p > line && infname(p[-1]))
  1018.             p--;
  1019.           if (++n == 0)
  1020.             break;
  1021.           q = p;
  1022.         }
  1023.       } else { /* n == 0 */
  1024.         p = line;
  1025.         q = endline;
  1026.       }
  1027.       wordlen = q - p;
  1028.       if (makespace(wordlen) != 0)
  1029.         return 1;
  1030.       bcopy(p, point, wordlen);
  1031.       mark = point;
  1032.       point += wordlen;
  1033.       return 0;
  1034. }
  1035.  
  1036.  
  1037. next_history(n) {
  1038.       return histadvance(n, SKIPEMPTY);
  1039. }
  1040.  
  1041.  
  1042. previous_history(n) {
  1043.       return histadvance(-n, SKIPEMPTY);
  1044. }
  1045.  
  1046.  
  1047. end_of_history(n) {
  1048.       loadhistline(endhist);
  1049.       return 0;
  1050. }
  1051.  
  1052.  
  1053. beginning_of_history(n) {
  1054.       histadvance(-32767, 0);
  1055.       return 0;
  1056. }
  1057.  
  1058.  
  1059. /*
  1060.  * Move through the history file, and load the result into curline.
  1061.  * N is the number of items to move:  a negative value of n moves
  1062.  * backward in the history file and a postive value of n moves forward.
  1063.  * The flags control which history lines are counted.  The search starts
  1064.  * from the location specified by the curhist variable.
  1065.  *
  1066.  * The structure of the history file is as follows.  Each line in the
  1067.  * history file consists of a header, defined as (struct histhdr), followed
  1068.  * by the text of the line.  Locations in the history file are identified
  1069.  * by the number of bytes written since the file was first created.  To
  1070.  * keep the file from growing without bound, the file wraps around at
  1071.  * HISTSIZE bytes.  Thus any location less than (endhist - HISTSIZE) has
  1072.  * been overwritten.  (Endhist points to the end of the history file.)
  1073.  */
  1074.  
  1075. int
  1076. histadvance(n, flags) {
  1077.       struct histline hline;
  1078.       long loc;
  1079.       long found;
  1080.       int back;
  1081.       int oldloc;
  1082.       int prgmid = 1;
  1083.  
  1084.       if (edrecurse)
  1085.         return 1;
  1086.       loc = curhist;
  1087.       if (loc == endhist) {
  1088.         lasthistlen = endcurline - curline;
  1089.         bcopy(curline, lasthist, lasthistlen);
  1090.       }
  1091.       back = 0;
  1092.       if (n < 0) {
  1093.         n = -n;
  1094.         back = 1;
  1095.       }
  1096.       found = loc;
  1097.       while (n >= 0) {
  1098.         oldloc = loc;
  1099.         if (back) {
  1100.           if (loc == endhist) {
  1101.             loc -= sizeof (struct histhdr) + histprevlen;
  1102.           } else if (loc < 0 || loc < endhist - HISTSIZE) {
  1103.             break;
  1104.           } else {
  1105.             gethist(loc, &hline);
  1106.             loc -= sizeof (struct histhdr) + hline.prevlen;
  1107.           }
  1108.         } else {
  1109.           if (loc == endhist) {
  1110.             loc++;
  1111.           } else if (loc > endhist) {
  1112.             break;
  1113.           } else {
  1114.             gethist(loc, &hline);
  1115.             loc += sizeof (struct histhdr) + hline.len;
  1116.           }
  1117.         }
  1118.         if (oldloc == endhist) {
  1119.           hline.prgmid = prgmid;
  1120.           hline.text = lasthist;
  1121.           hline.len = lasthistlen;
  1122.         }
  1123.         if (hline.prgmid != HISTFILLER
  1124.          && ((flags & SKIPEMPTY) == 0 || oldloc == endhist || hline.len > 0)
  1125.          && ((flags & SAMEPRGM) == 0 || hline.prgmid == prgmid)
  1126.          && ((flags & HSEARCH) == 0 || oldloc == curhist ||
  1127.             re_search(&pbuf, hline.text, hline.len, 0, hline.len,
  1128.                   (struct re_registers *)0) >= 0)) {
  1129.           n--;
  1130.           found = oldloc;
  1131.         }
  1132.       }
  1133.       if (found != curhist || n < 0)
  1134.         loadhistline(found);
  1135.       return n >= 0? 1 : 0;
  1136. }
  1137.  
  1138.  
  1139. /*
  1140.  * Add the current line to the history file.  If echoing is turned off,
  1141.  * we store a blank line rather than the actual line so that we don't
  1142.  * have passwords in the history file.
  1143.  */
  1144.  
  1145. void
  1146. addhist() {
  1147.       struct histhdr hdr;
  1148.       int space;
  1149.       int len = ttymode.echo? endcurline - curline : 0;
  1150.       char *lp;
  1151.  
  1152.       hdr.prevlen = histprevlen;
  1153.       space = HISTSIZE - (endhist & LOCMASK);
  1154.       if (space < len + 2 * sizeof (struct histhdr)) {
  1155.         hdr.len = space - sizeof (struct histhdr);
  1156.         hdr.prgmid = HISTFILLER;
  1157.         bcopy((char *)&hdr, histarea + (endhist & LOCMASK), sizeof hdr);
  1158.         hdr.prevlen = hdr.len;
  1159.         endhist += space;
  1160.       }
  1161.       lp = histarea + (endhist & LOCMASK);
  1162.       hdr.len = len;
  1163.       hdr.prgmid = 0;        /* for now */
  1164.       bcopy((char *)&hdr, lp, sizeof hdr);
  1165.       bcopy(curline, lp + sizeof hdr, len);
  1166.       histprevlen = len;
  1167.       endhist += sizeof hdr + len;
  1168. }
  1169.  
  1170.  
  1171. /*
  1172.  * Load the contents of a history line into curline.
  1173.  */
  1174.  
  1175. void
  1176. loadhistline(loc)
  1177.       long loc;
  1178.       {
  1179.       struct histline hline;
  1180.  
  1181.       if (loc == endhist) {
  1182.         if (curhist == endhist) {
  1183.           return;
  1184.         }
  1185.         hline.text = lasthist;
  1186.         hline.len = lasthistlen;
  1187.       } else {
  1188.         gethist(loc, &hline);
  1189.       }
  1190.       bcopy(hline.text, curline, hline.len);
  1191.       endcurline = curline + hline.len;
  1192.       point = endcurline;
  1193.       mark = NULL;
  1194.       curhist = loc;
  1195. }
  1196.  
  1197.  
  1198. /*
  1199.  * Read a history entry into hline.  To make the history file be a real
  1200.  * file rather than a block of memory, change this routine and the addhist
  1201.  * routine.
  1202.  */
  1203.  
  1204. void
  1205. gethist(loc, hline)
  1206.       long loc;
  1207.       struct histline *hline;
  1208.       {
  1209.       char *lp = histarea + (loc & LOCMASK);
  1210.  
  1211.       bcopy(lp, (char *)hline, sizeof (struct histhdr));
  1212.       hline->text = lp + sizeof (struct histhdr);
  1213. }
  1214.  
  1215.  
  1216. /*
  1217.  * Expand the abbreviation before point, if any.  Returns 1 if expansion
  1218.  * occurred, 0 if no expansion occurred, and -1 if the expansion could
  1219.  * not be performed because there was not sufficient space.
  1220.  */
  1221.  
  1222. int
  1223. expandabbrev() {
  1224.       char *start;
  1225.       int len;
  1226.       char *p;
  1227.  
  1228.       if (abbrevtbl == NULL)
  1229.         return 0;
  1230.       start = point;
  1231.       while (--start >= curline && inabbrev(*start));
  1232.       start++;
  1233.       if (start == point)
  1234.         return 0;
  1235.       len = point - start;
  1236.       p = abbrevtbl;
  1237.       for (;;) {
  1238.         if (*p == 0)
  1239.           return 0;        /* end of table */
  1240.         if (*p == len && bcmp(p + 1, start, len) == 0)
  1241.           break;
  1242.         p += *p + 1;
  1243.         p += *p + 1;
  1244.       }
  1245.       p += *p + 1;
  1246.       if (endcurline - curline + *p - len > MAXLLEN)
  1247.         return -1;
  1248.       delregion(start, 0);
  1249.       if (makespace(*p))
  1250.         return -1;            /* can't happen */
  1251.       bcopy(p + 1, point, *p);
  1252.       point += *p;
  1253.       return 1;
  1254. }
  1255.  
  1256.  
  1257. /* DEBUG */
  1258.  
  1259. int
  1260. genkeyhash() {
  1261.       int hash;
  1262.       unsigned char *p;
  1263.  
  1264.       hash = 123456; p = keytabs + 256;
  1265.       do { hash = ((hash << 8) + *--p) % 8380087; } while (p >= keytabs);
  1266.       return hash;
  1267. }
  1268.  
  1269.  
  1270. chkkeytab() {
  1271.       int hash;
  1272.       unsigned char *p;
  1273.  
  1274.       hash = 123456; p = keytabs + 256;
  1275.       do { hash = ((hash << 8) + *--p) % 8380087; } while (p >= keytabs);
  1276.       if (hash != keyhash)
  1277.         abort();
  1278. }
  1279.  
  1280. /* END DEBUG */
  1281. EOF
  1282. if test `wc -c < ed.c` -ne 25745
  1283. then    echo 'ed.c is the wrong size'
  1284. fi
  1285. echo extracting fmatch.1
  1286. cat > fmatch.1 <<\EOF
  1287. .TH FMATCH 1
  1288. .SH NAME
  1289. fmatch \- file name matching
  1290. .SH SYNOPSYS
  1291. .B fmatch
  1292. [
  1293. -lc
  1294. ]
  1295. .I file-prefix
  1296. .SH COPYRIGHT
  1297. .if n Copyright (C) 1989 by Kenneth Almquist.
  1298. .if t Copyright \(co 1989 by Kenneth Almquist.  
  1299. .SH DESCRIPTION
  1300. The
  1301. .I fmatch
  1302. command finds the files which are matched by the
  1303. .IR file-prefix .
  1304. If the
  1305. .B -l
  1306. option is given (the default),
  1307. .I fmatch
  1308. lists the matching files.
  1309. The default bindings for
  1310. .IR atty (1)
  1311. run
  1312. .B fmatch
  1313. .B -l
  1314. on the current word when
  1315. .B ESC\ =
  1316. is typed.
  1317. .PP
  1318. If the
  1319. .B -c
  1320. option is given,
  1321. .I fmatch
  1322. attemtps to perform file name completion.
  1323. If the prefix matches exactly one file,
  1324. .I fmatch
  1325. generates the rest of the file name matched by the prefix and
  1326. inserts it in the input line buffer used by
  1327. .IR atty (1).
  1328. If the file is a directory, a ``/'' is appended.
  1329. If the prefix matches no files or matches more than one file,
  1330. .I fmatch
  1331. inserts as many characters as it can (if the prefix matches multiple
  1332. files), and rings the terminal bell.
  1333. The default bindings for
  1334. .IR atty (1)
  1335. run
  1336. .B fmatch
  1337. .B -c
  1338. on the current word when
  1339. .B ESC\ ESC
  1340. is typed.
  1341. .SH BUGS
  1342. .I Fmatch
  1343. is kind of a kludge.
  1344. .I Atty
  1345. can't do file completion itself because it doesn't know the current working
  1346. directory of the process reading the input.  This lack of knowledge could be
  1347. corrected with some help from the kernel.
  1348. EOF
  1349. if test `wc -c < fmatch.1` -ne 1317
  1350. then    echo 'fmatch.1 is the wrong size'
  1351. fi
  1352. echo extracting fmatch.c
  1353. cat > fmatch.c <<\EOF
  1354. /*
  1355.  * File matching program run by the list-file-completions and file-complete
  1356.  * commands of atty(1).
  1357.  *
  1358.  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  1359.  * This file is part of atty, which is distributed under the terms specified
  1360.  * by the Atty General Public License.  See the file named LICENSE.
  1361.  */
  1362.  
  1363. #include <stdio.h>
  1364. #include <sys/types.h>
  1365. #include <sys/stat.h>
  1366. #include <sys/dir.h>
  1367.  
  1368.  
  1369. #ifndef __STDC__
  1370. #define const /* empty */
  1371. #endif
  1372. #define ARB 4
  1373.  
  1374.  
  1375. int list;            /* true if -l option given */
  1376. int completion;            /* true if -c option given */
  1377. char *dirname;            /* directory being searched */
  1378. int basenamelen;        /* number of characters of name specified */
  1379.  
  1380.  
  1381. #ifdef __STDC__
  1382. char **listfiles(char *);
  1383. int compar(char **, char **);
  1384. void printlist(char **);
  1385. void complete(char **);
  1386. int prefix(const char *, const char *);
  1387. #else
  1388. char **listfiles();
  1389. int compar();
  1390. void printlist();
  1391. void complete();
  1392. int prefix();
  1393. #endif
  1394.  
  1395. #ifdef __STDC__
  1396. void *malloc(unsigned);
  1397. void *realloc(void *, unsigned);
  1398. #else
  1399. char *malloc();
  1400. char *realloc();
  1401. #endif
  1402.  
  1403.  
  1404. #define equal(s1, s2)    (strcmp(s1, s2) == 0)
  1405.  
  1406.  
  1407.  
  1408. main(argc, argv)
  1409.       char **argv;
  1410.       {
  1411.       char **ap;
  1412.       char *p;
  1413.       char **flist;
  1414.  
  1415.       if (argc <= 1) {
  1416. usage:        fputs("Usage: fmatch -cl file_prefix\n", stdout);
  1417.         exit(2);
  1418.       }
  1419.       ap = argv + 1;
  1420.       p = *ap;
  1421.       if (*p == '-') {
  1422.         ap++;
  1423.         p++;
  1424.         while (*p) {
  1425.           switch (*p++) {
  1426.           case 'c':
  1427.             completion++;
  1428.             break;
  1429.           case 'l':
  1430.             list++;
  1431.             break;
  1432.           default:
  1433.             goto usage;
  1434.           }
  1435.         }
  1436.       }
  1437.       if (*ap == NULL)
  1438.         goto usage;
  1439.       if (! completion)
  1440.         list++;
  1441.       flist = listfiles(*ap);
  1442.       if (list)
  1443.         printlist(flist);
  1444.       if (completion)
  1445.         complete(flist);
  1446.       return 0;
  1447. }
  1448.  
  1449.  
  1450. char **
  1451. listfiles(name)
  1452.       char *name;
  1453.       {
  1454.       char *p;
  1455.       char *basename;
  1456.       char *dir;
  1457.       DIR *dp;
  1458.       struct direct *dirp;
  1459.       struct stat statb;
  1460.       char **flist;
  1461.       int listsize;
  1462.       int nfiles;
  1463.  
  1464.       basename = NULL;
  1465.       for (p = name ; *p ; p++) {
  1466.         if (*p == '/')
  1467.           basename = p;
  1468.       }
  1469.       if (basename == NULL) {
  1470.         basename = name;
  1471.         dir = ".";
  1472.       } else if (basename == name) {
  1473.         basename++;
  1474.         dir = "/";
  1475.       } else {
  1476.         *basename++ = '\0';
  1477.         dir = name;
  1478.       }
  1479.       if (! equal(dir, ".")
  1480.        && (stat(dir, &statb) < 0 || (statb.st_mode & S_IFMT) != S_IFDIR)) {
  1481.         fputs(dir, stdout);
  1482.         fputs(": no such directory\n", stdout);
  1483.         if (completion)
  1484.           putc('\7', stdout);
  1485.         exit(2);
  1486.       }
  1487.       if ((dp = opendir(dir)) == NULL) {
  1488.         fputs(dir, stdout);
  1489.         fputs(": cannot open\n", stdout);
  1490.         if (completion)
  1491.           putc('\7', stdout);
  1492.         exit(2);
  1493.       }
  1494.       listsize = 20;
  1495.       if ((flist = (char **)malloc(listsize * sizeof (char *))) == NULL) {
  1496. nospace:    fputs("fmatch: out of space\n", stdout);
  1497.         exit(2);
  1498.       }
  1499.       nfiles = 0;
  1500.       while ((dirp = readdir(dp)) != NULL) {
  1501.         if (*basename) {
  1502.           if (*dirp->d_name != *basename
  1503.            || ! prefix(basename, dirp->d_name))
  1504.             continue;
  1505.         } else {
  1506.           if (*dirp->d_name == '.')
  1507.             continue;
  1508.         }
  1509.         if (nfiles >= listsize - 1) {
  1510.           listsize += listsize >= 100? 40 : 20;
  1511.           if ((flist = (char **)realloc(flist,
  1512.                     listsize * sizeof (char *))) == NULL) {
  1513.             goto nospace;
  1514.           }
  1515.         }
  1516.         if ((p = malloc(strlen(dirp->d_name) + 1)) == NULL)
  1517.           goto nospace;
  1518.         strcpy(p, dirp->d_name);
  1519.         flist[nfiles++] = p;
  1520.       }
  1521.       flist[nfiles] = NULL;
  1522.       dirname = dir;
  1523.       basenamelen = strlen(basename);
  1524.       return flist;
  1525. }
  1526.  
  1527.  
  1528. int
  1529. compar(a, b)
  1530.       char **a, **b;
  1531.       {
  1532.       return strcmp(*a, *b);
  1533. }
  1534.  
  1535.  
  1536. void
  1537. printlist(flist)
  1538.       char **flist;
  1539.       {
  1540.       char **pp;
  1541.       int maxlen;
  1542.       int nfiles;
  1543.       int len;
  1544.       int ncolumns;
  1545.       int sep;
  1546.       int line;
  1547.       int col;
  1548.       int fnum;
  1549.       int colwidth;
  1550.       int i;
  1551.  
  1552.       if (flist[0] == NULL)
  1553.         fputs("No match", stdout);
  1554.       nfiles = 0;
  1555.       maxlen = 0;
  1556.       for (pp = flist ; *pp ; pp++) {
  1557.         len = strlen(*pp);
  1558.         if (maxlen < len)
  1559.           maxlen = len;
  1560.         nfiles++;
  1561.       }
  1562.       if (nfiles == 0)
  1563.         return;
  1564.       qsort((char *)flist, nfiles, sizeof (char *), compar);
  1565.       ncolumns = 80 / (maxlen + 2);
  1566.       if (ncolumns == 0)
  1567.         ncolumns = 1;
  1568.       colwidth = 80 / ncolumns;
  1569.       sep = (nfiles + ncolumns - 1) / ncolumns;
  1570.       for (line = 0 ; line < sep ; line++) {
  1571.         for (col = 0 ; ; ) {
  1572.           fnum = col * sep + line;
  1573.           if (fnum >= nfiles)
  1574.             break;
  1575.           fputs(flist[fnum], stdout);
  1576.           if (++col >= ncolumns)
  1577.             break;
  1578.           for (i = colwidth - strlen(flist[fnum]) ; --i >= 0 ; )
  1579.             putc(' ', stdout);
  1580.         }
  1581.         putc('\n', stdout);
  1582.       }
  1583. }
  1584.  
  1585.  
  1586. void
  1587. complete(flist)
  1588.       char **flist;
  1589.       {
  1590.       char *first;
  1591.       int nmatch;
  1592.       char **pp;
  1593.       char *p, *q;
  1594.       struct stat statb;
  1595.  
  1596.       if ((first = *flist) == NULL) {
  1597.         putc('\7', stdout);
  1598.         return;
  1599.       }
  1600.       nmatch = strlen(first);
  1601.       statb.st_mode = 0;
  1602.       if (flist[1] == NULL) {
  1603.         if ((p = malloc(strlen(dirname) + nmatch + 3)) == NULL) {
  1604.           fputs("Out of space\n", stdout);
  1605.           exit(2);
  1606.         }
  1607.         strcpy(p, dirname);
  1608.         strcat(p, "/");
  1609.         strcat(p, first);
  1610.         stat(p, &statb);
  1611.       } else {
  1612.         for (pp = flist ; *++pp != NULL ; ) {
  1613.           for (p = first, q = *pp ; *p == *q && p - first < nmatch ; p++, q++);
  1614.           nmatch = p - first;
  1615.         }
  1616.         if (nmatch <= basenamelen) {
  1617.           putc('\7', stdout);
  1618.           return;
  1619.         }
  1620.       }
  1621.       fputs("\033]I", stdout);
  1622.       fwrite(first + basenamelen, nmatch - basenamelen, 1, stdout);
  1623.       if ((statb.st_mode & S_IFMT) == S_IFDIR)
  1624.         putc('/', stdout);
  1625.       putc('\n', stdout);
  1626.       if (flist[1] != NULL)
  1627.         putc('\7', stdout);
  1628. }
  1629.  
  1630.  
  1631. int
  1632. prefix(pfx, string)
  1633.       register char const *pfx;
  1634.       register char const *string;
  1635.       {
  1636.       while (*pfx) {
  1637.         if (*pfx++ != *string++)
  1638.           return 0;
  1639.       }
  1640.       return 1;
  1641. }
  1642. EOF
  1643. if test `wc -c < fmatch.c` -ne 5900
  1644. then    echo 'fmatch.c is the wrong size'
  1645. fi
  1646. echo extracting INTERNALS
  1647. cat > INTERNALS <<\EOF
  1648. I was going to write something about the internals, but decided you
  1649. don't want to know!  This program is hack and should be rewritten
  1650. properly, with appropriate kernel support.  Here are some hints on
  1651. adding editing commands, if you want to do that.
  1652.  
  1653. First, select a name for your routine, and add it to the "command"
  1654. array at the top of kbind.c.  Then write a routine to implement the
  1655. command and put it in ed.c.  The name of the routine should be the
  1656. same as the name of the command, except that dashes should be replaced
  1657. with underscores.
  1658.  
  1659. The routine will be called with an integer argument specifying the
  1660. prefix arg passed to the command, or 1 if there is no prefix arg.
  1661. The global variable prefixarg will be nonzero if a prefix argument
  1662. was given.  The global variable cmdcharacter will contain the last
  1663. character of the key sequence used to invoke the command.  Your
  1664. routine should return zero on success and one on failure; the latter
  1665. will cause the editor to ring the terminal bell.
  1666.  
  1667. The current line being edited is stored in the global variable curline.
  1668. Endcurline points to the location after the last character of the
  1669. line, "point" points to the character after the cursor, and "mark"
  1670. points to the character following the mark.
  1671.  
  1672. To modify the line, you probably want to use the routine makespace and
  1673. delregion.  Makespace(n) creates space for n characters after the
  1674. point, moving the characters following the point to the right.  You
  1675. then have to copy characters into the space created.  The yank routine
  1676. illustrates its usage.  Be sure to check the return code from
  1677. makespace; a nonzero return means that the space could not be created.
  1678.  
  1679. Delregion(p, kill) deletes all the characters between the point and
  1680. the location pointed to by p.  For example, delregion(endcurline, 0)
  1681. deletes all characters between the point and the end of the line.  If
  1682. the integer flag "kill" is nonzero, delregion stores the deleted
  1683. characters in the kill buffer.
  1684.  
  1685. Beyond this, you're on your own!
  1686. EOF
  1687. if test `wc -c < INTERNALS` -ne 1990
  1688. then    echo 'INTERNALS is the wrong size'
  1689. fi
  1690. echo extracting kbind.1
  1691. cat > kbind.1 <<\EOF
  1692. .TH KBIND 1
  1693. .SH NAME
  1694. kbind \- construct key bindings file for
  1695. .IR atty (1)
  1696. .SH SYNOPSYS
  1697. .B kbind
  1698. .I file
  1699. \&...
  1700. .br
  1701. .B kbind -makefuncs
  1702. .SH COPYRIGHT
  1703. .if n Copyright (C) 1989 by Kenneth Almquist.
  1704. .if t Copyright \(co 1989 by Kenneth Almquist.  
  1705. .SH DESCRIPTION
  1706. .I Kbind
  1707. translates the key binding files given as arguments into a form that can
  1708. be read by
  1709. .IR atty (1).
  1710. For each file, the name of the output file is constructed by removing
  1711. the suffix ``.bind'' from the input file name if it is present, and
  1712. then appending the suffix ``.bindc''.
  1713. The
  1714. .B -makefuncs
  1715. option is intended to be used only by the makefile that compiles
  1716. .IR atty (1).
  1717. .sp 2
  1718. .I "Syntax"
  1719. .PP
  1720. An input file consists of a series of commands, one per line.  Blank
  1721. lines and lines beginning with a ``#'' are treated as comments are are
  1722. ignored.  The commands accepted are:
  1723. .sp
  1724. .nr i 6
  1725. .in +\ni
  1726. .ti -\ni
  1727. .B mode
  1728. .I mode-number
  1729. .br
  1730. Specify the mode that subsequent
  1731. .I bind
  1732. commands apply to.  Multiple modes are intended for emulating editors
  1733. like
  1734. .IR vi (1)
  1735. which have separate input and command modes.  Atty starts out in mode
  1736. zero.
  1737. .sp
  1738. .ti -\ni
  1739. .B b
  1740. .I key-sequence
  1741. .I command
  1742. .br
  1743. Bind the specified key sequence to
  1744. .IR command .
  1745. A list of commands will be given later on.
  1746. The key sequence may contain a ``^'' followed by a character to specify
  1747. a control character.  Control characters can also be specifed by a backslash
  1748. followed by up to three octal digits.  Finally, there are several sequences
  1749. consisting of a backslash followed by a letter:
  1750. .ta 1i, 1.6i
  1751. .nf
  1752.       \\b    backspace
  1753.       \\e    escape
  1754.       \\f    form feed
  1755.       \\n    newline
  1756.       \\r    carriage return
  1757.       \\s    space
  1758.       \\t    tab
  1759. .fi
  1760. The
  1761. .I key-sequence
  1762. is terminated by white space, so you must use ``\es'' and ``\et'' to include
  1763. spaces at tabs in the key sequence.  To include a ``^'' or backslash,
  1764. precede it with a backslash.
  1765. .sp
  1766. .ti -\ni
  1767. .B default
  1768. .I command
  1769. .br
  1770. If a key sequence is not explicitly bound to anything, it will normally
  1771. be bound to
  1772. .IR undefined .
  1773. The
  1774. .B default
  1775. command specifies a different default binding for single character key
  1776. sequences.  Multiple character sequences are always bound to
  1777. .IR undefined ,
  1778. independent of the default command.
  1779. (For example, if you bind
  1780. .B ^X^X
  1781. to
  1782. .IR exchange-point-and-mark ,
  1783. then ^X will become a prefix character.
  1784. All the other possible
  1785. character sequences beginning with ^X will then be bound to
  1786. .IR undefined ,
  1787. regardless of any
  1788. .B default
  1789. command,
  1790. unless they are explicitly bound to something else using the
  1791. .B b
  1792. command.)
  1793. .sp
  1794. .tr ~"
  1795. .ti -\ni
  1796. .B syntax
  1797. .I name
  1798. .RI ~ characters ~
  1799. .br
  1800. Defines the characters in the syntax class specified by
  1801. .IR name .
  1802. A minus sign may be used to specify a range of characters, and
  1803. a leading ``^'' may be used to invert the character class.
  1804. Three syntax classes currently exist.
  1805. The syntax class ``word''
  1806. specifies the characters which make up a word for the commands
  1807. that deal with words.
  1808. The syntax class ``filename''
  1809. specifies the characters which make up a filename.
  1810. The syntax class ``abbrev''
  1811. specifies the characters which make up an abbreviation.
  1812. .sp
  1813. .ti -\ni
  1814. .B syntax
  1815. .RI ~ abbreviation ~
  1816. .RI ~ replacement ~
  1817. .br
  1818. Defines an abbreviation.  When you type a character not in the abbreviation
  1819. syntax class, \fIatty\fR looks at the preceding characters which are in the
  1820. abbreviation syntax class (if any).  If these match one of the abbreviations
  1821. defined with the \fIabbrev\fR command, atty replaces the abbreviation with
  1822. the replacement text.
  1823. .in -\ni
  1824. .sp 2
  1825. .I "Editing Commands"
  1826. .de b
  1827. .in +0.6i
  1828. ..
  1829. .de c
  1830. .sp 0.5
  1831. .ti -0.6i
  1832. \\$1 \\$2
  1833. .br
  1834. ..
  1835. .de e
  1836. .sp
  1837. .in -0.6i
  1838. ..
  1839. .PP
  1840. This section lists the commands that can be bound to key sequences,
  1841. along with the key sequences that they are traditionally bound to.
  1842. Commands can be given prefix arguments using the
  1843. .IR universal-argument ,
  1844. .IR digit-argument ,
  1845. and
  1846. .I negative-argument
  1847. commands.
  1848. In the following, the value of the prefix argument is referred to as
  1849. .IR n .
  1850. The value of
  1851. .I n
  1852. is one if no prefix argument is given.
  1853. .sp
  1854. .B "Prefix arguments and modes:"
  1855. .b
  1856. .c digit-argument "(ESC 0, ESC 1, ...)"
  1857. Introduces a sequence of digits specifying the prefix argument to the
  1858. following command.  If this command is bound to a key sequence that ends
  1859. in a digit, that digit forms the first character of the digit sequence.
  1860. .c negative-argument "(ESC -)"
  1861. Introduces a sequence of digits.  The value of the prefix argument to
  1862. the following command will be the negation of the value specified by
  1863. the digits, or -1 if no digits are given.
  1864. .c universal-argument "(^U)"
  1865. Introduces a sequence of digits specifying the prefix argument to the
  1866. following command.  If no digits are given, the prefix argument to the
  1867. following command will be four times the value of the prefix argument
  1868. to the
  1869. .I universal-argument
  1870. command.  (Thus one ^U in front of a command sets the prefix argument to
  1871. 4, two ^U's set the prefix argument to 16, and so on.)
  1872. .c "mode-0"
  1873. Switch to mode zero, so that the bindings defined for mode zero apply.
  1874. Mode zero is the default mode.
  1875. .c "mode-1"
  1876. Switch to mode one, so that the bindings defined for that mode apply.
  1877. .e
  1878. .B "Fundamental commands:"
  1879. .b
  1880. .c self-insert "(all printing characters)"
  1881. .I N
  1882. copies of the last character in the key sequence that this command is
  1883. bound to are inserted.
  1884. .c quoted-insert "(^V, ^Q)"
  1885. .I N
  1886. copies of the following character are inserted in the buffer.
  1887. .c newline "(carriage return)"
  1888. Send the current line to the program, followed by a newline character.
  1889. The history location is set to the end of the history file and the line
  1890. after the history file is cleared.  (See information on history commands
  1891. below.)  The mode is reset to zero.
  1892. .c newline-and-insert "(^J)"
  1893. Send the current line to the program, followed by a newline character.
  1894. Advance to the next line in the history file.  Set the mode to zero.
  1895. .c end-of-file "(^_)"
  1896. Send the characters in the input buffer, without a trailing newline.
  1897. If the input buffer is empty, this has the effect of sending an end of
  1898. file.  Sets the mode to zero.
  1899. .c eof-or-delete-char
  1900. This is equivalent to
  1901. .I end-of-file
  1902. if the input line is empty, and to
  1903. .I delete-char
  1904. otherwise.
  1905. This is commonly bound to ^D.
  1906. .c undefined
  1907. Rings the bell on the terminal.
  1908. Key sequences which are not bound to anything else are bound to
  1909. .IR undefined .
  1910. .e
  1911. .B "Signals:"
  1912. .b
  1913. .c tty-intr "(^?)"
  1914. Send an interrupt signal, like typing the interrupt character under
  1915. .IR tty (4).
  1916. .c tty-quit "(^\e)"
  1917. Send a quit signal, like typing the quit character under
  1918. .IR tty (4).
  1919. .c tty-susp "(^Z)"
  1920. Send a suspend (SIGTSTP) signal, like typing the suspc character under
  1921. .IR tty (4).
  1922. .e
  1923. .B "Motion:"
  1924. These commands move the
  1925. .I point
  1926. (the location of the cursor).
  1927. .b
  1928. .c forward-char "(^F)"
  1929. Move point forward
  1930. .I n
  1931. characters.
  1932. .c backward-char "(^B)"
  1933. Move point backward
  1934. .I n
  1935. characters.
  1936. .c forward-word "(ESC f)"
  1937. Move point forward
  1938. .I n
  1939. words.
  1940. .c backward-word "(ESC b)"
  1941. Move point backward
  1942. .I n
  1943. words.
  1944. .c beginning-of-line "(^A)"
  1945. Move point to the beginning of the line.
  1946. .c end-of-line "(^E)"
  1947. Move point to the end of the line.
  1948. .c set-mark "(^@, ESC SPACE)"
  1949. Set the mark to the value of point.  The mark identifies a location in the
  1950. input line.
  1951. .c exchange-point-and-mark "(ESC x)"
  1952. Goto the location specified by mark, and set the mark to the old value of
  1953. point.
  1954. .e
  1955. .B "Deletions:"
  1956. These commands delete text.  Except as noted, the deleted text is stored
  1957. in the
  1958. .IR "kill buffer" ,
  1959. overwriting anything that was previously in the kill buffer.  (For
  1960. convenience, a command that kills zero characters will not modify the
  1961. kill buffer.)
  1962. .b
  1963. .c delete-char "(^D)"
  1964. Delete the next
  1965. .I n
  1966. characters.  (If
  1967. .I n
  1968. is negative, delete preceding characters.)  The deleted text is saved in
  1969. the kill buffer if a prefix argument is given.
  1970. .c delete-backward-char "(^H)"
  1971. Delete
  1972. .I n
  1973. characters before point (after point if
  1974. .I n
  1975. is negative).  The deleted text is saved in
  1976. the kill buffer if a prefix argument is given.
  1977. .c kill-word "(ESC d)"
  1978. Delete the next
  1979. .I n
  1980. words (preceding words if
  1981. .I n
  1982. is negative).
  1983. .c backward-kill-word "(ESC ^H, ESC h)"
  1984. Delete
  1985. .I n
  1986. words preceding point (following point if
  1987. .I n
  1988. is negative).
  1989. .c kill-line "(^K)"
  1990. Delete from point to the end of the line if
  1991. .I n
  1992. is positive, and from the beginning of the line to point if
  1993. .I n
  1994. is less than or equal to zero.
  1995. .c kill-input "(^X)"
  1996. Delete the entire input line.
  1997. .c kill-region "(^W)"
  1998. Delete the region between the mark and the point.  See the
  1999. .I set-mark
  2000. command above.
  2001. .c copy-region-as-kill "(ESC w)"
  2002. Set the contents of the kill buffer to the text between the point and the
  2003. mark, without deleting the text.
  2004. .e
  2005. .B Insertions:
  2006. See also the
  2007. .I self-insert
  2008. and
  2009. .I quoted-insert
  2010. commands described above,
  2011. and the
  2012. .I get-history-word
  2013. command described below.
  2014. .b
  2015. .c yank "(^Y)"
  2016. Insert the contents of the kill buffer.  The mark is normally set to the
  2017. start of the inserted text and the point to the end of the inserted text.
  2018. If a prefix argument is given, the point and mark are interchanged.
  2019. .c "insert \"text\""
  2020. The specified text is inserted.  The text to be inserted appears inside
  2021. double quotes following the word ``insert'' in the key bindings file.
  2022. The backslash escape sequences which apply to key sequences can be used
  2023. in
  2024. .IR text .
  2025. (The use of ``^'' to specify control characters is not supported,
  2026. though.)  The maximum length of the text is 127 characters.
  2027. .c last-output-line "(ESC ,)"
  2028. Inserts the contents of the last output line when run without a prefix
  2029. argument or with a prefix argument of zero.
  2030. With an argument, it inserts the
  2031. .IR n 'th
  2032. word of the output line.  This command uses the filename syntax rather
  2033. than the word syntax to identify words.  If
  2034. .I n
  2035. is negative, words are counted from the end of the line rather than the
  2036. beginning.
  2037. .e
  2038. .B "Case Conversion:"
  2039. .b
  2040. .c upcase-char "(^C)"
  2041. Convert the next
  2042. .I n
  2043. characters to upper case (if they are lower case letters).
  2044. Convert characters preceding point rather than following point if
  2045. .I n
  2046. is negative.
  2047. .c upcase-word "(ESC u)"
  2048. Convert the next
  2049. .I n
  2050. words (preceding words if
  2051. .I n
  2052. is negative) to upper case.
  2053. .c upcase-region
  2054. Convert all the characters in the region to upper case.
  2055. .e
  2056. .B Transpositions:
  2057. .b
  2058. .c gosling-transpose-chars "(^T)"
  2059. If no argument is given, transpose the two characters preceding the point.
  2060. If an argument is given, delete the character preceding point, and reinsert it
  2061. .I n
  2062. characters to the right.
  2063. .c transpose-chars
  2064. Like
  2065. .IR gosling-transpose-chars ,
  2066. except that it does the delete and reinsert even if a prefix argument is
  2067. not given, unless point is at the right end of the line.
  2068. .c transpose-words "(ESC t)"
  2069. If point is at the right end of the line, transpose the two words before
  2070. point and leave point unchanged.  Otherwise, transpose the word before point
  2071. and the word after point, and leave point after the second word.
  2072. The current implementation of this command ignores any prefix argument.
  2073. .e
  2074. .B History:
  2075. The history mechanism remembers lines which have been previously typed in
  2076. and allows you to access them.  Moving around the history file loads
  2077. lines from the history file into the current line buffer.  You can then
  2078. edit the line (which only modifies the copy of the line, and leaves
  2079. the contents of the history file unchanged).  There is also a line
  2080. beyond the end of the history file, which is where you normally are.
  2081. You can enter some text on this line, move back in the history file,
  2082. and then return to the line after the history file.  The
  2083. .I newline
  2084. command goes to the line after the history file.  It clears this line
  2085. if you were already on it.  The
  2086. .I newline-and-insert
  2087. command goes to the following line in the history file.  If you were
  2088. on the line after the history file, the
  2089. .I newline-and-insert
  2090. command leaves you there, without clearing it.  The commands for
  2091. accessing the history file are:
  2092. .b
  2093. .c previous-history "(^P)"
  2094. Go back
  2095. .I n
  2096. nonblank lines in the history file, skipping blank lines.
  2097. .c next-history "(^N)"
  2098. Go forward
  2099. .I n
  2100. nonblank lines in the history file.
  2101. .c re-search-backward "(^R)"
  2102. You are prompted for a regular expression, which you can enter using
  2103. normal editing commands.  The
  2104. .IR newline ,
  2105. .IR end-of-file ,
  2106. and
  2107. .I tty-intr
  2108. commands are redefined while you are entering the regular expression;
  2109. the first terminates the regular expression, and the latter two
  2110. get out of the
  2111. .I re-search-backward
  2112. command without doing a search.
  2113. After the regular expression is entered, the command goes to the
  2114. .IR n 'th
  2115. preceding line of the history file which the regular expression matches.
  2116. Regular expressions are as in GNU emacs.
  2117. With a negative argument, the command searches forward rather than backward.
  2118. .c re-search-forward "(^S)"
  2119. Like
  2120. .IR re-search-backward ,
  2121. but searches in the other direction.
  2122. (Although this command is bound to ^S by default, setting the stop character
  2123. to ^S overrides this binding.)
  2124. .c get-history-word "(ESC .)"
  2125. Insert the
  2126. IR n 'th
  2127. word of the last line of the history file.  This command uses filename
  2128. syntax rather than the word syntax to identify words.
  2129. If
  2130. .I n
  2131. is negative, count words from the end of the line rather than the beginning.
  2132. If
  2133. .I n
  2134. is zero, insert the entire line.  If
  2135. .I n
  2136. is omitted, insert the last word.
  2137. .e
  2138. .B "Output Lines:"
  2139. .b
  2140. .c last-output-line "(ESC ,)"
  2141. Insert the last line of output.  With a positive argument, insert the
  2142. .IR n 'th
  2143. word of the line.
  2144. If
  2145. .I n
  2146. is negative, count words from the end of the line rather than the beginning.
  2147. .e
  2148. .B "File Name Matching:"
  2149. .b
  2150. .c list-file-completions "(ESC =)"
  2151. Generate a
  2152. .B "fmatch -l"
  2153. command, with the current word (as determined by the filename syntax)
  2154. as the argument.  Assuming that the user is at a shell
  2155. prompt, this will produce a list of files which have the current word as
  2156. a prefix.
  2157. .c file-complete "(ESC ESC)"
  2158. Like
  2159. .IR list-file-completions ,
  2160. but generates a
  2161. .B "fmatch -c"
  2162. command rather than a
  2163. .B "fmatch -l"
  2164. command.
  2165. This will cause the remainder of the current file name to be inserted in
  2166. the input buffer if the file name is unique.  (There should be a cleaner
  2167. way to do file name completion.  Unfortunately,
  2168. .I atty
  2169. cannot easily do file name completion itself because it doesn't know the
  2170. current directory of the process reading the commands.)
  2171. .e
  2172. .SH AUTHOR
  2173. Kenneth Almquist
  2174. .SH BUGS
  2175. When new editor commands are added to
  2176. .IR atty (1),
  2177. the ``.bind'' files may have to be recompiled.
  2178. EOF
  2179. if test `wc -c < kbind.1` -ne 14303
  2180. then    echo 'kbind.1 is the wrong size'
  2181. fi
  2182. echo Archive 2 unpacked
  2183. exit
  2184.  
  2185.