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