tail.c 5.6 KB

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