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\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 }