aboutsummaryrefslogtreecommitdiff
path: root/toys/posix/echo.c
blob: 70f4ce5ecf5aa27acf1ad0c64da91667b2533d72 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/* echo.c - echo supporting -n and -e.
 *
 * Copyright 2007 Rob Landley <rob@landley.net>
 *
 * See http://opengroup.org/onlinepubs/9699919799/utilities/echo.html
 *
 * Deviations from posix: we parse command line options, as Linux has
 * consistently done since 1992. Posix defaults -e to on, we require -e.
 * We also honor -- to _stop_ option parsing (bash doesn't, we go with
 * consistency over compatibility here).

USE_ECHO(NEWTOY(echo, "^?Een[-eE]", TOYFLAG_BIN|TOYFLAG_MAYFORK))

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

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

    -n	No trailing newline
    -E	Print escape sequences literally (default)
    -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 (!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') return;
        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 {
                if (n==1) {
                  --c;
                  out = '\\';
                }
                break;
              }
            }
          }
        // Slash in front of unknown character, print literal.
        } else c--;
      }
      putchar(out);
    }
  }

  // Output "\n" if no -n
  if (!FLAG(n)) putchar('\n');
}