4c7a91743f017e11e8fd7040f45b3d2b91c59e54
[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 tarball for details.
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 /* argc is unused, but removing it precludes compiler from
29  * using call -> jump optimization */
30 int bb_echo(int argc, char **argv)
31 {
32         const char *arg;
33 #if !ENABLE_FEATURE_FANCY_ECHO
34         enum {
35                 eflag = '\\',
36                 nflag = 1,  /* 1 -- print '\n' */
37         };
38
39         /* We must check that stdout is not closed.
40          * The reason for this is highly non-obvious.
41          * bb_echo is used from shell. Shell must correctly handle "echo foo"
42          * if stdout is closed. With stdio, output gets shoveled into
43          * stdout buffer, and even fflush cannot clear it out. It seems that
44          * even if libc receives EBADF on write attempts, it feels determined
45          * to output data no matter what. So it will try later,
46          * and possibly will clobber future output. Not good. */
47         if (dup2(1, 1) != 1)
48                 return -1;
49
50         arg = *++argv;
51         if (!arg)
52                 goto newline_ret;
53 #else
54         const char *p;
55         char nflag = 1;
56         char eflag = 0;
57
58         /* We must check that stdout is not closed. */
59         if (dup2(1, 1) != 1)
60                 return -1;
61
62         while (1) {
63                 arg = *++argv;
64                 if (!arg)
65                         goto newline_ret;
66                 if (*arg != '-')
67                         break;
68
69                 /* If it appears that we are handling options, then make sure
70                  * that all of the options specified are actually valid.
71                  * Otherwise, the string should just be echoed.
72                  */
73                 p = arg + 1;
74                 if (!*p)        /* A single '-', so echo it. */
75                         goto just_echo;
76
77                 do {
78                         if (!strrchr("neE", *p))
79                                 goto just_echo;
80                 } while (*++p);
81
82                 /* All of the options in this arg are valid, so handle them. */
83                 p = arg + 1;
84                 do {
85                         if (*p == 'n')
86                                 nflag = 0;
87                         if (*p == 'e')
88                                 eflag = '\\';
89                 } while (*++p);
90         }
91  just_echo:
92 #endif
93         while (1) {
94                 /* arg is already == *argv and isn't NULL */
95                 int c;
96
97                 if (!eflag) {
98                         /* optimization for very common case */
99                         fputs(arg, stdout);
100                 } else while ((c = *arg++)) {
101                         if (c == eflag) {       /* Check for escape seq. */
102                                 if (*arg == 'c') {
103                                         /* '\c' means cancel newline and
104                                          * ignore all subsequent chars. */
105                                         goto ret;
106                                 }
107 #if !ENABLE_FEATURE_FANCY_ECHO
108                                 /* SUSv3 specifies that octal escapes must begin with '0'. */
109                                 if ( (((unsigned char)*arg) - '1') >= 7)
110 #endif
111                                 {
112                                         /* Since SUSv3 mandates a first digit of 0, 4-digit octals
113                                         * of the form \0### are accepted. */
114                                         if (*arg == '0' && ((unsigned char)(arg[1]) - '0') < 8) {
115                                                 arg++;
116                                         }
117                                         /* bb_process_escape_sequence can handle nul correctly */
118                                         c = bb_process_escape_sequence(&arg);
119                                 }
120                         }
121                         bb_putchar(c);
122                 }
123
124                 arg = *++argv;
125                 if (!arg)
126                         break;
127                 bb_putchar(' ');
128         }
129
130  newline_ret:
131         if (nflag) {
132                 bb_putchar('\n');
133         }
134  ret:
135         return fflush(stdout);
136 }
137
138 /* This is a NOFORK applet. Be very careful! */
139
140 int echo_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
141 int echo_main(int argc, char **argv)
142 {
143         return bb_echo(argc, argv);
144 }
145
146 /*-
147  * Copyright (c) 1991, 1993
148  *      The Regents of the University of California.  All rights reserved.
149  *
150  * This code is derived from software contributed to Berkeley by
151  * Kenneth Almquist.
152  *
153  * Redistribution and use in source and binary forms, with or without
154  * modification, are permitted provided that the following conditions
155  * are met:
156  * 1. Redistributions of source code must retain the above copyright
157  *    notice, this list of conditions and the following disclaimer.
158  * 2. Redistributions in binary form must reproduce the above copyright
159  *    notice, this list of conditions and the following disclaimer in the
160  *    documentation and/or other materials provided with the distribution.
161  *
162  * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
163  *              ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
164  *
165  *      California, Berkeley and its contributors.
166  * 4. Neither the name of the University nor the names of its contributors
167  *    may be used to endorse or promote products derived from this software
168  *    without specific prior written permission.
169  *
170  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
171  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
172  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
173  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
174  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
175  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
176  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
177  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
178  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
179  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
180  * SUCH DAMAGE.
181  *
182  *      @(#)echo.c      8.1 (Berkeley) 5/31/93
183  */
184
185 #ifdef VERSION_WITH_WRITEV
186 /* We can't use stdio.
187  * The reason for this is highly non-obvious.
188  * bb_echo is used from shell. Shell must correctly handle "echo foo"
189  * if stdout is closed. With stdio, output gets shoveled into
190  * stdout buffer, and even fflush cannot clear it out. It seems that
191  * even if libc receives EBADF on write attempts, it feels determined
192  * to output data no matter what. So it will try later,
193  * and possibly will clobber future output. Not good.
194  *
195  * Using writev instead, with 'direct' conversion of argv vector.
196  */
197
198 int bb_echo(int argc, char **argv)
199 {
200         struct iovec io[argc];
201         struct iovec *cur_io = io;
202         char *arg;
203         char *p;
204 #if !ENABLE_FEATURE_FANCY_ECHO
205         enum {
206                 eflag = '\\',
207                 nflag = 1,  /* 1 -- print '\n' */
208         };
209         arg = *++argv;
210         if (!arg)
211                 goto newline_ret;
212 #else
213         char nflag = 1;
214         char eflag = 0;
215
216         while (1) {
217                 arg = *++argv;
218                 if (!arg)
219                         goto newline_ret;
220                 if (*arg != '-')
221                         break;
222
223                 /* If it appears that we are handling options, then make sure
224                  * that all of the options specified are actually valid.
225                  * Otherwise, the string should just be echoed.
226                  */
227                 p = arg + 1;
228                 if (!*p)        /* A single '-', so echo it. */
229                         goto just_echo;
230
231                 do {
232                         if (!strrchr("neE", *p))
233                                 goto just_echo;
234                 } while (*++p);
235
236                 /* All of the options in this arg are valid, so handle them. */
237                 p = arg + 1;
238                 do {
239                         if (*p == 'n')
240                                 nflag = 0;
241                         if (*p == 'e')
242                                 eflag = '\\';
243                 } while (*++p);
244         }
245  just_echo:
246 #endif
247
248         while (1) {
249                 /* arg is already == *argv and isn't NULL */
250                 int c;
251
252                 cur_io->iov_base = p = arg;
253
254                 if (!eflag) {
255                         /* optimization for very common case */
256                         p += strlen(arg);
257                 } else while ((c = *arg++)) {
258                         if (c == eflag) {       /* Check for escape seq. */
259                                 if (*arg == 'c') {
260                                         /* '\c' means cancel newline and
261                                          * ignore all subsequent chars. */
262                                         cur_io->iov_len = p - (char*)cur_io->iov_base;
263                                         cur_io++;
264                                         goto ret;
265                                 }
266 #if !ENABLE_FEATURE_FANCY_ECHO
267                                 /* SUSv3 specifies that octal escapes must begin with '0'. */
268                                 if ( (((unsigned char)*arg) - '1') >= 7)
269 #endif
270                                 {
271                                         /* Since SUSv3 mandates a first digit of 0, 4-digit octals
272                                         * of the form \0### are accepted. */
273                                         if (*arg == '0' && ((unsigned char)(arg[1]) - '0') < 8) {
274                                                 arg++;
275                                         }
276                                         /* bb_process_escape_sequence can handle nul correctly */
277                                         c = bb_process_escape_sequence( (void*) &arg);
278                                 }
279                         }
280                         *p++ = c;
281                 }
282
283                 arg = *++argv;
284                 if (arg)
285                         *p++ = ' ';
286                 cur_io->iov_len = p - (char*)cur_io->iov_base;
287                 cur_io++;
288                 if (!arg)
289                         break;
290         }
291
292  newline_ret:
293         if (nflag) {
294                 cur_io->iov_base = (char*)"\n";
295                 cur_io->iov_len = 1;
296                 cur_io++;
297         }
298  ret:
299         /* TODO: implement and use full_writev? */
300         return writev(1, io, (cur_io - io)) >= 0; 
301 }
302 #endif