72e18c161209419894528f91e30a53885a16edb8
[oweals/busybox.git] / coreutils / echo.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * echo implementation for busybox
4  *
5  * Copyright (c) 1991, 1993
6  *      The Regents of the University of California.  All rights reserved.
7  *
8  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
9  *
10  * Original copyright notice is retained at the end of this file.
11  */
12
13 /* BB_AUDIT SUSv3 compliant -- unless configured as fancy echo. */
14 /* http://www.opengroup.org/onlinepubs/007904975/utilities/echo.html */
15
16 /* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
17  *
18  * Because of behavioral differences, implemented configurable SUSv3
19  * or 'fancy' gnu-ish behaviors.  Also, reduced size and fixed bugs.
20  * 1) In handling '\c' escape, the previous version only suppressed the
21  *     trailing newline.  SUSv3 specifies _no_ output after '\c'.
22  * 2) SUSv3 specifies that octal escapes are of the form \0{#{#{#}}}.
23  *    The previous version did not allow 4-digit octals.
24  */
25
26 #include "libbb.h"
27
28 /* This is a NOFORK applet. Be very careful! */
29
30 /* NB: can be used by shell even if not enabled as applet */
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
46 int echo_main(int argc UNUSED_PARAM, char **argv)
47 {
48         char **pp;
49         const char *arg;
50         char *out;
51         char *buffer;
52         unsigned buflen;
53 #if !ENABLE_FEATURE_FANCY_ECHO
54         enum {
55                 eflag = '\\',
56                 nflag = 1,  /* 1 -- print '\n' */
57         };
58
59         argv++;
60 #else
61         char nflag = 1;
62         char eflag = 0;
63
64         while ((arg = *++argv) != NULL) {
65                 char n, e;
66
67                 if (arg[0] != '-')
68                         break; /* not an option arg, echo it */
69
70                 /* If it appears that we are handling options, then make sure
71                  * that all of the options specified are actually valid.
72                  * Otherwise, the string should just be echoed.
73                  */
74                 arg++;
75                 n = nflag;
76                 e = eflag;
77                 do {
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 */
85                                 goto just_echo;
86                         }
87                 } while (*++arg);
88                 nflag = n;
89                 eflag = e;
90         }
91  just_echo:
92 #endif
93
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) {
103                 int c;
104
105                 if (!eflag) {
106                         /* optimization for very common case */
107                         out = stpcpy(out, arg);
108                 } else
109                 while ((c = *arg++) != '\0') {
110                         if (c == eflag) {
111                                 /* This is an "\x" sequence */
112
113                                 if (*arg == 'c') {
114                                         /* "\c" means cancel newline and
115                                          * ignore all subsequent chars. */
116                                         goto do_write;
117                                 }
118                                 /* Since SUSv3 mandates a first digit of 0, 4-digit octals
119                                 * of the form \0### are accepted. */
120                                 if (*arg == '0') {
121                                         if ((unsigned char)(arg[1] - '0') < 8) {
122                                                 /* 2nd char is 0..7: skip leading '0' */
123                                                 arg++;
124                                         }
125                                 }
126                                 /* bb_process_escape_sequence handles NUL correctly
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;
134                                 }
135                         }
136                         *out++ = c;
137                 }
138
139                 if (!*++argv)
140                         break;
141                 *out++ = ' ';
142         }
143
144         if (nflag) {
145                 *out++ = '\n';
146         }
147
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;
158 }
159
160 /*
161  * Copyright (c) 1991, 1993
162  *      The Regents of the University of California.  All rights reserved.
163  *
164  * This code is derived from software contributed to Berkeley by
165  * Kenneth Almquist.
166  *
167  * Redistribution and use in source and binary forms, with or without
168  * modification, are permitted provided that the following conditions
169  * are met:
170  * 1. Redistributions of source code must retain the above copyright
171  *    notice, this list of conditions and the following disclaimer.
172  * 2. Redistributions in binary form must reproduce the above copyright
173  *    notice, this list of conditions and the following disclaimer in the
174  *    documentation and/or other materials provided with the distribution.
175  *
176  * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
177  *              ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
178  *
179  *      California, Berkeley and its contributors.
180  * 4. Neither the name of the University nor the names of its contributors
181  *    may be used to endorse or promote products derived from this software
182  *    without specific prior written permission.
183  *
184  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
185  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
186  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
187  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
188  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
189  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
190  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
191  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
192  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
193  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
194  * SUCH DAMAGE.
195  *
196  *      @(#)echo.c      8.1 (Berkeley) 5/31/93
197  */
198
199 #ifdef VERSION_WITH_WRITEV
200 /* We can't use stdio.
201  * The reason for this is highly non-obvious.
202  * echo_main is used from shell. Shell must correctly handle "echo foo"
203  * if stdout is closed. With stdio, output gets shoveled into
204  * stdout buffer, and even fflush cannot clear it out. It seems that
205  * even if libc receives EBADF on write attempts, it feels determined
206  * to output data no matter what. So it will try later,
207  * and possibly will clobber future output. Not good.
208  *
209  * Using writev instead, with 'direct' conversion of argv vector.
210  */
211
212 int echo_main(int argc, char **argv)
213 {
214         struct iovec io[argc];
215         struct iovec *cur_io = io;
216         char *arg;
217         char *p;
218 #if !ENABLE_FEATURE_FANCY_ECHO
219         enum {
220                 eflag = '\\',
221                 nflag = 1,  /* 1 -- print '\n' */
222         };
223         arg = *++argv;
224         if (!arg)
225                 goto newline_ret;
226 #else
227         char nflag = 1;
228         char eflag = 0;
229
230         while (1) {
231                 arg = *++argv;
232                 if (!arg)
233                         goto newline_ret;
234                 if (*arg != '-')
235                         break;
236
237                 /* If it appears that we are handling options, then make sure
238                  * that all of the options specified are actually valid.
239                  * Otherwise, the string should just be echoed.
240                  */
241                 p = arg + 1;
242                 if (!*p)        /* A single '-', so echo it. */
243                         goto just_echo;
244
245                 do {
246                         if (!strchr("neE", *p))
247                                 goto just_echo;
248                 } while (*++p);
249
250                 /* All of the options in this arg are valid, so handle them. */
251                 p = arg + 1;
252                 do {
253                         if (*p == 'n')
254                                 nflag = 0;
255                         if (*p == 'e')
256                                 eflag = '\\';
257                 } while (*++p);
258         }
259  just_echo:
260 #endif
261
262         while (1) {
263                 /* arg is already == *argv and isn't NULL */
264                 int c;
265
266                 cur_io->iov_base = p = arg;
267
268                 if (!eflag) {
269                         /* optimization for very common case */
270                         p += strlen(arg);
271                 } else while ((c = *arg++)) {
272                         if (c == eflag) {
273                                 /* This is an "\x" sequence */
274
275                                 if (*arg == 'c') {
276                                         /* "\c" means cancel newline and
277                                          * ignore all subsequent chars. */
278                                         cur_io->iov_len = p - (char*)cur_io->iov_base;
279                                         cur_io++;
280                                         goto ret;
281                                 }
282                                 /* Since SUSv3 mandates a first digit of 0, 4-digit octals
283                                 * of the form \0### are accepted. */
284                                 if (*arg == '0' && (unsigned char)(arg[1] - '0') < 8) {
285                                         arg++;
286                                 }
287                                 /* bb_process_escape_sequence can handle nul correctly */
288                                 c = bb_process_escape_sequence( (void*) &arg);
289                         }
290                         *p++ = c;
291                 }
292
293                 arg = *++argv;
294                 if (arg)
295                         *p++ = ' ';
296                 cur_io->iov_len = p - (char*)cur_io->iov_base;
297                 cur_io++;
298                 if (!arg)
299                         break;
300         }
301
302  newline_ret:
303         if (nflag) {
304                 cur_io->iov_base = (char*)"\n";
305                 cur_io->iov_len = 1;
306                 cur_io++;
307         }
308  ret:
309         /* TODO: implement and use full_writev? */
310         return writev(1, io, (cur_io - io)) >= 0;
311 }
312 #endif