3
0

echo.c 9.0 KB

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