summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/echo.c252
1 files changed, 137 insertions, 115 deletions
diff --git a/src/echo.c b/src/echo.c
index d9ae62032..9467af345 100644
--- a/src/echo.c
+++ b/src/echo.c
@@ -39,28 +39,16 @@ following backslash-escaped characters is turned on:
\t horizontal tab
\v vertical tab
\\ backslash
- \num the character whose ASCII code is NUM (octal).
+ \0NNN the character whose ASCII code is NNN (octal).
You can explicitly turn off the interpretation of the above characters
on System V systems with the -E option.
*/
-/* If defined, interpret backslash escapes if -e is given. */
-#define V9_ECHO
-
-/* If defined, interpret backslash escapes unless -E is given.
- V9_ECHO must also be defined. */
-/* #define V9_DEFAULT */
-
-#if defined (V9_ECHO)
-# if defined (V9_DEFAULT)
-# define VALID_ECHO_OPTIONS "neE"
-# else
-# define VALID_ECHO_OPTIONS "ne"
-# endif /* !V9_DEFAULT */
-#else /* !V9_ECHO */
-# define VALID_ECHO_OPTIONS "n"
-#endif /* !V9_ECHO */
+/* If true, interpret backslash escapes by default. */
+#ifndef DEFAULT_ECHO_TO_XPG
+enum { DEFAULT_ECHO_TO_XPG = false };
+#endif
/* The name this program was run with. */
char *program_name;
@@ -86,9 +74,9 @@ Echo the STRING(s) to standard output.\n\
fputs (VERSION_OPTION_DESCRIPTION, stdout);
fputs (_("\
\n\
-Without -E, the following sequences are recognized and interpolated:\n\
+If -e is in effect, the following sequences are recognized:\n\
\n\
- \\NNN the character whose ASCII code is NNN (octal)\n\
+ \\0NNN the character whose ASCII code is NNN (octal)\n\
\\\\ backslash\n\
\\a alert (BEL)\n\
\\b backspace\n\
@@ -106,6 +94,22 @@ Without -E, the following sequences are recognized and interpolated:\n\
exit (status);
}
+/* Convert C from hexadecimal character to integer. */
+static int
+hextobin (unsigned char c)
+{
+ switch (c)
+ {
+ default: return c - '0';
+ case 'a': case 'A': return 10;
+ case 'b': case 'B': return 11;
+ case 'c': case 'C': return 12;
+ case 'd': case 'D': return 13;
+ case 'e': case 'E': return 14;
+ case 'f': case 'F': return 15;
+ }
+}
+
/* Print the words in LIST to standard output. If the first word is
`-n', then don't print a trailing newline. We also support the
echo syntax from Version 9 unix systems. */
@@ -113,8 +117,15 @@ Without -E, the following sequences are recognized and interpolated:\n\
int
main (int argc, char **argv)
{
- int display_return = 1, do_v9 = 0;
- int allow_options = 1;
+ bool display_return = true;
+ bool allow_options =
+ (! getenv ("POSIXLY_CORRECT")
+ || (! DEFAULT_ECHO_TO_XPG && 0 < argc && STREQ (argv[1], "-n")));
+
+ /* System V machines already have a /bin/sh with a v9 behavior.
+ Use the identical behavior for these machines so that the
+ existing system shell scripts won't barf. */
+ bool do_v9 = DEFAULT_ECHO_TO_XPG;
initialize_main (&argc, &argv);
program_name = argv[0];
@@ -124,124 +135,135 @@ main (int argc, char **argv)
atexit (close_stdout);
- /* Don't recognize --help or --version if POSIXLY_CORRECT is set. */
- if (getenv ("POSIXLY_CORRECT") == NULL)
+ if (allow_options)
parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
- usage, AUTHORS, (char const *) NULL, NULL);
- else
- allow_options = 0;
-
-/* System V machines already have a /bin/sh with a v9 behaviour. We
- use the identical behaviour for these machines so that the
- existing system shell scripts won't barf. */
-#if defined (V9_ECHO) && defined (V9_DEFAULT)
- do_v9 = allow_options;
-#endif
+ usage, AUTHORS, (char const *) NULL);
--argc;
++argv;
- while (argc > 0 && *argv[0] == '-')
- {
- register char *temp;
- register int i;
+ if (allow_options)
+ while (argc > 0 && *argv[0] == '-')
+ {
+ char const *temp = argv[0] + 1;
+ size_t i;
- /* If it appears that we are handling options, then make sure that
- all of the options specified are actually valid. Otherwise, the
- string should just be echoed. */
- temp = argv[0] + 1;
+ /* If it appears that we are handling options, then make sure that
+ all of the options specified are actually valid. Otherwise, the
+ string should just be echoed. */
- for (i = 0; temp[i]; i++)
- {
- if (strrchr (VALID_ECHO_OPTIONS, temp[i]) == 0)
- goto just_echo;
- }
+ for (i = 0; temp[i]; i++)
+ switch (temp[i])
+ {
+ case 'e': case 'E': case 'n':
+ break;
+ default:
+ goto just_echo;
+ }
- if (!*temp)
- goto just_echo;
+ if (i == 0)
+ goto just_echo;
- /* All of the options in TEMP are valid options to ECHO.
- Handle them. */
- while (*temp)
- {
- if (allow_options && *temp == 'n')
- display_return = 0;
-#if defined (V9_ECHO)
- else if (allow_options && *temp == 'e')
- do_v9 = 1;
-# if defined (V9_DEFAULT)
- else if (allow_options && *temp == 'E')
- do_v9 = 0;
-# endif /* V9_DEFAULT */
-#endif /* V9_ECHO */
- else
- goto just_echo;
-
- temp++;
- }
- argc--;
- argv++;
- }
+ /* All of the options in TEMP are valid options to ECHO.
+ Handle them. */
+ while (*temp)
+ switch (*temp++)
+ {
+ case 'e':
+ do_v9 = true;
+ break;
+
+ case 'E':
+ do_v9 = false;
+ break;
+
+ case 'n':
+ display_return = false;
+ break;
+ }
+
+ argc--;
+ argv++;
+ }
just_echo:
- if (argc > 0)
+ if (do_v9)
{
-#if defined (V9_ECHO)
- if (do_v9)
+ while (argc > 0)
{
- while (argc > 0)
- {
- register char *s = argv[0];
- register int c;
+ char const *s = argv[0];
+ unsigned char c;
- while ((c = *s++))
+ while ((c = *s++))
+ {
+ if (c == '\\' && *s)
{
- if (c == '\\' && *s)
+ switch (c = *s++)
{
- switch (c = *s++)
- {
- case 'a': c = '\007'; break;
- case 'b': c = '\b'; break;
- case 'c': display_return = 0; continue;
- case 'f': c = '\f'; break;
- case 'n': c = '\n'; break;
- case 'r': c = '\r'; break;
- case 't': c = '\t'; break;
- case 'v': c = (int) 0x0B; break;
- case '0': case '1': case '2': case '3':
- case '4': case '5': case '6': case '7':
- c -= '0';
- if (*s >= '0' && *s <= '7')
- c = c * 8 + (*s++ - '0');
- if (*s >= '0' && *s <= '7')
- c = c * 8 + (*s++ - '0');
- break;
- case '\\': break;
- default: putchar ('\\'); break;
- }
+ case 'a': c = '\a'; break;
+ case 'b': c = '\b'; break;
+ case 'c': exit (EXIT_SUCCESS);
+ case 'f': c = '\f'; break;
+ case 'n': c = '\n'; break;
+ case 'r': c = '\r'; break;
+ case 't': c = '\t'; break;
+ case 'v': c = '\v'; break;
+ case 'x':
+ {
+ unsigned char ch = *s;
+ if (! ISXDIGIT (ch))
+ goto not_an_escape;
+ s++;
+ c = hextobin (ch);
+ ch = *s;
+ if (ISXDIGIT (ch))
+ {
+ s++;
+ c = c * 16 + hextobin (ch);
+ }
+ }
+ break;
+ case '0':
+ c = 0;
+ if (! ('0' <= *s && *s <= '7'))
+ break;
+ c = *s++;
+ /* Fall through. */
+ case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ c -= '0';
+ if ('0' <= *s && *s <= '7')
+ c = c * 8 + (*s++ - '0');
+ if ('0' <= *s && *s <= '7')
+ c = c * 8 + (*s++ - '0');
+ break;
+ case '\\': break;
+
+ not_an_escape:
+ default: putchar ('\\'); break;
}
- putchar(c);
}
- argc--;
- argv++;
- if (argc > 0)
- putchar(' ');
+ putchar (c);
}
+ argc--;
+ argv++;
+ if (argc > 0)
+ putchar (' ');
}
- else
-#endif /* V9_ECHO */
+ }
+ else
+ {
+ while (argc > 0)
{
- while (argc > 0)
- {
- fputs (argv[0], stdout);
- argc--;
- argv++;
- if (argc > 0)
- putchar (' ');
- }
+ fputs (argv[0], stdout);
+ argc--;
+ argv++;
+ if (argc > 0)
+ putchar (' ');
}
}
+
if (display_return)
putchar ('\n');
exit (EXIT_SUCCESS);