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 }