view toys/posix/echo.c @ 1613:96aa7ec74936 draft

Fix yet another sed bug. The s/// command would copy the \ of substitutions before deciding what to do with them (generally overwriting the \ with the new data). When the substitution was A) at the very end of the new string, B) resolved to nothing, it could leave a trailing \ that didn't belong there and didn't get overwritten because the "copy trailing data" part that copies the original string's null terminator already happened before the \ overwrote it. The ghostwheel() function restarts regexes after embedded NUL bytes, but if the string it's passed is _longer_ than the length it's told then it gets confused (and it means we're off the end of our allocation so segfaults are likely). Fix: test for \ first and move the "copy byte" logic into an else case.
author Rob Landley <rob@landley.net>
date Mon, 15 Dec 2014 03:34:55 -0600
parents 95cb37adb024
children
line wrap: on
line source

/* echo.c - echo supporting -n and -e.
 *
 * Copyright 2007 Rob Landley <rob@landley.net>
 *
 * See http://opengroup.org/onlinepubs/9699919799/utilities/echo.html

USE_ECHO(NEWTOY(echo, "^?en", TOYFLAG_BIN))

config ECHO
  bool "echo"
  default y
  help
    usage: echo [-ne] [args...]

    Write each argument to stdout, with one space between each, followed
    by a newline.

    -n	No trailing newline.
    -e	Process the following escape sequences:
    	\\	backslash
    	\0NNN	octal values (1 to 3 digits)
    	\a	alert (beep/flash)
    	\b	backspace
    	\c	stop output here (avoids trailing newline)
    	\f	form feed
    	\n	newline
    	\r	carriage return
    	\t	horizontal tab
    	\v	vertical tab
    	\xHH	hexadecimal values (1 to 2 digits)
*/

#define FOR_echo
#include "toys.h"

void echo_main(void)
{
  int i = 0, out;
  char *arg, *c;

  for (;;) {
    arg = toys.optargs[i];
    if (!arg) break;
    if (i++) putchar(' ');

    // Should we output arg verbatim?

    if (!(toys.optflags & FLAG_e)) {
      xprintf("%s", arg);
      continue;
    }

    // Handle -e

    for (c = arg;;) {
      if (!(out = *(c++))) break;

      // handle \escapes
      if (out == '\\' && *c) {
        int slash = *(c++), n = unescape(slash);

        if (n) out = n;
        else if (slash=='c') goto done;
        else if (slash=='0') {
          out = 0;
          while (*c>='0' && *c<='7' && n++<3) out = (out*8)+*(c++)-'0';
        } else if (slash=='x') {
          out = 0;
          while (n++<2) {
            if (*c>='0' && *c<='9') out = (out*16)+*(c++)-'0';
            else {
              int temp = tolower(*c);
              if (temp>='a' && temp<='f') {
                out = (out*16)+temp-'a'+10;
                c++;
              } else break;
            }
          }
        // Slash in front of unknown character, print literal.
        } else c--;
      }
      putchar(out);
    }
  }

  // Output "\n" if no -n
  if (!(toys.optflags&FLAG_n)) putchar('\n');
done:
  xflush();
}