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