tac.c 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. /* vi: set sw=4 ts=4: */
  2. /*
  3. * tac implementation for busybox
  4. * tac - concatenate and print files in reverse
  5. *
  6. * Copyright (C) 2003 Yang Xiaopeng <yxp at hanwang.com.cn>
  7. * Copyright (C) 2007 Natanael Copa <natanael.copa@gmail.com>
  8. * Copyright (C) 2007 Tito Ragusa <farmatito@tiscali.it>
  9. *
  10. * Licensed under GPLv2, see file LICENSE in this source tree.
  11. */
  12. /* Based on Yang Xiaopeng's (yxp at hanwang.com.cn) patch
  13. * http://www.uclibc.org/lists/busybox/2003-July/008813.html
  14. */
  15. //config:config TAC
  16. //config: bool "tac (3.9 kb)"
  17. //config: default y
  18. //config: help
  19. //config: tac is used to concatenate and print files in reverse.
  20. //applet:IF_TAC(APPLET_NOEXEC(tac, tac, BB_DIR_USR_BIN, BB_SUID_DROP, tac))
  21. //kbuild:lib-$(CONFIG_TAC) += tac.o
  22. //usage:#define tac_trivial_usage
  23. //usage: "[FILE]..."
  24. //usage:#define tac_full_usage "\n\n"
  25. //usage: "Concatenate FILEs and print them in reverse"
  26. #include "libbb.h"
  27. /* This is a NOEXEC applet. Be very careful! */
  28. struct lstring {
  29. int size;
  30. char buf[1];
  31. };
  32. int tac_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  33. int tac_main(int argc UNUSED_PARAM, char **argv)
  34. {
  35. char **name;
  36. FILE *f;
  37. struct lstring *line = NULL;
  38. llist_t *list = NULL;
  39. int retval = EXIT_SUCCESS;
  40. #if ENABLE_DESKTOP
  41. /* tac from coreutils 6.9 supports:
  42. -b, --before
  43. attach the separator before instead of after
  44. -r, --regex
  45. interpret the separator as a regular expression
  46. -s, --separator=STRING
  47. use STRING as the separator instead of newline
  48. We support none, but at least we will complain or handle "--":
  49. */
  50. getopt32(argv, "");
  51. argv += optind;
  52. #else
  53. argv++;
  54. #endif
  55. if (!*argv)
  56. *--argv = (char *)"-";
  57. /* We will read from last file to first */
  58. name = argv;
  59. while (*name)
  60. name++;
  61. do {
  62. int ch, i;
  63. name--;
  64. f = fopen_or_warn_stdin(*name);
  65. if (f == NULL) {
  66. /* error message is printed by fopen_or_warn_stdin */
  67. retval = EXIT_FAILURE;
  68. continue;
  69. }
  70. errno = i = 0;
  71. do {
  72. ch = fgetc(f);
  73. if (ch != EOF) {
  74. if (!(i & 0x7f))
  75. /* Grow on every 128th char */
  76. line = xrealloc(line, i + 0x7f + sizeof(int) + 1);
  77. line->buf[i++] = ch;
  78. }
  79. if (ch == '\n' || (ch == EOF && i != 0)) {
  80. line = xrealloc(line, i + sizeof(int));
  81. line->size = i;
  82. llist_add_to(&list, line);
  83. line = NULL;
  84. i = 0;
  85. }
  86. } while (ch != EOF);
  87. /* fgetc sets errno to ENOENT on EOF, we don't want
  88. * to warn on this non-error! */
  89. if (errno && errno != ENOENT) {
  90. bb_simple_perror_msg(*name);
  91. retval = EXIT_FAILURE;
  92. }
  93. } while (name != argv);
  94. while (list) {
  95. line = (struct lstring *)list->data;
  96. xwrite(STDOUT_FILENO, line->buf, line->size);
  97. if (ENABLE_FEATURE_CLEAN_UP) {
  98. free(llist_pop(&list));
  99. } else {
  100. list = list->link;
  101. }
  102. }
  103. return retval;
  104. }