committing:
authorPaul Fox <pgf@brightstareng.com>
Wed, 7 Sep 2005 16:56:02 +0000 (16:56 -0000)
committerPaul Fox <pgf@brightstareng.com>
Wed, 7 Sep 2005 16:56:02 +0000 (16:56 -0000)
       Summary      0000242: ash: read -t broken
this also implements -n and -s options to read.  (they're configured
together because most of their code is in common, and separating them
seemed silly.

shell/Config.in
shell/ash.c

index f9fb848..eb46166 100644 (file)
@@ -53,16 +53,22 @@ config CONFIG_ASH_JOB_CONTROL
        help
          Enable job control in the ash shell.
 
-config CONFIG_ASH_TIMEOUT
-        bool "  Enable read timeout support."
+config CONFIG_ASH_READ_NCHARS
+        bool "  Enable 'read -n N' and 'read -s' support"
         default n
-        depends on CONFIG_ASH_JOB_CONTROL
+        depends on CONFIG_ASH
         help
-          This option provides read -t <seconds> support.
+         'read -n N' will return a value after N characters have been read.
+         'read -s' will read without echoing the user's input.
 
-         read builtin which allows the function to pass control back 
-         if no character input is read from the terminal within a set
-         number of seconds.
+config CONFIG_ASH_READ_TIMEOUT
+        bool "  Enable 'read -t S' support."
+        default n
+        depends on CONFIG_ASH
+        help
+         'read -t S' will return a value after S seconds have passed.
+         This implementation will allow fractional seconds, expressed
+         as a decimal fraction, e.g. 'read -t 2.5 foo'.
 
 config CONFIG_ASH_ALIAS
        bool "  Enable alias support"
index 5f6f7c6..7271535 100644 (file)
 #undef JOBS
 #endif
 
-#if JOBS
+#if JOBS || defined(CONFIG_ASH_READ_NCHARS)
 #include <termios.h>
 #endif
 
@@ -12598,34 +12598,80 @@ readcmd(int argc, char **argv)
        char *prompt;
        const char *ifs;
        char *p;
-#if defined(CONFIG_ASH_TIMEOUT)
-       fd_set set;
-       int timeout;
-       struct timeval timeout_struct;
-       struct termios tty, old_tty;
-#endif
        int startword;
        int status;
        int i;
+#if defined(CONFIG_ASH_READ_NCHARS)
+       int nch_flag = 0;
+       int nchars = 0;
+       int silent = 0;
+       struct termios tty, old_tty;
+#endif
+#if defined(CONFIG_ASH_READ_TIMEOUT)
+       fd_set set;
+       struct timeval ts;
+
+       ts.tv_sec = ts.tv_usec = 0;
+#endif
 
        rflag = 0;
        prompt = NULL;
-#if defined(CONFIG_ASH_TIMEOUT)
-       timeout = 0;
-
+#if defined(CONFIG_ASH_READ_NCHARS) && defined(CONFIG_ASH_READ_TIMEOUT)
+       while ((i = nextopt("p:rt:n:s")) != '\0')
+#elif defined(CONFIG_ASH_READ_NCHARS)
+       while ((i = nextopt("p:rn:s")) != '\0')
+#elif defined(CONFIG_ASH_READ_TIMEOUT)
        while ((i = nextopt("p:rt:")) != '\0')
 #else
        while ((i = nextopt("p:r")) != '\0')
 #endif
        {
-               if (i == 'p')
+               switch(i) {
+               case 'p':
                        prompt = optionarg;
-               else if (i == 'r')
-                       rflag = 1;
-#if defined(CONFIG_ASH_TIMEOUT)
-               else
-                       timeout = atoi(optionarg);
+                       break;
+#if defined(CONFIG_ASH_READ_NCHARS)
+               case 'n':
+                       nchars = strtol(optionarg, &p, 10);
+                       if (*p)
+                               error("invalid count");
+                       nch_flag = (nchars > 0);
+                       break;
+               case 's':
+                       silent = 1;
+                       break;
 #endif
+#if defined(CONFIG_ASH_READ_TIMEOUT)
+               case 't':
+                       ts.tv_sec = strtol(optionarg, &p, 10);
+                       ts.tv_usec = 0;
+                       if (*p == '.') {
+                               char *p2;
+                               if (*++p) {
+                                       int scale;
+                                       ts.tv_usec = strtol(p, &p2, 10);
+                                       if (*p2)
+                                               error("invalid timeout");
+                                       scale = p2 - p;
+                                       /* normalize to usec */
+                                       if (scale > 6)
+                                               error("invalid timeout");
+                                       while (scale++ < 6)
+                                               ts.tv_usec *= 10;
+                               }
+                       } else if (*p) {
+                               error("invalid timeout");
+                       }
+                       if ( ! ts.tv_sec && ! ts.tv_usec)
+                               error("invalid timeout");
+                       break;
+#endif
+               case 'r':
+                       rflag = 1;
+                       break;
+               default:
+                       break;
+               }
        }
        if (prompt && isatty(0)) {
                out2str(prompt);
@@ -12634,49 +12680,42 @@ readcmd(int argc, char **argv)
                error("arg count");
        if ((ifs = bltinlookup("IFS")) == NULL)
                ifs = defifs;
-#if defined(CONFIG_ASH_TIMEOUT)
-       c = 0;
-#endif
-       status = 0;
-       startword = 1;
-       backslash = 0;
-
-       STARTSTACKSTR(p);
-#if defined(CONFIG_ASH_TIMEOUT)
-       if (timeout > 0) {
+#if defined(CONFIG_ASH_READ_NCHARS)
+       if (nch_flag || silent) {
                tcgetattr(0, &tty);
                old_tty = tty;
+               if (nch_flag) {
+                   tty.c_lflag &= ~ICANON;
+                   tty.c_cc[VMIN] = nchars;
+               }
+               if (silent) {
+                   tty.c_lflag &= ~(ECHO|ECHOK|ECHONL);
 
-               /* cfmakeraw(...) disables too much; we just do this instead. */
-               tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN);
+               }
                tcsetattr(0, TCSANOW, &tty);
-
+       }
+#endif
+#if defined(CONFIG_ASH_READ_TIMEOUT)
+       if (ts.tv_sec || ts.tv_usec) {
                FD_ZERO (&set);
                FD_SET (0, &set);
 
-               timeout_struct.tv_sec = timeout;
-               timeout_struct.tv_usec = 0;
-
-               if ((i = select (FD_SETSIZE, &set, NULL, NULL, &timeout_struct)) == 1)
-               {
-                       read(0, &c, 1);
-                       if(c == '\n' || c == 4) /* Handle newlines and EOF */
-                               i = 0; /* Don't read further... */
-                       else
-                               STPUTC(c, p); /* Keep reading... */
+               i = select (FD_SETSIZE, &set, NULL, NULL, &ts);
+               if (!i) {
+#if defined(CONFIG_ASH_READ_NCHARS)
+                       if (nch_flag)
+                               tcsetattr(0, TCSANOW, &old_tty);
+#endif
+                       return 1;
                }
-               tcsetattr(0, TCSANOW, &old_tty);
-
-               /* Echo the character so the user knows it was read...
-                  Yes, this can be done by setting the ECHO flag, but that
-                  echoes ^D and other control characters at this state */
-               if(c != 0)
-                       write(1, &c, 1);
-
-       } else
-               i = 1;
-
-       for (;i == 1;)
+       }
+#endif
+       status = 0;
+       startword = 1;
+       backslash = 0;
+       STARTSTACKSTR(p);
+#if defined(CONFIG_ASH_READ_NCHARS)
+       while (!nch_flag || nchars--)
 #else
        for (;;)
 #endif
@@ -12714,6 +12753,11 @@ put:
                        STPUTC(c, p);
                }
        }
+#if defined(CONFIG_ASH_READ_NCHARS)
+       if (nch_flag || silent)
+               tcsetattr(0, TCSANOW, &old_tty);
+#endif
+
        STACKSTRNUL(p);
        /* Remove trailing blanks */
        while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)