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