echo.c 8.3 KB

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