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