TODO: add an item for a chmod optimization
[platform/upstream/coreutils.git] / src / echo.c
1 /* echo.c, derived from code echo.c in Bash.
2    Copyright (C) 87,89, 1991-1997, 1999-2005, 2007-2008 Free Software
3    Foundation, Inc.
4
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation, either version 3 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
17
18 #include <config.h>
19 #include <stdio.h>
20 #include <sys/types.h>
21 #include "system.h"
22 #include "long-options.h"
23
24 /* The official name of this program (e.g., no `g' prefix).  */
25 #define PROGRAM_NAME "echo"
26
27 #define AUTHORS \
28   proper_name ("Brian Fox"), \
29   proper_name ("Chet Ramey")
30
31 /* If true, interpret backslash escapes by default.  */
32 #ifndef DEFAULT_ECHO_TO_XPG
33 enum { DEFAULT_ECHO_TO_XPG = false };
34 #endif
35
36 void
37 usage (int status)
38 {
39   if (status != EXIT_SUCCESS)
40     fprintf (stderr, _("Try `%s --help' for more information.\n"),
41              program_name);
42   else
43     {
44       printf (_("Usage: %s [OPTION]... [STRING]...\n"), program_name);
45       fputs (_("\
46 Echo the STRING(s) to standard output.\n\
47 \n\
48   -n             do not output the trailing newline\n\
49 "), stdout);
50       fputs (_(DEFAULT_ECHO_TO_XPG
51                ? N_("\
52   -e             enable interpretation of backslash escapes (default)\n\
53   -E             disable interpretation of backslash escapes\n")
54                : N_("\
55   -e             enable interpretation of backslash escapes\n\
56   -E             disable interpretation of backslash escapes (default)\n")),
57              stdout);
58       fputs (HELP_OPTION_DESCRIPTION, stdout);
59       fputs (VERSION_OPTION_DESCRIPTION, stdout);
60       fputs (_("\
61 \n\
62 If -e is in effect, the following sequences are recognized:\n\
63 \n\
64   \\0NNN   the character whose ASCII code is NNN (octal)\n\
65   \\\\     backslash\n\
66   \\a     alert (BEL)\n\
67   \\b     backspace\n\
68 "), stdout);
69       fputs (_("\
70   \\c     produce no further output\n\
71   \\f     form feed\n\
72   \\n     new line\n\
73   \\r     carriage return\n\
74   \\t     horizontal tab\n\
75   \\v     vertical tab\n\
76 "), stdout);
77       printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
78       emit_bug_reporting_address ();
79     }
80   exit (status);
81 }
82
83 /* Convert C from hexadecimal character to integer.  */
84 static int
85 hextobin (unsigned char c)
86 {
87   switch (c)
88     {
89     default: return c - '0';
90     case 'a': case 'A': return 10;
91     case 'b': case 'B': return 11;
92     case 'c': case 'C': return 12;
93     case 'd': case 'D': return 13;
94     case 'e': case 'E': return 14;
95     case 'f': case 'F': return 15;
96     }
97 }
98
99 /* Print the words in LIST to standard output.  If the first word is
100    `-n', then don't print a trailing newline.  We also support the
101    echo syntax from Version 9 unix systems. */
102
103 int
104 main (int argc, char **argv)
105 {
106   bool display_return = true;
107   bool allow_options =
108     (! getenv ("POSIXLY_CORRECT")
109      || (! DEFAULT_ECHO_TO_XPG && 1 < argc && STREQ (argv[1], "-n")));
110
111   /* System V machines already have a /bin/sh with a v9 behavior.
112      Use the identical behavior for these machines so that the
113      existing system shell scripts won't barf.  */
114   bool do_v9 = DEFAULT_ECHO_TO_XPG;
115
116   initialize_main (&argc, &argv);
117   set_program_name (argv[0]);
118   setlocale (LC_ALL, "");
119   bindtextdomain (PACKAGE, LOCALEDIR);
120   textdomain (PACKAGE);
121
122   atexit (close_stdout);
123
124   if (allow_options)
125     parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE_NAME, Version,
126                         usage, AUTHORS, (char const *) NULL);
127
128   --argc;
129   ++argv;
130
131   if (allow_options)
132     while (argc > 0 && *argv[0] == '-')
133       {
134         char const *temp = argv[0] + 1;
135         size_t i;
136
137         /* If it appears that we are handling options, then make sure that
138            all of the options specified are actually valid.  Otherwise, the
139            string should just be echoed.  */
140
141         for (i = 0; temp[i]; i++)
142           switch (temp[i])
143             {
144             case 'e': case 'E': case 'n':
145               break;
146             default:
147               goto just_echo;
148             }
149
150         if (i == 0)
151           goto just_echo;
152
153         /* All of the options in TEMP are valid options to ECHO.
154            Handle them. */
155         while (*temp)
156           switch (*temp++)
157             {
158             case 'e':
159               do_v9 = true;
160               break;
161
162             case 'E':
163               do_v9 = false;
164               break;
165
166             case 'n':
167               display_return = false;
168               break;
169             }
170
171         argc--;
172         argv++;
173       }
174
175 just_echo:
176
177   if (do_v9)
178     {
179       while (argc > 0)
180         {
181           char const *s = argv[0];
182           unsigned char c;
183
184           while ((c = *s++))
185             {
186               if (c == '\\' && *s)
187                 {
188                   switch (c = *s++)
189                     {
190                     case 'a': c = '\a'; break;
191                     case 'b': c = '\b'; break;
192                     case 'c': exit (EXIT_SUCCESS);
193                     case 'f': c = '\f'; break;
194                     case 'n': c = '\n'; break;
195                     case 'r': c = '\r'; break;
196                     case 't': c = '\t'; break;
197                     case 'v': c = '\v'; break;
198                     case 'x':
199                       {
200                         unsigned char ch = *s;
201                         if (! isxdigit (ch))
202                           goto not_an_escape;
203                         s++;
204                         c = hextobin (ch);
205                         ch = *s;
206                         if (isxdigit (ch))
207                           {
208                             s++;
209                             c = c * 16 + hextobin (ch);
210                           }
211                       }
212                       break;
213                     case '0':
214                       c = 0;
215                       if (! ('0' <= *s && *s <= '7'))
216                         break;
217                       c = *s++;
218                       /* Fall through.  */
219                     case '1': case '2': case '3':
220                     case '4': case '5': case '6': case '7':
221                       c -= '0';
222                       if ('0' <= *s && *s <= '7')
223                         c = c * 8 + (*s++ - '0');
224                       if ('0' <= *s && *s <= '7')
225                         c = c * 8 + (*s++ - '0');
226                       break;
227                     case '\\': break;
228
229                     not_an_escape:
230                     default:  putchar ('\\'); break;
231                     }
232                 }
233               putchar (c);
234             }
235           argc--;
236           argv++;
237           if (argc > 0)
238             putchar (' ');
239         }
240     }
241   else
242     {
243       while (argc > 0)
244         {
245           fputs (argv[0], stdout);
246           argc--;
247           argv++;
248           if (argc > 0)
249             putchar (' ');
250         }
251     }
252
253   if (display_return)
254     putchar ('\n');
255   exit (EXIT_SUCCESS);
256 }