add UNLEASHED_OBJ to unleashed.mk
[unleashed/tickless.git] / usr / src / cmd / tail / tail.c
blobc641cfa592b1964d55740177b16421b19ffb7118
1 /*
2 * Copyright (c) 1991, 1993
3 * The Regents of the University of California. All rights reserved.
5 * This code is derived from software contributed to Berkeley by
6 * Edward Sze-Tyan Wang.
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 * 4. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
33 #include <sys/types.h>
34 #include <sys/stat.h>
36 #include <err.h>
37 #include <errno.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
43 #include "extern.h"
45 int Fflag, fflag, qflag, rflag, rval, no_files;
47 file_info_t *files;
49 static void obsolete(char **);
50 static void usage(void);
52 int
53 main(int argc, char *argv[])
55 struct stat sb;
56 const char *fn;
57 FILE *fp;
58 off_t off;
59 enum STYLE style;
60 int i, ch, first;
61 file_info_t *file;
62 char *p;
65 * Tail's options are weird. First, -n10 is the same as -n-10, not
66 * -n+10. Second, the number options are 1 based and not offsets,
67 * so -n+1 is the first line, and -c-1 is the last byte. Third, the
68 * number options for the -r option specify the number of things that
69 * get displayed, not the starting point in the file. The one major
70 * incompatibility in this version as compared to historical versions
71 * is that the 'r' option couldn't be modified by the -lbc options,
72 * i.e. it was always done in lines. This version treats -rc as a
73 * number of characters in reverse order. Finally, the default for
74 * -r is the entire file, not 10 lines.
76 #define ARG(units, forward, backward) { \
77 if (style) \
78 usage(); \
79 off = strtoll(optarg, &p, 10) * (units); \
80 if (*p) \
81 errx(1, "illegal offset -- %s", optarg); \
82 switch (optarg[0]) { \
83 case '+': \
84 if (off) \
85 off -= (units); \
86 style = (forward); \
87 break; \
88 case '-': \
89 off = -off; \
90 /* FALLTHROUGH */ \
91 default: \
92 style = (backward); \
93 break; \
94 } \
97 obsolete(argv);
98 style = NOTSET;
99 off = 0;
100 while ((ch = getopt(argc, argv, "Fb:c:fn:qr")) != -1)
101 switch (ch) {
102 case 'F': /* -F is superset of (and implies) -f */
103 Fflag = fflag = 1;
104 break;
105 case 'b':
106 ARG(512, FBYTES, RBYTES);
107 break;
108 case 'c':
109 ARG(1, FBYTES, RBYTES);
110 break;
111 case 'f':
112 fflag = 1;
113 break;
114 case 'n':
115 ARG(1, FLINES, RLINES);
116 break;
117 case 'q':
118 qflag = 1;
119 break;
120 case 'r':
121 rflag = 1;
122 break;
123 case '?':
124 default:
125 usage();
127 argc -= optind;
128 argv += optind;
130 no_files = argc ? argc : 1;
133 * If displaying in reverse, don't permit follow option, and convert
134 * style values.
136 if (rflag) {
137 if (fflag)
138 usage();
139 if (style == FBYTES)
140 style = RBYTES;
141 else if (style == FLINES)
142 style = RLINES;
146 * If style not specified, the default is the whole file for -r, and
147 * the last 10 lines if not -r.
149 if (style == NOTSET) {
150 if (rflag) {
151 off = 0;
152 style = REVERSE;
153 } else {
154 off = 10;
155 style = RLINES;
159 if (*argv && fflag) {
160 files = (struct file_info *)malloc(no_files *
161 sizeof (struct file_info));
162 if (!files)
163 err(1, "Couldn't malloc space for file descriptors.");
165 for (file = files; (fn = *argv++); file++) {
166 file->file_name = strdup(fn);
167 if (! file->file_name)
168 errx(1, "Couldn't malloc space for file name.");
169 if ((file->fp = fopen(file->file_name, "r")) == NULL ||
170 fstat(fileno(file->fp), &file->st)) {
171 if (file->fp != NULL) {
172 (void) fclose(file->fp);
173 file->fp = NULL;
175 if (!Fflag || errno != ENOENT)
176 ierr(file->file_name);
179 follow(files, style, off);
180 for (i = 0, file = files; i < no_files; i++, file++) {
181 free(file->file_name);
183 free(files);
184 } else if (*argv) {
185 for (first = 1; (fn = *argv++); ) {
186 if ((fp = fopen(fn, "r")) == NULL ||
187 fstat(fileno(fp), &sb)) {
188 ierr(fn);
189 continue;
191 if (argc > 1 && !qflag) {
192 (void) printf("%s==> %s <==\n",
193 first ? "" : "\n", fn);
194 first = 0;
195 (void) fflush(stdout);
198 if (rflag)
199 reverse(fp, fn, style, off, &sb);
200 else
201 forward(fp, fn, style, off, &sb);
203 } else {
204 fn = "stdin";
206 if (fstat(fileno(stdin), &sb)) {
207 ierr(fn);
208 exit(1);
212 * Determine if input is a pipe. 4.4BSD will set the SOCKET
213 * bit in the st_mode field for pipes. Fix this then.
215 if (lseek(fileno(stdin), (off_t)0, SEEK_CUR) == -1 &&
216 errno == ESPIPE) {
217 errno = 0;
218 fflag = 0; /* POSIX.2 requires this. */
221 if (rflag)
222 reverse(stdin, fn, style, off, &sb);
223 else
224 forward(stdin, fn, style, off, &sb);
226 exit(rval);
230 * Convert the obsolete argument form into something that getopt can handle.
231 * This means that anything of the form [+-][0-9][0-9]*[lbc][Ffr] that isn't
232 * the option argument for a -b, -c or -n option gets converted.
234 static void
235 obsolete(char *argv[])
237 char *ap, *p, *t;
238 size_t len;
239 char *start;
241 while ((ap = *++argv)) {
242 /* Return if "--" or not an option of any form. */
243 if (ap[0] != '-') {
244 if (ap[0] != '+')
245 return;
246 } else if (ap[1] == '-')
247 return;
249 switch (*++ap) {
250 /* Old-style option. */
251 case '0': case '1': case '2': case '3': case '4':
252 case '5': case '6': case '7': case '8': case '9':
254 /* Malloc space for dash, new option and argument. */
255 len = strlen(*argv);
256 if ((start = p = malloc(len + 3)) == NULL)
257 err(1, "malloc");
258 *p++ = '-';
261 * Go to the end of the option argument. Save off any
262 * trailing options (-3lf) and translate any trailing
263 * output style characters.
265 t = *argv + len - 1;
266 if (*t == 'F' || *t == 'f' || *t == 'r') {
267 *p++ = *t;
268 *t-- = '\0';
270 switch (*t) {
271 case 'b':
272 *p++ = 'b';
273 *t = '\0';
274 break;
275 case 'c':
276 *p++ = 'c';
277 *t = '\0';
278 break;
279 case 'l':
280 *t = '\0';
281 /* FALLTHROUGH */
282 case '0': case '1': case '2': case '3': case '4':
283 case '5': case '6': case '7': case '8': case '9':
284 *p++ = 'n';
285 break;
286 default:
287 errx(1, "illegal option -- %s", *argv);
289 *p++ = *argv[0];
290 (void) strcpy(p, ap);
291 *argv = start;
292 continue;
295 * Legacy Solaris tail supports "+c" "-c", "+l", "-l",
296 * "+b", and "-b" with an default number of 10. Map
297 * these arguments to an explicit +/-10 for FreeBSD.
298 * New argument will be of the form -[bcn][+-]10
300 case 'b':
301 case 'c':
302 case 'l':
303 if ((start = p = malloc(6)) == NULL)
304 err(1, "malloc");
305 *p++ = '-';
306 switch (ap[0]) {
307 case 'c':
308 *p++ = ap[0];
309 break;
310 case 'b':
311 *p++ = ap[0];
312 break;
313 case 'l':
314 *p++ = 'n';
315 break;
317 (void) snprintf(p, 6, "%c10", *argv[0]);
318 *argv = start;
320 continue;
322 * Options w/ arguments, skip the argument and continue
323 * with the next option.
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;
335 /* Illegal option, return and let getopt handle it. */
336 default:
337 return;
342 static void
343 usage(void)
345 (void) fprintf(stderr,
346 "usage: tail [-F | -f | -r] [-q] [-b # | -c # | -n #]"
347 " [file ...]\n");
348 exit(1);