| blind-peek-head.c - blind - suckless command-line video editing utility | |
| git clone git://git.suckless.org/blind | |
| Log | |
| Files | |
| Refs | |
| README | |
| 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… | |
| 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)) … | |
| 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 } |