blind-peek-head.c - blind - suckless command-line video editing utility
 (HTM) git clone git://git.suckless.org/blind
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       blind-peek-head.c (3073B)
       ---
            1 /* See LICENSE file for copyright and license details. */
            2 #include "common.h"
            3 
            4 USAGE("")
            5 
            6 static ssize_t
            7 peek_socket(char *buf, size_t n)
            8 {
            9         ssize_t r = recv(STDIN_FILENO, buf, n, MSG_PEEK);
           10         if (r < 0 && errno != ENOTSOCK)
           11                 eprintf("recv <stdin>:");
           12         return r;
           13 }
           14 
           15 static ssize_t
           16 peek_regular(char *buf, size_t n)
           17 {
           18         ssize_t r;
           19         off_t pos = lseek(STDIN_FILENO, 0, SEEK_CUR);
           20         if (pos < 0) {
           21                 if (errno != ESPIPE)
           22                         eprintf("lseek <stdin>:");
           23                 return -1;
           24         }
           25         r = pread(STDIN_FILENO, buf, n, pos);
           26         if (r < 0 && errno != ESPIPE)
           27                 eprintf("pread <stdin>:");
           28         return r;
           29 }
           30 
           31 #if defined(HAVE_TEE)
           32 static ssize_t
           33 peek_pipe(char *buf, size_t n)
           34 {
           35         int rw[2];
           36         ssize_t m;
           37         size_t p;
           38         if (pipe(rw))
           39                 eprintf("pipe");
           40         m = tee(STDIN_FILENO, rw[1], n, 0);
           41         if (m < 0) {
           42                 if (errno != EINVAL)
           43                         eprintf("tee <stdin>:");
           44                 return -1;
           45         }
           46         close(rw[1]);
           47         p = ereadall(rw[0], buf, (size_t)m, "<pipe>");
           48         close(rw[0]);
           49         return (ssize_t)p;
           50 }
           51 #endif
           52 
           53 static size_t
           54 peek(char *buf, size_t n)
           55 {
           56         static int method = 0;
           57         ssize_t r;
           58         switch (method) {
           59         case 0:
           60                 if ((r = peek_socket(buf, n)) >= 0)
           61                         return (size_t)r;
           62                 method++;
           63                 /* fall-through */
           64         case 1:
           65                 if ((r = peek_regular(buf, n)) >= 0)
           66                         return (size_t)r;
           67                 method++;
           68 #if defined(HAVE_TEE)
           69                 /* fall-through */
           70         default:
           71                 if ((r = peek_pipe(buf, n)) >= 0)
           72                         return (size_t)r;
           73                 eprintf("can only peek pipes, sockets, and regular files\n");
           74 #else
           75                 eprintf("can only peek sockets and regular files\n");
           76 #endif
           77         }
           78 }
           79 
           80 int
           81 main(int argc, char *argv[])
           82 {
           83         char buf[STREAM_HEAD_MAX], *p;
           84         char magic[] = {'\0', 'u', 'i', 'v', 'f'};
           85         size_t i, len = 0, last_len;
           86 #if defined(HAVE_EPOLL)
           87         struct epoll_event ev;
           88         int epfd, epr = 0;
           89 #endif
           90 
           91         UNOFLAGS(argc);
           92 
           93 #if defined(HAVE_EPOLL)
           94         epfd = epoll_create1(0);
           95         if (epfd < 0)
           96                 eprintf("epoll_create1:");
           97 
           98         memset(&ev, 0, sizeof(ev));
           99         ev.events = EPOLLIN | EPOLLRDHUP | EPOLLET;
          100         if (epoll_ctl(epfd, EPOLL_CTL_ADD, STDIN_FILENO, &ev))
          101                 eprintf("epoll_ctl EPOLL_CTL_ADD:");
          102 
          103         do {
          104                 last_len = len;
          105                 len = peek(buf, sizeof(buf));
          106                 p = memchr(buf, '\n', len);
          107                 if (p && len >= (size_t)(++p - buf) + ELEMENTSOF(magic))
          108                         goto ready;
          109         } while (len > last_len && (epr = epoll_wait(epfd, &ev, 1, -1)) >= 0);
          110         if (epr < 0)
          111                 eprintf("epoll_wait:");
          112 #else
          113         goto beginning;
          114         do {
          115                 usleep(50000);
          116         beginning:
          117                 last_len = len;
          118                 len = peek(buf, sizeof(buf));
          119                 p = memchr(buf, '\n', len);
          120                 if (p && len >= (size_t)(++p - buf) + ELEMENTSOF(magic))
          121                         goto ready;
          122         } while (len > last_len);
          123 #endif
          124         eprintf("could not read entire head\n");
          125 
          126 ready:
          127         len = (size_t)(p - buf);
          128         for (i = 0; i < ELEMENTSOF(magic); i++)
          129                 if (p[i] != magic[i])
          130                         goto bad_format;
          131         p = buf;
          132         for (i = 0; i < 3; i++) {
          133                 if (!isdigit(*p))
          134                         goto bad_format;
          135                 while (isdigit(*p)) p++;
          136                 if (*p++ != ' ')
          137                         goto bad_format;
          138         }
          139         while (isalnum(*p) || *p == ' ') {
          140                 if (p[0] == ' ' && p[-1] == ' ')
          141                         goto bad_format;
          142                 p++;
          143         }
          144         if (p[-1] == ' ' || p[0] != '\n')
          145                 goto bad_format;
          146 
          147         ewriteall(STDOUT_FILENO, buf, len, "<stdout>");
          148         return 0;
          149 
          150 bad_format:
          151         eprintf("<stdin>: file format not supported\n");
          152 }