PageRenderTime 47ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/usr.bin/tail/tail.c

https://bitbucket.org/freebsd/freebsd-head/
C | 338 lines | 232 code | 30 blank | 76 comment | 60 complexity | 06bdb81f199592d9d610e3bfee855e93 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, BSD-3-Clause, LGPL-2.0, LGPL-2.1, BSD-2-Clause, 0BSD, JSON, AGPL-1.0, GPL-2.0
  1. /*-
  2. * Copyright (c) 1991, 1993
  3. * The Regents of the University of California. All rights reserved.
  4. *
  5. * This code is derived from software contributed to Berkeley by
  6. * Edward Sze-Tyan Wang.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. * 2. Redistributions in binary form must reproduce the above copyright
  14. * notice, this list of conditions and the following disclaimer in the
  15. * documentation and/or other materials provided with the distribution.
  16. * 4. Neither the name of the University nor the names of its contributors
  17. * may be used to endorse or promote products derived from this software
  18. * without specific prior written permission.
  19. *
  20. * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  21. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  22. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  23. * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  24. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  25. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  26. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  27. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  28. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  29. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  30. * SUCH DAMAGE.
  31. */
  32. #include <sys/cdefs.h>
  33. __FBSDID("$FreeBSD$");
  34. #ifndef lint
  35. static const char copyright[] =
  36. "@(#) Copyright (c) 1991, 1993\n\
  37. The Regents of the University of California. All rights reserved.\n";
  38. #endif
  39. #ifndef lint
  40. static const char sccsid[] = "@(#)tail.c 8.1 (Berkeley) 6/6/93";
  41. #endif
  42. #include <sys/types.h>
  43. #include <sys/stat.h>
  44. #include <err.h>
  45. #include <errno.h>
  46. #include <stdio.h>
  47. #include <stdlib.h>
  48. #include <string.h>
  49. #include <unistd.h>
  50. #include "extern.h"
  51. int Fflag, fflag, qflag, rflag, rval, no_files;
  52. static file_info_t *files;
  53. static void obsolete(char **);
  54. static void usage(void);
  55. int
  56. main(int argc, char *argv[])
  57. {
  58. struct stat sb;
  59. const char *fn;
  60. FILE *fp;
  61. off_t off;
  62. enum STYLE style;
  63. int i, ch, first;
  64. file_info_t *file;
  65. char *p;
  66. /*
  67. * Tail's options are weird. First, -n10 is the same as -n-10, not
  68. * -n+10. Second, the number options are 1 based and not offsets,
  69. * so -n+1 is the first line, and -c-1 is the last byte. Third, the
  70. * number options for the -r option specify the number of things that
  71. * get displayed, not the starting point in the file. The one major
  72. * incompatibility in this version as compared to historical versions
  73. * is that the 'r' option couldn't be modified by the -lbc options,
  74. * i.e. it was always done in lines. This version treats -rc as a
  75. * number of characters in reverse order. Finally, the default for
  76. * -r is the entire file, not 10 lines.
  77. */
  78. #define ARG(units, forward, backward) { \
  79. if (style) \
  80. usage(); \
  81. off = strtoll(optarg, &p, 10) * (units); \
  82. if (*p) \
  83. errx(1, "illegal offset -- %s", optarg); \
  84. switch(optarg[0]) { \
  85. case '+': \
  86. if (off) \
  87. off -= (units); \
  88. style = (forward); \
  89. break; \
  90. case '-': \
  91. off = -off; \
  92. /* FALLTHROUGH */ \
  93. default: \
  94. style = (backward); \
  95. break; \
  96. } \
  97. }
  98. obsolete(argv);
  99. style = NOTSET;
  100. off = 0;
  101. while ((ch = getopt(argc, argv, "Fb:c:fn:qr")) != -1)
  102. switch(ch) {
  103. case 'F': /* -F is superset of (and implies) -f */
  104. Fflag = fflag = 1;
  105. break;
  106. case 'b':
  107. ARG(512, FBYTES, RBYTES);
  108. break;
  109. case 'c':
  110. ARG(1, FBYTES, RBYTES);
  111. break;
  112. case 'f':
  113. fflag = 1;
  114. break;
  115. case 'n':
  116. ARG(1, FLINES, RLINES);
  117. break;
  118. case 'q':
  119. qflag = 1;
  120. break;
  121. case 'r':
  122. rflag = 1;
  123. break;
  124. case '?':
  125. default:
  126. usage();
  127. }
  128. argc -= optind;
  129. argv += optind;
  130. no_files = argc ? argc : 1;
  131. /*
  132. * If displaying in reverse, don't permit follow option, and convert
  133. * style values.
  134. */
  135. if (rflag) {
  136. if (fflag)
  137. usage();
  138. if (style == FBYTES)
  139. style = RBYTES;
  140. else if (style == FLINES)
  141. style = RLINES;
  142. }
  143. /*
  144. * If style not specified, the default is the whole file for -r, and
  145. * the last 10 lines if not -r.
  146. */
  147. if (style == NOTSET) {
  148. if (rflag) {
  149. off = 0;
  150. style = REVERSE;
  151. } else {
  152. off = 10;
  153. style = RLINES;
  154. }
  155. }
  156. if (*argv && fflag) {
  157. files = (struct file_info *) malloc(no_files *
  158. sizeof(struct file_info));
  159. if (!files)
  160. err(1, "Couldn't malloc space for file descriptors.");
  161. for (file = files; (fn = *argv++); file++) {
  162. file->file_name = strdup(fn);
  163. if (! file->file_name)
  164. errx(1, "Couldn't malloc space for file name.");
  165. if ((file->fp = fopen(file->file_name, "r")) == NULL ||
  166. fstat(fileno(file->fp), &file->st)) {
  167. if (file->fp != NULL) {
  168. fclose(file->fp);
  169. file->fp = NULL;
  170. }
  171. if (!Fflag || errno != ENOENT)
  172. ierr(file->file_name);
  173. }
  174. }
  175. follow(files, style, off);
  176. for (i = 0, file = files; i < no_files; i++, file++) {
  177. free(file->file_name);
  178. }
  179. free(files);
  180. } else if (*argv) {
  181. for (first = 1; (fn = *argv++);) {
  182. if ((fp = fopen(fn, "r")) == NULL ||
  183. fstat(fileno(fp), &sb)) {
  184. ierr(fn);
  185. continue;
  186. }
  187. if (argc > 1 && !qflag) {
  188. (void)printf("%s==> %s <==\n",
  189. first ? "" : "\n", fn);
  190. first = 0;
  191. (void)fflush(stdout);
  192. }
  193. if (rflag)
  194. reverse(fp, fn, style, off, &sb);
  195. else
  196. forward(fp, fn, style, off, &sb);
  197. }
  198. } else {
  199. fn = "stdin";
  200. if (fstat(fileno(stdin), &sb)) {
  201. ierr(fn);
  202. exit(1);
  203. }
  204. /*
  205. * Determine if input is a pipe. 4.4BSD will set the SOCKET
  206. * bit in the st_mode field for pipes. Fix this then.
  207. */
  208. if (lseek(fileno(stdin), (off_t)0, SEEK_CUR) == -1 &&
  209. errno == ESPIPE) {
  210. errno = 0;
  211. fflag = 0; /* POSIX.2 requires this. */
  212. }
  213. if (rflag)
  214. reverse(stdin, fn, style, off, &sb);
  215. else
  216. forward(stdin, fn, style, off, &sb);
  217. }
  218. exit(rval);
  219. }
  220. /*
  221. * Convert the obsolete argument form into something that getopt can handle.
  222. * This means that anything of the form [+-][0-9][0-9]*[lbc][Ffr] that isn't
  223. * the option argument for a -b, -c or -n option gets converted.
  224. */
  225. static void
  226. obsolete(char *argv[])
  227. {
  228. char *ap, *p, *t;
  229. size_t len;
  230. char *start;
  231. while ((ap = *++argv)) {
  232. /* Return if "--" or not an option of any form. */
  233. if (ap[0] != '-') {
  234. if (ap[0] != '+')
  235. return;
  236. } else if (ap[1] == '-')
  237. return;
  238. switch(*++ap) {
  239. /* Old-style option. */
  240. case '0': case '1': case '2': case '3': case '4':
  241. case '5': case '6': case '7': case '8': case '9':
  242. /* Malloc space for dash, new option and argument. */
  243. len = strlen(*argv);
  244. if ((start = p = malloc(len + 3)) == NULL)
  245. err(1, "malloc");
  246. *p++ = '-';
  247. /*
  248. * Go to the end of the option argument. Save off any
  249. * trailing options (-3lf) and translate any trailing
  250. * output style characters.
  251. */
  252. t = *argv + len - 1;
  253. if (*t == 'F' || *t == 'f' || *t == 'r') {
  254. *p++ = *t;
  255. *t-- = '\0';
  256. }
  257. switch(*t) {
  258. case 'b':
  259. *p++ = 'b';
  260. *t = '\0';
  261. break;
  262. case 'c':
  263. *p++ = 'c';
  264. *t = '\0';
  265. break;
  266. case 'l':
  267. *t = '\0';
  268. /* FALLTHROUGH */
  269. case '0': case '1': case '2': case '3': case '4':
  270. case '5': case '6': case '7': case '8': case '9':
  271. *p++ = 'n';
  272. break;
  273. default:
  274. errx(1, "illegal option -- %s", *argv);
  275. }
  276. *p++ = *argv[0];
  277. (void)strcpy(p, ap);
  278. *argv = start;
  279. continue;
  280. /*
  281. * Options w/ arguments, skip the argument and continue
  282. * with the next option.
  283. */
  284. case 'b':
  285. case 'c':
  286. case 'n':
  287. if (!ap[1])
  288. ++argv;
  289. /* FALLTHROUGH */
  290. /* Options w/o arguments, continue with the next option. */
  291. case 'F':
  292. case 'f':
  293. case 'r':
  294. continue;
  295. /* Illegal option, return and let getopt handle it. */
  296. default:
  297. return;
  298. }
  299. }
  300. }
  301. static void
  302. usage(void)
  303. {
  304. (void)fprintf(stderr,
  305. "usage: tail [-F | -f | -r] [-q] [-b # | -c # | -n #]"
  306. " [file ...]\n");
  307. exit(1);
  308. }