tail.c 5.9 KB

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