📄 tail.c
字号:
/* tail - copy the end of a file Author: Norbert Schlenker *//* Syntax: tail [-f] [-c number | -n number] [file] * tail -[number][c|l][f] [file] (obsolescent) * tail +[number][c|l][f] [file] (obsolescent) * Flags: * -c number Measure starting point in bytes. If number begins * with '+', the starting point is relative to the * the file's beginning. If number begins with '-' * or has no sign, the starting point is relative to * the end of the file. * -f Keep trying to read after EOF on files and FIFOs. * -n number Measure starting point in lines. The number * following the flag has significance similar to * that described for the -c flag. * * If neither -c nor -n are specified, the default is tail -n 10. * * In the obsolescent syntax, an argument with a 'c' following the * (optional) number is equivalent to "-c number" in the standard * syntax, with number including the leading sign ('+' or '-') of the * argument. An argument with 'l' following the number is equivalent * to "-n number" in the standard syntax. If the number is not * specified, 10 is used as the default. If neither 'c' nor 'l' are * specified, 'l' is assumed. The character 'f' may be suffixed to * the argument and is equivalent to specifying "-f" in the standard * syntax. Look for lines marked "OBSOLESCENT". * * If no file is specified, standard input is assumed. * * P1003.2 does not specify tail's behavior when a count of 0 is given. * It also does not specify clearly whether the first byte (line) of a * file should be numbered 0 or 1. Historical behavior is that the * first byte is actually number 1 (contrary to all Unix standards). * Historically, a count of 0 (or -0) results in no output whatsoever, * while a count of +0 results in the entire file being copied (just like * +1). The implementor does not agree with these behaviors, but has * copied them slavishly. Look for lines marked "HISTORICAL". * * Author: Norbert Schlenker * Copyright: None. Released to the public domain. * Reference: P1003.2 section 4.59 (draft 10) * Notes: Under Minix, this program requires chmem =30000. * Bugs: No internationalization support; all messages are in English. *//* Force visible Posix names */#ifndef _POSIX_SOURCE#define _POSIX_SOURCE 1#endif/* External interfaces */#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>#include <ctype.h>#include <stdlib.h>#include <stdio.h>/* External interfaces that should have been standardized into <getopt.h> */#ifdef _MINIX_PROTOTYPE(int getopt, (int argc, char **argv, char *options));#elseextern int getopt();#endifextern char *optarg;extern int optind;/* We expect this constant to be defined in <limits.h> in a Posix program, * but we'll specify it here just in case it's been left out. */#ifndef LINE_MAX#define LINE_MAX 2048 /* minimum acceptable lower bound */#endif/* Magic numbers suggested or required by Posix specification */#define SUCCESS 0 /* exit code in case of success */#define FAILURE 1 /* or failure */#define DEFAULT_COUNT 10 /* default number of lines or bytes */#define MIN_BUFSIZE (LINE_MAX * DEFAULT_COUNT)#define SLEEP_INTERVAL 1 /* sleep for one second intervals with -f */#define FALSE 0#define TRUE 1/* Internal functions - prototyped under Minix */_PROTOTYPE(int main, (int argc, char **argv));_PROTOTYPE(int tail, (int count, int bytes, int read_until_killed));_PROTOTYPE(int keep_reading, (void));_PROTOTYPE(void usage, (void));int main(argc, argv)int argc;char *argv[];{ int cflag = FALSE; int nflag = FALSE; int fflag = FALSE; int number = -DEFAULT_COUNT; char *suffix; int opt; struct stat stat_buf;/* Determining whether this invocation is via the standard syntax or * via an obsolescent one is a nasty kludge. Here it is, but there is * no pretense at elegance. */ if (argc == 1) { /* simple: default read of a pipe */ exit(tail(-DEFAULT_COUNT, 0, fflag)); } if ((argv[1][0] == '+') || /* OBSOLESCENT */ (argv[1][0] == '-' && ((isdigit(argv[1][1])) || (argv[1][1] == 'l') || (argv[1][1] == 'c' && argv[1][2] == 'f')))) { --argc; ++argv; if (isdigit(argv[0][1])) { number = (int)strtol(argv[0], &suffix, 10); if (number == 0) { /* HISTORICAL */ if (argv[0][0] == '+') number = 1; else exit(SUCCESS); } } else { number = (argv[0][0] == '+') ? DEFAULT_COUNT : -DEFAULT_COUNT; suffix = &(argv[0][1]); } if (*suffix != '\0') { if (*suffix == 'c') { cflag = TRUE; ++suffix; } else if (*suffix == 'l') { nflag = TRUE; ++suffix; } } if (*suffix != '\0') { if (*suffix == 'f') { fflag = TRUE; ++suffix; } } if (*suffix != '\0') { /* bad form: assume to be a file name */ number = -DEFAULT_COUNT; cflag = nflag = FALSE; fflag = FALSE; } else { --argc; ++argv; } } else { /* new standard syntax */ while ((opt = getopt(argc, argv, "c:fn:")) != EOF) { switch (opt) { case 'c': cflag = TRUE; if (*optarg == '+' || *optarg == '-') number = atoi(optarg); else if (isdigit(*optarg)) number = -atoi(optarg); else usage(); if (number == 0) { /* HISTORICAL */ if (*optarg == '+') number = 1; else exit(SUCCESS); } break; case 'f': fflag = TRUE; break; case 'n': nflag = TRUE; if (*optarg == '+' || *optarg == '-') number = atoi(optarg); else if (isdigit(*optarg)) number = -atoi(optarg); else usage(); if (number == 0) { /* HISTORICAL */ if (*optarg == '+') number = 1; else exit(SUCCESS); } break; default: usage(); /* NOTREACHED */ } } argc -= optind; argv += optind; } if (argc > 1 || /* too many arguments */ (cflag && nflag)) { /* both bytes and lines specified */ usage(); } if (argc > 0) { /* an actual file */ if (freopen(argv[0], "r", stdin) != stdin) { fputs("tail: could not open ", stderr); fputs(argv[0], stderr); fputs("\n", stderr); exit(FAILURE); } /* There is an optimization possibility here. If a file is being * read, we need not look at the front of it. If we seek backwards * from the end, we can (potentially) avoid looking at most of the * file. Some systems fail when asked to seek backwards to a point * before the start of the file, so we avoid that possibility. */ if (number < 0 && fstat(fileno(stdin), &stat_buf) == 0) { long offset = cflag ? (long)number : (long)number * LINE_MAX; if (-offset < stat_buf.st_size) fseek(stdin, offset, SEEK_END); } } else { fflag = FALSE; /* force -f off when reading a pipe */ } exit(tail(number, cflag, fflag)); /* NOTREACHED */}int tail(count, bytes, read_until_killed)int count; /* lines or bytes desired */int bytes; /* TRUE if we want bytes */int read_until_killed; /* keep reading at EOF */{ int c; char *buf; /* pointer to input buffer */ char *buf_end; /* and one past its end */ char *start; /* pointer to first desired character in buf */ char *finish; /* pointer past last desired character */ int wrapped_once = FALSE; /* TRUE after buf has been filled once *//* This is magic. If count is positive, it means start at the count'th * line or byte, with the first line or byte considered number 1. Thus, * we want to SKIP one less line or byte than the number specified. In * the negative case, we look backward from the end of the file for the * (count + 1)'th newline or byte, so we really want the count to be one * LARGER than was specified (in absolute value). In either case, the * right thing to do is: */ --count;/* Count is positive: skip the desired lines or bytes and then copy. */ if (count >= 0) { while (count > 0 && (c = getchar()) != EOF) { if (bytes || c == '\n') --count; } while ((c = getchar()) != EOF) { if (putchar(c) == EOF) return FAILURE; } if (read_until_killed) return keep_reading(); return ferror(stdin) ? FAILURE : SUCCESS; }/* Count is negative: allocate a reasonably large buffer. */ if ((buf = (char *)malloc(MIN_BUFSIZE + 1)) == (char *)NULL) { fputs("tail: out of memory\n", stderr); return FAILURE; } buf_end = buf + (MIN_BUFSIZE + 1);/* Read the entire file into the buffer. */ finish = buf; while ((c = getchar()) != EOF) { *finish++ = c; if (finish == buf_end) { finish = buf; wrapped_once = TRUE; } } if (ferror(stdin)) return FAILURE;/* Back up inside the buffer. The count has already been adjusted to * back up exactly one character too far, so we will bump the buffer * pointer once after we're done. * * BUG: For large line counts, the buffer may not be large enough to * hold all the lines. The specification allows the program to * fail in such a case - this program will simply dump the entire * buffer's contents as its best attempt at the desired behavior. */ if (finish != buf || wrapped_once) { /* file was not empty */ start = (finish == buf) ? buf_end - 1 : finish - 1; while (start != finish) { if ((bytes || *start == '\n') && ++count == 0) break; if (start == buf) { start = buf_end - 1; if (!wrapped_once) /* never wrapped: stop now */ break; } else { --start; } } if (++start == buf_end) { /* bump after going too far */ start = buf; } if (finish > start) { fwrite(start, 1, finish - start, stdout); } else { fwrite(start, 1, buf_end - start, stdout); fwrite(buf, 1, finish - buf, stdout); } } if (read_until_killed) return keep_reading(); return ferror(stdout) ? FAILURE : SUCCESS;}/* Wake at intervals to reread standard input. Copy anything read to * standard output and then go to sleep again. */int keep_reading(){ int c; for (;;) { sleep(SLEEP_INTERVAL); clearerr(stdin); while ((c = getchar()) != EOF) { if (putchar(c) == EOF) return FAILURE; } if (ferror(stdin)) return FAILURE; }}/* Tell the user the standard syntax. */void usage(){ fputs("Usage: tail [-f] [-c number | -n number] [file]\n", stderr); exit(FAILURE);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -