| 1 | /* echo.c, derived from code echo.c in Bash.
|
|---|
| 2 | Copyright (C) 87,89, 1991-1997, 1999-2005 Free Software Foundation, Inc.
|
|---|
| 3 |
|
|---|
| 4 | This program is free software; you can redistribute it and/or modify
|
|---|
| 5 | it under the terms of the GNU General Public License as published by
|
|---|
| 6 | the Free Software Foundation; either version 2, or (at your option)
|
|---|
| 7 | any later version.
|
|---|
| 8 |
|
|---|
| 9 | This program is distributed in the hope that it will be useful,
|
|---|
| 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|---|
| 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|---|
| 12 | GNU General Public License for more details.
|
|---|
| 13 |
|
|---|
| 14 | You should have received a copy of the GNU General Public License
|
|---|
| 15 | along with this program; if not, write to the Free Software Foundation,
|
|---|
| 16 | Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
|
|---|
| 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 | usage (int status)
|
|---|
| 58 | {
|
|---|
| 59 | if (status != EXIT_SUCCESS)
|
|---|
| 60 | fprintf (stderr, _("Try `%s --help' for more information.\n"),
|
|---|
| 61 | program_name);
|
|---|
| 62 | else
|
|---|
| 63 | {
|
|---|
| 64 | printf (_("Usage: %s [OPTION]... [STRING]...\n"), program_name);
|
|---|
| 65 | fputs (_("\
|
|---|
| 66 | Echo the STRING(s) to standard output.\n\
|
|---|
| 67 | \n\
|
|---|
| 68 | -n do not output the trailing newline\n\
|
|---|
| 69 | "), stdout);
|
|---|
| 70 | 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 | fputs (HELP_OPTION_DESCRIPTION, stdout);
|
|---|
| 79 | fputs (VERSION_OPTION_DESCRIPTION, stdout);
|
|---|
| 80 | 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 | 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 | printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
|
|---|
| 98 | printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
|
|---|
| 99 | }
|
|---|
| 100 | exit (status);
|
|---|
| 101 | }
|
|---|
| 102 |
|
|---|
| 103 | /* Convert C from hexadecimal character to integer. */
|
|---|
| 104 | static int
|
|---|
| 105 | hextobin (unsigned char c)
|
|---|
| 106 | {
|
|---|
| 107 | switch (c)
|
|---|
| 108 | {
|
|---|
| 109 | default: return c - '0';
|
|---|
| 110 | case 'a': case 'A': return 10;
|
|---|
| 111 | case 'b': case 'B': return 11;
|
|---|
| 112 | case 'c': case 'C': return 12;
|
|---|
| 113 | case 'd': case 'D': return 13;
|
|---|
| 114 | case 'e': case 'E': return 14;
|
|---|
| 115 | 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 | main (int argc, char **argv)
|
|---|
| 125 | {
|
|---|
| 126 | bool display_return = true;
|
|---|
| 127 | bool allow_options =
|
|---|
| 128 | (! getenv ("POSIXLY_CORRECT")
|
|---|
| 129 | || (! 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 | bool do_v9 = DEFAULT_ECHO_TO_XPG;
|
|---|
| 135 |
|
|---|
| 136 | initialize_main (&argc, &argv);
|
|---|
| 137 | program_name = argv[0];
|
|---|
| 138 | setlocale (LC_ALL, "");
|
|---|
| 139 | bindtextdomain (PACKAGE, LOCALEDIR);
|
|---|
| 140 | textdomain (PACKAGE);
|
|---|
| 141 |
|
|---|
| 142 | atexit (close_stdout);
|
|---|
| 143 |
|
|---|
| 144 | if (allow_options)
|
|---|
| 145 | parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
|
|---|
| 146 | usage, AUTHORS, (char const *) NULL);
|
|---|
| 147 |
|
|---|
| 148 | --argc;
|
|---|
| 149 | ++argv;
|
|---|
| 150 |
|
|---|
| 151 | if (allow_options)
|
|---|
| 152 | while (argc > 0 && *argv[0] == '-')
|
|---|
| 153 | {
|
|---|
| 154 | 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 | for (i = 0; temp[i]; i++)
|
|---|
| 162 | switch (temp[i])
|
|---|
| 163 | {
|
|---|
| 164 | case 'e': case 'E': case 'n':
|
|---|
| 165 | break;
|
|---|
| 166 | default:
|
|---|
| 167 | goto just_echo;
|
|---|
| 168 | }
|
|---|
| 169 |
|
|---|
| 170 | if (i == 0)
|
|---|
| 171 | goto just_echo;
|
|---|
| 172 |
|
|---|
| 173 | /* All of the options in TEMP are valid options to ECHO.
|
|---|
| 174 | Handle them. */
|
|---|
| 175 | while (*temp)
|
|---|
| 176 | switch (*temp++)
|
|---|
| 177 | {
|
|---|
| 178 | case 'e':
|
|---|
| 179 | do_v9 = true;
|
|---|
| 180 | break;
|
|---|
| 181 |
|
|---|
| 182 | case 'E':
|
|---|
| 183 | do_v9 = false;
|
|---|
| 184 | break;
|
|---|
| 185 |
|
|---|
| 186 | case 'n':
|
|---|
| 187 | display_return = false;
|
|---|
| 188 | break;
|
|---|
| 189 | }
|
|---|
| 190 |
|
|---|
| 191 | argc--;
|
|---|
| 192 | argv++;
|
|---|
| 193 | }
|
|---|
| 194 |
|
|---|
| 195 | just_echo:
|
|---|
| 196 |
|
|---|
| 197 | if (do_v9)
|
|---|
| 198 | {
|
|---|
| 199 | while (argc > 0)
|
|---|
| 200 | {
|
|---|
| 201 | char const *s = argv[0];
|
|---|
| 202 | unsigned char c;
|
|---|
| 203 |
|
|---|
| 204 | while ((c = *s++))
|
|---|
| 205 | {
|
|---|
| 206 | if (c == '\\' && *s)
|
|---|
| 207 | {
|
|---|
| 208 | switch (c = *s++)
|
|---|
| 209 | {
|
|---|
| 210 | case 'a': c = '\a'; break;
|
|---|
| 211 | case 'b': c = '\b'; break;
|
|---|
| 212 | case 'c': exit (EXIT_SUCCESS);
|
|---|
| 213 | case 'f': c = '\f'; break;
|
|---|
| 214 | case 'n': c = '\n'; break;
|
|---|
| 215 | case 'r': c = '\r'; break;
|
|---|
| 216 | case 't': c = '\t'; break;
|
|---|
| 217 | case 'v': c = '\v'; break;
|
|---|
| 218 | case 'x':
|
|---|
| 219 | {
|
|---|
| 220 | unsigned char ch = *s;
|
|---|
| 221 | if (! ISXDIGIT (ch))
|
|---|
| 222 | goto not_an_escape;
|
|---|
| 223 | s++;
|
|---|
| 224 | c = hextobin (ch);
|
|---|
| 225 | ch = *s;
|
|---|
| 226 | if (ISXDIGIT (ch))
|
|---|
| 227 | {
|
|---|
| 228 | s++;
|
|---|
| 229 | c = c * 16 + hextobin (ch);
|
|---|
| 230 | }
|
|---|
| 231 | }
|
|---|
| 232 | break;
|
|---|
| 233 | case '0':
|
|---|
| 234 | c = 0;
|
|---|
| 235 | if (! ('0' <= *s && *s <= '7'))
|
|---|
| 236 | break;
|
|---|
| 237 | c = *s++;
|
|---|
| 238 | /* Fall through. */
|
|---|
| 239 | case '1': case '2': case '3':
|
|---|
| 240 | case '4': case '5': case '6': case '7':
|
|---|
| 241 | c -= '0';
|
|---|
| 242 | if ('0' <= *s && *s <= '7')
|
|---|
| 243 | c = c * 8 + (*s++ - '0');
|
|---|
| 244 | if ('0' <= *s && *s <= '7')
|
|---|
| 245 | c = c * 8 + (*s++ - '0');
|
|---|
| 246 | break;
|
|---|
| 247 | case '\\': break;
|
|---|
| 248 |
|
|---|
| 249 | not_an_escape:
|
|---|
| 250 | default: putchar ('\\'); break;
|
|---|
| 251 | }
|
|---|
| 252 | }
|
|---|
| 253 | putchar (c);
|
|---|
| 254 | }
|
|---|
| 255 | argc--;
|
|---|
| 256 | argv++;
|
|---|
| 257 | if (argc > 0)
|
|---|
| 258 | putchar (' ');
|
|---|
| 259 | }
|
|---|
| 260 | }
|
|---|
| 261 | else
|
|---|
| 262 | {
|
|---|
| 263 | while (argc > 0)
|
|---|
| 264 | {
|
|---|
| 265 | fputs (argv[0], stdout);
|
|---|
| 266 | argc--;
|
|---|
| 267 | argv++;
|
|---|
| 268 | if (argc > 0)
|
|---|
| 269 | putchar (' ');
|
|---|
| 270 | }
|
|---|
| 271 | }
|
|---|
| 272 |
|
|---|
| 273 | if (display_return)
|
|---|
| 274 | putchar ('\n');
|
|---|
| 275 | exit (EXIT_SUCCESS);
|
|---|
| 276 | }
|
|---|