Line data Source code
1 : /* echo.c, derived from code echo.c in Bash.
2 : Copyright (C) 87,89, 1991-1997, 1999-2005, 2007 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 "FIXME unknown"
28 :
29 : /* echo [-neE] [arg ...]
30 : Output the ARGs. If -n is specified, the trailing newline is
31 : suppressed. If the -e option is given, interpretation of the
32 : following backslash-escaped characters is turned on:
33 : \a alert (bell)
34 : \b backspace
35 : \c suppress trailing newline
36 : \f form feed
37 : \n new line
38 : \r carriage return
39 : \t horizontal tab
40 : \v vertical tab
41 : \\ backslash
42 : \0NNN the character whose ASCII code is NNN (octal).
43 :
44 : You can explicitly turn off the interpretation of the above characters
45 : on System V systems with the -E option.
46 : */
47 :
48 : /* If true, interpret backslash escapes by default. */
49 : #ifndef DEFAULT_ECHO_TO_XPG
50 : enum { DEFAULT_ECHO_TO_XPG = false };
51 : #endif
52 :
53 : /* The name this program was run with. */
54 : char *program_name;
55 :
56 : void
57 2 : usage (int status)
58 : {
59 2 : if (status != EXIT_SUCCESS)
60 0 : fprintf (stderr, _("Try `%s --help' for more information.\n"),
61 : program_name);
62 : else
63 : {
64 2 : printf (_("Usage: %s [OPTION]... [STRING]...\n"), program_name);
65 2 : fputs (_("\
66 : Echo the STRING(s) to standard output.\n\
67 : \n\
68 : -n do not output the trailing newline\n\
69 : "), stdout);
70 2 : fputs (_(DEFAULT_ECHO_TO_XPG
71 : ? "\
72 : -e enable interpretation of backslash escapes (default)\n\
73 : -E disable interpretation of backslash escapes\n"
74 : : "\
75 : -e enable interpretation of backslash escapes\n\
76 : -E disable interpretation of backslash escapes (default)\n"),
77 : stdout);
78 2 : fputs (HELP_OPTION_DESCRIPTION, stdout);
79 2 : fputs (VERSION_OPTION_DESCRIPTION, stdout);
80 2 : fputs (_("\
81 : \n\
82 : If -e is in effect, the following sequences are recognized:\n\
83 : \n\
84 : \\0NNN the character whose ASCII code is NNN (octal)\n\
85 : \\\\ backslash\n\
86 : \\a alert (BEL)\n\
87 : \\b backspace\n\
88 : "), stdout);
89 2 : fputs (_("\
90 : \\c suppress trailing newline\n\
91 : \\f form feed\n\
92 : \\n new line\n\
93 : \\r carriage return\n\
94 : \\t horizontal tab\n\
95 : \\v vertical tab\n\
96 : "), stdout);
97 2 : printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
98 2 : emit_bug_reporting_address ();
99 : }
100 2 : exit (status);
101 : }
102 :
103 : /* Convert C from hexadecimal character to integer. */
104 : static int
105 0 : hextobin (unsigned char c)
106 : {
107 0 : switch (c)
108 : {
109 0 : default: return c - '0';
110 0 : case 'a': case 'A': return 10;
111 0 : case 'b': case 'B': return 11;
112 0 : case 'c': case 'C': return 12;
113 0 : case 'd': case 'D': return 13;
114 0 : case 'e': case 'E': return 14;
115 0 : case 'f': case 'F': return 15;
116 : }
117 : }
118 :
119 : /* Print the words in LIST to standard output. If the first word is
120 : `-n', then don't print a trailing newline. We also support the
121 : echo syntax from Version 9 unix systems. */
122 :
123 : int
124 35 : main (int argc, char **argv)
125 : {
126 35 : bool display_return = true;
127 35 : bool allow_options =
128 35 : (! getenv ("POSIXLY_CORRECT")
129 35 : || (! DEFAULT_ECHO_TO_XPG && 1 < argc && STREQ (argv[1], "-n")));
130 :
131 : /* System V machines already have a /bin/sh with a v9 behavior.
132 : Use the identical behavior for these machines so that the
133 : existing system shell scripts won't barf. */
134 35 : bool do_v9 = DEFAULT_ECHO_TO_XPG;
135 :
136 : initialize_main (&argc, &argv);
137 35 : program_name = argv[0];
138 35 : setlocale (LC_ALL, "");
139 : bindtextdomain (PACKAGE, LOCALEDIR);
140 : textdomain (PACKAGE);
141 :
142 35 : atexit (close_stdout);
143 :
144 35 : if (allow_options)
145 35 : parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE_NAME, VERSION,
146 : usage, AUTHORS, (char const *) NULL);
147 :
148 32 : --argc;
149 32 : ++argv;
150 :
151 32 : if (allow_options)
152 132 : while (argc > 0 && *argv[0] == '-')
153 : {
154 38 : char const *temp = argv[0] + 1;
155 : size_t i;
156 :
157 : /* If it appears that we are handling options, then make sure that
158 : all of the options specified are actually valid. Otherwise, the
159 : string should just be echoed. */
160 :
161 66 : for (i = 0; temp[i]; i++)
162 32 : switch (temp[i])
163 : {
164 28 : case 'e': case 'E': case 'n':
165 28 : break;
166 4 : default:
167 4 : goto just_echo;
168 : }
169 :
170 34 : if (i == 0)
171 14 : goto just_echo;
172 :
173 : /* All of the options in TEMP are valid options to ECHO.
174 : Handle them. */
175 68 : while (*temp)
176 28 : switch (*temp++)
177 : {
178 25 : case 'e':
179 25 : do_v9 = true;
180 25 : break;
181 :
182 1 : case 'E':
183 1 : do_v9 = false;
184 1 : break;
185 :
186 2 : case 'n':
187 2 : display_return = false;
188 2 : break;
189 : }
190 :
191 20 : argc--;
192 20 : argv++;
193 : }
194 :
195 14 : just_echo:
196 :
197 32 : if (do_v9)
198 : {
199 58 : while (argc > 0)
200 : {
201 25 : char const *s = argv[0];
202 : unsigned char c;
203 :
204 70 : while ((c = *s++))
205 : {
206 34 : if (c == '\\' && *s)
207 : {
208 12 : switch (c = *s++)
209 : {
210 1 : case 'a': c = '\a'; break;
211 1 : case 'b': c = '\b'; break;
212 1 : case 'c': exit (EXIT_SUCCESS);
213 1 : case 'f': c = '\f'; break;
214 1 : case 'n': c = '\n'; break;
215 1 : case 'r': c = '\r'; break;
216 1 : case 't': c = '\t'; break;
217 1 : case 'v': c = '\v'; break;
218 1 : case 'x':
219 : {
220 1 : unsigned char ch = *s;
221 1 : if (! isxdigit (ch))
222 1 : goto not_an_escape;
223 0 : s++;
224 0 : c = hextobin (ch);
225 0 : ch = *s;
226 0 : if (isxdigit (ch))
227 : {
228 0 : s++;
229 0 : c = c * 16 + hextobin (ch);
230 : }
231 : }
232 0 : break;
233 1 : case '0':
234 1 : c = 0;
235 1 : if (! ('0' <= *s && *s <= '7'))
236 : break;
237 0 : c = *s++;
238 : /* Fall through. */
239 2 : case '1': case '2': case '3':
240 : case '4': case '5': case '6': case '7':
241 2 : c -= '0';
242 2 : if ('0' <= *s && *s <= '7')
243 0 : c = c * 8 + (*s++ - '0');
244 2 : if ('0' <= *s && *s <= '7')
245 0 : c = c * 8 + (*s++ - '0');
246 2 : break;
247 0 : case '\\': break;
248 :
249 1 : not_an_escape:
250 1 : default: putchar ('\\'); break;
251 : }
252 10 : }
253 20 : putchar (c);
254 : }
255 24 : argc--;
256 24 : argv++;
257 24 : if (argc > 0)
258 10 : putchar (' ');
259 : }
260 : }
261 : else
262 : {
263 52 : while (argc > 0)
264 : {
265 22 : fputs (argv[0], stdout);
266 22 : argc--;
267 22 : argv++;
268 22 : if (argc > 0)
269 9 : putchar (' ');
270 : }
271 : }
272 :
273 31 : if (display_return)
274 29 : putchar ('\n');
275 31 : exit (EXIT_SUCCESS);
276 : }
|