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 }