sbase

suckless unix tools
git clone git://git.2f30.org/sbase
Log | Files | Refs | README | LICENSE

tail.c (4360B)


      1 /* See LICENSE file for copyright and license details. */
      2 #include <sys/stat.h>
      3 
      4 #include <fcntl.h>
      5 #include <unistd.h>
      6 #include <stdint.h>
      7 #include <stdio.h>
      8 #include <stdlib.h>
      9 #include <string.h>
     10 #include <unistd.h>
     11 
     12 #include "utf.h"
     13 #include "util.h"
     14 
     15 static char mode = 'n';
     16 
     17 static int
     18 dropinit(int fd, const char *fname, size_t count)
     19 {
     20 	Rune r;
     21 	char buf[BUFSIZ], *p;
     22 	ssize_t n;
     23 	int nr;
     24 
     25 	if (count < 2)
     26 		goto copy;
     27 	count--;  /* numbering starts at 1 */
     28 	while (count && (n = read(fd, buf, sizeof(buf))) > 0) {
     29 		switch (mode) {
     30 		case 'n':  /* lines */
     31 			for (p = buf; count && n > 0; p++, n--) {
     32 				if (*p == '\n')
     33 					count--;
     34 			}
     35 			break;
     36 		case 'c':  /* bytes */
     37 			if (count > n) {
     38 				count -= n;
     39 			} else {
     40 				p = buf + count;
     41 				n -= count;
     42 				count = 0;
     43 			}
     44 			break;
     45 		case 'm':  /* runes */
     46 			for (p = buf; count && n > 0; p += nr, n -= nr, count--) {
     47 				nr = charntorune(&r, p, n);
     48 				if (!nr) {
     49 					/* we don't have a full rune, move
     50 					 * remaining data to beginning and read
     51 					 * again */
     52 					memmove(buf, p, n);
     53 					break;
     54 				}
     55 			}
     56 			break;
     57 		}
     58 	}
     59 	if (count) {
     60 		if (n < 0)
     61 			weprintf("read %s:", fname);
     62 		if (n <= 0)
     63 			return n;
     64 	}
     65 
     66 	/* write the rest of the buffer */
     67 	if (writeall(1, p, n) < 0)
     68 		eprintf("write:");
     69 copy:
     70 	switch (concat(fd, fname, 1, "<stdout>")) {
     71 	case -1:  /* read error */
     72 		return -1;
     73 	case -2:  /* write error */
     74 		exit(1);
     75 	default:
     76 		return 0;
     77 	}
     78 }
     79 
     80 static int
     81 taketail(int fd, const char *fname, size_t count)
     82 {
     83 	static char *buf = NULL;
     84 	static size_t size = 0;
     85 	char *p;
     86 	size_t len = 0, left;
     87 	ssize_t n;
     88 
     89 	if (!count)
     90 		return 0;
     91 	for (;;) {
     92 		if (len + BUFSIZ > size) {
     93 			/* make sure we have at least BUFSIZ to read */
     94 			size += 2 * BUFSIZ;
     95 			buf = erealloc(buf, size);
     96 		}
     97 		n = read(fd, buf + len, size - len);
     98 		if (n < 0) {
     99 			weprintf("read %s:", fname);
    100 			return -1;
    101 		}
    102 		if (n == 0)
    103 			break;
    104 		len += n;
    105 		switch (mode) {
    106 		case 'n':  /* lines */
    107 			/* ignore the last character; if it is a newline, it
    108 			 * ends the last line */
    109 			for (p = buf + len - 2, left = count; p >= buf; p--) {
    110 				if (*p != '\n')
    111 					continue;
    112 				left--;
    113 				if (!left) {
    114 					p++;
    115 					break;
    116 				}
    117 			}
    118 			break;
    119 		case 'c':  /* bytes */
    120 			p = count < len ? buf + len - count : buf;
    121 			break;
    122 		case 'm':  /* runes */
    123 			for (p = buf + len - 1, left = count; p >= buf; p--) {
    124 				/* skip utf-8 continuation bytes */
    125 				if ((*p & 0xc0) == 0x80)
    126 					continue;
    127 				left--;
    128 				if (!left)
    129 					break;
    130 			}
    131 			break;
    132 		}
    133 		if (p > buf) {
    134 			len -= p - buf;
    135 			memmove(buf, p, len);
    136 		}
    137 	}
    138 	if (writeall(1, buf, len) < 0)
    139 		eprintf("write:");
    140 	return 0;
    141 }
    142 
    143 static void
    144 usage(void)
    145 {
    146 	eprintf("usage: %s [-f] [-c num | -m num | -n num | -num] [file ...]\n", argv0);
    147 }
    148 
    149 int
    150 main(int argc, char *argv[])
    151 {
    152 	struct stat st1, st2;
    153 	int fd;
    154 	size_t n = 10;
    155 	int fflag = 0, ret = 0, newline = 0, many = 0;
    156 	char *numstr;
    157 	int (*tail)(int, const char *, size_t) = taketail;
    158 
    159 	ARGBEGIN {
    160 	case 'f':
    161 		fflag = 1;
    162 		break;
    163 	case 'c':
    164 	case 'm':
    165 	case 'n':
    166 		mode = ARGC();
    167 		numstr = EARGF(usage());
    168 		n = MIN(llabs(estrtonum(numstr, LLONG_MIN + 1,
    169 		                        MIN(LLONG_MAX, SIZE_MAX))), SIZE_MAX);
    170 		if (strchr(numstr, '+'))
    171 			tail = dropinit;
    172 		break;
    173 	ARGNUM:
    174 		n = ARGNUMF();
    175 		break;
    176 	default:
    177 		usage();
    178 	} ARGEND
    179 
    180 	if (!argc) {
    181 		if (tail(0, "<stdin>", n) < 0)
    182 			ret = 1;
    183 	} else {
    184 		if ((many = argc > 1) && fflag)
    185 			usage();
    186 		for (newline = 0; *argv; argc--, argv++) {
    187 			if (!strcmp(*argv, "-")) {
    188 				*argv = "<stdin>";
    189 				fd = 0;
    190 			} else if ((fd = open(*argv, O_RDONLY)) < 0) {
    191 				weprintf("open %s:", *argv);
    192 				ret = 1;
    193 				continue;
    194 			}
    195 			if (many)
    196 				printf("%s==> %s <==\n", newline ? "\n" : "", *argv);
    197 			if (fstat(fd, &st1) < 0)
    198 				eprintf("fstat %s:", *argv);
    199 			if (!(S_ISFIFO(st1.st_mode) || S_ISREG(st1.st_mode)))
    200 				fflag = 0;
    201 			newline = 1;
    202 			if (tail(fd, *argv, n) < 0) {
    203 				ret = 1;
    204 				fflag = 0;
    205 			}
    206 
    207 			if (!fflag) {
    208 				if (fd != 0)
    209 					close(fd);
    210 				continue;
    211 			}
    212 			for (;;) {
    213 				if (concat(fd, *argv, 1, "<stdout>") < 0)
    214 					exit(1);
    215 				if (fstat(fd, &st2) < 0)
    216 					eprintf("fstat %s:", *argv);
    217 				if (st2.st_size < st1.st_size) {
    218 					fprintf(stderr, "%s: file truncated\n", *argv);
    219 					if (lseek(fd, SEEK_SET, 0) < 0)
    220 						eprintf("lseek:");
    221 				}
    222 				st1 = st2;
    223 				sleep(1);
    224 			}
    225 		}
    226 	}
    227 
    228 	return ret;
    229 }