Mercurial > hg > toybox
annotate toys/tail.c @ 495:5f224a5773d6
Bugfix: use the right USE symbol.
author | Rob Landley <rob@landley.net> |
---|---|
date | Thu, 23 Feb 2012 20:56:10 -0600 |
parents | eebfb11e84db |
children | a497beb97eee |
rev | line source |
---|---|
494 | 1 /* vi: set sw=4 ts=4: |
2 * | |
3 * tail.c - copy last lines from input to stdout. | |
4 * | |
5 * Copyright 2012 Timothy Elliott <tle@holymonkey.com> | |
6 * | |
7 * See http://www.opengroup.org/onlinepubs/009695399/utilities/tail.html | |
8 | |
495
5f224a5773d6
Bugfix: use the right USE symbol.
Rob Landley <rob@landley.net>
parents:
494
diff
changeset
|
9 USE_TAIL(NEWTOY(tail, "c:fn:", TOYFLAG_BIN)) |
494 | 10 |
11 config TAIL | |
12 bool "tail" | |
13 default n | |
14 help | |
15 usage: tail [-n number] [-c number] [-f] [file...] | |
16 | |
17 Copy last lines from files to stdout. If no files listed, copy from | |
18 stdin. Filename "-" is a synonym for stdin. | |
19 | |
20 -n Line offset to start copying from. The number may be signed. | |
21 When negative, the line offset is counted from the end of the | |
22 file. When positive, the offset is counted from the beginning. | |
23 When the sign is omitted, the offset is counted from the end of | |
24 the file. Defaults to -10. | |
25 | |
26 -c Byte offset to start copying from. As above, may be a positive | |
27 or negative signed number. | |
28 | |
29 -f Continue reading input after reaching the last line of input. | |
30 This option is ignored if the input file is not a regular file | |
31 or the given file is a FIFO. | |
32 */ | |
33 | |
34 #include "toys.h" | |
35 | |
36 DEFINE_GLOBALS( | |
37 char *lines_str; | |
38 char *bytes_str; | |
39 long lines; | |
40 long bytes; | |
41 int file_no; | |
42 ) | |
43 | |
44 #define TT this.tail | |
45 | |
46 #define FLAG_n 1 | |
47 #define FLAG_f 2 | |
48 #define FLAG_c 4 | |
49 | |
50 struct line_list { | |
51 struct line_list *next; | |
52 char *data; | |
53 ssize_t len; | |
54 long lines; | |
55 }; | |
56 | |
57 static void print_after_offset(int fd, long bytes, long lines) | |
58 { | |
59 ssize_t read_len; | |
60 long size=sizeof(toybuf); | |
61 char c; | |
62 | |
63 while (bytes > 0 || lines > 0) { | |
64 if (1>read(fd, &c, 1)) break; | |
65 bytes--; | |
66 if (c == '\n') lines--; | |
67 } | |
68 | |
69 for (;;) { | |
70 read_len = xread(fd, toybuf, size); | |
71 if (read_len<1) break; | |
72 xwrite(1, toybuf, read_len); | |
73 } | |
74 } | |
75 | |
76 static void print_last_bytes(int fd, long bytes) | |
77 { | |
78 char *buf1, *buf2, *temp; | |
79 ssize_t read_len; | |
80 | |
81 buf1 = xmalloc(bytes); | |
82 buf2 = xmalloc(bytes); | |
83 | |
84 for(;;) { | |
85 // swap buf1 and buf2 | |
86 temp = buf1; | |
87 buf1 = buf2; | |
88 buf2 = temp; | |
89 | |
90 read_len = readall(fd, buf2, bytes); | |
91 if (read_len<bytes) break; | |
92 } | |
93 | |
94 // output part of buf1 and all of buf2 | |
95 xwrite(1, buf1 + read_len, bytes - read_len); | |
96 xwrite(1, buf2, read_len); | |
97 | |
98 if (CFG_TOYBOX_FREE) { | |
99 free(buf1); | |
100 free(buf2); | |
101 } | |
102 } | |
103 | |
104 static void free_line(void *data) | |
105 { | |
106 struct line_list *list = (struct line_list *)data; | |
107 free(list->data); | |
108 free(list); | |
109 } | |
110 | |
111 static void llist_add(struct line_list **head, struct line_list *new) | |
112 { | |
113 struct line_list *cur = *head; | |
114 | |
115 new->next = NULL; | |
116 if (cur) { | |
117 while (cur->next) | |
118 cur = cur->next; | |
119 cur->next = new; | |
120 } else *head = new; | |
121 } | |
122 | |
123 static void print_last_lines(int fd, long lines) | |
124 { | |
125 ssize_t i, total=0; | |
126 long size=sizeof(toybuf); | |
127 struct line_list *buf=NULL, *cur; | |
128 | |
129 for (;;) { | |
130 // read from input and append to buffer list | |
131 cur = xmalloc(sizeof(struct line_list)); | |
132 memset(cur, 0, sizeof(struct line_list)); | |
133 | |
134 cur->data = xmalloc(size); | |
135 cur->len = readall(fd, cur->data, size); | |
136 llist_add(&buf, cur); | |
137 | |
138 // count newlines in latest input | |
139 for (i=0; i<cur->len; i++) | |
140 if (cur->data[i] == '\n') cur->lines++; | |
141 total += cur->lines; | |
142 | |
143 // release first buffers if it leaves us enough newlines | |
144 while (total - buf->lines > lines) { | |
145 total -= buf->lines; | |
146 free_line(llist_pop(&buf)); | |
147 } | |
148 if (cur->len < size) break; | |
149 } | |
150 | |
151 // if last buffer doesn't end in a newline, pretend like it did | |
152 if (cur->data[cur->len - 1]!='\n') total++; | |
153 | |
154 // print out part of the first buffer | |
155 i = 0; | |
156 while (total>lines) | |
157 if (buf->data[i++]=='\n') total--; | |
158 xwrite(1, buf->data + i, buf->len - i); | |
159 | |
160 // print remaining buffers in their entirety | |
161 for (cur=buf->next; cur; cur=cur->next) | |
162 xwrite(1, cur->data, cur->len); | |
163 | |
164 if (CFG_TOYBOX_FREE) llist_free(buf, free_line); | |
165 } | |
166 | |
167 static void do_tail(int fd, char *name) | |
168 { | |
169 long lines=TT.lines, bytes=TT.bytes; | |
170 | |
171 if (toys.optc > 1) { | |
172 // print an extra newline for all but the first file | |
173 if (TT.file_no++) xprintf("\n"); | |
174 xprintf("==> %s <==\n", name); | |
175 xflush(); | |
176 } | |
177 | |
178 if (lines > 0 || bytes > 0) print_after_offset(fd, bytes, lines); | |
179 else if (bytes < 0) print_last_bytes(fd, bytes * -1); | |
180 else if (lines < 0) print_last_lines(fd, lines * -1); | |
181 } | |
182 | |
183 long atolx_default_negative(char *str) | |
184 { | |
185 long val = atolx(str); | |
186 return str[0] != '+' && str[0] != '-' ? val * -1 : val; | |
187 } | |
188 | |
189 void tail_main(void) | |
190 { | |
191 // if option -c or -n has no sign then we make it negative | |
192 if (toys.optflags & FLAG_c) | |
193 TT.bytes = atolx_default_negative(TT.bytes_str); | |
194 | |
195 if (toys.optflags & FLAG_n) | |
196 TT.lines = atolx_default_negative(TT.lines_str); | |
197 else | |
198 TT.lines = -10; | |
199 | |
200 loopfiles(toys.optargs, do_tail); | |
201 } |