aboutsummaryrefslogtreecommitdiff
path: root/coreutils/echo.c
diff options
context:
space:
mode:
Diffstat (limited to 'coreutils/echo.c')
-rw-r--r--coreutils/echo.c185
1 files changed, 97 insertions, 88 deletions
diff --git a/coreutils/echo.c b/coreutils/echo.c
index 3821e594e..72e18c161 100644
--- a/coreutils/echo.c
+++ b/coreutils/echo.c
@@ -29,122 +29,135 @@
29 29
30/* NB: can be used by shell even if not enabled as applet */ 30/* NB: can be used by shell even if not enabled as applet */
31 31
32/*
33 * NB2: we don't use stdio, we need better error handing.
34 * Examples include writing into non-opened stdout and error on write.
35 *
36 * With stdio, output gets shoveled into stdout buffer, and even
37 * fflush cannot clear it out. It seems that even if libc receives
38 * EBADF on write attempts, it feels determined to output data no matter what.
39 * If echo is called by shell, it will try writing again later, and possibly
40 * will clobber future output. Not good.
41 *
42 * Solaris has fpurge which discards buffered input. glibc has __fpurge.
43 * But this function is not standard.
44 */
45
32int echo_main(int argc UNUSED_PARAM, char **argv) 46int echo_main(int argc UNUSED_PARAM, char **argv)
33{ 47{
48 char **pp;
34 const char *arg; 49 const char *arg;
50 char *out;
51 char *buffer;
52 unsigned buflen;
35#if !ENABLE_FEATURE_FANCY_ECHO 53#if !ENABLE_FEATURE_FANCY_ECHO
36 enum { 54 enum {
37 eflag = '\\', 55 eflag = '\\',
38 nflag = 1, /* 1 -- print '\n' */ 56 nflag = 1, /* 1 -- print '\n' */
39 }; 57 };
40 58
41 /* We must check that stdout is not closed. 59 argv++;
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 60#else
59 const char *p;
60 char nflag = 1; 61 char nflag = 1;
61 char eflag = 0; 62 char eflag = 0;
62 63
63 /* We must check that stdout is not closed. */ 64 while ((arg = *++argv) != NULL) {
64 if (fcntl(1, F_GETFL) == -1) 65 char n, e;
65 return 1;
66 66
67 while (1) { 67 if (arg[0] != '-')
68 arg = *++argv; 68 break; /* not an option arg, echo it */
69 if (!arg)
70 goto newline_ret;
71 if (*arg != '-')
72 break;
73 69
74 /* If it appears that we are handling options, then make sure 70 /* If it appears that we are handling options, then make sure
75 * that all of the options specified are actually valid. 71 * that all of the options specified are actually valid.
76 * Otherwise, the string should just be echoed. 72 * Otherwise, the string should just be echoed.
77 */ 73 */
78 p = arg + 1; 74 arg++;
79 if (!*p) /* A single '-', so echo it. */ 75 n = nflag;
80 goto just_echo; 76 e = eflag;
81
82 do { 77 do {
83 if (!strrchr("neE", *p)) 78 if (*arg == 'n')
79 n = 0;
80 else if (*arg == 'e')
81 e = '\\';
82 else if (*arg != 'E') {
83 /* "-ccc" arg with one of c's invalid, echo it */
84 /* arg consisting from just "-" also handled here */
84 goto just_echo; 85 goto just_echo;
85 } while (*++p); 86 }
86 87 } while (*++arg);
87 /* All of the options in this arg are valid, so handle them. */ 88 nflag = n;
88 p = arg + 1; 89 eflag = e;
89 do {
90 if (*p == 'n')
91 nflag = 0;
92 if (*p == 'e')
93 eflag = '\\';
94 } while (*++p);
95 } 90 }
96 just_echo: 91 just_echo:
97#endif 92#endif
98 while (1) { 93
99 /* arg is already == *argv and isn't NULL */ 94 buflen = 0;
95 pp = argv;
96 while ((arg = *pp) != NULL) {
97 buflen += strlen(arg) + 1;
98 pp++;
99 }
100 out = buffer = xmalloc(buflen + 1); /* +1 is needed for "no args" case */
101
102 while ((arg = *argv) != NULL) {
100 int c; 103 int c;
101 104
102 if (!eflag) { 105 if (!eflag) {
103 /* optimization for very common case */ 106 /* optimization for very common case */
104 fputs(arg, stdout); 107 out = stpcpy(out, arg);
105 } else while ((c = *arg++)) { 108 } else
106 if (c == eflag) { /* Check for escape seq. */ 109 while ((c = *arg++) != '\0') {
110 if (c == eflag) {
111 /* This is an "\x" sequence */
112
107 if (*arg == 'c') { 113 if (*arg == 'c') {
108 /* '\c' means cancel newline and 114 /* "\c" means cancel newline and
109 * ignore all subsequent chars. */ 115 * ignore all subsequent chars. */
110 goto ret; 116 goto do_write;
111 } 117 }
112#if !ENABLE_FEATURE_FANCY_ECHO 118 /* Since SUSv3 mandates a first digit of 0, 4-digit octals
113 /* SUSv3 specifies that octal escapes must begin with '0'. */ 119 * of the form \0### are accepted. */
114 if ( ((int)(unsigned char)(*arg) - '0') >= 8) /* '8' or bigger */ 120 if (*arg == '0') {
115#endif 121 if ((unsigned char)(arg[1] - '0') < 8) {
116 { 122 /* 2nd char is 0..7: skip leading '0' */
117 /* Since SUSv3 mandates a first digit of 0, 4-digit octals 123 arg++;
118 * of the form \0### are accepted. */
119 if (*arg == '0') {
120 /* NB: don't turn "...\0" into "...\" */
121 if (arg[1] && ((unsigned char)(arg[1]) - '0') < 8) {
122 arg++;
123 }
124 } 124 }
125 /* bb_process_escape_sequence handles NUL correctly 125 }
126 * ("...\" case). */ 126 /* bb_process_escape_sequence handles NUL correctly
127 c = bb_process_escape_sequence(&arg); 127 * ("...\" case). */
128 {
129 /* optimization: don't force arg to be on-stack,
130 * use another variable for that. ~30 bytes win */
131 const char *z = arg;
132 c = bb_process_escape_sequence(&z);
133 arg = z;
128 } 134 }
129 } 135 }
130 bb_putchar(c); 136 *out++ = c;
131 } 137 }
132 138
133 arg = *++argv; 139 if (!*++argv)
134 if (!arg)
135 break; 140 break;
136 bb_putchar(' '); 141 *out++ = ' ';
137 } 142 }
138 143
139 newline_ret:
140 if (nflag) { 144 if (nflag) {
141 bb_putchar('\n'); 145 *out++ = '\n';
142 } 146 }
143 ret: 147
144 return fflush_all(); 148 do_write:
149 /* Careful to error out on partial writes too (think ENOSPC!) */
150 errno = 0;
151 /*r =*/ full_write(STDOUT_FILENO, buffer, out - buffer);
152 free(buffer);
153 if (/*WRONG:r < 0*/ errno) {
154 bb_perror_msg(bb_msg_write_error);
155 return 1;
156 }
157 return 0;
145} 158}
146 159
147/*- 160/*
148 * Copyright (c) 1991, 1993 161 * Copyright (c) 1991, 1993
149 * The Regents of the University of California. All rights reserved. 162 * The Regents of the University of California. All rights reserved.
150 * 163 *
@@ -230,7 +243,7 @@ int echo_main(int argc, char **argv)
230 goto just_echo; 243 goto just_echo;
231 244
232 do { 245 do {
233 if (!strrchr("neE", *p)) 246 if (!strchr("neE", *p))
234 goto just_echo; 247 goto just_echo;
235 } while (*++p); 248 } while (*++p);
236 249
@@ -256,27 +269,23 @@ int echo_main(int argc, char **argv)
256 /* optimization for very common case */ 269 /* optimization for very common case */
257 p += strlen(arg); 270 p += strlen(arg);
258 } else while ((c = *arg++)) { 271 } else while ((c = *arg++)) {
259 if (c == eflag) { /* Check for escape seq. */ 272 if (c == eflag) {
273 /* This is an "\x" sequence */
274
260 if (*arg == 'c') { 275 if (*arg == 'c') {
261 /* '\c' means cancel newline and 276 /* "\c" means cancel newline and
262 * ignore all subsequent chars. */ 277 * ignore all subsequent chars. */
263 cur_io->iov_len = p - (char*)cur_io->iov_base; 278 cur_io->iov_len = p - (char*)cur_io->iov_base;
264 cur_io++; 279 cur_io++;
265 goto ret; 280 goto ret;
266 } 281 }
267#if !ENABLE_FEATURE_FANCY_ECHO 282 /* Since SUSv3 mandates a first digit of 0, 4-digit octals
268 /* SUSv3 specifies that octal escapes must begin with '0'. */ 283 * of the form \0### are accepted. */
269 if ( (((unsigned char)*arg) - '1') >= 7) 284 if (*arg == '0' && (unsigned char)(arg[1] - '0') < 8) {
270#endif 285 arg++;
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 } 286 }
287 /* bb_process_escape_sequence can handle nul correctly */
288 c = bb_process_escape_sequence( (void*) &arg);
280 } 289 }
281 *p++ = c; 290 *p++ = c;
282 } 291 }