blob: 954e4cd14c94a8bc9b6d0fe34957b3539b381f59 [file] [log] [blame]
Denys Vlasenko73067272010-01-12 22:11:24 +01001/* vi: set sw=4 ts=4: */
2/*
3 * Adapted from ash applet code
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Kenneth Almquist.
7 *
8 * Copyright (c) 1989, 1991, 1993, 1994
9 * The Regents of the University of California. All rights reserved.
10 *
11 * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
12 * was re-ported from NetBSD and debianized.
13 *
14 * Copyright (c) 2010 Denys Vlasenko
15 * Split from ash.c
16 *
17 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
18 */
19#include "libbb.h"
20#include "shell_common.h"
21#include "builtin_read.h"
22
Denys Vlasenko03d81ef2010-01-13 14:53:49 +010023//TODO: use more efficient setvar() which takes a pointer to malloced "VAR=VAL"
24//string. hush naturally has it, and ash has setvareq().
25//Here we can simply store "VAR=" at buffer start and store read data directly
26//after "=", then pass buffer to setvar() to consume.
27
Denys Vlasenko73067272010-01-12 22:11:24 +010028const char* FAST_FUNC
Denys Vlasenko03dad222010-01-12 23:29:57 +010029shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
Denys Vlasenko73067272010-01-12 22:11:24 +010030 char **argv,
31 const char *ifs,
32 int read_flags,
33 const char *opt_n,
34 const char *opt_p,
35 const char *opt_t,
36 const char *opt_u
37)
38{
Denys Vlasenko73067272010-01-12 22:11:24 +010039 unsigned end_ms; /* -t TIMEOUT */
40 int fd; /* -u FD */
41 int nchars; /* -n NUM */
Denys Vlasenko25d9b912010-01-13 18:22:35 +010042 char **pp;
Denys Vlasenko73067272010-01-12 22:11:24 +010043 char *buffer;
44 struct termios tty, old_tty;
45 const char *retval;
46 int bufpos; /* need to be able to hold -1 */
47 int startword;
48 smallint backslash;
49
Denys Vlasenko25d9b912010-01-13 18:22:35 +010050 pp = argv;
51 while (*pp) {
52 if (!is_well_formed_var_name(*pp, '\0')) {
53 /* Mimic bash message */
54 bb_error_msg("read: '%s': not a valid identifier", *pp);
55 return (const char *)(uintptr_t)1;
56 }
57 pp++;
58 }
59
Denys Vlasenko73067272010-01-12 22:11:24 +010060 nchars = 0; /* if != 0, -n is in effect */
61 if (opt_n) {
62 nchars = bb_strtou(opt_n, NULL, 10);
63 if (nchars < 0 || errno)
64 return "invalid count";
65 /* note: "-n 0": off (bash 3.2 does this too) */
66 }
67 end_ms = 0;
68 if (opt_t) {
69 end_ms = bb_strtou(opt_t, NULL, 10);
70 if (errno || end_ms > UINT_MAX / 2048)
71 return "invalid timeout";
72 end_ms *= 1000;
73#if 0 /* even bash has no -t N.NNN support */
74 ts.tv_sec = bb_strtou(opt_t, &p, 10);
75 ts.tv_usec = 0;
76 /* EINVAL means number is ok, but not terminated by NUL */
77 if (*p == '.' && errno == EINVAL) {
78 char *p2;
79 if (*++p) {
80 int scale;
81 ts.tv_usec = bb_strtou(p, &p2, 10);
82 if (errno)
83 return "invalid timeout";
84 scale = p2 - p;
85 /* normalize to usec */
86 if (scale > 6)
87 return "invalid timeout";
88 while (scale++ < 6)
89 ts.tv_usec *= 10;
90 }
91 } else if (ts.tv_sec < 0 || errno) {
92 return "invalid timeout";
93 }
94 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
95 return "invalid timeout";
96 }
97#endif /* if 0 */
98 }
99 fd = STDIN_FILENO;
100 if (opt_u) {
101 fd = bb_strtou(opt_u, NULL, 10);
102 if (fd < 0 || errno)
103 return "invalid file descriptor";
104 }
105
106 if (opt_p && isatty(fd)) {
107 fputs(opt_p, stderr);
108 fflush_all();
109 }
110
Denys Vlasenko73067272010-01-12 22:11:24 +0100111 if (ifs == NULL)
112 ifs = defifs;
113
114 if (nchars || (read_flags & BUILTIN_READ_SILENT)) {
115 tcgetattr(fd, &tty);
116 old_tty = tty;
117 if (nchars) {
118 tty.c_lflag &= ~ICANON;
119 tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
120 }
121 if (read_flags & BUILTIN_READ_SILENT) {
122 tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
123 }
124 /* This forces execution of "restoring" tcgetattr later */
125 read_flags |= BUILTIN_READ_SILENT;
126 /* if tcgetattr failed, tcsetattr will fail too.
127 * Ignoring, it's harmless. */
128 tcsetattr(fd, TCSANOW, &tty);
129 }
130
131 retval = (const char *)(uintptr_t)0;
132 startword = 1;
133 backslash = 0;
134 if (end_ms) /* NB: end_ms stays nonzero: */
135 end_ms = ((unsigned)monotonic_ms() + end_ms) | 1;
136 buffer = NULL;
137 bufpos = 0;
138 do {
139 char c;
Denys Vlasenko73067272010-01-12 22:11:24 +0100140
141 if (end_ms) {
142 int timeout;
143 struct pollfd pfd[1];
144
145 pfd[0].fd = fd;
146 pfd[0].events = POLLIN;
147 timeout = end_ms - (unsigned)monotonic_ms();
148 if (timeout <= 0 /* already late? */
149 || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */
150 ) { /* timed out! */
151 retval = (const char *)(uintptr_t)1;
152 goto ret;
153 }
154 }
155
156 if ((bufpos & 0xff) == 0)
157 buffer = xrealloc(buffer, bufpos + 0x100);
158 if (nonblock_safe_read(fd, &buffer[bufpos], 1) != 1) {
159 retval = (const char *)(uintptr_t)1;
160 break;
161 }
162 c = buffer[bufpos];
163 if (c == '\0')
164 continue;
165 if (backslash) {
166 backslash = 0;
167 if (c != '\n')
168 goto put;
169 continue;
170 }
171 if (!(read_flags & BUILTIN_READ_RAW) && c == '\\') {
172 backslash = 1;
173 continue;
174 }
175 if (c == '\n')
176 break;
Denys Vlasenko045f4ad2010-01-12 22:12:10 +0100177
178 /* $IFS splitting. NOT done if we run "read"
179 * without variable names (bash compat).
180 * Thus, "read" and "read REPLY" are not the same.
181 */
182 if (argv[0]) {
Denys Vlasenko73067272010-01-12 22:11:24 +0100183/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 */
Denys Vlasenko045f4ad2010-01-12 22:12:10 +0100184 const char *is_ifs = strchr(ifs, c);
185 if (startword && is_ifs) {
186 if (isspace(c))
187 continue;
188 /* it is a non-space ifs char */
189 startword--;
190 if (startword == 1) /* first one? */
191 continue; /* yes, it is not next word yet */
192 }
193 startword = 0;
194 if (argv[1] != NULL && is_ifs) {
195 buffer[bufpos] = '\0';
196 bufpos = 0;
Denys Vlasenko03dad222010-01-12 23:29:57 +0100197 setvar(*argv, buffer);
Denys Vlasenko045f4ad2010-01-12 22:12:10 +0100198 argv++;
199 /* can we skip one non-space ifs char? (2: yes) */
200 startword = isspace(c) ? 2 : 1;
Denys Vlasenko73067272010-01-12 22:11:24 +0100201 continue;
Denys Vlasenko045f4ad2010-01-12 22:12:10 +0100202 }
Denys Vlasenko73067272010-01-12 22:11:24 +0100203 }
204 put:
205 bufpos++;
206 } while (--nchars);
207
Denys Vlasenko045f4ad2010-01-12 22:12:10 +0100208 if (argv[0]) {
209 /* Remove trailing space $IFS chars */
210 while (--bufpos >= 0 && isspace(buffer[bufpos]) && strchr(ifs, buffer[bufpos]) != NULL)
211 continue;
212 buffer[bufpos + 1] = '\0';
213 /* Use the remainder as a value for the next variable */
Denys Vlasenko03dad222010-01-12 23:29:57 +0100214 setvar(*argv, buffer);
Denys Vlasenko045f4ad2010-01-12 22:12:10 +0100215 /* Set the rest to "" */
216 while (*++argv)
Denys Vlasenko03dad222010-01-12 23:29:57 +0100217 setvar(*argv, "");
Denys Vlasenko045f4ad2010-01-12 22:12:10 +0100218 } else {
219 /* Note: no $IFS removal */
220 buffer[bufpos] = '\0';
Denys Vlasenko03dad222010-01-12 23:29:57 +0100221 setvar("REPLY", buffer);
Denys Vlasenko045f4ad2010-01-12 22:12:10 +0100222 }
Denys Vlasenko73067272010-01-12 22:11:24 +0100223
Denys Vlasenko73067272010-01-12 22:11:24 +0100224 ret:
225 free(buffer);
226 if (read_flags & BUILTIN_READ_SILENT)
227 tcsetattr(fd, TCSANOW, &old_tty);
228 return retval;
229}