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