tail.c - sbase - suckless unix tools
 (HTM) git clone git://git.suckless.org/sbase
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       tail.c (4355B)
       ---
            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 (UTF8_POINT(*p))
          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 }