tail.c 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. /*
  2. * This file is part of the UCB release of Plan 9. It is subject to the license
  3. * terms in the LICENSE file found in the top-level directory of this
  4. * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
  5. * part of the UCB release of Plan 9, including this file, may be copied,
  6. * modified, propagated, or distributed except according to the terms contained
  7. * in the LICENSE file.
  8. */
  9. #include <u.h>
  10. #include <libc.h>
  11. #include <ctype.h>
  12. #include <bio.h>
  13. /*
  14. * tail command, posix plus v10 option -r.
  15. * the simple command tail -c, legal in v10, is illegal
  16. */
  17. int32_t count;
  18. int anycount;
  19. int follow;
  20. int file = 0;
  21. char* umsg = "usage: tail [-n N] [-c N] [-f] [-r] [+-N[bc][fr]] [file]";
  22. Biobuf bout;
  23. enum
  24. {
  25. BEG,
  26. END
  27. } origin = END;
  28. enum
  29. {
  30. CHARS,
  31. LINES
  32. } units = LINES;
  33. enum
  34. {
  35. FWD,
  36. REV
  37. } dir = FWD;
  38. extern void copy(void);
  39. extern void fatal(char*);
  40. extern int getnumber(char*);
  41. extern void keep(void);
  42. extern void reverse(void);
  43. extern void skip(void);
  44. extern void suffix(char*);
  45. extern int32_t tread(char*, int32_t);
  46. extern void trunc(Dir*, Dir**);
  47. extern int64_t tseek(int64_t, int);
  48. extern void twrite(char*, int32_t);
  49. extern void usage(void);
  50. static int isseekable(int fd);
  51. #define JUMP(o,p) tseek(o,p), copy()
  52. void
  53. main(int argc, char **argv)
  54. {
  55. Binit(&bout, 1, OWRITE);
  56. for(int c; argc > 1 && ((c=*argv[1])=='-'||c=='+'); argc--,argv++ ) {
  57. if(getnumber(argv[1])) {
  58. suffix(argv[1]);
  59. continue;
  60. } else
  61. if(c == '-')
  62. switch(argv[1][1]) {
  63. case 'c':
  64. units = CHARS;
  65. case 'n':
  66. if(getnumber(argv[1]+2))
  67. continue;
  68. else
  69. if(argc > 2 && getnumber(argv[2])) {
  70. argc--, argv++;
  71. continue;
  72. } else
  73. usage();
  74. case 'r':
  75. dir = REV;
  76. continue;
  77. case 'f':
  78. follow++;
  79. continue;
  80. case '-':
  81. argc--, argv++;
  82. }
  83. break;
  84. }
  85. if(dir==REV && (units==CHARS || follow || origin==BEG))
  86. fatal("incompatible options");
  87. if(!anycount)
  88. count = dir==REV? -1 : 10;
  89. if(origin==BEG && units==LINES && count>0)
  90. count--;
  91. if(argc > 2)
  92. usage();
  93. if(argc > 1 && (file=open(argv[1],0)) < 0)
  94. fatal(argv[1]);
  95. int seekable = isseekable(file);
  96. if(!seekable && origin==END)
  97. keep();
  98. else
  99. if(!seekable && origin==BEG)
  100. skip();
  101. else
  102. if(units==CHARS && origin==END)
  103. JUMP(-count, 2);
  104. else
  105. if(units==CHARS && origin==BEG)
  106. JUMP(count, 0);
  107. else
  108. if(units==LINES && origin==END)
  109. reverse();
  110. else
  111. if(units==LINES && origin==BEG)
  112. skip();
  113. if(follow && seekable)
  114. for(;;) {
  115. static Dir *sb0, *sb1;
  116. trunc(sb1, &sb0);
  117. copy();
  118. trunc(sb0, &sb1);
  119. sleep(5000);
  120. }
  121. exits(0);
  122. }
  123. void
  124. trunc(Dir *old, Dir **new)
  125. {
  126. Dir *d;
  127. int64_t olength;
  128. d = dirfstat(file);
  129. if(d == nil)
  130. return;
  131. olength = 0;
  132. if(old)
  133. olength = old->length;
  134. if(d->length < olength)
  135. d->length = tseek(0LL, 0);
  136. free(*new);
  137. *new = d;
  138. }
  139. void
  140. suffix(char *s)
  141. {
  142. while(*s && strchr("0123456789+-", *s))
  143. s++;
  144. switch(*s) {
  145. case 'b':
  146. if((count *= 1024) < 0)
  147. fatal("too big");
  148. case 'c':
  149. units = CHARS;
  150. case 'l':
  151. s++;
  152. }
  153. switch(*s) {
  154. case 'r':
  155. dir = REV;
  156. return;
  157. case 'f':
  158. follow++;
  159. return;
  160. case 0:
  161. return;
  162. }
  163. usage();
  164. }
  165. /*
  166. * read past head of the file to find tail
  167. */
  168. void
  169. skip(void)
  170. {
  171. int i;
  172. int32_t n;
  173. char buf[Bsize];
  174. if(units == CHARS) {
  175. for( ; count>0; count -=n) {
  176. n = count<Bsize? count: Bsize;
  177. if(!(n = tread(buf, n)))
  178. return;
  179. }
  180. } else /*units == LINES*/ {
  181. n = i = 0;
  182. while(count > 0) {
  183. if(!(n = tread(buf, Bsize)))
  184. return;
  185. for(i=0; i<n && count>0; i++)
  186. if(buf[i]=='\n')
  187. count--;
  188. }
  189. twrite(buf+i, n-i);
  190. }
  191. copy();
  192. }
  193. void
  194. copy(void)
  195. {
  196. int32_t n;
  197. char buf[Bsize];
  198. while((n=tread(buf, Bsize)) > 0) {
  199. twrite(buf, n);
  200. Bflush(&bout); /* for FWD on pipe; else harmless */
  201. }
  202. }
  203. /*
  204. * read whole file, keeping the tail
  205. * complexity is length(file)*length(tail).
  206. * could be linear.
  207. */
  208. void
  209. keep(void)
  210. {
  211. int len = 0;
  212. int32_t bufsiz = 0;
  213. char *buf = 0;
  214. int j, k, n;
  215. for(n=1; n;) {
  216. if(len+Bsize > bufsiz) {
  217. bufsiz += 2*Bsize;
  218. if(!(buf = realloc(buf, bufsiz+1)))
  219. fatal("out of space");
  220. }
  221. for(; n && len<bufsiz; len+=n)
  222. n = tread(buf+len, bufsiz-len);
  223. if(count >= len)
  224. continue;
  225. if(units == CHARS)
  226. j = len - count;
  227. else {
  228. /* units == LINES */
  229. j = buf[len-1]=='\n'? len-1: len;
  230. for(k=0; j>0; j--)
  231. if(buf[j-1] == '\n')
  232. if(++k >= count)
  233. break;
  234. }
  235. memmove(buf, buf+j, len-=j);
  236. }
  237. if(dir == REV) {
  238. if(len>0 && buf[len-1]!='\n')
  239. buf[len++] = '\n';
  240. for(j=len-1 ; j>0; j--)
  241. if(buf[j-1] == '\n') {
  242. twrite(buf+j, len-j);
  243. if(--count <= 0)
  244. return;
  245. len = j;
  246. }
  247. }
  248. if(count > 0)
  249. twrite(buf, len);
  250. }
  251. /*
  252. * count backward and print tail of file
  253. */
  254. void
  255. reverse(void)
  256. {
  257. int first;
  258. int32_t len = 0;
  259. int32_t n = 0;
  260. int32_t bufsiz = 0;
  261. char *buf = 0;
  262. int64_t pos = tseek(0LL, 2);
  263. for(first=1; pos>0 && count>0; first=0) {
  264. n = pos>Bsize? Bsize: (int32_t)pos;
  265. pos -= n;
  266. if(len+n > bufsiz) {
  267. bufsiz += 2*Bsize;
  268. if(!(buf = realloc(buf, bufsiz+1)))
  269. fatal("out of space");
  270. }
  271. memmove(buf+n, buf, len);
  272. len += n;
  273. tseek(pos, 0);
  274. if(tread(buf, n) != n)
  275. fatal("length error");
  276. if(first && buf[len-1]!='\n')
  277. buf[len++] = '\n';
  278. for(n=len-1 ; n>0 && count>0; n--)
  279. if(buf[n-1] == '\n') {
  280. count--;
  281. if(dir == REV)
  282. twrite(buf+n, len-n);
  283. len = n;
  284. }
  285. }
  286. if(dir == FWD) {
  287. if(n)
  288. tseek(pos+n+1, 0);
  289. else
  290. tseek(0, 0);
  291. copy();
  292. } else
  293. if(count > 0)
  294. twrite(buf, len);
  295. }
  296. int64_t
  297. tseek(int64_t o, int p)
  298. {
  299. o = seek(file, o, p);
  300. if(o == -1)
  301. fatal("");
  302. return o;
  303. }
  304. int32_t
  305. tread(char *buf, int32_t n)
  306. {
  307. int r = read(file, buf, n);
  308. if(r == -1)
  309. fatal("");
  310. return r;
  311. }
  312. void
  313. twrite(char *s, int32_t n)
  314. {
  315. if(Bwrite(&bout, s, n) != n)
  316. fatal("");
  317. }
  318. int
  319. getnumber(char *s)
  320. {
  321. if(*s=='-' || *s=='+')
  322. s++;
  323. if(!isdigit(*s))
  324. return 0;
  325. if(s[-1] == '+')
  326. origin = BEG;
  327. if(anycount++)
  328. fatal("excess option");
  329. count = atol(s);
  330. /* check range of count */
  331. if(count < 0 || (int)count != count)
  332. fatal("too big");
  333. return 1;
  334. }
  335. void
  336. fatal(char *s)
  337. {
  338. char buf[ERRMAX];
  339. errstr(buf, sizeof buf);
  340. fprint(2, "tail: %s: %s\n", s, buf);
  341. exits(s);
  342. }
  343. void
  344. usage(void)
  345. {
  346. fprint(2, "%s\n", umsg);
  347. exits("usage");
  348. }
  349. /* return true if seeks work and if the file is > 0 length.
  350. * this will eventually bite me in the ass if seeking a file
  351. * is not conservative. - presotto
  352. */
  353. static int
  354. isseekable(int fd)
  355. {
  356. int64_t m;
  357. m = seek(fd, 0, 1);
  358. if(m < 0)
  359. return 0;
  360. return 1;
  361. }