summaryrefslogtreecommitdiff
path: root/src/echo.c
blob: 991a57425312588d30ea19c66844b52969e93c3e (plain)
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
/* echo.c, derived from code echo.c in Bash. Copyright (C) 1987-2025 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. */#include <config.h>#include <stdio.h>#include <sys/types.h>#include"system.h"#include"assure.h"#include"c-ctype.h"/* The official name of this program (e.g., no 'g' prefix). */#define PROGRAM_NAME"echo"#define AUTHORS \ proper_name ("Brian Fox"), \ proper_name ("Chet Ramey")/* If true, interpret backslash escapes by default. */#ifndef DEFAULT_ECHO_TO_XPGenum{ DEFAULT_ECHO_TO_XPG =false};#endifvoidusage(int status){/* STATUS should always be EXIT_SUCCESS (unlike in most other utilities which would call emit_try_help otherwise). */affirm(status == EXIT_SUCCESS);printf(_("\Usage: %s [SHORT-OPTION]... [STRING]...\n\ or: %s LONG-OPTION\n\"), program_name, program_name);fputs(_("\Echo the STRING(s) to standard output.\n\\n\ -n do not output the trailing newline\n\"), stdout);fputs(_(DEFAULT_ECHO_TO_XPG ?N_("\ -e enable interpretation of backslash escapes (default)\n\ -E disable interpretation of backslash escapes\n"):N_("\ -e enable interpretation of backslash escapes\n\ -E disable interpretation of backslash escapes (default)\n")), stdout);fputs(HELP_OPTION_DESCRIPTION, stdout);fputs(VERSION_OPTION_DESCRIPTION, stdout);fputs(_("\\n\If -e is in effect, the following sequences are recognized:\n\\n\"), stdout);fputs(_("\\\\\backslash\n\\\a alert (BEL)\n\\\b backspace\n\\\c produce no further output\n\\\e escape\n\\\f form feed\n\\\n new line\n\\\r carriage return\n\\\t horizontal tab\n\\\v vertical tab\n\"), stdout);fputs(_("\\\0NNN byte with octal value NNN (1 to 3 digits)\n\\\xHH byte with hexadecimal value HH (1 to 2 digits)\n\"), stdout);printf(USAGE_BUILTIN_WARNING, PROGRAM_NAME);fputs(_("\n\Consider using the printf(1) command instead,\n\as it avoids problems when outputting option-like strings.\n\"), stdout);emit_ancillary_info(PROGRAM_NAME);exit(status);}/* Convert C from hexadecimal character to integer. */static inthextobin(unsigned char c){switch(c){default:return c -'0';case'a':case'A':return10;case'b':case'B':return11;case'c':case'C':return12;case'd':case'D':return13;case'e':case'E':return14;case'f':case'F':return15;}}/* 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. */intmain(int argc,char**argv){bool display_return =true;bool posixly_correct = !!getenv("POSIXLY_CORRECT");bool allow_options =(! posixly_correct || (! DEFAULT_ECHO_TO_XPG &&1< 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);set_program_name(argv[0]);setlocale(LC_ALL,"");bindtextdomain(PACKAGE, LOCALEDIR);textdomain(PACKAGE);atexit(close_stdout);/* We directly parse options, rather than use parse_long_options, in order to avoid accepting abbreviations. */if(allow_options && argc ==2){if(STREQ(argv[1],"--help"))usage(EXIT_SUCCESS);if(STREQ(argv[1],"--version")){version_etc(stdout, PROGRAM_NAME, PACKAGE_NAME, Version, AUTHORS,(char*)nullptr);return EXIT_SUCCESS;}}--argc;++argv;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. */for(i =0; temp[i]; i++)switch(temp[i]){case'e':case'E':case'n':break;default:goto just_echo;}if(i ==0)goto just_echo;/* 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(do_v9 || posixly_correct){while(argc >0){char const*s = argv[0];unsigned char c;while((c = *s++)){if(c =='\\'&& *s){switch(c = *s++){case'a': c ='\a';break;case'b': c ='\b';break;case'c':return EXIT_SUCCESS;case'e': c ='\x1B';break;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(!c_isxdigit(ch))goto not_an_escape; s++; c =hextobin(ch); ch = *s;if(c_isxdigit(ch)){ s++; c = c *16+hextobin(ch);}}break;case'0': c =0;if(! ('0'<= *s && *s <='7'))break; c = *s++; FALLTHROUGH;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(' ');}}else{while(argc >0){fputs(argv[0], stdout); argc--; argv++;if(argc >0)putchar(' ');}}if(display_return)putchar('\n');return EXIT_SUCCESS;}
close