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
  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