Subversion Repositories HelenOS

Rev

Rev 2714 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Termios command line History and Editting for NetBSD sh (ash)
  3.  * Copyright (c) 1999
  4.  *  Main code:  Adam Rogoyski <rogoyski@cs.utexas.edu>
  5.  *  Etc:        Dave Cinege <dcinege@psychosis.com>
  6.  *
  7.  * You may use this code as you wish, so long as the original author(s)
  8.  * are attributed in any redistributions of the source code.
  9.  * This code is 'as is' with no warranty.
  10.  * This code may safely be consumed by a BSD or GPL license.
  11.  *
  12.  * v 0.5  19990328  Initial release
  13.  *
  14.  * Future plans: Simple file and path name completion. (like BASH)
  15.  *
  16.  */
  17.  
  18. /*
  19. Usage and Known bugs:
  20.     Terminal key codes are not extensive, and more will probably
  21.     need to be added. This version was created on Debian GNU/Linux 2.x.
  22.     Delete, Backspace, Home, End, and the arrow keys were tested
  23.     to work in an Xterm and console. Ctrl-A also works as Home.
  24.     Ctrl-E also works as End. The binary size increase is <3K.
  25.    
  26.     Editting will not display correctly for lines greater then the
  27.     terminal width. (more then one line.) However, history will.
  28. */
  29.  
  30. #include <stdio.h>
  31. #include <unistd.h>
  32. #include <stdlib.h>
  33. #include <string.h>
  34. #include <termios.h>
  35. #include <ctype.h>
  36. #include <sys/ioctl.h>
  37.  
  38. #include "input.h"
  39. #include "output.h"
  40.  
  41. #ifdef HETIO
  42.  
  43. #include "hetio.h"
  44.  
  45.    
  46. #define  MAX_HISTORY   15           /* Maximum length of the linked list for the command line history */
  47.  
  48. #define ESC 27
  49. #define DEL 127
  50.  
  51. static struct history *his_front = NULL;    /* First element in command line list */
  52. static struct history *his_end = NULL;      /* Last element in command line list */
  53. static struct termios old_term, new_term;   /* Current termio and the previous termio before starting ash */
  54.  
  55. static int history_counter = 0;         /* Number of commands in history list */
  56. static int reset_term = 0;          /* Set to true if the terminal needs to be reset upon exit */
  57. //static int hetio_inter = 0;
  58. int hetio_inter = 0;
  59.  
  60. struct history
  61. {
  62.    char *s;
  63.    struct history *p;
  64.    struct history *n;
  65. };
  66.  
  67.  
  68. void input_delete    (int);
  69. void input_home      (int *);
  70. void input_end       (int *, int);
  71. void input_backspace (int *, int *);
  72.  
  73.  
  74.  
  75. void hetio_init(void)
  76. {
  77.     hetio_inter = 1;
  78. }
  79.  
  80.  
  81. void hetio_reset_term(void)
  82. {
  83.     if (reset_term)
  84.         tcsetattr(1, TCSANOW, &old_term);
  85. }
  86.  
  87.  
  88. void setIO(struct termios *new, struct termios *old)    /* Set terminal IO to canonical mode, and save old term settings. */
  89. {
  90.     tcgetattr(0, old);
  91.     memcpy(new, old, sizeof(*new));
  92.     new->c_cc[VMIN] = 1;
  93.     new->c_cc[VTIME] = 0;
  94.     new->c_lflag &= ~ICANON; /* unbuffered input */
  95.     new->c_lflag &= ~ECHO;
  96.     tcsetattr(0, TCSANOW, new);
  97. }
  98.  
  99. void input_home(int *cursor)                /* Command line input routines */
  100. {
  101.     while (*cursor > 0) {
  102.         out1c('\b');
  103.         --*cursor;
  104.     }
  105.     flushout(&output);
  106. }
  107.  
  108.  
  109. void input_delete(int cursor)
  110. {
  111.     int j = 0;
  112.  
  113.     memmove(parsenextc + cursor, parsenextc + cursor + 1,
  114.         BUFSIZ - cursor - 1);
  115.     for (j = cursor; j < (BUFSIZ - 1); j++) {
  116.         if (!*(parsenextc + j))
  117.             break;
  118.         else
  119.             out1c(*(parsenextc + j));
  120.     }
  121.  
  122.     out1str(" \b");
  123.    
  124.     while (j-- > cursor)
  125.         out1c('\b');
  126.     flushout(&output);
  127. }
  128.  
  129.  
  130. void input_end(int *cursor, int len)
  131. {
  132.     while (*cursor < len) {
  133.         out1str("\033[C");
  134.         ++*cursor;
  135.     }
  136.     flushout(&output);
  137. }
  138.  
  139.  
  140. void
  141. input_backspace(int *cursor, int *len)
  142. {
  143.     int j = 0;
  144.  
  145.     if (*cursor > 0) {
  146.         out1str("\b \b");
  147.         --*cursor;
  148.         memmove(parsenextc + *cursor, parsenextc + *cursor + 1,
  149.             BUFSIZ - *cursor + 1);
  150.        
  151.         for (j = *cursor; j < (BUFSIZ - 1); j++) {
  152.             if (!*(parsenextc + j))
  153.                 break;
  154.             else
  155.                 out1c(*(parsenextc + j));
  156.         }
  157.        
  158.         out1str(" \b");
  159.        
  160.         while (j-- > *cursor)
  161.             out1c('\b');
  162.        
  163.         --*len;
  164.         flushout(&output);
  165.     }
  166. }
  167.  
  168. int hetio_read_input(int fd)
  169. {
  170.     int nr = 0;
  171.  
  172.     if (!hetio_inter) {     /* Are we an interactive shell? */
  173.         return -255;       
  174.     } else {
  175.         int len = 0;
  176.         int j = 0;
  177.         int cursor = 0;
  178.         int break_out = 0;
  179.         int ret = 0;
  180.         char c = 0;
  181.         struct history *hp = his_end;
  182.  
  183.         if (!reset_term) {
  184.             setIO(&new_term, &old_term);
  185.             reset_term = 1;
  186.         } else {
  187.             tcsetattr(0, TCSANOW, &new_term);
  188.         }
  189.        
  190.         memset(parsenextc, 0, BUFSIZ);
  191.        
  192.         while (1) {
  193.             if ((ret = read(fd, &c, 1)) < 1)
  194.                 return ret;
  195.            
  196.             switch (c) {
  197.                 case 1:     /* Control-A Beginning of line */
  198.                     input_home(&cursor);
  199.                     break;
  200.                 case 5:     /* Control-E EOL */
  201.                     input_end(&cursor, len);
  202.                     break;
  203.                 case 4:     /* Control-D */
  204. #ifndef CTRL_D_DELETE
  205.                     return 0;
  206. #else
  207.                     if (cursor != len) {
  208.                         input_delete(cursor);
  209.                         len--;
  210.                     }
  211.                     break;
  212. #endif
  213.                 case '\b':  /* Backspace */
  214.                 case DEL:
  215.                     input_backspace(&cursor, &len);
  216.                     break;
  217.                 case '\n':  /* Enter */
  218.                     *(parsenextc + len++ + 1) = c;
  219.                     out1c(c);
  220.                     flushout(&output);
  221.                     break_out = 1;
  222.                     break;
  223.                 case ESC:   /* escape sequence follows */
  224.                     if ((ret = read(fd, &c, 1)) < 1)
  225.                         return ret;
  226.                                        
  227.                     if (c == '[' || c == 'O' ) {    /* 91 */
  228.                         if ((ret = read(fd, &c, 1)) < 1)
  229.                             return ret;
  230.                        
  231.                         switch (c) {
  232.                             case 'A':
  233.                                 if (hp && hp->p) {      /* Up */
  234.                                     hp = hp->p;
  235.                                     goto hop;
  236.                                 }
  237.                                 break;
  238.                             case 'B':
  239.                                 if (hp && hp->n && hp->n->s) {  /* Down */
  240.                                     hp = hp->n;
  241.                                     goto hop;
  242.                                 }
  243.                                 break;
  244.  
  245. hop:                        /* hop */                          
  246.                                 len = strlen(parsenextc);
  247.  
  248.                                 for (; cursor > 0; cursor--)        /* return to begining of line */
  249.                                     out1c('\b');
  250.  
  251.                                 for (j = 0; j < len; j++)       /* erase old command */
  252.                                     out1c(' ');
  253.  
  254.                                 for (j = len; j > 0; j--)       /* return to begining of line */
  255.                                     out1c('\b');
  256.  
  257.                                 strcpy (parsenextc, hp->s);     /* write new command */
  258.                                 len = strlen (hp->s);
  259.                                 out1str(parsenextc);
  260.                                 flushout(&output);
  261.                                 cursor = len;
  262.                                 break;
  263.                             case 'C':       /* Right */
  264.                                     if (cursor < len) {
  265.                                     out1str("\033[C");
  266.                                     cursor++;
  267.                                     flushout(&output);
  268.                                 }
  269.                                 break;
  270.                             case 'D':       /* Left */
  271.                                 if (cursor > 0) {
  272.                                     out1str("\033[D");
  273.                                     cursor--;
  274.                                     flushout(&output);
  275.                                 }
  276.                                 break;
  277.                             case '3':       /* Delete */
  278.                                 if (cursor != len) {
  279.                                     input_delete(cursor);
  280.                                     len--;
  281.                                 }
  282.                                 break;                             
  283.                             case 'H':       /* Home (xterm) */
  284.                             case '1':       /* Home (Ctrl-A) */
  285.                                     input_home(&cursor);
  286.                                 break;
  287.                             case 'F':       /* End (xterm_ */
  288.                             case '4':       /* End (Ctrl-E) */
  289.                                 input_end(&cursor, len);
  290.                                 break;
  291.                         }
  292.                         if (c == '1' || c == '3' || c == '4')
  293.                             if ((ret = read(fd, &c, 1)) < 1)
  294.                                 return ret;  /* read 126 (~) */
  295.                     }
  296.            
  297.                     c = 0;
  298.                     break;
  299.        
  300.                 default:                /* If it's regular input, do the normal thing */
  301.            
  302.                     if (!isprint(c))        /* Skip non-printable characters */
  303.                         break;
  304.                                    
  305.                         if (len >= (BUFSIZ - 2))    /* Need to leave space for enter */
  306.                         break;
  307.                
  308.                     len++;
  309.            
  310.                     if (cursor == (len - 1)) {  /* Append if at the end of the line */
  311.                         *(parsenextc + cursor) = c;
  312.                     } else {            /* Insert otherwise */
  313.                         memmove(parsenextc + cursor + 1, parsenextc + cursor,
  314.                             len - cursor - 1);
  315.                    
  316.                         *(parsenextc + cursor) = c;
  317.            
  318.                         for (j = cursor; j < len; j++)
  319.                             out1c(*(parsenextc + j));
  320.                         for (; j > cursor; j--)
  321.                             out1str("\033[D");
  322.                     }
  323.        
  324.                     cursor++;
  325.                     out1c(c);
  326.                     flushout(&output);
  327.                     break;
  328.             }
  329.            
  330.             if (break_out)      /* Enter is the command terminator, no more input. */
  331.                 break;
  332.         }
  333.    
  334.         nr = len + 1;
  335.         tcsetattr(0, TCSANOW, &old_term);
  336.        
  337.        
  338.         if (*(parsenextc)) {        /* Handle command history log */
  339.             struct history *h = his_end;
  340.  
  341.             if (!h) {       /* No previous history */
  342.                 h = his_front = malloc(sizeof (struct history));
  343.                 h->n = malloc(sizeof (struct history));
  344.                 h->p = NULL;
  345.                 h->s = strdup(parsenextc);
  346.  
  347.                 h->n->p = h;
  348.                 h->n->n = NULL;
  349.                 h->n->s = NULL;
  350.                 his_end = h->n;
  351.                 history_counter++;
  352.             } else {    /* Add a new history command */
  353.  
  354.                 h->n = malloc(sizeof (struct history));
  355.  
  356.                 h->n->p = h;
  357.                 h->n->n = NULL;
  358.                 h->n->s = NULL;
  359.                 h->s = strdup(parsenextc);
  360.                 his_end = h->n;
  361.  
  362.                 if (history_counter >= MAX_HISTORY) {   /* After max history, remove the last known command */
  363.                     struct history *p = his_front->n;
  364.                    
  365.                     p->p = NULL;
  366.                     free(his_front->s);
  367.                     free(his_front);
  368.                     his_front = p;
  369.                 } else {
  370.                     history_counter++;
  371.                 }
  372.             }
  373.         }
  374.     }
  375.  
  376.     return nr;
  377. }
  378. #endif
  379.