home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / Tools / ile-2.01 / ile.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-06-30  |  60.8 KB  |  3,251 lines

  1. /*
  2.                           COPYRIGHT 1988
  3.               Evans & Sutherland Computer Corporation
  4.                         Salt Lake City, Utah
  5.                         All Rights Reserved.
  6.  
  7.      THE INFORMATION  IN  THIS  SOFTWARE  IS  SUBJECT  TO  CHANGE
  8.      WITHOUT  NOTICE  AND SHOULD NOT BE CONSTRUED AS A COMMITMENT
  9.      BY  EVANS  &  SUTHERLAND.   EVANS  &  SUTHERLAND   MAKES  NO
  10.      REPRESENTATIONS  ABOUT  THE SUITABILITY OF THIS SOFTWARE FOR
  11.      ANY PURPOSE.  IT IS SUPPLIED  "AS  IS"  WITHOUT  EXPRESS  OR
  12.      IMPLIED WARRANTY.
  13.  
  14.      IF THE SOFTWARE IS MODIFIED IN A MANNER CREATING  DERIVATIVE
  15.      COPYRIGHT  RIGHTS,  APPROPRIATE LEGENDS MAY BE PLACED ON THE
  16.      DERIVATIVE WORK IN ADDITION TO THAT SET FORTH ABOVE.
  17.  
  18.      Permission  to  use,  copy,  modify,  and  distribute   this
  19.      software  and  its documentation for any purpose and without
  20.      fee is hereby granted, provided  that  the  above  copyright
  21.      notice  appear  in  all  copies  and that both the copyright
  22.      notice and this permission notice appear in supporting docu-
  23.      mentation,  and  that  the name of Evans & Sutherland not be
  24.      used in advertising or publicity pertaining to  distribution
  25.      of the software without specific, written prior permission.
  26.  
  27. Written by:
  28.  
  29.                         Robert C. Pendleton <bobp@hal.com>
  30.  
  31. Grateful acknowledgement is made of code and ideas contributed by
  32.  
  33.      Ian Donaldson,
  34.      Department of Communications & Electronic Engineering,
  35.      Royal Melbourne Institute of Technology,
  36.      Melbourne, Australia.
  37.  
  38. $Header: ile.c,v 2.3 88/11/11 10:31:56 bpendlet Exp $
  39. */
  40.  
  41. /*
  42. ile is compiled using:
  43.  
  44. cc ile.c -o ile -ltermcap
  45. */
  46.  
  47. #include <stdio.h>
  48. #include <fcntl.h>
  49. #include <signal.h>
  50. #include <string.h>
  51. #include <strings.h>
  52. #include <pwd.h>
  53. #include <utmp.h>
  54. #include <errno.h>
  55. #include <sys/ioctl.h>
  56. #include <sys/types.h>
  57. #include <sys/file.h>
  58. #include <sys/time.h>
  59.  
  60.  
  61. #ifdef INCLUDE_CURSES
  62. #include <curses.h>
  63. #include <term.h>
  64. #endif
  65.  
  66. #ifdef USE_DIR
  67. #include <sys/dir.h>
  68. #define dirent direct
  69. #endif
  70.  
  71. #ifdef USE_DIRENT
  72. #include <dirent.h>
  73. #endif
  74.  
  75. #include <unistd.h>
  76. #include <stdlib.h>
  77.  
  78. #ifndef MAXNAMLEN
  79. #define MAXNAMLEN   255
  80. #endif
  81.  
  82. #include "ile.h"
  83.  
  84. /* the version below is a guess since the only info was a rev.2 where
  85.  * this is printed - I've revved iit to 2.01 supposedly for the
  86.  * SCO port
  87.  */
  88. #define VERSION_STR "2.01"  
  89.  
  90. /*------------------------------------------------------------------*/
  91. /*
  92. Definitions of system stuff.
  93. */
  94.  
  95. extern int errno;
  96.  
  97. long lseek();
  98. time_t time();
  99.  
  100. /*------------------------------------------------------------------*/
  101.  
  102. #define READ  0
  103. #define WRITE 1
  104. #define ERROR 2
  105.  
  106. #define BUFFER_SIZE 255
  107.  
  108. #define HISTORY_SIZE 101
  109.  
  110. #define USER_NAME_SIZE 8
  111.  
  112. #define EOL (-2)
  113. /*------------------------------------------------------------------*/
  114.  
  115. /* special characters used by ile */
  116.  
  117. #define del '\177'
  118.  
  119. #define CA '\1'
  120. #define CB '\2'
  121. #define CC '\3'
  122. #define CD '\4'
  123. #define CE '\5'
  124. #define CF '\6'
  125. #define bel '\7'
  126. #define bs '\10'
  127. #define CI '\11'
  128. #define nl '\12'
  129. #define CK '\13'
  130. #define CL '\14'
  131. #define cr '\15'
  132. #define CN '\16'
  133. #define CO '\17'
  134. #define CP '\20'
  135. #define CQ '\21'
  136. #define CR '\22'
  137. #define CS '\23'
  138. #define CT '\24'
  139. #define CU '\25'
  140. #define CV '\26'
  141. #define CW '\27'
  142. #define CX '\30'
  143. #define CY '\31'
  144. #define CZ '\32'
  145.  
  146. #define esc '\33'
  147.  
  148. /*------------------------------------------------------------------*/
  149. /* areas and varaibles used to get termcap information */
  150.  
  151. char *getenv();
  152.  
  153. #ifndef INCLUDE_CURSES
  154. char *tgetnum();
  155. char *tgetflag();
  156. char *tgetstr();
  157. #endif
  158.  
  159. char termcap_entry[1024];    /* termcap entry for the users terminal */
  160. char term_seqs[1024];        /* area to store control sequences in */
  161. char *where = term_seqs;
  162.  
  163. char *cle;            /* move cursor left one space */
  164.  
  165. char *cce;            /* clear to end of line */
  166.  
  167. char *cbl;            /* audible bell */
  168.  
  169. char *cnl;            /* new line character */
  170.  
  171. char *ccr;            /* carriage return */
  172.  
  173. /*------------------------------------------------------------------*/
  174. /*
  175. The value of HOME
  176. */
  177.  
  178. char *homedir = NULL;
  179.  
  180. /*------------------------------------------------------------------*/
  181. /*
  182. The current working directory as set by query_path.
  183. initialized to PWD
  184. */
  185.  
  186. char currentdir[MAXNAMLEN + 1] = "";
  187.  
  188. /*------------------------------------------------------------------*/
  189.  
  190. /* tty status flags */
  191. /*
  192.   The original tty status flags are stored so that they can be
  193.   restored when ile exits.
  194. */
  195.  
  196. int windowchanged;
  197.  
  198. /*------------------------------------------------------------------*/
  199.  
  200. /* file descriptors for tty and pty */
  201.  
  202. int master_pty;
  203. int slave_tty;
  204.  
  205. /* the names of the tty and pty opened by getpty */
  206.  
  207. char ttydev[20];
  208. char ptydev[20]; 
  209.  
  210. /* defaulkt ptty/tty name patterns */
  211. #ifndef PTYDEV
  212. #define PTYDEV          "/dev/ptyxx"
  213. #endif
  214.  
  215. #ifndef TTYDEV
  216. #define TTYDEV          "/dev/ttyxx"
  217. #endif 
  218.  
  219. /* path and name of the lock file */
  220.  
  221. char lock[] = "/tmp/ile.lock";
  222.  
  223. /*------------------------------------------------------------------*/
  224. /*
  225.   getpty opens a pty, storing file descriptors in pty and tty.
  226.   It trys pairs in order until it finds a pair that is not in use.
  227. */
  228.  
  229. #ifdef HAVE_MERGED_PTY
  230.  
  231. #define SCO_PTY         "/dev/ptyp"
  232. #define SCO_TTY         "/dev/ttyp"
  233. #define MAX_SCO_PTYS    256
  234.  
  235. #endif 
  236.  
  237. getpty(pty, tty)
  238.     int *pty;
  239.     int *tty;
  240.  
  241. {
  242.     int devindex ;
  243.     int letter;
  244.  
  245.     static char ptychar1[] = "pqrstuvwxyz";
  246.     static char ptychar2[] = "0123456789abcdef";
  247.  
  248.  
  249. #ifdef HAVE_MERGED_PTY
  250.                         /* this may also be SYSV i386 specific - untested */
  251.     struct utmp line;
  252.     static int sco_devindex = 0;
  253.     char sco_ttydev[20];
  254.     char sco_ptydev[20];
  255.  
  256.     /*
  257.      * First check to for sco merged ptys.
  258.      */
  259.     if (1)  /* mergedPtys */
  260.     {
  261.         for (; sco_devindex < MAX_SCO_PTYS; sco_devindex++) 
  262.         {
  263.             sprintf (sco_ttydev, "%s%d", SCO_TTY, sco_devindex);
  264.             sprintf (sco_ptydev, "%s%d", SCO_PTY, sco_devindex);
  265.  
  266.             if (access(sco_ttydev, F_OK) < 0)
  267.             {
  268.  
  269.             sco_devindex = MAX_SCO_PTYS;
  270.                 break;
  271.             }
  272.     
  273.                 strcpy(line.ut_line,strrchr(sco_ptydev,'/')+1);
  274.                 setutent();
  275.  
  276.                 if ((getutline(&line) == NULL) &&
  277.                 (*pty = open (sco_ptydev, O_RDWR)) >= 0) 
  278.             {
  279.                 /* 
  280.              * Set things up for our next entry
  281.              * into this function!
  282.              */
  283.             if ((*tty = open(sco_ttydev, O_RDWR)) < 0)
  284.             {
  285.                 (void) close(*pty);
  286.                 continue;
  287.             }
  288.             (void) sco_devindex++;
  289.             strcpy(ttydev, sco_ttydev);
  290.             strcpy(ptydev, sco_ptydev);
  291. /*
  292. printf("ptydev=[%s] fd=%d\n", ptydev, *pty);
  293. printf("ttydev=[%s] fd=%d\n", ttydev, *tty );
  294. */
  295.                     endutent();
  296.                 return ;
  297.             }
  298.     }
  299.     }
  300. #endif  /* HAVE_MERGED_PTY */
  301.  
  302.     strcpy(ptydev, PTYDEV);   /* Default format names - non SCO */
  303.     strcpy(ttydev, TTYDEV);
  304.  
  305.     letter = 0;
  306.     while (letter < 11)
  307.     {
  308.     ttydev[strlen(ttydev) - 2] = ptychar1[letter];
  309.     ptydev[strlen(ptydev) - 2] = ptychar1[letter];
  310.     letter++;
  311.  
  312.     devindex = 0;
  313.     while (devindex < 16)
  314.     {
  315.         ttydev[strlen(ttydev) - 1] = ptychar2[devindex];
  316.         ptydev[strlen(ptydev) - 1] = ptychar2[devindex];
  317.         devindex++;
  318.  
  319.         if ((*pty = open(ptydev, O_RDWR)) >= 0)
  320.         {
  321.         if ((*tty = open(ttydev, O_RDWR)) >= 0)
  322.         {
  323.             return;
  324.         }
  325.         else
  326.         {
  327.             (void) close(*pty);
  328.         }
  329.         }
  330.     }
  331.     }
  332.  
  333.     (void) fprintf(stderr, "ile: unable to allocate pty/tty pair\n");
  334.     exit(1);
  335.     /* NOTREACHED */
  336. }
  337.  
  338. /*------------------------------------------------------------------*/
  339. /*
  340. Termcap entries may have a sequences of digits optionally followed
  341. by a '*' in front of the actual sequence. This routine increments
  342. the pointer past this information.
  343.  
  344. Terminfo entries have %sequences which this rtn strips
  345. */
  346. void
  347. strip(ptr)
  348.     char **ptr;
  349. {
  350. #ifdef OLD
  351.  
  352.     while (('0' <= **ptr) && (**ptr <= '9'))
  353.     {
  354.     (*ptr)++;
  355.     }
  356.  
  357.     if (**ptr == '*')
  358.     {
  359.     (*ptr)++;
  360.     }
  361. #else
  362.  
  363. char *p = *ptr;
  364. while (*p)
  365. {
  366.     if (*p == '%' )
  367.     {
  368.         char *p1 = p+1;
  369.         char *d = p;
  370.         if (*p1 == 'p' )
  371.             p1 += 2;
  372.         else
  373.             p1++;    
  374.         while (*p1)
  375.         {
  376.             *d++ = *p1++;
  377.         }
  378.         *d = '\0';
  379.     }
  380.     p++;
  381. }
  382.  
  383. #endif
  384. }
  385. /*------------------------------------------------------------------*/
  386. /*
  387. Set up everything needed to use the control sequences from the
  388. termcap entry for the terminal.
  389. */
  390. void
  391. get_termcap()
  392. {
  393.     char *terminal_type;    /* type of terminal */
  394.     
  395.     /* get the terminal name */
  396.  
  397.     terminal_type = getenv("TERM");
  398.  
  399.     /* get termcap entry */
  400.  
  401.     if (tgetent(termcap_entry, terminal_type) < 1)
  402.     {
  403.     (void) fprintf(stderr, "ile: can't find %s\n", terminal_type);
  404.     exit(1);
  405.     /* NOTREACHED */
  406.     }
  407.  
  408.     /* get the control sequences ile needs */
  409.  
  410.     if ((cbl = tgetstr("bl", &where)) == NULL)
  411.     {
  412.     cbl = "\7";
  413.     }
  414.  
  415.     if ((cnl = tgetstr("nl", &where)) == NULL)
  416.     {
  417.     cnl = "\n";
  418.     }
  419.  
  420.     if ((ccr = tgetstr("cr", &where)) == NULL)
  421.     {
  422.     ccr = "\r";
  423.     }
  424.  
  425.   /* cursor left */
  426.     cle = tgetstr("le", &where);
  427.     if ( cle  == NULL)
  428.         cle = tgetstr("LE", &where);
  429.     if ( cle == NULL )
  430.         cle = tgetstr("cub", &where);
  431.     if ( cle == NULL)
  432.         cle = tgetstr("cub1", &where);
  433.     if ( cle == NULL && tgetflag("bs") )
  434.         cle = "\b";
  435.  
  436.  
  437.     /* clr to EOL */
  438.     cce = tgetstr("ce", &where);
  439.     if (cce  == NULL )
  440.         cce = tgetstr("el", &where);
  441.  
  442.     if (cle == NULL || cce == NULL)
  443.     {
  444.     (void) fprintf(stderr,
  445.         "ile: can't run on %s\n(need CsrLeft and ClrEOL capabilities \"le\" and \"ce\" or  \"cub1\" and \"el\")\n",
  446.         terminal_type);
  447.     exit(1);
  448.     /* NOTREACHED */
  449.     }
  450.  
  451.     /* strip timing info from strings */
  452.  
  453.     strip(&cle);
  454.     strip(&cce);
  455.     strip(&cbl);
  456.     strip(&cnl);
  457.     strip(&ccr);
  458. }
  459.  
  460. /*------------------------------------------------------------------*/
  461. /*
  462.  * set/clear the utmp slot for the pty
  463.  */
  464. int
  465. setutmp(fd, set)
  466. {
  467. #ifdef HAVE_GETUT   /* getutline */
  468.  
  469.     struct utmp ut;
  470.     char *cp;
  471.  
  472.  
  473.     setutent();
  474.     d_zero((char *) &ut, sizeof(ut));
  475.  
  476.     /* skip "/dev/" */
  477.  
  478.     cp = strrchr(ttydev, '/');
  479.     if (cp == 0)
  480.     {
  481.         cp = ttydev;
  482.     }
  483.     else
  484.     {
  485.         cp++;
  486.     }
  487.     (void) strncpy(ut.ut_line, cp, sizeof(ut.ut_line));
  488.  
  489.  
  490.     if (set)
  491.     {
  492.         struct passwd *pw;
  493.  
  494.     pw = getpwuid(getuid());
  495.     if (pw == 0)
  496.     {
  497.         (void) fprintf(stderr, "ile: who are you?\n");
  498.         return (-1);
  499.     }
  500.  
  501.     (void) strncpy(ut.ut_user, pw->pw_name, sizeof(ut.ut_line));
  502.         ut.ut_type = USER_PROCESS;
  503.         ut.ut_pid = getpid();
  504.         (void) time(&ut.ut_time);
  505.     }
  506.     else    /* clear entry */
  507.     {
  508.         if (getutline(&ut) == NULL )
  509.         {
  510.            /* perror("ile - getutline cannot position"); */
  511.             return -1;
  512.         }
  513.         ut.ut_user[0] = '\0';
  514.         ut.ut_type   = DEAD_PROCESS;
  515.     }
  516.  
  517.  
  518.     if (pututline(&ut) == NULL )
  519.     {
  520.         /* perror("ile - pututline cannot set"); */
  521.         return -1;
  522.     }
  523.     return 0;
  524. #endif    
  525. #ifdef HAVE_TTYSLOT_UT
  526.  
  527.     int old0;
  528.     int old1;
  529.     int old2;
  530.     int slot;
  531.     int f;
  532.     struct utmp ut;
  533.  
  534.  
  535.     /* Must make fd's 0,1,2 correspond to slave_tty for ttyslot() to
  536.      * function.  Ugh!  Why doesn't ttyslot() accept a fd argument?
  537.      * 
  538.      * save fd's */
  539.  
  540.     old0 = dup(0);
  541.     old1 = dup(1);
  542.     old2 = dup(2);
  543.  
  544.     if (old0 == -1 || old1 == -1 || old2 == -1)
  545.     {
  546.     perror("ile: dup");
  547.     return (-1);        /* file table full ? */
  548.     }
  549.  
  550.     /* set fd's 0,1,2 for ttyslot() */
  551.  
  552.     (void) dup2(fd, 0);
  553.     (void) dup2(fd, 1);
  554.     (void) dup2(fd, 2);
  555.  
  556.     slot = ttyslot();
  557.  
  558.     /* put the fd's back */
  559.  
  560.     (void) dup2(old0, 0);
  561.     (void) dup2(old1, 1);
  562.     (void) dup2(old2, 2);
  563.  
  564.     (void) close(old0);
  565.     (void) close(old1);
  566.     (void) close(old2);
  567.  
  568.     if (slot < 0)
  569.     {
  570.     (void) fprintf(stderr, "ile: don't know where you are\n");
  571.     return (-1);
  572.     }
  573.  
  574.     f = open("/etc/utmp", O_WRONLY);
  575.     if (f == -1)
  576.     {
  577.     return (-1);
  578.     }
  579.  
  580.     d_zero((char *) &ut, sizeof(ut));
  581.  
  582.     if (set)
  583.     {
  584.     struct passwd *pw;
  585.     char *cp;
  586.  
  587.     pw = getpwuid(getuid());
  588.     if (pw == 0)
  589.     {
  590.         (void) fprintf(stderr, "ile: who are you?\n");
  591.         (void) close(f);
  592.         return (-1);
  593.     }
  594.  
  595.     /* skip "/dev/" */
  596.  
  597.     cp = strrchr(ttydev, '/');
  598.     if (cp == 0)
  599.     {
  600.         cp = ttydev;
  601.     }
  602.     else
  603.     {
  604.         cp++;
  605.     }
  606.  
  607.     (void) strncpy(ut.ut_line, cp, sizeof(ut.ut_line));
  608.     (void) strncpy(ut.ut_name, pw->pw_name, sizeof(ut.ut_line));
  609.     (void) time(&ut.ut_time);
  610.     }
  611.     (void) lseek(f, (long) (slot * sizeof(struct utmp)), SEEK_SET);
  612.     (void) write(f, (char *) &ut, sizeof(ut));
  613.     (void) close(f);
  614.     return (0);
  615.  
  616. #endif
  617. }
  618. /*------------------------------------------------------------------*/
  619. /*
  620. clean up and leave.
  621.  
  622. This function is bound to the SIGCHLD signal so that when the
  623. child process exits, so does ile. It is also called when an exception
  624. is detected by select() in ile().
  625. */
  626. void
  627. clean_up()
  628. {
  629.  
  630.     /* kill off the child process */
  631.  
  632.     tty_killpg(slave_tty);
  633.  
  634.     /* restore terminal status */
  635.     if ( tty_reset(READ) < 0 )
  636.     {
  637.         fprintf(stderr, "tty_reset() failed! Going sane....\n");
  638.             (void) tty_sane(slave_tty);
  639.     }
  640.  
  641.     /* "logout" the user */
  642.  
  643.     (void) setutmp(slave_tty, FALSE);
  644.  
  645.     /* clean up the tty/pty pair */
  646.  
  647.     (void) close(master_pty);
  648.     (void) close(slave_tty);
  649.  
  650.     /* make things look nice */
  651.  
  652.     fputs(cnl, stdout);
  653.  
  654.     exit(0);
  655.     /* NOTREACHED */
  656. }
  657. /*------------------------------------------------------------------*/
  658. /*
  659. Write a line to the slave_tty.
  660. Get the slave_tty parameters, turn off echo, send the line to the
  661. slave_tty, restore the slave_tty paramters to the way they were
  662. before. If echo was already off, this will have no effect.
  663. */
  664. write_line(line, length)
  665.     char *line;
  666.     int length;
  667. {
  668.     
  669.     /* get the current parameters */
  670.  
  671.     tty_noecho( slave_tty, PUSH );
  672.  
  673.     (void) write(master_pty, line, length);
  674.  
  675.      tty_noecho( slave_tty, POP );
  676.  
  677.  
  678. }
  679.  
  680. /*------------------------------------------------------------------*/
  681. /*
  682. The editing routines are called through the edit variable. This allows
  683. the quote and escape commands to be implemented as a straight forward
  684. state machine instead of requiring state flags and complex switch
  685. statements.
  686. */
  687. /*------------------------------------------------------------------*/
  688.  
  689. /* line edit buffer */
  690.  
  691. static char line[BUFFER_SIZE];
  692.  
  693. static int point;        /* insertion point */
  694. static int length;        /* total chars in buffer */
  695.  
  696.  
  697. /* procedure to edit next character */
  698.  
  699. void (*edit) ();
  700.  
  701. /* history buffer */
  702.  
  703. struct
  704. {
  705.     int length;
  706.     char *line;
  707. } hist[HISTORY_SIZE];
  708.  
  709. int head;            /* insertion point */
  710. int here;            /* current displayed line */
  711.  
  712. /*------------------------------------------------------------------*/
  713. /*
  714. The delimiter vector is used by the forward, backward, and delete
  715. word operations to decide that a character is a delimiter.
  716. */
  717. /*------------------------------------------------------------------*/
  718.  
  719. #define CHAR_SET_SIZE 127
  720. #define CHAR_MASK 0177
  721.  
  722. char delimit[CHAR_SET_SIZE];
  723.  
  724. /*------------------------------------------------------------------*/
  725. /*
  726. The action_table is used to bind sequences of keys to operations or strings.
  727. */
  728. /*------------------------------------------------------------------*/
  729.  
  730. typedef enum
  731. {
  732.     is_action, is_string
  733. } action_type;
  734.  
  735. struct
  736. {
  737.     action_type flag;
  738.     union
  739.     {
  740.     void (*action) ();
  741.     char *string;
  742.     } aors;
  743. } action_table[4][CHAR_SET_SIZE];
  744.  
  745. /*------------------------------------------------------------------*/
  746.  
  747. void echo();
  748. void echoline();
  749. void cleartoend();
  750. void clearline();
  751. void backspace();
  752. void quote_edit();
  753. void edit_0();
  754. void edit_1();
  755. void edit_2();
  756. void edit_3();
  757. void bell();
  758. void insert();
  759.  
  760. /*------------------------------------------------------------------*/
  761. /*
  762. The following routines are action routines that are executed by the
  763. editor to carry out commands. Each routine has a single character
  764. argument. Each routine is invoked with the character that caused it
  765. to be invoked as its argument.
  766.  
  767. The argument isn't always useful, but it is included to provide a
  768. consistent interface for the routines.
  769. */
  770. /*------------------------------------------------------------------*/
  771. /*
  772. Given a specific directory and the starting string of a file name,
  773. find the longest partial file name that starts with the substring.
  774. */
  775. void
  776. complete_file_name(dir, name)
  777.     char *dir;
  778.     char *name;
  779. {
  780.     DIR *dirp;
  781.     struct dirent *dp;
  782.  
  783.     int len;
  784.     int maxlen;
  785.     int oldlen;
  786.  
  787.     char oldname[MAXNAMLEN + 1];
  788.     char newname[MAXNAMLEN + 1];
  789.  
  790.     if ((dir != NULL) &&
  791.     (name != NULL) &&
  792.     ((oldlen = strlen(name)) > 0) &&
  793.     ((dirp = opendir(dir)) != NULL))
  794.     {
  795.     maxlen = oldlen;
  796.     (void) strcpy(oldname, name);
  797.     (void) strcpy(newname, name);
  798.  
  799.     /* find the longest name starting with name */
  800.  
  801.     for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp))
  802.     {
  803.         if (dp->d_name != NULL)
  804.         {
  805.         len = strlen(dp->d_name);
  806.         if ((maxlen < len) &&
  807.             (strncmp(oldname, dp->d_name, oldlen) == 0))
  808.         {
  809.             maxlen = len;
  810.             (void) strcpy(newname, dp->d_name);
  811.         }
  812.         }
  813.     }
  814.  
  815.     rewinddir(dirp);
  816.  
  817.     /* find the longest common sub string */
  818.  
  819.     for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp))
  820.     {
  821.         if (dp->d_name != NULL)
  822.         {
  823.         len = strlen(dp->d_name);
  824.         if ((len <= maxlen) &&
  825.             (strncmp(oldname, dp->d_name, oldlen) == 0))
  826.         {
  827.             for (;
  828.             (oldlen < len) &&
  829.             (strncmp(newname, dp->d_name, len) != 0);
  830.             len--);
  831.  
  832.             maxlen = len;
  833.             newname[maxlen] = '\0';
  834.         }
  835.         }
  836.     }
  837.  
  838.     if (strlen(name) != strlen(newname))
  839.     {
  840.         /* return the extended name */
  841.  
  842.         (void) strcpy(name, newname);
  843.     }
  844.     else
  845.     {
  846.         /* no difference so beep */
  847.  
  848.         bell('\0');
  849.     }
  850.  
  851.     (void) closedir(dirp);
  852.     }
  853. }
  854. /*------------------------------------------------------------------*/
  855. /*
  856. Hidden parameters to dirselect. They must be hidden because dirselect
  857. is passed as an argument to scandir.
  858. */
  859.  
  860. static char *namep;
  861. static int namelen;
  862.  
  863. /*------------------------------------------------------------------*/
  864. /*
  865. Passed to scandir. It is used to decide which files to display.
  866. */
  867.  
  868. static
  869. dirselect(dp)
  870.     struct dirent *dp;
  871. {
  872.     return (strncmp(dp->d_name, namep, namelen) == 0);
  873. }
  874.  
  875. /*------------------------------------------------------------------*/
  876. /*
  877. List all the files in a given directory that start with the string
  878. passed as name.
  879. */
  880. void
  881. list_file_names(dir, name)
  882.     char *dir;
  883.     char *name;
  884. {
  885. #if 0
  886.     struct direct **dlist;
  887.     int i;
  888.     int nfiles;
  889.     int colwidth;
  890.     int cols;
  891.     int ncols;
  892.     int nlines;
  893.     int alphasort();
  894.  
  895.     if (dir == NULL || name == NULL)
  896.     {
  897.     return;
  898.     }
  899.  
  900.     cols = tty_wincols();
  901.  
  902.     namelen = strlen(name);
  903.     namep = name;
  904.  
  905.     nfiles = scandir(dir, &dlist, dirselect, alphasort);
  906.  
  907.     /* determine the longest file name length */
  908.  
  909.     colwidth = 8;        /* minimum width */
  910.     for (i = 0; i < nfiles; i++)
  911.     {
  912.     struct dirent *dp;
  913.  
  914.     dp = dlist[i];
  915.  
  916.     if (dp->d_namlen > colwidth)
  917.     {
  918.         colwidth = dp->d_namlen;
  919.     }
  920.     }
  921.  
  922.     colwidth++;            /* at least 1 space between them */
  923.  
  924.     /* print the names, sorted vertically per column */
  925.     ncols = cols / colwidth;
  926.     if (ncols == 0)
  927.     {
  928.     /* longest filename is wider than the screen */
  929.     ncols = 1;
  930.     }
  931.  
  932.     nlines = (nfiles + ncols - 1) / ncols;
  933.  
  934.     if (nfiles > 0)
  935.     {
  936.     for (i = 0; i < nlines; i++)
  937.     {
  938.         int j;
  939.         int l;
  940.  
  941.         l = 0;
  942.         for (j = 0; j < ncols; j++)
  943.         {
  944.         int m;
  945.         struct dirent *dp;
  946.  
  947.         m = l + i;
  948.         if (m >= nfiles)
  949.         {
  950.             break;
  951.         }
  952.  
  953.         dp = dlist[m];
  954.         fputs(dp->d_name, stdout);
  955.  
  956.         if (j < (ncols - 1))
  957.         {
  958.             int k;
  959.  
  960.             for (k = dp->d_namlen; k < colwidth; k++)
  961.             {
  962.             (void) fputc(' ', stdout);
  963.             }
  964.             l += nlines;
  965.         }
  966.         }
  967.         fputs(ccr, stdout);
  968.         fputs(cnl, stdout);
  969.     }
  970.     }
  971.     free((char *) dlist);
  972. #else
  973.     printf("Sorted dir list fnality not yet implemented\n");
  974. #endif
  975. }
  976. /*------------------------------------------------------------------*/
  977. /*
  978. Assuming that there is a file name under the cursor, return a path
  979. and a file name. If there is no path name return "."
  980. */
  981. int
  982. get_dir_and_name(dir, name, username, userend, start, middle, tail)
  983.     char *dir;
  984.     char *name;
  985.     char *username;
  986.     int *userend;
  987.     int *start;
  988.     int *middle;
  989.     int *tail;
  990. {
  991.     int dirlen;
  992.  
  993.     int newstart;
  994.  
  995.     int punlen;
  996.     char pun[USER_NAME_SIZE + 1];
  997.     struct passwd *userpwd;
  998.  
  999.     int i;
  1000.  
  1001.     /* set the default path and file name */
  1002.  
  1003.     dir[0] = '\0';
  1004.     name[0] = '\0';
  1005.     username[0] = '\0';
  1006.  
  1007.     /* search for the start of the file name */
  1008.     /* start will be left pointing to the first character of the path  */
  1009.  
  1010.     for ((*start) = point;
  1011.     ((0 < (*start)) && (line[(*start) - 1] != ' '));
  1012.     (*start)--
  1013.     );
  1014.  
  1015.     /* search for the end of the file name */
  1016.     /* tail will be left pointing at the last character of the path */
  1017.  
  1018.     for ((*tail) = point - 1;
  1019.     (((*tail) < (length - 1)) && (line[(*tail) + 1] != ' '));
  1020.     (*tail)++
  1021.     );
  1022.  
  1023.     /* search for the middle of the file name */
  1024.     /* middle will be left pointing at the first character of the last
  1025.      * element of the path */
  1026.  
  1027.     for ((*middle) = (*tail) + 1;
  1028.     ((0 < (*middle)) &&
  1029.         (line[(*middle) - 1] != '/') &&
  1030.         (line[(*middle) - 1] != ' '));
  1031.     (*middle)--
  1032.     );
  1033.  
  1034.     /* copy path from line to dir */
  1035.  
  1036.     /* what base path */
  1037.  
  1038.     newstart = (*start);
  1039.  
  1040.     if ((line[newstart] == '~') &&
  1041.     ((newstart + 1) < length) &&
  1042.     (line[newstart + 1] == '/'))
  1043.     {
  1044.     /* "~/" means use the value of HOME */
  1045.  
  1046.     newstart++;
  1047.     (void) strcpy(dir, homedir);
  1048.     }
  1049.     else if (line[newstart] == '~')
  1050.     {
  1051.     /* "~username" means use the users login directory */
  1052.  
  1053.     /* search for the end of the user name */
  1054.  
  1055.     for ((*userend) = newstart,
  1056.         punlen = 0;
  1057.         (((*userend) < (length - 1)) &&
  1058.         (line[(*userend) + 1] != ' ') &&
  1059.         (line[(*userend) + 1] != '/'));
  1060.         (*userend)++,
  1061.         punlen++);
  1062.  
  1063.     /* make middle point to middle */
  1064.  
  1065.     if ((*start) == (*middle))
  1066.     {
  1067.         (*middle) = (*start) + punlen + 1;
  1068.     }
  1069.  
  1070.     /* extract partial user name from line */
  1071.  
  1072.     (void) strncpy(pun, &line[newstart + 1], punlen);
  1073.     pun[punlen] = '\0';
  1074.  
  1075.     /* search passwd file for partial match */
  1076.  
  1077.     for (userpwd = getpwent();
  1078.         userpwd != NULL;
  1079.         userpwd = getpwent())
  1080.     {
  1081.         if ((punlen <= strlen(userpwd->pw_name)) &&
  1082.         (strncmp(pun, userpwd->pw_name, punlen) == 0))
  1083.         {
  1084.  
  1085.         /* we have a partial match, record it */
  1086.  
  1087.         if (strlen(dir) == 0)
  1088.         {
  1089.             newstart = (*userend) + 1;
  1090.             (void) strcpy(dir, userpwd->pw_dir);
  1091.             (void) strcpy(username, userpwd->pw_name);
  1092.         }
  1093.         else
  1094.         {
  1095.             /* second partial match, forget the first one. */
  1096.  
  1097.             newstart = (*start);
  1098.             dir[0] = '\0';
  1099.             username[0] = '\0';
  1100.             return (FALSE);
  1101.         }
  1102.  
  1103.         }
  1104.     }
  1105.     (void) setpwent();
  1106.     }
  1107.     else if ((line[newstart] == '.') &&
  1108.         ((newstart + 1) < length) &&
  1109.     (line[newstart + 1] == '/'))
  1110.     {
  1111.     /* if it's "./" use current dir */
  1112.  
  1113.     newstart++;
  1114.     (void) strcpy(dir, currentdir);
  1115.     }
  1116.     else if ((line[newstart] == '.') &&
  1117.         ((newstart + 1) < length) &&
  1118.         (line[newstart + 1] == '.') &&
  1119.         ((newstart + 2) < length) &&
  1120.     (line[newstart + 2] == '/'))
  1121.     {
  1122.     /* if it's "../" strip off one name from currentdir and use that */
  1123.  
  1124.     newstart += 2;
  1125.     (void) strcpy(dir, currentdir);
  1126.     for (i = strlen(dir); (i > 0) && (dir[i] != '/'); i--)
  1127.     {
  1128.         /* nothing */
  1129.     }
  1130.     dir[i] = '\0';
  1131.     }
  1132.     else if (line[newstart] != '/')
  1133.     {
  1134.     /* doesn't start with a "/"? use currentdir */
  1135.  
  1136.     (void) strcpy(dir, currentdir);
  1137.     (void) strcat(dir, "/");
  1138.     }
  1139.  
  1140.     /* add on the rest of the path */
  1141.  
  1142.     dirlen = strlen(dir);
  1143.     for (i = 0; i < ((*middle) - newstart); i++)
  1144.     {
  1145.     dir[dirlen + i] = line[newstart + i];
  1146.     }
  1147.     dir[dirlen + i] = '\0';
  1148.  
  1149.     /* copy file name from line to name */
  1150.  
  1151.     for (i = 0; i < ((*tail) - (*middle) + 1); i++)
  1152.     {
  1153.     name[i] = line[(*middle) + i];
  1154.     }
  1155.     name[i] = '\0';
  1156.  
  1157.     return (TRUE);
  1158. }
  1159. /*------------------------------------------------------------------*/
  1160. /*
  1161. Perform file name completion. Put the full path and file name in the
  1162. line.
  1163. */
  1164. /*ARGSUSED*/
  1165. void
  1166. complete_file_full(ch)
  1167.     char ch;
  1168. {
  1169.     char dir[10 * (MAXNAMLEN + 1)];
  1170.     char name[MAXNAMLEN + 1];
  1171.     char username[USER_NAME_SIZE + 1];
  1172.  
  1173.     char newline[BUFFER_SIZE];
  1174.     int newlength;
  1175.     int newpoint;
  1176.  
  1177.     int userend;
  1178.     int start;
  1179.     int middle;
  1180.     int tail;
  1181.  
  1182.     int i;
  1183.  
  1184.     /* get the path and file name in the line */
  1185.  
  1186.     if (get_dir_and_name(dir,
  1187.         name,
  1188.         username,
  1189.         &userend,
  1190.         &start,
  1191.         &middle,
  1192.         &tail))
  1193.     {
  1194.  
  1195.     /* complete the file name if possible */
  1196.  
  1197.     complete_file_name(dir, name);
  1198.  
  1199.     /* create a new line */
  1200.  
  1201.     /* start with the line prefix */
  1202.  
  1203.     (void) strncpy(newline, line, start);
  1204.     newline[start] = '\0';
  1205.  
  1206.     /* add in the new path */
  1207.  
  1208.     (void) strcat(newline, dir);
  1209.  
  1210.     /* stick in the new file name */
  1211.  
  1212.     (void) strcat(newline, name);
  1213.     newpoint = strlen(newline);
  1214.  
  1215.     /* finish with the line postfix */
  1216.  
  1217.     (void) strncat(newline, &line[tail + 1], (length - tail - 1));
  1218.     newlength = strlen(newline);
  1219.  
  1220.     /* display the new line */
  1221.  
  1222.     clearline('\0');
  1223.  
  1224.     point = newpoint;
  1225.     length = newlength;
  1226.     (void) strncpy(line, newline, newlength);
  1227.  
  1228.     echoline(line, length);
  1229.  
  1230.     for (i = point; i < length; i++)
  1231.     {
  1232.         backspace(line[i]);
  1233.     }
  1234.     }
  1235.     else
  1236.     {
  1237.     bell('\0');
  1238.     }
  1239. }
  1240. /*------------------------------------------------------------------*/
  1241. /*
  1242. Perform file name completion in much the same style as csh.
  1243. */
  1244. /*ARGSUSED*/
  1245. void
  1246. complete_file(ch)
  1247.     char ch;
  1248. {
  1249.     char dir[10 * (MAXNAMLEN + 1)];
  1250.     char name[MAXNAMLEN + 1];
  1251.     char username[USER_NAME_SIZE + 1];
  1252.  
  1253.     int userend;
  1254.     int start;
  1255.     int middle;
  1256.     int tail;
  1257.  
  1258.     char newline[BUFFER_SIZE];
  1259.     int newlength;
  1260.     int newpoint;
  1261.  
  1262.     int userlen;
  1263.     int len;
  1264.  
  1265.     int i;
  1266.  
  1267. #if 0
  1268. /* Hops dbl press does dhow_files() */
  1269.     if (last_char == ch )
  1270.     {
  1271.         show_files(ch);
  1272.         return;
  1273.     }
  1274. #endif
  1275.     /* get the path and file name in the line */
  1276.  
  1277.     if (get_dir_and_name(dir,
  1278.         name,
  1279.         username,
  1280.         &userend,
  1281.         &start,
  1282.         &middle,
  1283.         &tail))
  1284.     {
  1285.     /* how long is the user name */
  1286.  
  1287.     userlen = strlen(username);
  1288.  
  1289.     /* complete the file name if possible */
  1290.  
  1291.     complete_file_name(dir, name);
  1292.     /* create a new line */
  1293.  
  1294.     /* start with the line prefix */
  1295.  
  1296.     (void) strncpy(newline, line, start);
  1297.     newline[start] = '\0';
  1298.  
  1299.     /* add in the new username */
  1300.  
  1301.     if (userlen != 0)
  1302.     {
  1303.         /* put in new user name */
  1304.  
  1305.         (void) strcat(newline, "~");
  1306.         (void) strcat(newline, username);
  1307.         len = strlen(newline);
  1308.  
  1309.         /* put in the existing path */
  1310.  
  1311.         (void) strncat(newline, &line[userend + 1], middle - userend - 1);
  1312.         newline[len + (middle - userend - 1)] = '\0';
  1313.     }
  1314.     else
  1315.     {
  1316.         /* put in the existing path */
  1317.  
  1318.         len = strlen(newline);
  1319.         (void) strncat(newline, &line[start], middle - start);
  1320.         newline[len + (middle - start)] = '\0';
  1321.     }
  1322.  
  1323.     /* stick in the new file name */
  1324.  
  1325.     (void) strcat(newline, name);
  1326.     newpoint = strlen(newline);
  1327.  
  1328.     /* finish with the line postfix */
  1329.  
  1330.     (void) strncat(newline, &line[tail + 1], (length - tail - 1));
  1331.     newlength = strlen(newline);
  1332.  
  1333.     /* display the new line */
  1334.  
  1335.     clearline('\0');
  1336.  
  1337.     point = newpoint;
  1338.     length = newlength;
  1339.     (void) strncpy(line, newline, newlength);
  1340.  
  1341.     echoline(line, length);
  1342.  
  1343.     for (i = point; i < length; i++)
  1344.     {
  1345.         backspace(line[i]);
  1346.     }
  1347.     }
  1348.     else
  1349.     {
  1350.     bell('\0');
  1351.     }
  1352. }
  1353. /*------------------------------------------------------------------*/
  1354. /*
  1355. List the names of files that start with the directory path and
  1356. file name under the cursor.
  1357. */
  1358. /*ARGSUSED*/
  1359. void
  1360. show_files(ch)
  1361.     char ch;
  1362. {
  1363.     static char divider[] = "----------";
  1364.  
  1365.     void retype_line();
  1366.  
  1367.     char dir[10 * (MAXNAMLEN + 1)];
  1368.     char name[MAXNAMLEN + 1];
  1369.     char username[USER_NAME_SIZE + 1];
  1370.  
  1371.     int userend;
  1372.     int start;
  1373.     int middle;
  1374.     int tail;
  1375.  
  1376.     if (get_dir_and_name(dir,
  1377.         name,
  1378.         username,
  1379.         &userend,
  1380.         &start,
  1381.         &middle,
  1382.         &tail))
  1383.     {
  1384.  
  1385.     fputs(ccr, stdout);
  1386.     fputs(cnl, stdout);
  1387.  
  1388.     fputs(divider, stdout);
  1389.  
  1390.     fputs(ccr, stdout);
  1391.     fputs(cnl, stdout);
  1392.  
  1393.     list_file_names(dir, name);
  1394.  
  1395.     fputs(divider, stdout);
  1396.  
  1397.     fputs(ccr, stdout);
  1398.     fputs(cnl, stdout);
  1399.  
  1400.     retype_line('\0');
  1401.     }
  1402.     else
  1403.     {
  1404.     bell('\0');
  1405.     }
  1406. }
  1407. /*------------------------------------------------------------------*/
  1408. /*
  1409. Make the gross assumption that the program we are talking to is
  1410. a shell and send "pwd\n" to it. Whatever comes back is saved as the
  1411. value of currentdir.
  1412. */
  1413. /*ARGSUSED*/
  1414. void
  1415. query_path(ch)
  1416.     char ch;
  1417. {
  1418.     static char command[] = "pwd\n";
  1419.     char buffer[BUFFER_SIZE];
  1420.     int readfd;
  1421.     struct timeval timeout;
  1422.     int status;
  1423.     int cc;
  1424.     int i;
  1425.  
  1426.     /* send the command to the shell, we hope. */
  1427.  
  1428.     write_line(command, strlen(command));
  1429.  
  1430.     /* read the directory path back */
  1431.  
  1432.     readfd = 1 << master_pty;
  1433.     timeout.tv_sec = 2;
  1434.     timeout.tv_usec = 0;
  1435.  
  1436.     do
  1437.     {
  1438.     status = select(32,
  1439.         (fd_set *) & readfd,
  1440.         (fd_set *) NULL,
  1441.         (fd_set *) NULL,
  1442.         &timeout);
  1443.  
  1444.     if (0 < status)
  1445.     {
  1446.         cc = read(master_pty, currentdir, sizeof(currentdir));
  1447.  
  1448.         /* strip off trailing control chars and blanks */
  1449.  
  1450.         for (i = cc - 1; (currentdir[i] <= ' ') && (i > 0); i--)
  1451.         {
  1452.         currentdir[i] = '\0';
  1453.         }
  1454.  
  1455.         /* read the prompt so it can be ignored */
  1456.  
  1457.         readfd = 1 << master_pty;
  1458.         timeout.tv_sec = 2;
  1459.         timeout.tv_usec = 0;
  1460.  
  1461.         do
  1462.         {
  1463.         status = select(32,
  1464.             (fd_set *) & readfd,
  1465.             (fd_set *) NULL,
  1466.             (fd_set *) NULL,
  1467.             &timeout);
  1468.  
  1469.         if (0 < status)
  1470.         {
  1471.             cc = read(master_pty, buffer, sizeof(buffer));
  1472.         }
  1473.         else if ((-1 == status) && windowchanged)
  1474.         {
  1475.             windowchanged = FALSE;
  1476.         }
  1477.         } while (status == -1);
  1478.     }
  1479.     else if ((-1 == status) && windowchanged)
  1480.     {
  1481.         windowchanged = FALSE;
  1482.     }
  1483.     } while (status == -1);
  1484. }
  1485. /*------------------------------------------------------------------*/
  1486. /*
  1487. Ring the bell on the terminal.
  1488. */
  1489. /*ARGSUSED*/
  1490. void
  1491. bell(ch)
  1492.     char ch;
  1493. {
  1494.     fputs(cbl, stdout);
  1495. }
  1496. /*------------------------------------------------------------------*/
  1497. /*
  1498. Pass characters to the slave. Don't mess with them at all.
  1499. */
  1500. void
  1501. pass(ch)
  1502.     char ch;
  1503. {
  1504.     (void) write(master_pty, &ch, 1);
  1505. }
  1506. /*------------------------------------------------------------------*/
  1507. /*
  1508. Insert a character at point in the line buffer. While we are at it
  1509. update the display to show the insertion.
  1510. */
  1511. void
  1512. insert(ch)
  1513.     char ch;
  1514. {
  1515.     int i;
  1516.  
  1517.     if (length < (BUFFER_SIZE - 2))
  1518.     {
  1519.  
  1520.     /* display the character */
  1521.  
  1522.     echo(ch);
  1523.  
  1524.     /* redisplay the rest of the line */
  1525.  
  1526.     echoline(&line[point], (length - point));
  1527.  
  1528.     /* move the characters in the line buffer */
  1529.     /* and put the cursor back at point */
  1530.  
  1531.     for (i = length; i > point; i--)
  1532.     {
  1533.         line[i] = line[i - 1];
  1534.         backspace(line[i]);
  1535.     }
  1536.  
  1537.     /* add the character to the line buffer */
  1538.     /* and increment point and length */
  1539.  
  1540.     line[point] = ch;
  1541.     length++;
  1542.     point++;
  1543.     }
  1544.     else
  1545.     {
  1546.     bell('\0');
  1547.     }
  1548. }
  1549. /*------------------------------------------------------------------*/
  1550. /*
  1551. Transpose the letter under the cursor and the letter immediately to
  1552. the left of the cursor.
  1553. */
  1554. /*ARGSUSED*/
  1555. void
  1556. transpose_chars(ch)
  1557.     char ch;
  1558. {
  1559.     char tch;
  1560.  
  1561.     if ((0 < point) && (point < length))
  1562.     {
  1563.     /* first, update the display */
  1564.  
  1565.     backspace(line[point]);
  1566.  
  1567.     echo(line[point]);
  1568.     echo(line[point - 1]);
  1569.  
  1570.     /* now swap the chars in the line buffer */
  1571.  
  1572.     tch = line[point];
  1573.     line[point] = line[point - 1];
  1574.     line[point - 1] = tch;
  1575.  
  1576.     /* point moved forward one char */
  1577.  
  1578.     point++;
  1579.     }
  1580. }
  1581. /*------------------------------------------------------------------*/
  1582. /*
  1583. Delete a character at point in the line buffer. While we are at it
  1584. update the display to reflect the deletion.
  1585. */
  1586. /*ARGSUSED*/
  1587. void
  1588. delete_char_under(ch)
  1589.     char ch;
  1590. {
  1591.     int i;
  1592.  
  1593. /* Hops - ^d at empty line */
  1594.     if ( length == 0 && ch == CD )
  1595.     {
  1596.         pass(ch);
  1597.         return;
  1598.     }    
  1599.  
  1600.     if (point < length)
  1601.     {
  1602.  
  1603.     /* clear to the end of the line */
  1604.  
  1605.     cleartoend();
  1606.  
  1607.     /* retype the rest of the line */
  1608.  
  1609.     echoline(&line[point + 1], (length - point - 1));
  1610.  
  1611.     /* build the new line */
  1612.  
  1613.     for (i = point + 1; i < length; i++)
  1614.     {
  1615.         line[i - 1] = line[i];
  1616.         backspace(line[i]);
  1617.     }
  1618.  
  1619.     length--;
  1620.  
  1621.     if (point > length)
  1622.     {
  1623.         point = length;
  1624.     }
  1625.     }
  1626.  
  1627. }
  1628. /*------------------------------------------------------------------*/
  1629. /*
  1630. Delete the character to the left of point in the line buffer. While we
  1631. are at it update the display to reflect the deletion.
  1632. */
  1633. /*ARGSUSED*/
  1634. void
  1635. delete_char(ch)
  1636.     char ch;
  1637. {
  1638.     int i;
  1639.  
  1640.     if (point > 0)
  1641.     {
  1642.     /* move the cursor left one character */
  1643.  
  1644.     backspace(line[point - 1]);
  1645.  
  1646.     /* clear to the end of the line */
  1647.  
  1648.     cleartoend();
  1649.  
  1650.     /* retype the rest of the line */
  1651.  
  1652.     echoline(&line[point], (length - point));
  1653.  
  1654.     /* build the new line */
  1655.  
  1656.     for (i = point; i < length; i++)
  1657.     {
  1658.         line[i - 1] = line[i];
  1659.         backspace(line[i]);
  1660.     }
  1661.  
  1662.     length--;
  1663.     point--;
  1664.     }
  1665.  
  1666. }
  1667. /*------------------------------------------------------------------*/
  1668. /*
  1669. Bind the edit vector to quote_edit so that the next character
  1670. will be placed in the line buffer.
  1671. */
  1672. /*ARGSUSED*/
  1673. void
  1674. quote(ch)
  1675.     char ch;
  1676. {
  1677.     edit = quote_edit;
  1678. }
  1679. /*------------------------------------------------------------------*/
  1680. /*
  1681. The next character will select an action from action_table[1]
  1682. */
  1683. /*ARGSUSED*/
  1684. void
  1685. escape_1(ch)
  1686.     char ch;
  1687. {
  1688.     edit = edit_1;
  1689. }
  1690. /*------------------------------------------------------------------*/
  1691. /*
  1692. The next character will select an action from action_table[2]
  1693. */
  1694. /*ARGSUSED*/
  1695. void
  1696. escape_2(ch)
  1697.     char ch;
  1698. {
  1699.     edit = edit_2;
  1700. }
  1701. /*------------------------------------------------------------------*/
  1702. /*
  1703. The next character will select an action from action_table[3]
  1704. */
  1705. /*ARGSUSED*/
  1706. void
  1707. escape_3(ch)
  1708.     char ch;
  1709. {
  1710.     edit = edit_3;
  1711. }
  1712. /*------------------------------------------------------------------*/
  1713. /*
  1714. Delete the word to the left of the cursor.
  1715. */
  1716. /*ARGSUSED*/
  1717. void
  1718. delete_word(ch)
  1719.     char ch;
  1720. {
  1721.     int i;
  1722.     int old;
  1723.  
  1724.     if (length > 0)
  1725.     {
  1726.     /* find the new deletion point */
  1727.  
  1728.     old = point;
  1729.  
  1730.     /* first skip over any delimiters */
  1731.  
  1732.     for (; (point > 0) && (delimit[line[point - 1]]); point--)
  1733.     {
  1734.         backspace(line[point - 1]);
  1735.     }
  1736.  
  1737.     /* now delete until we find a delimiter */
  1738.  
  1739.     for (; (point > 0) && (!delimit[line[point - 1]]); point--)
  1740.     {
  1741.         backspace(line[point - 1]);
  1742.     }
  1743.  
  1744.     /* clear to the end of the line */
  1745.  
  1746.     cleartoend();
  1747.  
  1748.     /* retype the rest of the line */
  1749.  
  1750.     echoline(&line[old], (length - old));
  1751.  
  1752.     /* construct the new line */
  1753.  
  1754.     for (i = 0; i < (length - old); i++)
  1755.     {
  1756.         line[point + i] = line[old + i];
  1757.         backspace(line[point + i]);
  1758.     }
  1759.  
  1760.     /* update the length */
  1761.  
  1762.     length = length - (old - point);
  1763.     }
  1764. }
  1765. /*------------------------------------------------------------------*/
  1766. /*
  1767. Go forward one word.
  1768. */
  1769. /*ARGSUSED*/
  1770. void
  1771. forward_word(ch)
  1772.     char ch;
  1773. {
  1774.     if (length > 0)
  1775.     {
  1776.     /* first skip any delimiters */
  1777.  
  1778.     for (; (point < length) && (delimit[line[point]]); point++)
  1779.     {
  1780.         echo(line[point]);
  1781.     }
  1782.  
  1783.     /* now skip until we find a delimiter */
  1784.  
  1785.     for (; (point < length) && (!delimit[line[point]]); point++)
  1786.     {
  1787.         echo(line[point]);
  1788.     }
  1789.     }
  1790.  
  1791. }
  1792. /*------------------------------------------------------------------*/
  1793. /*
  1794. Lower case the word.
  1795. */
  1796. /*ARGSUSED*/
  1797. void
  1798. lower_word(ch)
  1799.     char ch;
  1800. {
  1801.     if (length > 0)
  1802.     {
  1803.     /* first skip any delimiters */
  1804.  
  1805.     for (; (point < length) && (delimit[line[point]]); point++)
  1806.     {
  1807.         echo(line[point]);
  1808.     }
  1809.  
  1810.     /* now skip until we find a delimiter */
  1811.  
  1812.     for (; (point < length) && (!delimit[line[point]]); point++)
  1813.     {
  1814.         if ((line[point] >= 'A') && (line[point] <= 'Z'))
  1815.         {
  1816.         line[point] = line[point] - 'A' + 'a';
  1817.         echo(line[point]);
  1818.         }
  1819.         else
  1820.         {
  1821.         echo(line[point]);
  1822.         }
  1823.     }
  1824.     }
  1825.  
  1826. }
  1827. /*------------------------------------------------------------------*/
  1828. /*
  1829. Upper case the word.
  1830. */
  1831. /*ARGSUSED*/
  1832. void
  1833. upper_word(ch)
  1834.     char ch;
  1835. {
  1836.     if (length > 0)
  1837.     {
  1838.     /* first skip any delimiters */
  1839.  
  1840.     for (; (point < length) && (delimit[line[point]]); point++)
  1841.     {
  1842.         echo(line[point]);
  1843.     }
  1844.  
  1845.     /* now skip until we find a delimiter */
  1846.  
  1847.     for (; (point < length) && (!delimit[line[point]]); point++)
  1848.     {
  1849.         if ((line[point] >= 'a') && (line[point] <= 'z'))
  1850.         {
  1851.         line[point] = line[point] - 'a' + 'A';
  1852.         echo(line[point]);
  1853.         }
  1854.         else
  1855.         {
  1856.         echo(line[point]);
  1857.         }
  1858.     }
  1859.     }
  1860.  
  1861. }
  1862. /*------------------------------------------------------------------*/
  1863. /*
  1864. Capitalize the word.
  1865. */
  1866. /*ARGSUSED*/
  1867. void
  1868. capitalize_word(ch)
  1869.     char ch;
  1870. {
  1871.     if (length > 0)
  1872.     {
  1873.     /* first skip any delimiters */
  1874.  
  1875.     for (; (point < length) && (delimit[line[point]]); point++)
  1876.     {
  1877.         echo(line[point]);
  1878.     }
  1879.  
  1880.     /* now skip until we find a delimiter */
  1881.  
  1882.     if ((point < length) && (!delimit[line[point]]))
  1883.     {
  1884.         if ((line[point] >= 'a') && (line[point] <= 'z'))
  1885.         {
  1886.         line[point] = line[point] - 'a' + 'A';
  1887.         echo(line[point]);
  1888.         }
  1889.         else
  1890.         {
  1891.         echo(line[point]);
  1892.         }
  1893.     }
  1894.     point++;
  1895.  
  1896.     for (; (point < length) && (!delimit[line[point]]); point++)
  1897.     {
  1898.         if ((line[point] >= 'A') && (line[point] <= 'Z'))
  1899.         {
  1900.         line[point] = line[point] - 'A' + 'a';
  1901.         echo(line[point]);
  1902.         }
  1903.         else
  1904.         {
  1905.         echo(line[point]);
  1906.         }
  1907.     }
  1908.     }
  1909.  
  1910. }
  1911. /*------------------------------------------------------------------*/
  1912. /*
  1913. Go backward one word.
  1914. */
  1915. /*ARGSUSED*/
  1916. void
  1917. backward_word(ch)
  1918.     char ch;
  1919. {
  1920.     if (length > 0)
  1921.     {
  1922.     /* first backspace over any delimiters */
  1923.  
  1924.     for (; (point > 0) && (delimit[line[point - 1]]); point--)
  1925.     {
  1926.         backspace(line[point - 1]);
  1927.     }
  1928.  
  1929.     /* now backspace until we find a delimiter */
  1930.  
  1931.     for (; (point > 0) && (!delimit[line[point - 1]]); point--)
  1932.     {
  1933.         backspace(line[point - 1]);
  1934.     }
  1935.     }
  1936.  
  1937. }
  1938. /*------------------------------------------------------------------*/
  1939. /*
  1940. Move the cursor to the start of the line.
  1941. */
  1942. /*ARGSUSED*/
  1943. void
  1944. start_of_line(ch)
  1945.     char ch;
  1946. {
  1947.     int i;
  1948.  
  1949.     if (length > 0)
  1950.     {
  1951.     for (i = 0; i < point; i++)
  1952.     {
  1953.         backspace(line[i]);
  1954.     }
  1955.     point = 0;
  1956.     }
  1957. }
  1958. /*------------------------------------------------------------------*/
  1959. /*
  1960. Move the cursor one character to the left.
  1961. */
  1962. /*ARGSUSED*/
  1963. void
  1964. backward_char(ch)
  1965.     char ch;
  1966. {
  1967.     if ((length > 0) && (point > 0))
  1968.     {
  1969.     backspace(line[point - 1]);
  1970.     point--;
  1971.     }
  1972. }
  1973. /*------------------------------------------------------------------*/
  1974. /*
  1975. Move the cursor to the right of the last character on the line.
  1976. */
  1977. /*ARGSUSED*/
  1978. void
  1979. end_of_line(ch)
  1980.     char ch;
  1981. {
  1982.     if ((length > 0) && (point < length))
  1983.     {
  1984.     echoline(&line[point], (length - point));
  1985.     point = length;
  1986.     }
  1987. }
  1988. /*------------------------------------------------------------------*/
  1989. /*
  1990. Move the cursor one character to the right.
  1991. */
  1992. /*ARGSUSED*/
  1993. void
  1994. forward_char(ch)
  1995.     char ch;
  1996. {
  1997.     if ((length > 0) && (point < length))
  1998.     {
  1999.     echo(line[point]);
  2000.     point++;
  2001.     }
  2002. }
  2003. /*------------------------------------------------------------------*/
  2004. /*
  2005. Add a line to the history buffer and pass it to the child process
  2006. as input.
  2007. */
  2008. /*ARGSUSED*/
  2009. void
  2010. add_to_history(ch)
  2011.     char ch;
  2012. {
  2013.     /* Put the line in the history buffer. Make here point to the current
  2014.      * line. And increment head to point to the next history slot. */
  2015.  
  2016.     /* If the current line is identical to the current history line, don't
  2017.      * add it. */
  2018.  
  2019.     /* don't save blank lines */
  2020.  
  2021.     int prev;
  2022.  
  2023.     if ((head - 1) < 0)
  2024.     {
  2025.     prev = HISTORY_SIZE - 1;
  2026.     }
  2027.     else
  2028.     {
  2029.     prev = head - 1;
  2030.     }
  2031.  
  2032.     if ((length != 0) &&
  2033.     ((length != hist[prev].length) ||
  2034.         (strncmp(hist[prev].line, line, length) != 0)))
  2035.     {
  2036.     /* set the length of the entry */
  2037.  
  2038.     hist[head].length = length;
  2039.  
  2040.     /* make sure there is enough storage for the new line */
  2041.  
  2042.     if (hist[head].line == NULL)
  2043.     {
  2044.         if ((hist[head].line = (char *) malloc((unsigned) length)) == NULL)
  2045.         {
  2046.         perror("ile - malloc failed");
  2047.         }
  2048.     }
  2049.     else
  2050.     {
  2051.         if ((hist[head].line =
  2052.             (char *) realloc(hist[head].line, (unsigned) length))
  2053.         == NULL)
  2054.         {
  2055.         perror("ile - realloc failed");
  2056.         }
  2057.     }
  2058.  
  2059.     (void) strncpy(hist[head].line, line, length);
  2060.  
  2061.     head = (head + 1) % HISTORY_SIZE;
  2062.  
  2063.     if (hist[head].line != NULL)
  2064.     {
  2065.         free(hist[head].line);
  2066.         hist[head].length = 0;
  2067.         hist[head].line = NULL;
  2068.     }
  2069.     }
  2070.  
  2071.     /* reset here */
  2072.  
  2073.     here = head;
  2074.  
  2075.     /* Echo a carriage return or a newline as a cr-nl sequence. Then send the
  2076.      * line to the child process. Finally, clear the buffer for reuse. */
  2077.  
  2078.     fputs(ccr, stdout);
  2079.     fputs(cnl, stdout);
  2080.  
  2081.     line[length] = nl;
  2082.     length++;
  2083.  
  2084.     write_line(line, length);
  2085.  
  2086.     point = 0;
  2087.     length = 0;
  2088.  
  2089. }
  2090. /*------------------------------------------------------------------*/
  2091. /*
  2092. Erase the entire line.
  2093. */
  2094. /*ARGSUSED*/
  2095. void
  2096. erase_line(ch)
  2097.     char ch;
  2098. {
  2099.     /* remove any text from the display */
  2100.  
  2101.     clearline(ch);
  2102.  
  2103.     /* nothing in the line buffer */
  2104.  
  2105.     point = 0;
  2106.     length = 0;
  2107.  
  2108.     /* reset here */
  2109.  
  2110.     here = head;
  2111.  
  2112. }
  2113. /*------------------------------------------------------------------*/
  2114. /*
  2115. Erase from the current cursor position to the end of the line.
  2116. */
  2117. /*ARGSUSED*/
  2118. void
  2119. erase_to_end_of_line(ch)
  2120.     char ch;
  2121. {
  2122.     if ((length > 0) && (point < length))
  2123.     {
  2124.     cleartoend();
  2125.     length = point;
  2126.     }
  2127.  
  2128. }
  2129. /*------------------------------------------------------------------*/
  2130. /*
  2131. Retype the current contents of the edit buffer.
  2132. */
  2133. /*ARGSUSED*/
  2134. void
  2135. retype_line(ch)
  2136.     char ch;
  2137. {
  2138.     int i;
  2139.  
  2140.     fputs(ccr, stdout);
  2141.     fputs(cnl, stdout);
  2142.  
  2143.     echoline(line, length);
  2144.  
  2145.     for (i = point; i < length; i++)
  2146.     {
  2147.     backspace(line[i]);
  2148.     }
  2149. }
  2150. /*------------------------------------------------------------------*/
  2151. /*
  2152. Go to the the next entry in the history buffer and display it.
  2153. If we are past the last history entry, then beep.
  2154. */
  2155. void
  2156. forward_history(ch)
  2157.     char ch;
  2158. {
  2159.     if (here != head)
  2160.     {
  2161.     clearline(ch);
  2162.  
  2163.     here = (here + 1) % HISTORY_SIZE;
  2164.     length = hist[here].length;
  2165.     point = length;
  2166.  
  2167.     (void) strncpy(line, hist[here].line, length);
  2168.     echoline(line, length);
  2169.     }
  2170.     else
  2171.     {
  2172.     bell('\0');
  2173.     }
  2174. }
  2175. /*------------------------------------------------------------------*/
  2176. /*
  2177. Search backward in the history list for a line that starts with
  2178. the characters left of the cursor. If it is found make it the
  2179. current line.
  2180. */
  2181. void
  2182. search_backward_history(ch)
  2183.     char ch;
  2184. {
  2185.     int prev;
  2186.     int i;
  2187.  
  2188.     /* search backward in the history */
  2189.  
  2190.     prev = here;
  2191.  
  2192.     do
  2193.     {
  2194.     prev--;
  2195.  
  2196.     if (prev < 0)
  2197.     {
  2198.         prev = HISTORY_SIZE - 1;
  2199.     }
  2200.     }
  2201.     while ((hist[prev].line != NULL) &&
  2202.     (strncmp(line, hist[prev].line, point) != 0));
  2203.  
  2204.     /* if something was found, make it the current line */
  2205.  
  2206.     if (hist[prev].line != NULL)
  2207.     {
  2208.     /* remember the position in the history */
  2209.  
  2210.     here = prev;
  2211.  
  2212.     /* set the length and point correctly */
  2213.  
  2214.     length = hist[here].length;
  2215.     if (point > length)
  2216.     {
  2217.         point = length;
  2218.     }
  2219.  
  2220.     /* redraw the line */
  2221.  
  2222.     clearline(ch);
  2223.     (void) strncpy(line, hist[here].line, hist[here].length);
  2224.     echoline(line, length);
  2225.  
  2226.     for (i = point; i < length; i++)
  2227.     {
  2228.         backspace(line[i]);
  2229.     }
  2230.     }
  2231.     else
  2232.     {
  2233.     bell('\0');
  2234.     }
  2235. }
  2236. /*------------------------------------------------------------------*/
  2237. /*
  2238. Go back one entry in the history buffer and display it. If we are
  2239. already at the last entry, then beep.
  2240. */
  2241. void
  2242. backward_history(ch)
  2243.     char ch;
  2244. {
  2245.     int prev;
  2246.  
  2247.     prev = here - 1;
  2248.  
  2249.     if (prev < 0)
  2250.     {
  2251.     prev = HISTORY_SIZE - 1;
  2252.     }
  2253.  
  2254.     if (hist[prev].line != NULL)
  2255.     {
  2256.     clearline(ch);
  2257.  
  2258.     here = prev;
  2259.     length = hist[here].length;
  2260.     point = length;
  2261.  
  2262.     (void) strncpy(line, hist[here].line, length);
  2263.     echoline(line, length);
  2264.     }
  2265.     else
  2266.     {
  2267.     bell('\0');
  2268.     }
  2269. }
  2270. /*------------------------------------------------------------------*/
  2271. /*
  2272. The following routines are utility routines used by the editing
  2273. routines.
  2274. */
  2275. /*------------------------------------------------------------------*/
  2276. /*
  2277. Clear to the end of the current input line.
  2278. */
  2279. void
  2280. cleartoend()
  2281. {
  2282.     /* send the clear character */
  2283.  
  2284.      fputs(cce, stdout);
  2285.  
  2286.     /* send somes nulls for padding */
  2287.  
  2288.     fputs("\0\0\0\0", stdout);
  2289.     fflush(stdout);
  2290. }
  2291. /*------------------------------------------------------------------*/
  2292. /*
  2293. Clear the input line. Backspace to the start of the line. Then clear
  2294. to the end of the line.
  2295. */
  2296. /*ARGSUSED*/
  2297. void
  2298. clearline(ch)
  2299.     char ch;
  2300. {
  2301.     int i;
  2302.  
  2303.     for (i = 0; i < point; i++)
  2304.     {
  2305.     backspace(line[i]);
  2306.     }
  2307.  
  2308.     cleartoend();
  2309. }
  2310. /*------------------------------------------------------------------*/
  2311. /*
  2312. Echo a character. Not all characters are created equal. Control characters
  2313. are echoed in ^X form. So they take up two character positions instead of
  2314. the normal 1 character position.
  2315. */
  2316. void
  2317. echo(ch)
  2318.     char ch;
  2319. {
  2320.     /* how should we echo the char? */
  2321.  
  2322.     if (ch < ' ')
  2323.     {
  2324.     (void) fputc('^', stdout);
  2325.     (void) fputc('@' + ch, stdout);
  2326.     }
  2327.     else
  2328.     {
  2329.     (void) fputc(ch, stdout);
  2330.     }
  2331.     (void) fflush(stdout);
  2332.     
  2333. }
  2334. /*------------------------------------------------------------------*/
  2335. /*
  2336. Echo a line. Print a whole line with control characters printed in
  2337. ^X form.
  2338. */
  2339. void
  2340. echoline(line, length)
  2341.     char *line;
  2342.     int length;
  2343. {
  2344.     int i;
  2345.  
  2346.     for (i = 0; i < length; i++)
  2347.     {
  2348.     echo(*line++);
  2349.     }
  2350.  
  2351. }
  2352. /*------------------------------------------------------------------*/
  2353. /*
  2354. Backspace over a character. Generate enough bs characters to backspace
  2355. over any character.
  2356. */
  2357. void
  2358. backspace(ch)
  2359.     char ch;
  2360. {
  2361.     if (ch < ' ')
  2362.     {
  2363.  
  2364.     fputs(cle, stdout);
  2365.     fputs(cle, stdout);
  2366.     }
  2367.     else
  2368.     {
  2369.     fputs(cle, stdout);
  2370.     }
  2371. }
  2372. /*------------------------------------------------------------------*/
  2373. /*
  2374. Add any character to the line buffer.
  2375. */
  2376. void
  2377. quote_edit(ch)
  2378.     char ch;
  2379. {
  2380.     insert(ch);
  2381.  
  2382.     edit = edit_0;
  2383. }
  2384. /*------------------------------------------------------------------*/
  2385. /*
  2386. Given a character and an action table number either execute the
  2387. action or pass the string to (*edit)(ch)
  2388. */
  2389. void
  2390. dispatch(table, ch)
  2391.     int table;
  2392.     char ch;
  2393. {
  2394.     char *cptr;
  2395.  
  2396.     switch (action_table[table][ch].flag)
  2397.     {
  2398.     case is_action:
  2399.  
  2400.     (*(action_table[table][ch].aors.action)) (ch);
  2401.     break;
  2402.  
  2403.     case is_string:
  2404.  
  2405.     cptr = action_table[table][ch].aors.string;
  2406.     while ((*cptr) != '\0')
  2407.     {
  2408.         (*edit) (*cptr);
  2409.         cptr++;
  2410.     }
  2411.  
  2412.     break;
  2413.     }
  2414. }
  2415. /*------------------------------------------------------------------*/
  2416. /*
  2417. Select an action from action_table[3] and execute it.
  2418. */
  2419. void
  2420. edit_3(ch)
  2421.     char ch;
  2422. {
  2423.     /* reset so that next input is handled by edit_0 unless over ridden by
  2424.      * the action. */
  2425.  
  2426.     edit = edit_0;
  2427.     dispatch(3, ch);
  2428.     (void) fflush(stdout);
  2429. }
  2430. /*------------------------------------------------------------------*/
  2431. /*
  2432. Select an action from action_table[2] and execute it.
  2433. */
  2434. void
  2435. edit_2(ch)
  2436.     char ch;
  2437. {
  2438.     /* reset so that next input is handled by edit_0 unless over ridden by
  2439.      * the action. */
  2440.  
  2441.     edit = edit_0;
  2442.     dispatch(2, ch);
  2443.     (void) fflush(stdout);
  2444. }
  2445. /*------------------------------------------------------------------*/
  2446. /*
  2447. Select an action from action_table[1] and execute it.
  2448. */
  2449. void
  2450. edit_1(ch)
  2451.     char ch;
  2452. {
  2453.     /* reset so that next input is handled by edit_0 unless over ridden by
  2454.      * the action. */
  2455.  
  2456.     edit = edit_0;
  2457.     dispatch(1, ch);
  2458.     (void) fflush(stdout);
  2459. }
  2460. /*------------------------------------------------------------------*/
  2461. /*
  2462. Select an action from action_table[0] and execute it.
  2463. */
  2464. void
  2465. edit_0(ch)
  2466.     char ch;
  2467. {
  2468.     dispatch(0, ch);
  2469.     (void) fflush(stdout);
  2470. }
  2471. /*------------------------------------------------------------------*/
  2472. /*
  2473. Input line editor.
  2474.  
  2475. Initialize the world. Then loop forever using select to wait for
  2476. characters to be available from either stdin or from master_pty.
  2477. When characters are available, pass them on after doing any needed
  2478. editing.
  2479. */
  2480. void
  2481. ile()
  2482. {
  2483.     /* general purpose integer variable */
  2484.  
  2485.     int i;
  2486.  
  2487.     /* arguments for read and write calls */
  2488.  
  2489.     char buffer[BUFFER_SIZE];
  2490.     int cc;
  2491.  
  2492.     /* Arguments for select call */
  2493.  
  2494.     int nfds;
  2495.     int width;
  2496.     int readfds;
  2497.  
  2498.     /* what to do if the child or parent dies */
  2499.  
  2500.     (void) signal(SIGCHLD, (void (*)()) clean_up);
  2501.     (void) signal(SIGSEGV, (void (*)()) clean_up);
  2502.     (void) signal(SIGBUS,  (void (*)()) clean_up);
  2503.     (void) signal(SIGTERM, (void (*)()) clean_up);
  2504.     (void) signal(SIGHUP,  (void (*)()) clean_up);
  2505.     (void) signal(SIGINT,  (void (*)()) clean_up);
  2506.     (void) signal(SIGQUIT, (void (*)()) clean_up);
  2507.  
  2508.     /* what to do it the window changes size */
  2509.  
  2510.     (void) signal(SIGWINCH, change_window);
  2511.  
  2512.     /* replicate current tty state onto slave */
  2513.     tty_getmode(READ);
  2514.     tty_replic(READ, slave_tty) ;
  2515.  
  2516.     (void) tty_raw(READ);
  2517.  
  2518.     windowchanged = FALSE;
  2519.  
  2520.     /* "login" the user */
  2521.  
  2522.     (void) setutmp(slave_tty, TRUE);
  2523.  
  2524.     /* set raw mode on tty */
  2525.     (void) tty_raw(READ);
  2526.  
  2527.  
  2528.     /* get descriptor table size */
  2529.     width = getdtablesize();
  2530.     if (width > 32)
  2531.     {
  2532.     width = 32;
  2533.     }
  2534.  
  2535.     /* set initial edit function */
  2536.  
  2537.     edit = edit_0;
  2538.  
  2539.     /* initialize line buffer */
  2540.  
  2541.     point = 0;
  2542.     length = 0;
  2543.  
  2544.     /* initialize history buffer */
  2545.  
  2546.     head = 0;
  2547.     here = 0;
  2548.  
  2549.     for (i = 0; i < HISTORY_SIZE; i++)
  2550.     {
  2551.     hist[i].length = 0;
  2552.     hist[i].line = NULL;
  2553.     }
  2554.  
  2555.     for (;;)
  2556.     {
  2557.     readfds = (1 << READ) | (1 << master_pty);
  2558.  
  2559.     /* wait for input from stdin or master_pty */
  2560.  
  2561.     nfds = select(width,
  2562.         (fd_set *) & readfds,
  2563.         (fd_set *) NULL,
  2564.         (fd_set *) NULL,
  2565.         (struct timeval *) NULL);
  2566.  
  2567.     if (nfds == -1)        /* an exception has occured */
  2568.     {
  2569.         if (windowchanged)
  2570.         {
  2571.         /* nothing serious, the window changed size */
  2572.  
  2573.         windowchanged = FALSE;
  2574.         }
  2575.         else
  2576.         {
  2577.         perror("ile - select exception");
  2578.         clean_up();
  2579.         }
  2580.     }
  2581.     else if ((nfds > 0) && (readfds != 0))    /* something to read */
  2582.     {
  2583.         if ((readfds & (1 << master_pty)) != 0)
  2584.         {
  2585.         /* read the pending characters. */
  2586.  
  2587.         cc = read(master_pty, buffer, BUFFER_SIZE);
  2588.  
  2589.         /* display the characters. */
  2590.  
  2591.         (void) write(WRITE, buffer, cc);
  2592.         }
  2593.  
  2594.         if ((readfds & (1 << READ)) != 0)
  2595.         {
  2596.         /* read the pending characters. */
  2597.  
  2598.         cc = read(READ, buffer, BUFFER_SIZE);
  2599.  
  2600.         /* if the slave is not echoing chars
  2601.          *  should not mess with its input characters
  2602.          */
  2603.                 if ( tty_silentmode(slave_tty) )
  2604.         {
  2605.             edit = pass;
  2606.         }
  2607.         else if (edit == (void (*) ()) pass)
  2608.         {
  2609.             edit = edit_0;
  2610.         }
  2611.  
  2612.         /* decide what to do with the characters. */
  2613.  
  2614.         for (i = 0; i < cc; i++)
  2615.         {
  2616.             (*edit) (CHAR_MASK & buffer[i]);
  2617.         }
  2618.         }
  2619.  
  2620.     }
  2621.     }
  2622.  
  2623. }
  2624. /*------------------------------------------------------------------*/
  2625. /*
  2626. The child process.
  2627.  
  2628. Make the pty the processes controling terminal. Bind the pty to
  2629. stdin, stdout, and stderr. Then exec the users program.
  2630. */
  2631. void
  2632. child(argv)
  2633.     char *argv[];
  2634. {
  2635.     /* shell name pointers */
  2636.  
  2637.     char *shellname;
  2638.     char *shellpath;
  2639.     char *dashshellname;
  2640.  
  2641.     /* close all file descriptors */
  2642.  
  2643.     (void) close(READ);
  2644.     (void) close(WRITE);
  2645.     (void) close(ERROR);
  2646.     (void) close(slave_tty);
  2647.  
  2648.     /* get rid of controlling terminal */
  2649.     tty_NoCtty();
  2650.  
  2651.     /* open the tty again */
  2652.     /* this makes the pty the controlling terminal */
  2653.  
  2654.     if ((slave_tty = open(ttydev, O_RDWR)) == -1)
  2655.     {
  2656.     perror("ile - reopening slave tty");
  2657.     }
  2658.  
  2659.     /* slave_tty is now stdin */
  2660.  
  2661.     /* bind slave_tty to stdout */
  2662.  
  2663.     (void) dup2(slave_tty, WRITE);
  2664.  
  2665.     /* bind slave_tty to stderr */
  2666.  
  2667.     (void) dup2(slave_tty, ERROR);
  2668.  
  2669.     /* close master_pty descriptor */
  2670.  
  2671.     (void) close(master_pty);
  2672.  
  2673.     /* Fire up application program. If no program name is given then fire up
  2674.      * users favorite shell. */
  2675.  
  2676.     /* get the name of the users shell. default to /bin/csh */
  2677.  
  2678.     if ((shellpath = getenv("SHELL")) == NULL ||
  2679.     (*shellpath == '\0'))
  2680.     {
  2681.     shellpath = "/bin/csh";
  2682.     }
  2683.  
  2684.     /* get just the name */
  2685.  
  2686.     if ((shellname = strrchr(shellpath, '/')) != NULL)
  2687.     {
  2688.     shellname += sizeof(char);
  2689.     }
  2690.  
  2691.     /* if the current argv[0] starts with -, then the new argv[0] */
  2692.     /* should start with - */
  2693.  
  2694.     if (*(argv[0]) == '-')
  2695.     {
  2696.     dashshellname = (char *) malloc((unsigned) strlen(shellname) + 2);
  2697.     (void) strcpy(dashshellname, "-");
  2698.     (void) strcat(dashshellname, shellpath);
  2699.     }
  2700.     else
  2701.     {
  2702.     dashshellname = shellname;
  2703.     }
  2704.  
  2705. #ifdef SIGTSTP
  2706.         /* Prevent non-job-control programs from dying on SIGTSTP */
  2707.                 signal(SIGTSTP, SIG_IGN);
  2708. #endif
  2709.  
  2710.     /* execute the shell or the specified program */
  2711.  
  2712.     if (argv[1] == NULL)
  2713.     {
  2714.     /* execute default shell */
  2715.  
  2716.     execlp(shellpath, dashshellname, 0);
  2717.     }
  2718.     else if (*argv[1] == '-')
  2719.     {
  2720.     /* there is an initialization file */
  2721.  
  2722.     if (argv[2] == NULL)
  2723.     {
  2724.         /* execute default shell */
  2725.  
  2726.         execlp(shellpath, dashshellname, 0);
  2727.     }
  2728.     else
  2729.     {
  2730.         /* execute specified program */
  2731.  
  2732.         execvp(argv[2], &argv[2]);
  2733.     }
  2734.     }
  2735.     else
  2736.     {
  2737.     /* execute specified program */
  2738.  
  2739.     execvp(argv[1], &argv[1]);
  2740.     }
  2741.  
  2742.     /* this executes if exec fails */
  2743.  
  2744.     perror("ile exec failed");
  2745.     exit(1);
  2746.     /* NOTREACHED */
  2747. }
  2748. /*------------------------------------------------------------------*/
  2749. /*
  2750. Set up default key bindings and delimeters.
  2751. */
  2752. void
  2753. default_bindings()
  2754. {
  2755.     int i;
  2756.  
  2757.     /* clear delimiter vector and the action table */
  2758.  
  2759.     for (i = 0; i < CHAR_SET_SIZE; i++)
  2760.     {
  2761.     delimit[i] = FALSE;
  2762.  
  2763.     action_table[0][i].aors.action = insert;
  2764.     action_table[1][i].aors.action = bell;
  2765.     action_table[2][i].aors.action = bell;
  2766.     action_table[3][i].aors.action = bell;
  2767.  
  2768.     action_table[0][i].flag = is_action;
  2769.     action_table[1][i].flag = is_action;
  2770.     action_table[2][i].flag = is_action;
  2771.     action_table[3][i].flag = is_action;
  2772.     }
  2773.  
  2774.     /* default delimiters */
  2775.  
  2776.     delimit[' '] = TRUE;    /* blank */
  2777.     delimit['/'] = TRUE;    /* slash */
  2778.     delimit['.'] = TRUE;    /* dot */
  2779.     delimit['-'] = TRUE;    /* dash */
  2780.  
  2781.     /* default action_table[0] */
  2782.  
  2783.     action_table[0][CA].aors.action = start_of_line;
  2784.     action_table[0][CB].aors.action = backward_char;
  2785.     action_table[0][CE].aors.action = end_of_line;
  2786.     action_table[0][CF].aors.action = forward_char;
  2787.     action_table[0][CK].aors.action = erase_to_end_of_line;
  2788.     action_table[0][CU].aors.action = erase_line;
  2789.     action_table[0][CL].aors.action = retype_line;
  2790.     action_table[0][CN].aors.action = forward_history;
  2791.     action_table[0][CP].aors.action = backward_history;
  2792.     action_table[0][CR].aors.action = search_backward_history;
  2793.     action_table[0][CT].aors.action = transpose_chars;
  2794.     action_table[0][CV].aors.action = quote;
  2795.     action_table[0][del].aors.action = delete_char;
  2796.     action_table[0][esc].aors.action = escape_1;
  2797.     action_table[0][cr].aors.action = add_to_history;
  2798.     action_table[0][nl].aors.action = add_to_history;
  2799.     action_table[0][CX].aors.action = delete_char_under;
  2800.  
  2801.     action_table[0][CC].aors.action = pass;
  2802.     action_table[0][CD].aors.action = delete_char_under /* pass */;
  2803.     action_table[0][CQ].aors.action = pass;
  2804.     action_table[0][CS].aors.action = pass;
  2805.     action_table[0][CZ].aors.action = pass;
  2806.  
  2807.     /* default action_table[1] ^[ c */
  2808.  
  2809.     action_table[1]['b'].aors.action = backward_word;
  2810.     action_table[1]['f'].aors.action = forward_word;
  2811.     action_table[1][del].aors.action = delete_word;
  2812.     action_table[1]['u'].aors.action = upper_word;
  2813.     action_table[1]['l'].aors.action = lower_word;
  2814.     action_table[1]['c'].aors.action = capitalize_word;
  2815.     action_table[1]['['].aors.action = escape_2;
  2816.     action_table[1][esc].aors.action = complete_file;
  2817.     action_table[1]['s'].aors.action = complete_file_full;
  2818.     action_table[1]['d'].aors.action = show_files;
  2819.     action_table[1]['p'].aors.action = query_path;
  2820.  
  2821.     /* default action_table[2] ^[ [ */
  2822.  
  2823.     action_table[2]['A'].aors.action = backward_history;
  2824.     action_table[2]['B'].aors.action = forward_history;
  2825.     action_table[2]['C'].aors.action = forward_char;
  2826.     action_table[2]['D'].aors.action = backward_char;
  2827.  
  2828. }
  2829. /*------------------------------------------------------------------*/
  2830. /*
  2831. Return a character or EOF. This routine reads characters from input
  2832. and converts them into a character using the following rules.
  2833.  
  2834. The character may be a single character, a control
  2835. character indicated by ^x, an octal number starting with \, or an
  2836. escaped character indictated by \x.
  2837. */
  2838. int
  2839. scan_char(input)
  2840.     FILE *input;
  2841. {
  2842.     int ch;
  2843.     int value;
  2844.  
  2845.     ch = fgetc(input);
  2846.     switch (ch)
  2847.     {
  2848.     case '^':
  2849.  
  2850.     /* it is a control character */
  2851.  
  2852.     for (ch = fgetc(input); '@' <= ch; ch = ch - '@');
  2853.  
  2854.     break;
  2855.  
  2856.     case '\\':
  2857.  
  2858.     /* octal or an escaped character? */
  2859.  
  2860.     ch = fgetc(input);
  2861.     if (('0' <= ch) && (ch <= '7'))
  2862.     {
  2863.  
  2864.         /* its an octal number */
  2865.  
  2866.         value = 0;
  2867.         while (('0' <= ch) && (ch <= '7'))
  2868.         {
  2869.         value = (value * 8) + (ch - '0');
  2870.         ch = fgetc(input);
  2871.         }
  2872.         (void) ungetc(ch, input);
  2873.  
  2874.         ch = value & 0177;    /* make sure it is in range */
  2875.     }
  2876.     else
  2877.     {
  2878.         /* its an escaped character */
  2879.  
  2880.         ch = fgetc(input);
  2881.     }
  2882.  
  2883.     break;
  2884.  
  2885.     case '\n':
  2886.  
  2887.     /* the real end of the line */
  2888.  
  2889.     ch = EOL;
  2890.  
  2891.     break;
  2892.  
  2893.     default:
  2894.  
  2895.     /* it is just itself */
  2896.  
  2897.     break;
  2898.     }
  2899.  
  2900.     return (ch);
  2901.  
  2902. }
  2903. /*------------------------------------------------------------------*/
  2904. /*
  2905. Set key bindings and delimiters from the users file.
  2906. */
  2907. void
  2908. user_bindings(file)
  2909.     FILE *file;
  2910. {
  2911.  
  2912. #define NAME_SIZE 40
  2913.  
  2914.     static struct action_name_table
  2915.     {
  2916.     char *name;
  2917.     void (*action) ();
  2918.     } action_name_table[] =
  2919.     {
  2920.     {
  2921.         "complete_file_full", complete_file_full
  2922.     },
  2923.     {
  2924.         "complete_file", complete_file
  2925.     },
  2926.     {
  2927.         "show_files", show_files
  2928.     },
  2929.     {
  2930.         "query_path", query_path
  2931.     },
  2932.     {
  2933.         "bell", bell
  2934.     },
  2935.     {
  2936.         "pass", pass
  2937.     },
  2938.     {
  2939.         "insert", insert
  2940.     },
  2941.     {
  2942.         "transpose_chars", transpose_chars
  2943.     },
  2944.     {
  2945.         "delete_char", delete_char
  2946.     },
  2947.     {
  2948.         "delete_char_under", delete_char_under
  2949.     },
  2950.     {
  2951.         "quote", quote
  2952.     },
  2953.     {
  2954.         "escape_1", escape_1
  2955.     },
  2956.     {
  2957.         "escape_2", escape_2
  2958.     },
  2959.     {
  2960.         "escape_3", escape_3
  2961.     },
  2962.     {
  2963.         "delete_word", delete_word
  2964.     },
  2965.     {
  2966.         "upper_word", upper_word
  2967.     },
  2968.     {
  2969.         "lower_word", lower_word
  2970.     },
  2971.     {
  2972.         "capitalize_word", capitalize_word
  2973.     },
  2974.     {
  2975.         "forward_word", forward_word
  2976.     },
  2977.     {
  2978.         "backward_word", backward_word
  2979.     },
  2980.     {
  2981.         "start_of_line", start_of_line
  2982.     },
  2983.     {
  2984.         "backward_char", backward_char
  2985.     },
  2986.     {
  2987.         "end_of_line", end_of_line
  2988.     },
  2989.     {
  2990.         "forward_char", forward_char
  2991.     },
  2992.     {
  2993.         "add_to_history", add_to_history
  2994.     },
  2995.     {
  2996.         "erase_line", erase_line
  2997.     },
  2998.     {
  2999.         "erase_to_end_of_line", erase_to_end_of_line
  3000.     },
  3001.     {
  3002.         "retype_line", retype_line
  3003.     },
  3004.     {
  3005.         "forward_history", forward_history
  3006.     },
  3007.     {
  3008.         "backward_history", backward_history
  3009.     },
  3010.     {
  3011.         "search_backward_history", search_backward_history
  3012.     },
  3013.     {
  3014.         "", (void(*)())NULL
  3015.     }
  3016.     };
  3017.  
  3018.     char name[NAME_SIZE];
  3019.  
  3020.     int ch;
  3021.     int i;
  3022.  
  3023.     int linecount;
  3024.     int table;
  3025.     int entry;
  3026.  
  3027.     /* First clear the default delimiters */
  3028.  
  3029.     for (i = 0; i < CHAR_SET_SIZE; i++)
  3030.     {
  3031.     delimit[i] = FALSE;
  3032.     }
  3033.  
  3034.     /* Now read the delimiter characters */
  3035.  
  3036.     while (((int) (ch = fgetc(file)) != EOF) && (ch != '\n'))
  3037.     {
  3038.     delimit[ch] = TRUE;
  3039.     }
  3040.  
  3041.     linecount = 2;
  3042.  
  3043.     /* Now read the character binding pairs */
  3044.  
  3045.     while ((int) (ch = fgetc(file)) != EOF)
  3046.     {
  3047.     switch (ch)
  3048.     {
  3049.     case '\n':
  3050.  
  3051.         /* skipping a blank line */
  3052.         linecount++;
  3053.  
  3054.         break;
  3055.  
  3056.     case '0':
  3057.     case '1':
  3058.     case '2':
  3059.     case '3':
  3060.  
  3061.         /* which table is this entry directed to? */
  3062.  
  3063.         table = ch - '0';
  3064.  
  3065.         /* get the character code */
  3066.  
  3067.         entry = scan_char(file);
  3068.  
  3069.         /* make sure the '=' is there */
  3070.  
  3071.         ch = fgetc(file);
  3072.         if (ch != '=')
  3073.         {
  3074.         (void) fprintf(stderr,
  3075.             "ile: '=' missing on line %d\n",
  3076.             linecount);
  3077.         exit(1);
  3078.         /* NOTREACHED */
  3079.         }
  3080.  
  3081.         /* collect the action name or string */
  3082.  
  3083.         for (ch = scan_char(file), i = 0;
  3084.         ((int) ch != EOL) && (i < (NAME_SIZE - 1));
  3085.         ch = scan_char(file), i++)
  3086.         {
  3087.         name[i] = ch;
  3088.         name[i + 1] = '\0';
  3089.         }
  3090.  
  3091.         /* look it up in the action_name_table */
  3092.  
  3093.         for (i = 0;
  3094.         (action_name_table[i].action != (void(*)()) NULL) &&
  3095.         (strcmp(name, action_name_table[i].name) != 0);
  3096.         i++);
  3097.  
  3098.         /* if it was found, put it in the action array */
  3099.  
  3100.         if (action_name_table[i].action == (void(*)())NULL)
  3101.         {
  3102.         /* must be a string */
  3103.  
  3104.         action_table[table][entry].flag = is_string;
  3105.         action_table[table][entry].aors.string =
  3106.             (char *) malloc((unsigned) strlen(name) + 1);
  3107.         (void) strcpy(action_table[table][entry].aors.string, name);
  3108.         }
  3109.         else
  3110.         {
  3111.         /* its an action */
  3112.  
  3113.         action_table[table][entry].flag = is_action;
  3114.         action_table[table][entry].aors.action =
  3115.             action_name_table[i].action;
  3116.         }
  3117.  
  3118.         linecount++;    /* count the line */
  3119.  
  3120.         break;
  3121.  
  3122.     default:
  3123.         (void) fprintf(stderr,
  3124.         "\nile: error in initialization file on line %d\n",
  3125.         linecount);
  3126.         exit(1);
  3127.         /* NOTREACHED */
  3128.     }
  3129.     }
  3130.  
  3131.     (void) fclose(file);
  3132. }
  3133. /*------------------------------------------------------------------*/
  3134. /*
  3135. Initialize key bindings and delimiters.
  3136. */
  3137. void
  3138. initialize(argv)
  3139.     char *argv[];
  3140. {
  3141.     FILE *file;
  3142.     char name[BUFFER_SIZE];
  3143.     char *pwd;
  3144.  
  3145.     /* set up the default bindings */
  3146.  
  3147.     default_bindings();
  3148.  
  3149.     /* Look for an initialization file. If it's there, load it. */
  3150.  
  3151.     name[0] = '\0';
  3152.     homedir = getenv("HOME");
  3153.     if (homedir == NULL)
  3154.     {
  3155.     /* no home dir, use / instead */
  3156.     name[0] = '\0';
  3157.     }
  3158.     else
  3159.     {
  3160.     (void) strcpy(name, homedir);
  3161.     }
  3162.     (void) strcat(name, "/.ilerc");
  3163.  
  3164.     /* initialize currentdir */
  3165.  
  3166.     pwd = getenv("PWD");
  3167.     if (pwd == NULL)
  3168.     {
  3169. #if DEFAULT_CWD_TO_HOMEDIR
  3170.     /* no pwd, use homedir instead */
  3171.     (void) strcpy(currentdir, homedir);
  3172. #else
  3173.         getcwd(currentdir, MAXNAMLEN);
  3174. #endif
  3175.     
  3176.     }
  3177.     else
  3178.     {
  3179.     (void) strcpy(currentdir, pwd);
  3180.     }
  3181.  
  3182.     if ((argv[1] != NULL) &&
  3183.     (*argv[1] == '-') &&
  3184.     ((file = fopen(argv[1] + 1, "r")) != NULL))
  3185.     {
  3186.     /* load the users bindings */
  3187.  
  3188.     user_bindings(file);
  3189.     }
  3190.     else if (((file = fopen("./.ilerc", "r")) != NULL) ||
  3191.     ((file = fopen(name, "r")) != NULL))
  3192.     {
  3193.     user_bindings(file);
  3194.     }
  3195. }
  3196. /*------------------------------------------------------------------*/
  3197. /*
  3198. */
  3199. /*ARGSUSED*/
  3200. main(argc, argv)
  3201.     int argc;
  3202.     char *argv[];
  3203. {
  3204.  
  3205.     /* Child process id */
  3206.  
  3207.     int childpid;
  3208.  
  3209.     /* identify yourself */
  3210.  
  3211.     if (setvbuf(stdout, NULL, _IONBF, 0) < 0 )
  3212.         perror("setbuf");
  3213.         
  3214.     (void) fprintf(stdout, "ile rev.%s\n\r", VERSION_STR);
  3215.  
  3216.     /* create the tty/pty pair */
  3217.  
  3218.     getpty(&master_pty, &slave_tty);
  3219.  
  3220.     /* get control sequences from termcap */
  3221.  
  3222.     get_termcap();
  3223.  
  3224.     /* initialize the dispatch vectors */
  3225.  
  3226.     initialize(argv);
  3227.  
  3228.     /* create the child process */
  3229.  
  3230.     childpid = fork();
  3231.  
  3232.     switch (childpid)
  3233.     {
  3234.     case 0:            /* child process */
  3235.  
  3236.     child(argv);
  3237.     break;
  3238.  
  3239.     case -1:            /* fork failed */
  3240.  
  3241.     perror("ile - fork failed");
  3242.     exit(1);
  3243.     /* NOTREACHED */
  3244.  
  3245.     default:            /* parent process */
  3246.     ile();
  3247.     break;
  3248.     }
  3249.  
  3250. }
  3251.