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 }