Bump to version 1.22.1
[platform/upstream/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 //usage:#define echo_trivial_usage
27 //usage:        IF_FEATURE_FANCY_ECHO("[-neE] ") "[ARG]..."
28 //usage:#define echo_full_usage "\n\n"
29 //usage:       "Print the specified ARGs to stdout"
30 //usage:        IF_FEATURE_FANCY_ECHO( "\n"
31 //usage:     "\n        -n      Suppress trailing newline"
32 //usage:     "\n        -e      Interpret backslash escapes (i.e., \\t=tab)"
33 //usage:     "\n        -E      Don't interpret backslash escapes (default)"
34 //usage:        )
35 //usage:
36 //usage:#define echo_example_usage
37 //usage:       "$ echo \"Erik is cool\"\n"
38 //usage:       "Erik is cool\n"
39 //usage:        IF_FEATURE_FANCY_ECHO("$ echo -e \"Erik\\nis\\ncool\"\n"
40 //usage:       "Erik\n"
41 //usage:       "is\n"
42 //usage:       "cool\n"
43 //usage:       "$ echo \"Erik\\nis\\ncool\"\n"
44 //usage:       "Erik\\nis\\ncool\n")
45
46 #include "libbb.h"
47
48 /* This is a NOFORK applet. Be very careful! */
49
50 /* NB: can be used by shell even if not enabled as applet */
51
52 /*
53  * NB2: we don't use stdio, we need better error handing.
54  * Examples include writing into non-opened stdout and error on write.
55  *
56  * With stdio, output gets shoveled into stdout buffer, and even
57  * fflush cannot clear it out. It seems that even if libc receives
58  * EBADF on write attempts, it feels determined to output data no matter what.
59  * If echo is called by shell, it will try writing again later, and possibly
60  * will clobber future output. Not good.
61  *
62  * Solaris has fpurge which discards buffered input. glibc has __fpurge.
63  * But this function is not standard.
64  */
65
66 int echo_main(int argc UNUSED_PARAM, char **argv)
67 {
68         char **pp;
69         const char *arg;
70         char *out;
71         char *buffer;
72         unsigned buflen;
73 #if !ENABLE_FEATURE_FANCY_ECHO
74         enum {
75                 eflag = '\\',
76                 nflag = 1,  /* 1 -- print '\n' */
77         };
78
79         argv++;
80 #else
81         char nflag = 1;
82         char eflag = 0;
83
84         while ((arg = *++argv) != NULL) {
85                 char n, e;
86
87                 if (arg[0] != '-')
88                         break; /* not an option arg, echo it */
89
90                 /* If it appears that we are handling options, then make sure
91                  * that all of the options specified are actually valid.
92                  * Otherwise, the string should just be echoed.
93                  */
94                 arg++;
95                 n = nflag;
96                 e = eflag;
97                 do {
98                         if (*arg == 'n')
99                                 n = 0;
100                         else if (*arg == 'e')
101                                 e = '\\';
102                         else if (*arg != 'E') {
103                                 /* "-ccc" arg with one of c's invalid, echo it */
104                                 /* arg consisting from just "-" also handled here */
105                                 goto just_echo;
106                         }
107                 } while (*++arg);
108                 nflag = n;
109                 eflag = e;
110         }
111  just_echo:
112 #endif
113
114         buflen = 0;
115         pp = argv;
116         while ((arg = *pp) != NULL) {
117                 buflen += strlen(arg) + 1;
118                 pp++;
119         }
120         out = buffer = xmalloc(buflen + 1); /* +1 is needed for "no args" case */
121
122         while ((arg = *argv) != NULL) {
123                 int c;
124
125                 if (!eflag) {
126                         /* optimization for very common case */
127                         out = stpcpy(out, arg);
128                 } else
129                 while ((c = *arg++) != '\0') {
130                         if (c == eflag) {
131                                 /* This is an "\x" sequence */
132
133                                 if (*arg == 'c') {
134                                         /* "\c" means cancel newline and
135                                          * ignore all subsequent chars. */
136                                         goto do_write;
137                                 }
138                                 /* Since SUSv3 mandates a first digit of 0, 4-digit octals
139                                 * of the form \0### are accepted. */
140                                 if (*arg == '0') {
141                                         if ((unsigned char)(arg[1] - '0') < 8) {
142                                                 /* 2nd char is 0..7: skip leading '0' */
143                                                 arg++;
144                                         }
145                                 }
146                                 /* bb_process_escape_sequence handles NUL correctly
147                                  * ("...\" case). */
148                                 {
149                                         /* optimization: don't force arg to be on-stack,
150                                          * use another variable for that. ~30 bytes win */
151                                         const char *z = arg;
152                                         c = bb_process_escape_sequence(&z);
153                                         arg = z;
154                                 }
155                         }
156                         *out++ = c;
157                 }
158
159                 if (!*++argv)
160                         break;
161                 *out++ = ' ';
162         }
163
164         if (nflag) {
165                 *out++ = '\n';
166         }
167
168  do_write:
169         /* Careful to error out on partial writes too (think ENOSPC!) */
170         errno = 0;
171         /*r =*/ full_write(STDOUT_FILENO, buffer, out - buffer);
172         free(buffer);
173         if (/*WRONG:r < 0*/ errno) {
174                 bb_perror_msg(bb_msg_write_error);
175                 return 1;
176         }
177         return 0;
178 }
179
180 /*
181  * Copyright (c) 1991, 1993
182  *      The Regents of the University of California.  All rights reserved.
183  *
184  * This code is derived from software contributed to Berkeley by
185  * Kenneth Almquist.
186  *
187  * Redistribution and use in source and binary forms, with or without
188  * modification, are permitted provided that the following conditions
189  * are met:
190  * 1. Redistributions of source code must retain the above copyright
191  *    notice, this list of conditions and the following disclaimer.
192  * 2. Redistributions in binary form must reproduce the above copyright
193  *    notice, this list of conditions and the following disclaimer in the
194  *    documentation and/or other materials provided with the distribution.
195  *
196  * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
197  *              ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
198  *
199  *      California, Berkeley and its contributors.
200  * 4. Neither the name of the University nor the names of its contributors
201  *    may be used to endorse or promote products derived from this software
202  *    without specific prior written permission.
203  *
204  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
205  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
206  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
207  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
208  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
209  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
210  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
211  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
212  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
213  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
214  * SUCH DAMAGE.
215  *
216  *      @(#)echo.c      8.1 (Berkeley) 5/31/93
217  */
218
219 #ifdef VERSION_WITH_WRITEV
220 /* We can't use stdio.
221  * The reason for this is highly non-obvious.
222  * echo_main is used from shell. Shell must correctly handle "echo foo"
223  * if stdout is closed. With stdio, output gets shoveled into
224  * stdout buffer, and even fflush cannot clear it out. It seems that
225  * even if libc receives EBADF on write attempts, it feels determined
226  * to output data no matter what. So it will try later,
227  * and possibly will clobber future output. Not good.
228  *
229  * Using writev instead, with 'direct' conversion of argv vector.
230  */
231
232 int echo_main(int argc, char **argv)
233 {
234         struct iovec io[argc];
235         struct iovec *cur_io = io;
236         char *arg;
237         char *p;
238 #if !ENABLE_FEATURE_FANCY_ECHO
239         enum {
240                 eflag = '\\',
241                 nflag = 1,  /* 1 -- print '\n' */
242         };
243         arg = *++argv;
244         if (!arg)
245                 goto newline_ret;
246 #else
247         char nflag = 1;
248         char eflag = 0;
249
250         while (1) {
251                 arg = *++argv;
252                 if (!arg)
253                         goto newline_ret;
254                 if (*arg != '-')
255                         break;
256
257                 /* If it appears that we are handling options, then make sure
258                  * that all of the options specified are actually valid.
259                  * Otherwise, the string should just be echoed.
260                  */
261                 p = arg + 1;
262                 if (!*p)        /* A single '-', so echo it. */
263                         goto just_echo;
264
265                 do {
266                         if (!strchr("neE", *p))
267                                 goto just_echo;
268                 } while (*++p);
269
270                 /* All of the options in this arg are valid, so handle them. */
271                 p = arg + 1;
272                 do {
273                         if (*p == 'n')
274                                 nflag = 0;
275                         if (*p == 'e')
276                                 eflag = '\\';
277                 } while (*++p);
278         }
279  just_echo:
280 #endif
281
282         while (1) {
283                 /* arg is already == *argv and isn't NULL */
284                 int c;
285
286                 cur_io->iov_base = p = arg;
287
288                 if (!eflag) {
289                         /* optimization for very common case */
290                         p += strlen(arg);
291                 } else while ((c = *arg++)) {
292                         if (c == eflag) {
293                                 /* This is an "\x" sequence */
294
295                                 if (*arg == 'c') {
296                                         /* "\c" means cancel newline and
297                                          * ignore all subsequent chars. */
298                                         cur_io->iov_len = p - (char*)cur_io->iov_base;
299                                         cur_io++;
300                                         goto ret;
301                                 }
302                                 /* Since SUSv3 mandates a first digit of 0, 4-digit octals
303                                 * of the form \0### are accepted. */
304                                 if (*arg == '0' && (unsigned char)(arg[1] - '0') < 8) {
305                                         arg++;
306                                 }
307                                 /* bb_process_escape_sequence can handle nul correctly */
308                                 c = bb_process_escape_sequence( (void*) &arg);
309                         }
310                         *p++ = c;
311                 }
312
313                 arg = *++argv;
314                 if (arg)
315                         *p++ = ' ';
316                 cur_io->iov_len = p - (char*)cur_io->iov_base;
317                 cur_io++;
318                 if (!arg)
319                         break;
320         }
321
322  newline_ret:
323         if (nflag) {
324                 cur_io->iov_base = (char*)"\n";
325                 cur_io->iov_len = 1;
326                 cur_io++;
327         }
328  ret:
329         /* TODO: implement and use full_writev? */
330         return writev(1, io, (cur_io - io)) >= 0;
331 }
332 #endif