commit 23100155b5d0ecbf95a8d1c4fa8d02557b69de94
Author:     Mattias Andrée <[email protected]>
AuthorDate: Wed Aug 23 00:39:26 2017 +0200
Commit:     Mattias Andrée <[email protected]>
CommitDate: Wed Aug 23 00:39:26 2017 +0200

    Add blind-peek-head
    
    Signed-off-by: Mattias Andrée <[email protected]>

diff --git a/Makefile b/Makefile
index 1cdafbb..3eb6163 100644
--- a/Makefile
+++ b/Makefile
@@ -60,8 +60,9 @@ BIN =\
        blind-multiply-matrices\
        blind-next-frame\
        blind-norm\
-       blind-quaternion-product\
+       blind-peek-head\
        blind-premultiply\
+       blind-quaternion-product\
        blind-radial-gradient\
        blind-read-head\
        blind-rectangle-tessellation\
diff --git a/README b/README
index 7109d0e..56ec042 100644
--- a/README
+++ b/README
@@ -177,6 +177,9 @@ UTILITIES
        blind-norm(1)
               Calculate the norm of colours in a video
 
+       blind-peek-head
+              Peeks the head from a video
+
        blind-premultiply(1)
               Premultiply the alpha channel of a video
 
diff --git a/config.mk b/config.mk
index 0608008..a046a4d 100644
--- a/config.mk
+++ b/config.mk
@@ -13,9 +13,9 @@ KORN_SHELL = bash
 # Commands
 LN = ln -s
 
-# You may want to remove -DHAVE_PRCTL, -DHAVE_EPOLL, and
-# -DHAVE_SENDFILE from CPPFLAGS if you are not using Linux.
+# You may want to remove -DHAVE_PRCTL, -DHAVE_EPOLL, -DHAVE_TEE,
+# and -DHAVE_SENDFILE from CPPFLAGS if you are not using Linux.
 CFLAGS   = -std=c11 -Wall -pedantic -O2
-CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 
-D_FILE_OFFSET_BITS=64\
-          -DHAVE_PRCTL -DHAVE_EPOLL -DHAVE_SENDFILE
+CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_GNU_SOURCE \
+          -D_FILE_OFFSET_BITS=64 -DHAVE_PRCTL -DHAVE_EPOLL -DHAVE_TEE 
-DHAVE_SENDFILE
 LDFLAGS  = -lm -s
diff --git a/man/blind-peek-head.1 b/man/blind-peek-head.1
new file mode 100644
index 0000000..08f832f
--- /dev/null
+++ b/man/blind-peek-head.1
@@ -0,0 +1,28 @@
+.TH BLIND-PEEK-HEAD 1 blind
+.SH NAME
+blind-peek-head - Peeks the head from a video
+.SH SYNOPSIS
+.B blind-peek-head
+.SH DESCRIPTION
+.B blind-peek-head
+peeks the head a video from stdin, and
+prints it, without the magic number, to stdout.
+The output will contain: the number of frames,
+<space>, the width, <space>, the height, <space>,
+and the pixel format. The output is text, and
+thus ends with a <newline>. The state of stdin
+will remain unchanged.
+.SH NOTES
+.B blind-peek-head
+requires that stdin is a socket, a regular file,
+or a (on Linux only) a pipe. Direct pipes and
+datagram sockets are supported but require that
+the entire head must be written in one write.
+.SH SEE ALSO
+.BR blind (7),
+.BR blind-read-head (1),
+.BR blind-write-head (1),
+.BR blind-next-frame (1)
+.SH AUTHORS
+Mattias Andrée
+.RI < [email protected] >
diff --git a/man/blind-read-head.1 b/man/blind-read-head.1
index 9dac8e9..4d75d7f 100644
--- a/man/blind-read-head.1
+++ b/man/blind-read-head.1
@@ -18,6 +18,7 @@ can be used to get the first, and
 any following, frame from stdin.
 .SH SEE ALSO
 .BR blind (7),
+.BR blind-peek-head (1),
 .BR blind-write-head (1),
 .BR blind-next-frame (1)
 .SH AUTHORS
diff --git a/man/blind.7 b/man/blind.7
index 77c1030..5976903 100644
--- a/man/blind.7
+++ b/man/blind.7
@@ -195,6 +195,9 @@ Extracts the next frame from a video
 .BR blind-norm (1)
 Calculate the norm of colours in a video
 .TP
+.BR blind-peek-head (1)
+Peeks the head from a video
+.TP
 .BR blind-premultiply (1)
 Premultiply the alpha channel of a video
 .TP
diff --git a/src/blind-peek-head.c b/src/blind-peek-head.c
new file mode 100644
index 0000000..86c96b8
--- /dev/null
+++ b/src/blind-peek-head.c
@@ -0,0 +1,152 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+USAGE("")
+
+static ssize_t
+peek_socket(char *buf, size_t n)
+{
+       ssize_t r = recv(STDIN_FILENO, buf, n, MSG_PEEK);
+       if (r < 0 && errno != ENOTSOCK)
+               eprintf("recv <stdin>:");
+       return r;
+}
+
+static ssize_t
+peek_regular(char *buf, size_t n)
+{
+       ssize_t r;
+       off_t pos = lseek(STDIN_FILENO, 0, SEEK_CUR);
+       if (pos < 0) {
+               if (errno != ESPIPE)
+                       eprintf("lseek <stdin>:");
+               return -1;
+       }
+       r = pread(STDIN_FILENO, buf, n, pos);
+       if (r < 0 && errno != ESPIPE)
+               eprintf("pread <stdin>:");
+       return r;
+}
+
+#if defined(HAVE_TEE)
+static ssize_t
+peek_pipe(char *buf, size_t n)
+{
+       int rw[2];
+       ssize_t m;
+       size_t p;
+       if (pipe(rw))
+               eprintf("pipe");
+       m = tee(STDIN_FILENO, rw[1], n, 0);
+       if (m < 0) {
+               if (errno != EINVAL)
+                       eprintf("tee <stdin>:");
+               return -1;
+       }
+       close(rw[1]);
+       p = ereadall(rw[0], buf, (size_t)m, "<pipe>");
+       close(rw[0]);
+       return (ssize_t)p;
+}
+#endif
+
+static size_t
+peek(char *buf, size_t n)
+{
+       static int method = 0;
+       ssize_t r;
+       switch (method) {
+       case 0:
+               if ((r = peek_socket(buf, n)) >= 0)
+                       return (size_t)r;
+               method++;
+               /* fall-through */
+       case 1:
+               if ((r = peek_regular(buf, n)) >= 0)
+                       return (size_t)r;
+               method++;
+#if defined(HAVE_TEE)
+               /* fall-through */
+       default:
+               if ((r = peek_pipe(buf, n)) >= 0)
+                       return (size_t)r;
+               eprintf("can only peek pipes, sockets, and regular files\n");
+#else
+               eprintf("can only peek sockets and regular files\n");
+#endif
+       }
+}
+
+int
+main(int argc, char *argv[])
+{
+       char buf[STREAM_HEAD_MAX], *p;
+       char magic[] = {'\0', 'u', 'i', 'v', 'f'};
+       size_t i, len = 0, last_len;
+#if defined(HAVE_EPOLL)
+       struct epoll_event ev;
+       int epfd, epr = 0;
+#endif
+
+       UNOFLAGS(argc);
+
+#if defined(HAVE_EPOLL)
+       epfd = epoll_create1(0);
+       if (epfd < 0)
+               eprintf("epoll_create1:");
+
+       memset(&ev, 0, sizeof(ev));
+       ev.events = EPOLLIN | EPOLLRDHUP | EPOLLET;
+       if (epoll_ctl(epfd, EPOLL_CTL_ADD, STDIN_FILENO, &ev))
+               eprintf("epoll_ctl EPOLL_CTL_ADD:");
+
+       do {
+               last_len = len;
+               len = peek(buf, sizeof(buf));
+               p = memchr(buf, '\n', len);
+               if (p && len >= (size_t)(++p - buf) + ELEMENTSOF(magic))
+                       goto ready;
+       } while (len > last_len && (epr = epoll_wait(epfd, &ev, 1, -1)) >= 0);
+       if (epr < 0)
+               eprintf("epoll_wait:");
+#else
+       goto beginning;
+       do {
+               usleep(50000);
+       beginning:
+               last_len = len;
+               len = peek(buf, n);
+               p = memchr(buf, '\n', len);
+               if (p && len >= (size_t)(++p - buf) + ELEMENTSOF(magic))
+                       goto ready;
+       } while (len > last_len);
+#endif
+       eprintf("could not read entire head\n");
+
+ready:
+       len = (size_t)(p - buf);
+       for (i = 0; i < ELEMENTSOF(magic); i++)
+               if (p[i] != magic[i])
+                       goto bad_format;
+       p = buf;
+       for (i = 0; i < 3; i++) {
+               if (!isdigit(*p))
+                       goto bad_format;
+               while (isdigit(*p)) p++;
+               if (*p++ != ' ')
+                       goto bad_format;
+       }
+       while (isalnum(*p) || *p == ' ') {
+               if (p[0] == ' ' && p[-1] == ' ')
+                       goto bad_format;
+               p++;
+       }
+       if (p[-1] == ' ' || p[0] != '\n')
+               goto bad_format;
+
+       ewriteall(STDOUT_FILENO, buf, len, "<stdout>");
+       return 0;
+
+bad_format:
+       eprintf("<stdin>: file format not supported\n");
+}

Reply via email to