]> git.cameronkatri.com Git - apple_cmds.git/blob - text_cmds/tail/tail.c
patch_cmds: diffstat without autotools
[apple_cmds.git] / text_cmds / tail / tail.c
1 /*-
2 * Copyright (c) 1991, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Edward Sze-Tyan Wang.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37 #include <sys/cdefs.h>
38
39 __FBSDID("$FreeBSD$");
40
41 #ifndef lint
42 static const char copyright[] =
43 "@(#) Copyright (c) 1991, 1993\n\
44 The Regents of the University of California. All rights reserved.\n";
45 #endif
46
47 #ifndef lint
48 static const char sccsid[] = "@(#)tail.c 8.1 (Berkeley) 6/6/93";
49 #endif
50
51 #include <sys/types.h>
52 #include <sys/stat.h>
53
54 #include <err.h>
55 #include <errno.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <unistd.h>
60
61 #include "extern.h"
62
63 int Fflag, fflag, qflag, rflag, rval, no_files;
64 const char *fname;
65
66 file_info_t *files;
67
68 static void obsolete(char **);
69 static void usage(void);
70
71 int
72 main(int argc, char *argv[])
73 {
74 struct stat sb;
75 FILE *fp;
76 off_t off;
77 enum STYLE style;
78 int i, ch, first;
79 file_info_t *file;
80 char *p;
81
82 /*
83 * Tail's options are weird. First, -n10 is the same as -n-10, not
84 * -n+10. Second, the number options are 1 based and not offsets,
85 * so -n+1 is the first line, and -c-1 is the last byte. Third, the
86 * number options for the -r option specify the number of things that
87 * get displayed, not the starting point in the file. The one major
88 * incompatibility in this version as compared to historical versions
89 * is that the 'r' option couldn't be modified by the -lbc options,
90 * i.e. it was always done in lines. This version treats -rc as a
91 * number of characters in reverse order. Finally, the default for
92 * -r is the entire file, not 10 lines.
93 */
94 #define ARG(units, forward, backward) { \
95 if (style) \
96 usage(); \
97 off = strtoll(optarg, &p, 10) * (units); \
98 if (*p) \
99 errx(1, "illegal offset -- %s", optarg); \
100 switch(optarg[0]) { \
101 case '+': \
102 if (off) \
103 off -= (units); \
104 style = (forward); \
105 break; \
106 case '-': \
107 off = -off; \
108 /* FALLTHROUGH */ \
109 default: \
110 style = (backward); \
111 break; \
112 } \
113 }
114
115 obsolete(argv);
116 style = NOTSET;
117 while ((ch = getopt(argc, argv, "Fb:c:fn:qr")) != -1)
118 switch(ch) {
119 case 'F': /* -F is superset of (and implies) -f */
120 Fflag = fflag = 1;
121 break;
122 case 'b':
123 ARG(512, FBYTES, RBYTES);
124 break;
125 case 'c':
126 ARG(1, FBYTES, RBYTES);
127 break;
128 case 'f':
129 fflag = 1;
130 break;
131 case 'n':
132 ARG(1, FLINES, RLINES);
133 break;
134 case 'q':
135 qflag = 1;
136 break;
137 case 'r':
138 rflag = 1;
139 break;
140 case '?':
141 default:
142 usage();
143 }
144 argc -= optind;
145 argv += optind;
146
147 no_files = argc ? argc : 1;
148
149 /*
150 * If displaying in reverse, don't permit follow option, and convert
151 * style values.
152 */
153 if (rflag) {
154 if (fflag)
155 usage();
156 if (style == FBYTES)
157 style = RBYTES;
158 else if (style == FLINES)
159 style = RLINES;
160 }
161
162 /*
163 * If style not specified, the default is the whole file for -r, and
164 * the last 10 lines if not -r.
165 */
166 if (style == NOTSET) {
167 if (rflag) {
168 off = 0;
169 style = REVERSE;
170 } else {
171 off = 10;
172 style = RLINES;
173 }
174 }
175
176 if (fflag && !argc) {
177 /*
178 * Determine if input is a pipe. 4.4BSD will set the SOCKET
179 * bit in the st_mode field for pipes. Fix this then.
180 */
181 if (lseek(fileno(stdin), (off_t)0, SEEK_CUR) == -1 &&
182 errno == ESPIPE) {
183 errno = 0;
184 fflag = 0; /* POSIX.2 requires this. */
185 }
186 }
187
188 if (fflag) {
189 files = (struct file_info *) malloc(no_files * sizeof(struct file_info));
190 if (! files)
191 err(1, "Couldn't malloc space for file descriptors.");
192
193 for (file = files; (fname = argc ? *argv++ : "stdin"); file++) {
194 file->file_name = malloc(strlen(fname)+1);
195 if (! file->file_name)
196 errx(1, "Couldn't malloc space for file name.");
197 strncpy(file->file_name, fname, strlen(fname)+1);
198 file->fp = argc ? fopen(file->file_name, "r") : stdin;
199 if (file->fp == NULL ||
200 fstat(fileno(file->fp), &file->st)) {
201 file->fp = NULL;
202 ierr();
203 continue;
204 }
205 if (!argc)
206 break;
207 }
208 follow(files, style, off);
209 for (i = 0, file = files; i < no_files; i++, file++) {
210 free(file->file_name);
211 }
212 free(files);
213 } else if (*argv) {
214 for (first = 1; (fname = *argv++);) {
215 if ((fp = fopen(fname, "r")) == NULL ||
216 fstat(fileno(fp), &sb)) {
217 ierr();
218 continue;
219 }
220 if (argc > 1 && !qflag) {
221 (void)printf("%s==> %s <==\n",
222 first ? "" : "\n", fname);
223 first = 0;
224 (void)fflush(stdout);
225 }
226
227 #ifdef __APPLE__
228 /* 3849683: don't read a directory */
229 if (S_IFDIR == (sb.st_mode & S_IFMT))
230 continue;
231 #endif
232
233 if (rflag)
234 reverse(fp, style, off, &sb);
235 else
236 forward(fp, style, off, &sb);
237 }
238 } else {
239 fname = "stdin";
240
241 if (fstat(fileno(stdin), &sb)) {
242 ierr();
243 exit(1);
244 }
245
246 if (rflag)
247 reverse(stdin, style, off, &sb);
248 else
249 forward(stdin, style, off, &sb);
250 }
251 exit(rval);
252 }
253
254 /*
255 * Convert the obsolete argument form into something that getopt can handle.
256 * This means that anything of the form [+-][0-9][0-9]*[lbc][Ffr] that isn't
257 * the option argument for a -b, -c or -n option gets converted.
258 */
259 static void
260 obsolete(char *argv[])
261 {
262 char *ap, *p, *t;
263 size_t len;
264 char *start;
265
266 while ((ap = *++argv)) {
267 /* Return if "--" or not an option of any form. */
268 if (ap[0] != '-') {
269 if (ap[0] != '+')
270 return;
271 } else if (ap[1] == '-')
272 return;
273
274 switch(*++ap) {
275 /* Old-style option. */
276 case '0': case '1': case '2': case '3': case '4':
277 case '5': case '6': case '7': case '8': case '9':
278
279 /* Malloc space for dash, new option and argument. */
280 len = strlen(*argv);
281 if ((start = p = malloc(len + 3)) == NULL)
282 err(1, "malloc");
283 *p++ = '-';
284
285 /*
286 * Go to the end of the option argument. Save off any
287 * trailing options (-3lf) and translate any trailing
288 * output style characters.
289 */
290 t = *argv + len - 1;
291 if (*t == 'F' || *t == 'f' || *t == 'r') {
292 *p++ = *t;
293 *t-- = '\0';
294 }
295 switch(*t) {
296 case 'b':
297 *p++ = 'b';
298 *t = '\0';
299 break;
300 case 'c':
301 *p++ = 'c';
302 *t = '\0';
303 break;
304 case 'l':
305 *t = '\0';
306 /* FALLTHROUGH */
307 case '0': case '1': case '2': case '3': case '4':
308 case '5': case '6': case '7': case '8': case '9':
309 *p++ = 'n';
310 break;
311 default:
312 errx(1, "illegal option -- %s", *argv);
313 }
314 *p++ = *argv[0];
315 (void)strcpy(p, ap);
316 *argv = start;
317 continue;
318
319 /*
320 * Options w/ arguments, skip the argument and continue
321 * with the next option.
322 */
323 case 'b':
324 case 'c':
325 case 'n':
326 if (!ap[1])
327 ++argv;
328 /* FALLTHROUGH */
329 /* Options w/o arguments, continue with the next option. */
330 case 'F':
331 case 'f':
332 case 'r':
333 continue;
334
335 /* Illegal option, return and let getopt handle it. */
336 default:
337 return;
338 }
339 }
340 }
341
342 static void
343 usage(void)
344 {
345 (void)fprintf(stderr,
346 "usage: tail [-F | -f | -r] [-q] [-b # | -c # | -n #]"
347 " [file ...]\n");
348 exit(1);
349 }