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 83int 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 249int 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