⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tail.c

📁 操作系统源代码
💻 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 + -