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