tail.c 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. /*
  2. * tail.c - replaces Unix version which uses a fixed size buffer and
  3. * therefore can only deliver a limited number of lines.
  4. * The -r option (print backwards) is unimplemented, because I
  5. * haven't figured out the right thing to do. Printing the entire
  6. * file, as the manual says, could run out of memory and probably
  7. * isn't what you want anyway.
  8. * --phr, 25-27 June 84.
  9. */
  10. #include <stdio.h>
  11. #include <sys/file.h>
  12. #include <errno.h>
  13. #define DEFAULTLINES 20 /* number of lines/chars/blocks to read */
  14. #define BLOCKSIZE 512
  15. #define BUFSIZE (BLOCKSIZE*8)
  16. /* modes */
  17. #define BACKWARD 02
  18. #define CHARS 010
  19. #define FOREVER 020
  20. #define HARDWAY 040
  21. extern int errno;
  22. char *index (); /* why the f* do I need this? */
  23. char *malloc (), *xmalloc ();
  24. char *progname; /* "tail" */
  25. main (argc, argv)
  26. int argc;
  27. char **argv;
  28. {
  29. int fd;
  30. int mode = 0;
  31. char c, bgetc ();
  32. int number = -DEFAULTLINES;
  33. progname = argv[0];
  34. if (argv[1][0] == '+' || argv[1][0] == '-')
  35. {
  36. number = atoi (argv[1]);
  37. --argc;
  38. argv++;
  39. if (number == 0)
  40. number = -DEFAULTLINES;
  41. /* now skip over the number */
  42. while (index ("+-0123456789 ", argv[0][0]) != NULL)
  43. if (*++argv[0] == '\0')
  44. break;
  45. while (**argv)
  46. {
  47. switch (*argv[0]++)
  48. {
  49. case 'l':
  50. mode &= ~CHARS; /* do-nothing */
  51. break;
  52. case 'b':
  53. number *= BLOCKSIZE;
  54. /* fall through */
  55. case 'c':
  56. mode |= CHARS;
  57. break;
  58. case 'r':
  59. mode |= BACKWARD;
  60. break;
  61. case 'f':
  62. mode |= FOREVER;
  63. break;
  64. case 'H':
  65. mode |= HARDWAY;
  66. break;
  67. }
  68. }
  69. if ((mode & (FOREVER|BACKWARD)) == (FOREVER|BACKWARD))
  70. {
  71. fprintf (2, "%s: CAN'T print backwards forever!\n", progname);
  72. exit (1);
  73. }
  74. }
  75. if (argc > 1 && (fd = open (argv[1], O_RDONLY)) < 0)
  76. {
  77. perror (argv[1]);
  78. exit (1);
  79. }
  80. tail (fd, mode, number);
  81. exit (0);
  82. }
  83. tail (f, mode, number)
  84. int f;
  85. int mode, number;
  86. {
  87. char buf[BUFSIZE];
  88. int pos;
  89. if (mode & CHARS)
  90. {
  91. if ((mode & HARDWAY) ||
  92. lseek (f, number, number>0 ? L_SET : L_XTND) < 0)
  93. {
  94. hardchars (f, mode, number);
  95. return 0;
  96. }
  97. if (!(mode & BACKWARD))
  98. {
  99. dump_remainder (f, mode);
  100. return 0;
  101. }
  102. /* do backward chars here (?) */
  103. }
  104. else
  105. {
  106. if ((mode & HARDWAY) ||
  107. number > 0 ||
  108. (pos = lseek (f, 0, L_XTND)) < 0)
  109. {
  110. hardlines (f, mode, number);
  111. return 0;
  112. }
  113. if (!(mode & BACKWARD))
  114. {
  115. register int n;
  116. /*
  117. * set n to the no. of chars to be read,
  118. * 0 < n <= BUFSIZE;
  119. * new pos will be a multiple of BUFSIZE
  120. */
  121. n = (pos - 1) % BUFSIZE + 1;
  122. pos -= n;
  123. lseek (f, pos, L_SET);
  124. n = read (f, buf, n);
  125. if (n >= 0) /* Can be 0 doing tail-f of zero-length file */
  126. do
  127. {
  128. register int j;
  129. for (j = n-1; j >= 0; j--)
  130. {
  131. if (buf[j] == '\n' && number++ >= 0)
  132. {
  133. /* flush partial block */
  134. if (j != n-1)
  135. write (1, &buf[j+1], n-j-1);
  136. dump_remainder (f, mode);
  137. return 0;
  138. }
  139. }
  140. /* didn't find enough newlines in that bufferload */
  141. if (pos == 0)
  142. {
  143. /* not enough lines, print entire file */
  144. lseek (f, 0, L_SET);
  145. dump_remainder (f, mode);
  146. return 0;
  147. }
  148. pos -= BUFSIZE;
  149. lseek (f, pos, L_SET);
  150. } while ((n = read (f, buf, BUFSIZE)) > 0);
  151. }
  152. }
  153. }
  154. dump_remainder (f, mode)
  155. int f, mode;
  156. {
  157. int n;
  158. char buf[BUFSIZE];
  159. output:
  160. while ((n = read (f, buf, BUFSIZE)) > 0)
  161. write (1, buf, n);
  162. if (mode & FOREVER)
  163. {
  164. sleep (1);
  165. goto output;
  166. }
  167. }
  168. hardlines (f, mode, number)
  169. int f, mode, number;
  170. {
  171. struct buf { struct buf *next;
  172. int nchars, linect;
  173. char buf[BUFSIZE];
  174. };
  175. typedef struct buf BUF;
  176. BUF *first;
  177. register BUF *last, *spare;
  178. register int i;
  179. int nlines;
  180. if (number > 0)
  181. return (head (f, mode, number));
  182. /*
  183. * Input is always read into a fresh buffer.
  184. * If there is enough room in the last buffer read, then the
  185. * contents of the new one are just moved into it. This is because
  186. * when reading from pipes, nread can often be very small.
  187. *
  188. * If there's not enough room, link the new buffer onto the end
  189. * of the list, then either free up the oldest buffer for the
  190. * next read if that would leave enough lines, or else malloc
  191. * a new one.
  192. * Some compaction mechanism is possible but probably not worthwhile.
  193. */
  194. first = last = (BUF *) xmalloc (sizeof (BUF));
  195. first->nchars = first->linect = 0;
  196. last->nchars = last->linect = 0;
  197. spare = (BUF *) xmalloc (sizeof (BUF));
  198. nlines = 0;
  199. while ((spare->nchars = read (f, spare->buf, BUFSIZE)) > 0)
  200. {
  201. spare->linect = 0;
  202. spare->next = NULL;
  203. for (i = 0; i < spare->nchars; i++)
  204. if (spare->buf[i] == '\n')
  205. ++spare->linect;
  206. nlines += spare->linect;
  207. if (spare->nchars + last->nchars < BUFSIZE)
  208. {
  209. strncpy (&last->buf[last->nchars], spare->buf, spare->nchars);
  210. last->nchars += spare->nchars;
  211. last->linect += spare->linect;
  212. }
  213. else
  214. {
  215. last = last->next = spare;
  216. if (nlines - first->linect > -number)
  217. {
  218. spare = first;
  219. nlines -= first->linect;
  220. first = first->next;
  221. }
  222. else
  223. spare = (BUF *) xmalloc (sizeof (BUF));
  224. }
  225. }
  226. /* now run through the list, printing lines. */
  227. /* first, skip over unneeded buffers */
  228. for (spare = first; spare != NULL; spare = spare->next)
  229. {
  230. if (nlines - spare->linect <= -number)
  231. break;
  232. nlines -= spare->linect;
  233. }
  234. if (spare == NULL)
  235. {
  236. fprintf (2, "%s: Impossible error in hardlines! Please report bug\n", progname);
  237. exit (3);
  238. }
  239. /* find correct beginning, then print the rest of the file. */
  240. if (nlines > -number)
  241. {
  242. register char *p = spare->buf;
  243. /* remember: number < 0; nlines+number < spare->linect */
  244. for (i = 0; i < nlines + number; i++)
  245. /* Can't use index() -- that loses on lines containing \0 */
  246. while (*p++ != '\n')
  247. ;
  248. i = p - spare->buf;
  249. }
  250. else
  251. i = 0;
  252. write (1, &spare->buf[i], spare->nchars-i);
  253. for (spare = spare->next; spare != NULL; spare = spare->next)
  254. write (1, spare->buf, spare->nchars);
  255. }
  256. hardchars (f, mode, number) /* stripped down version of hardlines */
  257. int f, mode, number;
  258. {
  259. struct buf { struct buf *next;
  260. int nchars;
  261. char buf[BUFSIZE];
  262. };
  263. typedef struct buf BUF;
  264. BUF *first;
  265. register BUF *last, *spare;
  266. register int i;
  267. int nchars;
  268. if (number > 0)
  269. return (head (f, mode, number));
  270. /*
  271. * Same comment as last time, more or less.
  272. */
  273. first = last = (BUF *) xmalloc (sizeof (BUF));
  274. first->nchars = last->nchars = 0;
  275. spare = (BUF *) xmalloc (sizeof (BUF));
  276. nchars = 0;
  277. while ((spare->nchars = read (f, spare->buf, BUFSIZE)) > 0)
  278. {
  279. spare->next = NULL;
  280. nchars += spare->nchars;
  281. if (spare->nchars + last->nchars < BUFSIZE)
  282. {
  283. strncpy (&last->buf[last->nchars], spare->buf, spare->nchars);
  284. last->nchars += spare->nchars;
  285. }
  286. else
  287. {
  288. last = last->next = spare;
  289. if (nchars - first->nchars > -number)
  290. {
  291. spare = first;
  292. nchars -= first->nchars;
  293. first = first->next;
  294. }
  295. else
  296. {
  297. spare = (BUF *) xmalloc (sizeof (BUF));
  298. }
  299. }
  300. }
  301. /* now run through the list, printing lines. */
  302. /* first, skip over unneeded buffers */
  303. for (spare = first; spare != NULL; spare = spare->next)
  304. {
  305. if (nchars - spare->nchars <= -number)
  306. break;
  307. nchars -= spare->nchars;
  308. }
  309. if (spare == NULL)
  310. {
  311. fprintf (2, "%s: Impossible error in hardchars! Please report bug\n",
  312. progname);
  313. exit (3);
  314. }
  315. /* find correct beginning, then print the rest of the file. */
  316. if (nchars > -number)
  317. i = nchars + number;
  318. else
  319. i = 0;
  320. write (1, &spare->buf[i], spare->nchars-i);
  321. for (spare = spare->next; spare != NULL; spare = spare->next)
  322. write (1, spare->buf, spare->nchars);
  323. }
  324. head (f, mode, number)
  325. int f, mode, number;
  326. {
  327. int n, nread;
  328. register int i;
  329. char buf[BUFSIZE];
  330. if (number <= 0)
  331. return 0;
  332. while (nread < number)
  333. {
  334. n = read (f, buf, BUFSIZE);
  335. if (n <= 0)
  336. break;
  337. if (mode & CHARS)
  338. nread += n;
  339. else
  340. for (i = 0; i < n; i++)
  341. if (buf[i] == '\n')
  342. if (++nread >= number-1)
  343. goto out;
  344. }
  345. out:
  346. if (mode & CHARS)
  347. {
  348. if (nread > number)
  349. write (1, &buf[n-(nread-number)], nread-number);
  350. }
  351. else
  352. {
  353. if (++i < n) /* skip over newline */
  354. write (1, &buf[i], n-i);
  355. }
  356. dump_remainder (f, mode);
  357. }
  358. char *
  359. xmalloc (size)
  360. int size;
  361. {
  362. register char *val;
  363. val = malloc (size);
  364. if (val) return (val);
  365. fprintf (2, "%s: memory exhausted.\n", progname);
  366. exit (3);
  367. }