20 |
* 1) In handling '\c' escape, the previous version only suppressed the |
* 1) In handling '\c' escape, the previous version only suppressed the |
21 |
* trailing newline. SUSv3 specifies _no_ output after '\c'. |
* trailing newline. SUSv3 specifies _no_ output after '\c'. |
22 |
* 2) SUSv3 specifies that octal escapes are of the form \0{#{#{#}}}. |
* 2) SUSv3 specifies that octal escapes are of the form \0{#{#{#}}}. |
23 |
* The previous version version did not allow 4-digit octals. |
* The previous version did not allow 4-digit octals. |
24 |
*/ |
*/ |
25 |
|
|
26 |
|
#include "libbb.h" |
27 |
|
|
28 |
#include <stdio.h> |
/* This is a NOFORK applet. Be very careful! */ |
|
#include <string.h> |
|
|
#include <stdlib.h> |
|
|
#include "busybox.h" |
|
29 |
|
|
30 |
int bb_echo(char **argv) |
/* NB: can be used by shell even if not enabled as applet */ |
31 |
|
|
32 |
|
int echo_main(int argc UNUSED_PARAM, char **argv) |
33 |
{ |
{ |
34 |
#ifndef CONFIG_FEATURE_FANCY_ECHO |
const char *arg; |
35 |
#define eflag '\\' |
#if !ENABLE_FEATURE_FANCY_ECHO |
36 |
++argv; |
enum { |
37 |
|
eflag = '\\', |
38 |
|
nflag = 1, /* 1 -- print '\n' */ |
39 |
|
}; |
40 |
|
|
41 |
|
/* We must check that stdout is not closed. |
42 |
|
* The reason for this is highly non-obvious. |
43 |
|
* echo_main is used from shell. Shell must correctly handle "echo foo" |
44 |
|
* if stdout is closed. With stdio, output gets shoveled into |
45 |
|
* stdout buffer, and even fflush cannot clear it out. It seems that |
46 |
|
* even if libc receives EBADF on write attempts, it feels determined |
47 |
|
* to output data no matter what. So it will try later, |
48 |
|
* and possibly will clobber future output. Not good. */ |
49 |
|
// TODO: check fcntl() & O_ACCMODE == O_WRONLY or O_RDWR? |
50 |
|
if (fcntl(1, F_GETFL) == -1) |
51 |
|
return 1; /* match coreutils 6.10 (sans error msg to stderr) */ |
52 |
|
//if (dup2(1, 1) != 1) - old way |
53 |
|
// return 1; |
54 |
|
|
55 |
|
arg = *++argv; |
56 |
|
if (!arg) |
57 |
|
goto newline_ret; |
58 |
#else |
#else |
59 |
const char *p; |
const char *p; |
60 |
int nflag = 1; |
char nflag = 1; |
61 |
int eflag = 0; |
char eflag = 0; |
62 |
|
|
63 |
|
/* We must check that stdout is not closed. */ |
64 |
|
if (fcntl(1, F_GETFL) == -1) |
65 |
|
return 1; |
66 |
|
|
67 |
|
while (1) { |
68 |
|
arg = *++argv; |
69 |
|
if (!arg) |
70 |
|
goto newline_ret; |
71 |
|
if (*arg != '-') |
72 |
|
break; |
73 |
|
|
|
while (*++argv && (**argv == '-')) { |
|
74 |
/* If it appears that we are handling options, then make sure |
/* If it appears that we are handling options, then make sure |
75 |
* that all of the options specified are actually valid. |
* that all of the options specified are actually valid. |
76 |
* Otherwise, the string should just be echoed. |
* Otherwise, the string should just be echoed. |
77 |
*/ |
*/ |
78 |
|
p = arg + 1; |
79 |
if (!*(p = *argv + 1)) { /* A single '-', so echo it. */ |
if (!*p) /* A single '-', so echo it. */ |
80 |
goto just_echo; |
goto just_echo; |
|
} |
|
81 |
|
|
82 |
do { |
do { |
83 |
if (strrchr("neE", *p) == 0) { |
if (!strrchr("neE", *p)) |
84 |
goto just_echo; |
goto just_echo; |
|
} |
|
85 |
} while (*++p); |
} while (*++p); |
86 |
|
|
87 |
/* All of the options in this arg are valid, so handle them. */ |
/* All of the options in this arg are valid, so handle them. */ |
88 |
p = *argv + 1; |
p = arg + 1; |
89 |
do { |
do { |
90 |
if (*p == 'n') { |
if (*p == 'n') |
91 |
nflag = 0; |
nflag = 0; |
92 |
} else if (*p == 'e') { |
if (*p == 'e') |
93 |
eflag = '\\'; |
eflag = '\\'; |
|
} else { |
|
|
eflag = 0; |
|
|
} |
|
94 |
} while (*++p); |
} while (*++p); |
95 |
} |
} |
96 |
|
just_echo: |
|
just_echo: |
|
97 |
#endif |
#endif |
98 |
while (*argv) { |
while (1) { |
99 |
|
/* arg is already == *argv and isn't NULL */ |
100 |
int c; |
int c; |
101 |
|
|
102 |
while ((c = *(*argv)++)) { |
if (!eflag) { |
103 |
|
/* optimization for very common case */ |
104 |
|
fputs(arg, stdout); |
105 |
|
} else while ((c = *arg++)) { |
106 |
if (c == eflag) { /* Check for escape seq. */ |
if (c == eflag) { /* Check for escape seq. */ |
107 |
if (**argv == 'c') { |
if (*arg == 'c') { |
108 |
/* '\c' means cancel newline and |
/* '\c' means cancel newline and |
109 |
* ignore all subsequent chars. */ |
* ignore all subsequent chars. */ |
110 |
return 0; |
goto ret; |
111 |
} |
} |
112 |
#ifndef CONFIG_FEATURE_FANCY_ECHO |
#if !ENABLE_FEATURE_FANCY_ECHO |
113 |
/* SUSv3 specifies that octal escapes must begin with '0'. */ |
/* SUSv3 specifies that octal escapes must begin with '0'. */ |
114 |
if (((unsigned int)(**argv - '1')) >= 7) |
if ( ((int)(unsigned char)(*arg) - '0') >= 8) /* '8' or bigger */ |
115 |
#endif |
#endif |
116 |
{ |
{ |
117 |
/* Since SUSv3 mandates a first digit of 0, 4-digit octals |
/* Since SUSv3 mandates a first digit of 0, 4-digit octals |
118 |
* of the form \0### are accepted. */ |
* of the form \0### are accepted. */ |
119 |
if ((**argv == '0') && (((unsigned int)(argv[0][1] - '0')) < 8)) { |
if (*arg == '0') { |
120 |
(*argv)++; |
/* NB: don't turn "...\0" into "...\" */ |
121 |
|
if (arg[1] && ((unsigned char)(arg[1]) - '0') < 8) { |
122 |
|
arg++; |
123 |
|
} |
124 |
} |
} |
125 |
/* bb_process_escape_sequence can handle nul correctly */ |
/* bb_process_escape_sequence handles NUL correctly |
126 |
c = bb_process_escape_sequence((const char **) argv); |
* ("...\" case). */ |
127 |
|
c = bb_process_escape_sequence(&arg); |
128 |
} |
} |
129 |
} |
} |
130 |
putchar(c); |
bb_putchar(c); |
131 |
} |
} |
132 |
|
|
133 |
if (*++argv) { |
arg = *++argv; |
134 |
putchar(' '); |
if (!arg) |
135 |
} |
break; |
136 |
|
bb_putchar(' '); |
137 |
} |
} |
138 |
|
|
139 |
#ifdef CONFIG_FEATURE_FANCY_ECHO |
newline_ret: |
140 |
if (nflag) { |
if (nflag) { |
141 |
putchar('\n'); |
bb_putchar('\n'); |
142 |
} |
} |
143 |
#else |
ret: |
144 |
putchar('\n'); |
return fflush_all(); |
|
#endif |
|
|
return 0; |
|
|
} |
|
|
|
|
|
int echo_main(int argc, char** argv) |
|
|
{ |
|
|
(void)bb_echo(argv); |
|
|
fflush_stdout_and_exit(EXIT_SUCCESS); |
|
145 |
} |
} |
146 |
|
|
147 |
/*- |
/*- |
182 |
* |
* |
183 |
* @(#)echo.c 8.1 (Berkeley) 5/31/93 |
* @(#)echo.c 8.1 (Berkeley) 5/31/93 |
184 |
*/ |
*/ |
185 |
|
|
186 |
|
#ifdef VERSION_WITH_WRITEV |
187 |
|
/* We can't use stdio. |
188 |
|
* The reason for this is highly non-obvious. |
189 |
|
* echo_main is used from shell. Shell must correctly handle "echo foo" |
190 |
|
* if stdout is closed. With stdio, output gets shoveled into |
191 |
|
* stdout buffer, and even fflush cannot clear it out. It seems that |
192 |
|
* even if libc receives EBADF on write attempts, it feels determined |
193 |
|
* to output data no matter what. So it will try later, |
194 |
|
* and possibly will clobber future output. Not good. |
195 |
|
* |
196 |
|
* Using writev instead, with 'direct' conversion of argv vector. |
197 |
|
*/ |
198 |
|
|
199 |
|
int echo_main(int argc, char **argv) |
200 |
|
{ |
201 |
|
struct iovec io[argc]; |
202 |
|
struct iovec *cur_io = io; |
203 |
|
char *arg; |
204 |
|
char *p; |
205 |
|
#if !ENABLE_FEATURE_FANCY_ECHO |
206 |
|
enum { |
207 |
|
eflag = '\\', |
208 |
|
nflag = 1, /* 1 -- print '\n' */ |
209 |
|
}; |
210 |
|
arg = *++argv; |
211 |
|
if (!arg) |
212 |
|
goto newline_ret; |
213 |
|
#else |
214 |
|
char nflag = 1; |
215 |
|
char eflag = 0; |
216 |
|
|
217 |
|
while (1) { |
218 |
|
arg = *++argv; |
219 |
|
if (!arg) |
220 |
|
goto newline_ret; |
221 |
|
if (*arg != '-') |
222 |
|
break; |
223 |
|
|
224 |
|
/* If it appears that we are handling options, then make sure |
225 |
|
* that all of the options specified are actually valid. |
226 |
|
* Otherwise, the string should just be echoed. |
227 |
|
*/ |
228 |
|
p = arg + 1; |
229 |
|
if (!*p) /* A single '-', so echo it. */ |
230 |
|
goto just_echo; |
231 |
|
|
232 |
|
do { |
233 |
|
if (!strrchr("neE", *p)) |
234 |
|
goto just_echo; |
235 |
|
} while (*++p); |
236 |
|
|
237 |
|
/* All of the options in this arg are valid, so handle them. */ |
238 |
|
p = arg + 1; |
239 |
|
do { |
240 |
|
if (*p == 'n') |
241 |
|
nflag = 0; |
242 |
|
if (*p == 'e') |
243 |
|
eflag = '\\'; |
244 |
|
} while (*++p); |
245 |
|
} |
246 |
|
just_echo: |
247 |
|
#endif |
248 |
|
|
249 |
|
while (1) { |
250 |
|
/* arg is already == *argv and isn't NULL */ |
251 |
|
int c; |
252 |
|
|
253 |
|
cur_io->iov_base = p = arg; |
254 |
|
|
255 |
|
if (!eflag) { |
256 |
|
/* optimization for very common case */ |
257 |
|
p += strlen(arg); |
258 |
|
} else while ((c = *arg++)) { |
259 |
|
if (c == eflag) { /* Check for escape seq. */ |
260 |
|
if (*arg == 'c') { |
261 |
|
/* '\c' means cancel newline and |
262 |
|
* ignore all subsequent chars. */ |
263 |
|
cur_io->iov_len = p - (char*)cur_io->iov_base; |
264 |
|
cur_io++; |
265 |
|
goto ret; |
266 |
|
} |
267 |
|
#if !ENABLE_FEATURE_FANCY_ECHO |
268 |
|
/* SUSv3 specifies that octal escapes must begin with '0'. */ |
269 |
|
if ( (((unsigned char)*arg) - '1') >= 7) |
270 |
|
#endif |
271 |
|
{ |
272 |
|
/* Since SUSv3 mandates a first digit of 0, 4-digit octals |
273 |
|
* of the form \0### are accepted. */ |
274 |
|
if (*arg == '0' && ((unsigned char)(arg[1]) - '0') < 8) { |
275 |
|
arg++; |
276 |
|
} |
277 |
|
/* bb_process_escape_sequence can handle nul correctly */ |
278 |
|
c = bb_process_escape_sequence( (void*) &arg); |
279 |
|
} |
280 |
|
} |
281 |
|
*p++ = c; |
282 |
|
} |
283 |
|
|
284 |
|
arg = *++argv; |
285 |
|
if (arg) |
286 |
|
*p++ = ' '; |
287 |
|
cur_io->iov_len = p - (char*)cur_io->iov_base; |
288 |
|
cur_io++; |
289 |
|
if (!arg) |
290 |
|
break; |
291 |
|
} |
292 |
|
|
293 |
|
newline_ret: |
294 |
|
if (nflag) { |
295 |
|
cur_io->iov_base = (char*)"\n"; |
296 |
|
cur_io->iov_len = 1; |
297 |
|
cur_io++; |
298 |
|
} |
299 |
|
ret: |
300 |
|
/* TODO: implement and use full_writev? */ |
301 |
|
return writev(1, io, (cur_io - io)) >= 0; |
302 |
|
} |
303 |
|
#endif |