echo.c 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. /* vi: set sw=4 ts=4: */
  2. /*
  3. * echo implementation for busybox
  4. *
  5. * Copyright (c) 1991, 1993
  6. * The Regents of the University of California. All rights reserved.
  7. *
  8. * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  9. *
  10. * Original copyright notice is retained at the end of this file.
  11. */
  12. /* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
  13. *
  14. * Because of behavioral differences, implemented configurable SUSv3
  15. * or 'fancy' gnu-ish behaviors. Also, reduced size and fixed bugs.
  16. * 1) In handling '\c' escape, the previous version only suppressed the
  17. * trailing newline. SUSv3 specifies _no_ output after '\c'.
  18. * 2) SUSv3 specifies that octal escapes are of the form \0{#{#{#}}}.
  19. * The previous version did not allow 4-digit octals.
  20. */
  21. //config:config ECHO
  22. //config: bool "echo (1.8 kb)"
  23. //config: default y
  24. //config: help
  25. //config: echo prints a specified string to stdout.
  26. //config:
  27. //config:# this entry also appears in shell/Config.in, next to the echo builtin
  28. //config:config FEATURE_FANCY_ECHO
  29. //config: bool "Enable -n and -e options"
  30. //config: default y
  31. //config: depends on ECHO || ASH_ECHO || HUSH_ECHO
  32. //applet:IF_ECHO(APPLET_NOFORK(echo, echo, BB_DIR_BIN, BB_SUID_DROP, echo))
  33. //kbuild:lib-$(CONFIG_ECHO) += echo.o
  34. //kbuild:lib-$(CONFIG_ASH_ECHO) += echo.o
  35. //kbuild:lib-$(CONFIG_HUSH_ECHO) += echo.o
  36. /* BB_AUDIT SUSv3 compliant -- unless configured as fancy echo. */
  37. /* http://www.opengroup.org/onlinepubs/007904975/utilities/echo.html */
  38. //usage:#define echo_trivial_usage
  39. //usage: IF_FEATURE_FANCY_ECHO("[-neE] ") "[ARG]..."
  40. //usage:#define echo_full_usage "\n\n"
  41. //usage: "Print ARGs to stdout"
  42. //usage: IF_FEATURE_FANCY_ECHO( "\n"
  43. //usage: "\n -n No trailing newline"
  44. //usage: "\n -e Interpret backslash escapes (\\t=tab etc)"
  45. //usage: "\n -E Don't interpret backslash escapes (default)"
  46. //usage: )
  47. //usage:
  48. //usage:#define echo_example_usage
  49. //usage: "$ echo \"Erik is cool\"\n"
  50. //usage: "Erik is cool\n"
  51. //usage: IF_FEATURE_FANCY_ECHO("$ echo -e \"Erik\\nis\\ncool\"\n"
  52. //usage: "Erik\n"
  53. //usage: "is\n"
  54. //usage: "cool\n"
  55. //usage: "$ echo \"Erik\\nis\\ncool\"\n"
  56. //usage: "Erik\\nis\\ncool\n")
  57. #include "libbb.h"
  58. /* This is a NOFORK applet. Be very careful! */
  59. /* NB: can be used by shell even if not enabled as applet */
  60. /*
  61. * NB2: we don't use stdio, we need better error handing.
  62. * Examples include writing into non-opened stdout and error on write.
  63. *
  64. * With stdio, output gets shoveled into stdout buffer, and even
  65. * fflush cannot clear it out. It seems that even if libc receives
  66. * EBADF on write attempts, it feels determined to output data no matter what.
  67. * If echo is called by shell, it will try writing again later, and possibly
  68. * will clobber future output. Not good.
  69. *
  70. * Solaris has fpurge which discards buffered input. glibc has __fpurge.
  71. * But this function is not standard.
  72. */
  73. int echo_main(int argc UNUSED_PARAM, char **argv)
  74. {
  75. char **pp;
  76. const char *arg;
  77. char *out;
  78. char *buffer;
  79. unsigned buflen;
  80. int err;
  81. #if !ENABLE_FEATURE_FANCY_ECHO
  82. enum {
  83. eflag = 0, /* 0 -- disable escape sequences */
  84. nflag = 1, /* 1 -- print '\n' */
  85. };
  86. argv++;
  87. #else
  88. char nflag = 1;
  89. char eflag = 0;
  90. while ((arg = *++argv) != NULL) {
  91. char n, e;
  92. if (arg[0] != '-')
  93. break; /* not an option arg, echo it */
  94. /* If it appears that we are handling options, then make sure
  95. * that all of the options specified are actually valid.
  96. * Otherwise, the string should just be echoed.
  97. */
  98. arg++;
  99. n = nflag;
  100. e = eflag;
  101. do {
  102. if (*arg == 'n')
  103. n = 0;
  104. else if (*arg == 'e')
  105. e = '\\';
  106. else if (*arg != 'E') {
  107. /* "-ccc" arg with one of c's invalid, echo it */
  108. /* arg consisting from just "-" also handled here */
  109. goto just_echo;
  110. }
  111. } while (*++arg);
  112. nflag = n;
  113. eflag = e;
  114. }
  115. just_echo:
  116. #endif
  117. buflen = 0;
  118. pp = argv;
  119. while ((arg = *pp) != NULL) {
  120. buflen += strlen(arg) + 1;
  121. pp++;
  122. }
  123. out = buffer = xmalloc(buflen + 1); /* +1 is needed for "no args" case */
  124. while ((arg = *argv) != NULL) {
  125. int c;
  126. if (!eflag) {
  127. /* optimization for very common case */
  128. out = stpcpy(out, arg);
  129. } else
  130. while ((c = *arg++) != '\0') {
  131. if (c == eflag) {
  132. /* This is an "\x" sequence */
  133. if (*arg == 'c') {
  134. /* "\c" means cancel newline and
  135. * ignore all subsequent chars. */
  136. goto do_write;
  137. }
  138. /* Since SUSv3 mandates a first digit of 0, 4-digit octals
  139. * of the form \0### are accepted. */
  140. if (*arg == '0') {
  141. if ((unsigned char)(arg[1] - '0') < 8) {
  142. /* 2nd char is 0..7: skip leading '0' */
  143. arg++;
  144. }
  145. }
  146. /* bb_process_escape_sequence handles NUL correctly
  147. * ("...\" case). */
  148. {
  149. /* optimization: don't force arg to be on-stack,
  150. * use another variable for that. ~30 bytes win */
  151. const char *z = arg;
  152. c = bb_process_escape_sequence(&z);
  153. arg = z;
  154. }
  155. }
  156. *out++ = c;
  157. }
  158. if (!*++argv)
  159. break;
  160. *out++ = ' ';
  161. }
  162. if (nflag) {
  163. *out++ = '\n';
  164. }
  165. do_write:
  166. /* Careful to error out on partial writes too (think ENOSPC!) */
  167. errno = 0;
  168. err = full_write(STDOUT_FILENO, buffer, out - buffer) != out - buffer;
  169. if (err) {
  170. bb_simple_perror_msg(bb_msg_write_error);
  171. }
  172. free(buffer);
  173. return err;
  174. }
  175. /*
  176. * Copyright (c) 1991, 1993
  177. * The Regents of the University of California. All rights reserved.
  178. *
  179. * This code is derived from software contributed to Berkeley by
  180. * Kenneth Almquist.
  181. *
  182. * Redistribution and use in source and binary forms, with or without
  183. * modification, are permitted provided that the following conditions
  184. * are met:
  185. * 1. Redistributions of source code must retain the above copyright
  186. * notice, this list of conditions and the following disclaimer.
  187. * 2. Redistributions in binary form must reproduce the above copyright
  188. * notice, this list of conditions and the following disclaimer in the
  189. * documentation and/or other materials provided with the distribution.
  190. *
  191. * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
  192. * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
  193. *
  194. * California, Berkeley and its contributors.
  195. * 4. Neither the name of the University nor the names of its contributors
  196. * may be used to endorse or promote products derived from this software
  197. * without specific prior written permission.
  198. *
  199. * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND
  200. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  201. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  202. * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  203. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  204. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  205. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  206. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  207. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  208. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  209. * SUCH DAMAGE.
  210. *
  211. * @(#)echo.c 8.1 (Berkeley) 5/31/93
  212. */
  213. #ifdef VERSION_WITH_WRITEV
  214. /* We can't use stdio.
  215. * The reason for this is highly non-obvious.
  216. * echo_main is used from shell. Shell must correctly handle "echo foo"
  217. * if stdout is closed. With stdio, output gets shoveled into
  218. * stdout buffer, and even fflush cannot clear it out. It seems that
  219. * even if libc receives EBADF on write attempts, it feels determined
  220. * to output data no matter what. So it will try later,
  221. * and possibly will clobber future output. Not good.
  222. *
  223. * Using writev instead, with 'direct' conversion of argv vector.
  224. */
  225. int echo_main(int argc, char **argv)
  226. {
  227. struct iovec io[argc];
  228. struct iovec *cur_io = io;
  229. char *arg;
  230. char *p;
  231. #if !ENABLE_FEATURE_FANCY_ECHO
  232. enum {
  233. eflag = '\\',
  234. nflag = 1, /* 1 -- print '\n' */
  235. };
  236. arg = *++argv;
  237. if (!arg)
  238. goto newline_ret;
  239. #else
  240. char nflag = 1;
  241. char eflag = 0;
  242. while (1) {
  243. arg = *++argv;
  244. if (!arg)
  245. goto newline_ret;
  246. if (*arg != '-')
  247. break;
  248. /* If it appears that we are handling options, then make sure
  249. * that all of the options specified are actually valid.
  250. * Otherwise, the string should just be echoed.
  251. */
  252. p = arg + 1;
  253. if (!*p) /* A single '-', so echo it. */
  254. goto just_echo;
  255. do {
  256. if (!strchr("neE", *p))
  257. goto just_echo;
  258. } while (*++p);
  259. /* All of the options in this arg are valid, so handle them. */
  260. p = arg + 1;
  261. do {
  262. if (*p == 'n')
  263. nflag = 0;
  264. if (*p == 'e')
  265. eflag = '\\';
  266. } while (*++p);
  267. }
  268. just_echo:
  269. #endif
  270. while (1) {
  271. /* arg is already == *argv and isn't NULL */
  272. int c;
  273. cur_io->iov_base = p = arg;
  274. if (!eflag) {
  275. /* optimization for very common case */
  276. p += strlen(arg);
  277. } else while ((c = *arg++)) {
  278. if (c == eflag) {
  279. /* This is an "\x" sequence */
  280. if (*arg == 'c') {
  281. /* "\c" means cancel newline and
  282. * ignore all subsequent chars. */
  283. cur_io->iov_len = p - (char*)cur_io->iov_base;
  284. cur_io++;
  285. goto ret;
  286. }
  287. /* Since SUSv3 mandates a first digit of 0, 4-digit octals
  288. * of the form \0### are accepted. */
  289. if (*arg == '0' && (unsigned char)(arg[1] - '0') < 8) {
  290. arg++;
  291. }
  292. //FIXME? we also accept non-0 starting sequences (see echo-prints-slash_41 test)
  293. // echo -ne '-\41-' prints "-!-". bash 5.0.17 does not (prints "-\41-").
  294. /* bb_process_escape_sequence can handle nul correctly */
  295. c = bb_process_escape_sequence( (void*) &arg);
  296. }
  297. *p++ = c;
  298. }
  299. arg = *++argv;
  300. if (arg)
  301. *p++ = ' ';
  302. cur_io->iov_len = p - (char*)cur_io->iov_base;
  303. cur_io++;
  304. if (!arg)
  305. break;
  306. }
  307. newline_ret:
  308. if (nflag) {
  309. cur_io->iov_base = (char*)"\n";
  310. cur_io->iov_len = 1;
  311. cur_io++;
  312. }
  313. ret:
  314. /* TODO: implement and use full_writev? */
  315. return writev(1, io, (cur_io - io)) >= 0;
  316. }
  317. #endif