echo.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  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 tarball for details.
  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. #include "libbb.h"
  24. /* This is a NOFORK applet. Be very careful! */
  25. /* NB: can be used by shell even if not enabled as applet */
  26. int echo_main(int argc ATTRIBUTE_UNUSED, char **argv)
  27. {
  28. const char *arg;
  29. #if !ENABLE_FEATURE_FANCY_ECHO
  30. enum {
  31. eflag = '\\',
  32. nflag = 1, /* 1 -- print '\n' */
  33. };
  34. /* We must check that stdout is not closed.
  35. * The reason for this is highly non-obvious.
  36. * echo_main is used from shell. Shell must correctly handle "echo foo"
  37. * if stdout is closed. With stdio, output gets shoveled into
  38. * stdout buffer, and even fflush cannot clear it out. It seems that
  39. * even if libc receives EBADF on write attempts, it feels determined
  40. * to output data no matter what. So it will try later,
  41. * and possibly will clobber future output. Not good. */
  42. if (dup2(1, 1) != 1)
  43. return -1;
  44. arg = *++argv;
  45. if (!arg)
  46. goto newline_ret;
  47. #else
  48. const char *p;
  49. char nflag = 1;
  50. char eflag = 0;
  51. /* We must check that stdout is not closed. */
  52. if (dup2(1, 1) != 1)
  53. return -1;
  54. while (1) {
  55. arg = *++argv;
  56. if (!arg)
  57. goto newline_ret;
  58. if (*arg != '-')
  59. break;
  60. /* If it appears that we are handling options, then make sure
  61. * that all of the options specified are actually valid.
  62. * Otherwise, the string should just be echoed.
  63. */
  64. p = arg + 1;
  65. if (!*p) /* A single '-', so echo it. */
  66. goto just_echo;
  67. do {
  68. if (!strrchr("neE", *p))
  69. goto just_echo;
  70. } while (*++p);
  71. /* All of the options in this arg are valid, so handle them. */
  72. p = arg + 1;
  73. do {
  74. if (*p == 'n')
  75. nflag = 0;
  76. if (*p == 'e')
  77. eflag = '\\';
  78. } while (*++p);
  79. }
  80. just_echo:
  81. #endif
  82. while (1) {
  83. /* arg is already == *argv and isn't NULL */
  84. int c;
  85. if (!eflag) {
  86. /* optimization for very common case */
  87. fputs(arg, stdout);
  88. } else while ((c = *arg++)) {
  89. if (c == eflag) { /* Check for escape seq. */
  90. if (*arg == 'c') {
  91. /* '\c' means cancel newline and
  92. * ignore all subsequent chars. */
  93. goto ret;
  94. }
  95. #if !ENABLE_FEATURE_FANCY_ECHO
  96. /* SUSv3 specifies that octal escapes must begin with '0'. */
  97. if ( ((int)(unsigned char)(*arg) - '0') >= 8) /* '8' or bigger */
  98. #endif
  99. {
  100. /* Since SUSv3 mandates a first digit of 0, 4-digit octals
  101. * of the form \0### are accepted. */
  102. if (*arg == '0') {
  103. /* NB: don't turn "...\0" into "...\" */
  104. if (arg[1] && ((unsigned char)(arg[1]) - '0') < 8) {
  105. arg++;
  106. }
  107. }
  108. /* bb_process_escape_sequence handles NUL correctly
  109. * ("...\" case). */
  110. c = bb_process_escape_sequence(&arg);
  111. }
  112. }
  113. bb_putchar(c);
  114. }
  115. arg = *++argv;
  116. if (!arg)
  117. break;
  118. bb_putchar(' ');
  119. }
  120. newline_ret:
  121. if (nflag) {
  122. bb_putchar('\n');
  123. }
  124. ret:
  125. return fflush(stdout);
  126. }
  127. /*-
  128. * Copyright (c) 1991, 1993
  129. * The Regents of the University of California. All rights reserved.
  130. *
  131. * This code is derived from software contributed to Berkeley by
  132. * Kenneth Almquist.
  133. *
  134. * Redistribution and use in source and binary forms, with or without
  135. * modification, are permitted provided that the following conditions
  136. * are met:
  137. * 1. Redistributions of source code must retain the above copyright
  138. * notice, this list of conditions and the following disclaimer.
  139. * 2. Redistributions in binary form must reproduce the above copyright
  140. * notice, this list of conditions and the following disclaimer in the
  141. * documentation and/or other materials provided with the distribution.
  142. *
  143. * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
  144. * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
  145. *
  146. * California, Berkeley and its contributors.
  147. * 4. Neither the name of the University nor the names of its contributors
  148. * may be used to endorse or promote products derived from this software
  149. * without specific prior written permission.
  150. *
  151. * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  152. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  153. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  154. * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  155. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  156. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  157. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  158. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  159. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  160. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  161. * SUCH DAMAGE.
  162. *
  163. * @(#)echo.c 8.1 (Berkeley) 5/31/93
  164. */
  165. #ifdef VERSION_WITH_WRITEV
  166. /* We can't use stdio.
  167. * The reason for this is highly non-obvious.
  168. * echo_main is used from shell. Shell must correctly handle "echo foo"
  169. * if stdout is closed. With stdio, output gets shoveled into
  170. * stdout buffer, and even fflush cannot clear it out. It seems that
  171. * even if libc receives EBADF on write attempts, it feels determined
  172. * to output data no matter what. So it will try later,
  173. * and possibly will clobber future output. Not good.
  174. *
  175. * Using writev instead, with 'direct' conversion of argv vector.
  176. */
  177. int echo_main(int argc, char **argv)
  178. {
  179. struct iovec io[argc];
  180. struct iovec *cur_io = io;
  181. char *arg;
  182. char *p;
  183. #if !ENABLE_FEATURE_FANCY_ECHO
  184. enum {
  185. eflag = '\\',
  186. nflag = 1, /* 1 -- print '\n' */
  187. };
  188. arg = *++argv;
  189. if (!arg)
  190. goto newline_ret;
  191. #else
  192. char nflag = 1;
  193. char eflag = 0;
  194. while (1) {
  195. arg = *++argv;
  196. if (!arg)
  197. goto newline_ret;
  198. if (*arg != '-')
  199. break;
  200. /* If it appears that we are handling options, then make sure
  201. * that all of the options specified are actually valid.
  202. * Otherwise, the string should just be echoed.
  203. */
  204. p = arg + 1;
  205. if (!*p) /* A single '-', so echo it. */
  206. goto just_echo;
  207. do {
  208. if (!strrchr("neE", *p))
  209. goto just_echo;
  210. } while (*++p);
  211. /* All of the options in this arg are valid, so handle them. */
  212. p = arg + 1;
  213. do {
  214. if (*p == 'n')
  215. nflag = 0;
  216. if (*p == 'e')
  217. eflag = '\\';
  218. } while (*++p);
  219. }
  220. just_echo:
  221. #endif
  222. while (1) {
  223. /* arg is already == *argv and isn't NULL */
  224. int c;
  225. cur_io->iov_base = p = arg;
  226. if (!eflag) {
  227. /* optimization for very common case */
  228. p += strlen(arg);
  229. } else while ((c = *arg++)) {
  230. if (c == eflag) { /* Check for escape seq. */
  231. if (*arg == 'c') {
  232. /* '\c' means cancel newline and
  233. * ignore all subsequent chars. */
  234. cur_io->iov_len = p - (char*)cur_io->iov_base;
  235. cur_io++;
  236. goto ret;
  237. }
  238. #if !ENABLE_FEATURE_FANCY_ECHO
  239. /* SUSv3 specifies that octal escapes must begin with '0'. */
  240. if ( (((unsigned char)*arg) - '1') >= 7)
  241. #endif
  242. {
  243. /* Since SUSv3 mandates a first digit of 0, 4-digit octals
  244. * of the form \0### are accepted. */
  245. if (*arg == '0' && ((unsigned char)(arg[1]) - '0') < 8) {
  246. arg++;
  247. }
  248. /* bb_process_escape_sequence can handle nul correctly */
  249. c = bb_process_escape_sequence( (void*) &arg);
  250. }
  251. }
  252. *p++ = c;
  253. }
  254. arg = *++argv;
  255. if (arg)
  256. *p++ = ' ';
  257. cur_io->iov_len = p - (char*)cur_io->iov_base;
  258. cur_io++;
  259. if (!arg)
  260. break;
  261. }
  262. newline_ret:
  263. if (nflag) {
  264. cur_io->iov_base = (char*)"\n";
  265. cur_io->iov_len = 1;
  266. cur_io++;
  267. }
  268. ret:
  269. /* TODO: implement and use full_writev? */
  270. return writev(1, io, (cur_io - io)) >= 0;
  271. }
  272. #endif