busybox/coreutils/echo.c
<<
>>
Prefs
   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