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