id: fix infinite loop on some systems
[coreutils/ericb.git] / src / echo.c
blob7b069e0e52bf58e8d76d9aff487c4fb5f9391e31
1 /* echo.c, derived from code echo.c in Bash.
2 Copyright (C) 87,89, 1991-1997, 1999-2005, 2007-2009 Free Software
3 Foundation, Inc.
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.
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.
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/>. */
18 #include <config.h>
19 #include <stdio.h>
20 #include <sys/types.h>
21 #include "system.h"
23 /* The official name of this program (e.g., no `g' prefix). */
24 #define PROGRAM_NAME "echo"
26 #define AUTHORS \
27 proper_name ("Brian Fox"), \
28 proper_name ("Chet Ramey")
30 /* If true, interpret backslash escapes by default. */
31 #ifndef DEFAULT_ECHO_TO_XPG
32 enum { DEFAULT_ECHO_TO_XPG = false };
33 #endif
35 void
36 usage (int status)
38 if (status != EXIT_SUCCESS)
39 fprintf (stderr, _("Try `%s --help' for more information.\n"),
40 program_name);
41 else
43 printf (_("\
44 Usage: %s [SHORT-OPTION]... [STRING]...\n\
45 or: %s LONG-OPTION\n\
46 "), program_name, program_name);
47 fputs (_("\
48 Echo the STRING(s) to standard output.\n\
49 \n\
50 -n do not output the trailing newline\n\
51 "), stdout);
52 fputs (_(DEFAULT_ECHO_TO_XPG
53 ? N_("\
54 -e enable interpretation of backslash escapes (default)\n\
55 -E disable interpretation of backslash escapes\n")
56 : N_("\
57 -e enable interpretation of backslash escapes\n\
58 -E disable interpretation of backslash escapes (default)\n")),
59 stdout);
60 fputs (HELP_OPTION_DESCRIPTION, stdout);
61 fputs (VERSION_OPTION_DESCRIPTION, stdout);
62 fputs (_("\
63 \n\
64 If -e is in effect, the following sequences are recognized:\n\
65 \n\
66 \\0NNN the character whose ASCII code is NNN (octal)\n\
67 \\\\ backslash\n\
68 \\a alert (BEL)\n\
69 \\b backspace\n\
70 "), stdout);
71 fputs (_("\
72 \\c produce no further output\n\
73 \\f form feed\n\
74 \\n new line\n\
75 \\r carriage return\n\
76 \\t horizontal tab\n\
77 \\v vertical tab\n\
78 "), stdout);
79 printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
80 emit_bug_reporting_address ();
82 exit (status);
85 /* Convert C from hexadecimal character to integer. */
86 static int
87 hextobin (unsigned char c)
89 switch (c)
91 default: return c - '0';
92 case 'a': case 'A': return 10;
93 case 'b': case 'B': return 11;
94 case 'c': case 'C': return 12;
95 case 'd': case 'D': return 13;
96 case 'e': case 'E': return 14;
97 case 'f': case 'F': return 15;
101 /* Print the words in LIST to standard output. If the first word is
102 `-n', then don't print a trailing newline. We also support the
103 echo syntax from Version 9 unix systems. */
106 main (int argc, char **argv)
108 bool display_return = true;
109 bool allow_options =
110 (! getenv ("POSIXLY_CORRECT")
111 || (! DEFAULT_ECHO_TO_XPG && 1 < argc && STREQ (argv[1], "-n")));
113 /* System V machines already have a /bin/sh with a v9 behavior.
114 Use the identical behavior for these machines so that the
115 existing system shell scripts won't barf. */
116 bool do_v9 = DEFAULT_ECHO_TO_XPG;
118 initialize_main (&argc, &argv);
119 set_program_name (argv[0]);
120 setlocale (LC_ALL, "");
121 bindtextdomain (PACKAGE, LOCALEDIR);
122 textdomain (PACKAGE);
124 atexit (close_stdout);
126 /* We directly parse options, rather than use parse_long_options, in
127 order to avoid accepting abbreviations. */
128 if (allow_options && argc == 2)
130 if (STREQ (argv[1], "--help"))
131 usage (EXIT_SUCCESS);
133 if (STREQ (argv[1], "--version"))
135 version_etc (stdout, PROGRAM_NAME, PACKAGE_NAME, Version, AUTHORS,
136 (char *) NULL);
137 exit (EXIT_SUCCESS);
141 --argc;
142 ++argv;
144 if (allow_options)
145 while (argc > 0 && *argv[0] == '-')
147 char const *temp = argv[0] + 1;
148 size_t i;
150 /* If it appears that we are handling options, then make sure that
151 all of the options specified are actually valid. Otherwise, the
152 string should just be echoed. */
154 for (i = 0; temp[i]; i++)
155 switch (temp[i])
157 case 'e': case 'E': case 'n':
158 break;
159 default:
160 goto just_echo;
163 if (i == 0)
164 goto just_echo;
166 /* All of the options in TEMP are valid options to ECHO.
167 Handle them. */
168 while (*temp)
169 switch (*temp++)
171 case 'e':
172 do_v9 = true;
173 break;
175 case 'E':
176 do_v9 = false;
177 break;
179 case 'n':
180 display_return = false;
181 break;
184 argc--;
185 argv++;
188 just_echo:
190 if (do_v9)
192 while (argc > 0)
194 char const *s = argv[0];
195 unsigned char c;
197 while ((c = *s++))
199 if (c == '\\' && *s)
201 switch (c = *s++)
203 case 'a': c = '\a'; break;
204 case 'b': c = '\b'; break;
205 case 'c': exit (EXIT_SUCCESS);
206 case 'f': c = '\f'; break;
207 case 'n': c = '\n'; break;
208 case 'r': c = '\r'; break;
209 case 't': c = '\t'; break;
210 case 'v': c = '\v'; break;
211 case 'x':
213 unsigned char ch = *s;
214 if (! isxdigit (ch))
215 goto not_an_escape;
216 s++;
217 c = hextobin (ch);
218 ch = *s;
219 if (isxdigit (ch))
221 s++;
222 c = c * 16 + hextobin (ch);
225 break;
226 case '0':
227 c = 0;
228 if (! ('0' <= *s && *s <= '7'))
229 break;
230 c = *s++;
231 /* Fall through. */
232 case '1': case '2': case '3':
233 case '4': case '5': case '6': case '7':
234 c -= '0';
235 if ('0' <= *s && *s <= '7')
236 c = c * 8 + (*s++ - '0');
237 if ('0' <= *s && *s <= '7')
238 c = c * 8 + (*s++ - '0');
239 break;
240 case '\\': break;
242 not_an_escape:
243 default: putchar ('\\'); break;
246 putchar (c);
248 argc--;
249 argv++;
250 if (argc > 0)
251 putchar (' ');
254 else
256 while (argc > 0)
258 fputs (argv[0], stdout);
259 argc--;
260 argv++;
261 if (argc > 0)
262 putchar (' ');
266 if (display_return)
267 putchar ('\n');
268 exit (EXIT_SUCCESS);