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