Add -% to blind-to-text, blind-colour-srgb, and blind-colour-ciexyz - 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
       ---
 (DIR) commit bcac04b4316d64063e743e7a49195173a0c175a0
 (DIR) parent 4154609e5de76bc8858d3f86e0af09ba2700c488
 (HTM) Author: Mattias Andrée <maandree@kth.se>
       Date:   Sun, 24 Sep 2017 01:26:08 +0200
       
       Add -% to blind-to-text, blind-colour-srgb, and blind-colour-ciexyz
       
       Signed-off-by: Mattias Andrée <maandree@kth.se>
       
       Diffstat:
         M TODO                                |       2 --
         M man/blind-colour-ciexyz.1           |      38 +++++++++++++++++++++++++++++++
         M man/blind-colour-srgb.1             |      31 +++++++++++++++++++++++++++++++
         M man/blind-to-text.1                 |      39 +++++++++++++++++++++++++++++++
         M src/blind-colour-ciexyz.c           |      20 +++++++++++++++++---
         M src/blind-colour-srgb.c             |      10 ++++++++--
         M src/blind-to-text.c                 |      27 ++++++++++++++++++---------
         M src/define-functions.h              |      14 --------------
         M src/stream.c                        |     154 +++++++++++++++++++++++++++++++
         M src/stream.h                        |       2 ++
         M src/util.c                          |       6 +++++-
       
       11 files changed, 312 insertions(+), 31 deletions(-)
       ---
 (DIR) diff --git a/TODO b/TODO
       @@ -35,8 +35,6 @@ blind-preview                a graphical tool for previewing the output of a pipeline
                                        should have sliders to tune environment variables
        ---                        tool from creating blockwise transitions
        
       -blind-to-text, blind-from-text: support %a, %e, %g, and custom precision
       -
        Add [-j jobs] to blind-from-video, blind-to-video, blind-convert, and blind-apply-kernel.
        
        long double is slightly faster than long.
 (DIR) diff --git a/man/blind-colour-ciexyz.1 b/man/blind-colour-ciexyz.1
       @@ -3,6 +3,8 @@
        blind-colour-ciexyz - Convert CIE XYZ for use with blind-single-colour(1)
        .SH SYNOPSIS
        .B blind-colour-ciexyz
       +[-%
       +.IR format ]
        .RI ( X
        .I Y
        .I Z
       @@ -21,6 +23,42 @@ that colour model. If only
        is specified, the colour will be CIE Standard Illuminant D65-grey
        with a luminosity of
        .IR Y .
       +.SH OPTIONS
       +.TP
       +.BR -% \ \fIformat\fP
       +Selects in what format parameters are printed.
       +.I format
       +may include the prefix
       +.B +
       +that specified that non-negative values should be prefixed with a
       +.BR + .
       +After any prefix, there may be a positive number specifying
       +the percision of the output, optionally followed by either of
       +the letters
       +.BR a ,
       +.BR e ,
       +.BR f ,
       +other
       +.B g
       +(other their synonymous uppercases),
       +at most once, with the same semantics as in
       +.BR printf (3).
       +.B f
       +is used if this letter is omitted.
       +
       +If ommited,
       +.B 25f
       +is used unless all three of
       +.IR X ,
       +.IR Y ,
       +and
       +.I Z
       +is specified, in which case the parameters are
       +printed as-is. This default is selected for ease
       +of interoperability with other software, however,
       +.B a
       +is recommeded to improve performance and remove
       +truncation error.
        .SH SEE ALSO
        .BR blind (7),
        .BR blind-single-colour (1),
 (DIR) diff --git a/man/blind-colour-srgb.1 b/man/blind-colour-srgb.1
       @@ -3,6 +3,8 @@
        blind-colour-srgb - Convert sRGB for use with blind-single-colour(1) and blind-colour-matrix(1)
        .SH SYNOPSIS
        .B blind-colour-srgb
       +[-%
       +.IR format ]
        [-d
        .IR depth ]
        [-l]
       @@ -38,6 +40,35 @@ unsigned integer with
        bits.
        .SH OPTIONS
        .TP
       +.BR -% \ \fIformat\fP
       +Selects in what format parameters are printed.
       +.I format
       +may include the prefix
       +.B +
       +that specified that non-negative values should be prefixed with a
       +.BR + .
       +After any prefix, there may be a positive number specifying
       +the percision of the output, optionally followed by either of
       +the letters
       +.BR a ,
       +.BR e ,
       +.BR f ,
       +other
       +.B g
       +(other their synonymous uppercases),
       +at most once, with the same semantics as in
       +.BR printf (3).
       +.B f
       +is used if this letter is omitted.
       +
       +If ommited,
       +.B 25f
       +is used. This default is selected for ease of
       +interoperability with other software, however,
       +.B a
       +is recommeded to improve performance and remove
       +truncation error.
       +.TP
        .BR -d " "\fIdepth\fP
        If all three parameters are
        .RI 2^ depth -1,
 (DIR) diff --git a/man/blind-to-text.1 b/man/blind-to-text.1
       @@ -3,6 +3,8 @@
        blind-to-text - Convert a video to text
        .SH SYNOPSIS
        .B blind-to-text
       +[-%
       +.IR format ]
        .SH DESCRIPTION
        .B blind-to-text
        reads a video from stdin and prints it
       @@ -36,6 +38,43 @@ line, after the head, where the second
        frame begins. The pixels are printed
        from left to right, from top to bottom,
        and from first frame to last frame.
       +.SH OPTIONS
       +.TP
       +.BR -% \ \fIformat\fP
       +Selects in what format parameters are printed.
       +.I format
       +may include the prefix
       +.B +
       +that specified that non-negative values should be prefixed with a
       +.BR + .
       +After any prefix, there may be a positive number specifying
       +the percision of the output, optionally followed by either of
       +the letters
       +.BR a ,
       +.BR e ,
       +.BR f ,
       +other
       +.B g
       +(other their synonymous uppercases),
       +or if the input is integer typed,
       +.B d
       +or
       +.BR i ,
       +at most once, with the same semantics as in
       +.BR printf (3).
       +.B f
       +is used if this letter is omitted.
       +The percision must be omitted the input is integer typed.
       +
       +If ommited,
       +.B 25f
       +or
       +.B i
       +is used. These defaults are selected for ease of
       +interoperability with other software, however,
       +.B a
       +is recommeded to improve performance and remove
       +truncation error.
        .SH SEE ALSO
        .BR blind (7),
        .BR blind-from-text (1),
 (DIR) diff --git a/src/blind-colour-ciexyz.c b/src/blind-colour-ciexyz.c
       @@ -1,20 +1,34 @@
        /* See LICENSE file for copyright and license details. */
        #include "common.h"
        
       -USAGE("(X Y Z | Y)")
       +USAGE("[-% format] (X Y Z | Y)")
        
        int
        main(int argc, char *argv[])
        {
                double X, Y, Z;
       +        const char *fmt = NULL;
        
       -        UNOFLAGS(0);
       +        ARGBEGIN {
       +        case '%':
       +                fmt = UARGF();
       +                break;
       +        default:
       +                usage();
       +        } ARGEND;
        
                if (argc == 1) {
       +                fmt = select_print_format("%! %! %!\n", DOUBLE, fmt);
                        Y = etolf_arg("the Y value", argv[0]);
                        X = Y * D65_XYZ_X;
                        Z = Y * D65_XYZ_Z;
       -                printf("%.50lf %.50lf %.50lf\n", X, Y, Z);
       +                printf(fmt, X, Y, Z);
       +        } else if (argc == 3 && fmt) {
       +                fmt = select_print_format("%! %! %!\n", DOUBLE, fmt);
       +                X = etolf_arg("the X value", argv[0]);
       +                Y = etolf_arg("the Y value", argv[1]);
       +                Z = etolf_arg("the Z value", argv[2]);
       +                printf(fmt, X, Y, Z);
                } else if (argc == 3) {
                        printf("%s %s %s\n", argv[0], argv[1], argv[2]);
                } else {
 (DIR) diff --git a/src/blind-colour-srgb.c b/src/blind-colour-srgb.c
       @@ -1,7 +1,7 @@
        /* See LICENSE file for copyright and license details. */
        #include "common.h"
        
       -USAGE("[-d depth] [-l] red green blue")
       +USAGE("[-% format] [-d depth] [-l] red green blue")
        
        int
        main(int argc, char *argv[])
       @@ -9,6 +9,7 @@ main(int argc, char *argv[])
                unsigned long long int max;
                double red, green, blue, X, Y, Z;
                int depth = 8, linear = 0;
       +        const char *fmt = NULL;
        
                ARGBEGIN {
                case 'd':
       @@ -17,6 +18,9 @@ main(int argc, char *argv[])
                case 'l':
                        linear = 1;
                        break;
       +        case '%':
       +                fmt = UARGF();
       +                break;
                default:
                        usage();
                } ARGEND;
       @@ -24,6 +28,8 @@ main(int argc, char *argv[])
                if (argc != 3)
                        usage();
        
       +        fmt = select_print_format("%! %! %!\n", DOUBLE, fmt);
       +
                max   = 1ULL << (depth - 1);
                max  |= max - 1;
                red   = etolf_arg("the red value",   argv[0]) / (double)max;
       @@ -36,7 +42,7 @@ main(int argc, char *argv[])
                }
        
                srgb_to_ciexyz(red, green, blue, &X, &Y, &Z);
       -        printf("%.50lf %.50lf %.50lf\n", X, Y, Z);
       +        printf(fmt, X, Y, Z);
                efshut(stdout, "<stdout>");
                return 0;
        }
 (DIR) diff --git a/src/blind-to-text.c b/src/blind-to-text.c
       @@ -3,7 +3,9 @@
        #define INCLUDE_UINT16
        #include "common.h"
        
       -USAGE("")
       +USAGE("[-% format]")
       +
       +const char *fmt = NULL;
        
        #define FILE "blind-to-text.c"
        #include "define-functions.h"
       @@ -14,10 +16,19 @@ main(int argc, char *argv[])
                struct stream stream;
                void (*process)(struct stream *stream, size_t n);
        
       -        UNOFLAGS(argc);
       +        ARGBEGIN {
       +        case '%':
       +                fmt = UARGF();
       +                break;
       +        default:
       +                usage();
       +        } ARGEND;
        
       -        eopen_stream(&stream, NULL);
       +        if (argc)
       +                usage();
        
       +        eopen_stream(&stream, NULL);
       +        fmt = select_print_format("%! %! %! %!\n", stream.encoding, fmt);
                SELECT_PROCESS_FUNCTION(&stream);
                printf("%zu %zu %zu %s\n", stream.frames, stream.width, stream.height, stream.pixfmt);
                process_stream(&stream, process);
       @@ -32,12 +43,10 @@ PROCESS(struct stream *stream, size_t n)
        {
                size_t i;
                TYPE *p = (TYPE *)(stream->buf);
       -        for (i = 0, n /= stream->chan_size; i < n; i++)
       -#ifdef INTEGER_TYPE
       -                printf("%"PRINT_TYPE"%c", (PRINT_CAST)(p[i]), (i + 1) % stream->n_chan ? ' ' : '\n');
       -#else
       -                printf("%.25"PRINT_TYPE"%c", (PRINT_CAST)(p[i]), (i + 1) % stream->n_chan ? ' ' : '\n');
       -#endif
       +        for (i = 0, n /= stream->chan_size; i < n; i += 4) {
       +                printf(fmt, (PRINT_CAST)(p[i + 0]), (PRINT_CAST)(p[i + 1]),
       +                            (PRINT_CAST)(p[i + 2]), (PRINT_CAST)(p[i + 3]));
       +        }
        }
        
        #endif
 (DIR) diff --git a/src/define-functions.h b/src/define-functions.h
       @@ -4,13 +4,11 @@
        # define PROCESS process_lf
        # define TYPE double
        # define SCAN_TYPE "lf"
       -# define PRINT_TYPE "lf"
        # define PRINT_CAST double
        # include FILE
        # undef PROCESS
        # undef TYPE
        # undef SCAN_TYPE
       -# undef PRINT_TYPE
        # undef PRINT_CAST
        #endif
        
       @@ -18,13 +16,11 @@
        # define PROCESS process_f
        # define TYPE float
        # define SCAN_TYPE "f"
       -# define PRINT_TYPE "lf"
        # define PRINT_CAST double
        # include FILE
        # undef PROCESS
        # undef TYPE
        # undef SCAN_TYPE
       -# undef PRINT_TYPE
        # undef PRINT_CAST
        #endif
        
       @@ -32,13 +28,11 @@
        # define PROCESS process_llf
        # define TYPE long double
        # define SCAN_TYPE "Lf"
       -# define PRINT_TYPE "Lf"
        # define PRINT_CAST long double
        # include FILE
        # undef PROCESS
        # undef TYPE
        # undef SCAN_TYPE
       -# undef PRINT_TYPE
        # undef PRINT_CAST
        #endif
        
       @@ -46,14 +40,12 @@
        # define PROCESS process_u8
        # define TYPE uint8_t
        # define SCAN_TYPE SCNu8
       -# define PRINT_TYPE "u"
        # define PRINT_CAST unsigned
        # define INTEGER_TYPE
        # include FILE
        # undef PROCESS
        # undef TYPE
        # undef SCAN_TYPE
       -# undef PRINT_TYPE
        # undef PRINT_CAST
        # undef INTEGER_TYPE
        #endif
       @@ -62,14 +54,12 @@
        # define PROCESS process_u16
        # define TYPE uint16_t
        # define SCAN_TYPE SCNu16
       -# define PRINT_TYPE "u"
        # define PRINT_CAST unsigned
        # define INTEGER_TYPE
        # include FILE
        # undef PROCESS
        # undef TYPE
        # undef SCAN_TYPE
       -# undef PRINT_TYPE
        # undef PRINT_CAST
        # undef INTEGER_TYPE
        #endif
       @@ -78,14 +68,12 @@
        # define PROCESS process_u32
        # define TYPE uint32_t
        # define SCAN_TYPE SCNu32
       -# define PRINT_TYPE PRIu32
        # define PRINT_CAST uint32_t
        # define INTEGER_TYPE
        # include FILE
        # undef PROCESS
        # undef TYPE
        # undef SCAN_TYPE
       -# undef PRINT_TYPE
        # undef PRINT_CAST
        # undef INTEGER_TYPE
        #endif
       @@ -94,14 +82,12 @@
        # define PROCESS process_u64
        # define TYPE uint64_t
        # define SCAN_TYPE SCNu64
       -# define PRINT_TYPE PRIu64
        # define PRINT_CAST uint64_t
        # define INTEGER_TYPE
        # include FILE
        # undef PROCESS
        # undef TYPE
        # undef SCAN_TYPE
       -# undef PRINT_TYPE
        # undef PRINT_CAST
        # undef INTEGER_TYPE
        #endif
 (DIR) diff --git a/src/stream.c b/src/stream.c
       @@ -369,6 +369,160 @@ get_pixel_format(const char *specified, const char *current)
        }
        
        
       +const char *
       +nselect_print_format(int status, const char *format, enum encoding encoding, const char *fmt)
       +{
       +        static char retbuf[512];
       +        int with_plus = 0, inttyped = -1;
       +        const char *f = "", *orig = fmt;
       +        char *proto = alloca((fmt ? strlen(fmt) : 0) + sizeof("%+#.50llx")), *p;
       +        char *ret = retbuf;
       +        size_t n, len;
       +
       +        if (!orig)
       +                goto check_done;
       +
       +        for (; *fmt == '+'; fmt++)
       +                with_plus = 1;
       +        f = fmt + strspn(fmt, "0123456789");
       +        if (f[0] && f[1])
       +                enprintf(status, "invalid format: %s\n", orig);
       +
       +        switch (*f) {
       +        case '\0':
       +                inttyped = -1;
       +                break;
       +        case 'd': case 'i':
       +                inttyped = 1;
       +                break;
       +        case 'a': case 'A':
       +        case 'e': case 'E':
       +        case 'f': case 'F':
       +        case 'g': case 'G':
       +                inttyped = 0;
       +                break;
       +        default:
       +                enprintf(status, "invalid format: %s\n", orig);
       +        }
       +
       +        switch (encoding) {
       +        case FLOAT:
       +        case DOUBLE:
       +        case LONG_DOUBLE:
       +                if (inttyped == 1)
       +                        enprintf(status, "invalid format `%s' is incompatible with the video format\n", orig);
       +                inttyped = 0;
       +                break;
       +        case UINT8:
       +        case UINT16:
       +        case UINT32:
       +        case UINT64:
       +                if (*f != *fmt)
       +                        enprintf(status, "invalid format: %s\n", orig);
       +                if (inttyped == 0)
       +                        enprintf(status, "invalid format `%s' is incompatible with the video format\n", orig);
       +                inttyped = 1;
       +                break;
       +        default:
       +                abort();
       +        }
       +check_done:
       +
       +        p = proto;
       +        *p++ = '%';
       +        if (with_plus)
       +                *p++ = '+';
       +
       +        if (orig && *f != *fmt) {
       +                *p++ = '.';
       +                p = stpncpy(p, fmt, (size_t)(f - fmt));
       +        } else if (orig && inttyped && *f != 'a' && *f != 'A') {
       +                *p++ = '.';
       +                *p++ = '2';
       +                *p++ = '5';
       +        }
       +
       +        inttyped = 1;
       +        switch (encoding) {
       +        case FLOAT:
       +                inttyped = 0;
       +                break;
       +        case DOUBLE:
       +                *p++ = 'l';
       +                inttyped = 0;
       +                break;
       +        case LONG_DOUBLE:
       +                *p++ = 'L';
       +                inttyped = 0;
       +                break;
       +        case UINT8:
       +                fmt = PRIi8;
       +                break;
       +        case UINT16:
       +                fmt = PRIi16;
       +                break;
       +        case UINT32:
       +                fmt = PRIi32;
       +                break;
       +        case UINT64:
       +                fmt = PRIi64;
       +                break;
       +        default:
       +                abort();
       +        }
       +
       +        if (inttyped)
       +                while (*fmt == 'l')
       +                        *p++ = *fmt++;
       +
       +        switch (orig ? *f : '\0') {
       +        case '\0':
       +                *p++ = inttyped ? 'i' : 'f';
       +                break;
       +        case 'd': case 'i':
       +                *p++ = 'i';
       +                break;
       +        case 'a': case 'A':
       +                *p++ = 'a';
       +                break;
       +        case 'e': case 'E':
       +                *p++ = 'e';
       +                break;
       +        case 'f': case 'F':
       +                *p++ = 'f';
       +                break;
       +        case 'g': case 'G':
       +                *p++ = 'g';
       +                break;
       +        }
       +
       +        *p = '\0';
       +
       +        len = strlen(proto);
       +        for (n = 1, f = format; *f; f++) {
       +                if (f[0] == '%' && f[1] == '!') {
       +                        f++;
       +                        n += len;
       +                } else {
       +                        n++;
       +                }
       +        }
       +
       +        if (n > sizeof(retbuf))
       +                ret = enmalloc(status, n);
       +        for (p = ret, f = format; *f; f++) {
       +                if (f[0] == '%' && f[1] == '!') {
       +                        f++;
       +                        p = stpcpy(p, proto);
       +                } else {
       +                        *p++ = *f;
       +                }
       +        }
       +
       +        return ret;
       +}
       +
       +
        int
        enread_segment(int status, struct stream *stream, void *buf, size_t n)
        {
 (DIR) diff --git a/src/stream.h b/src/stream.h
       @@ -43,6 +43,7 @@
        #define echeck_dimensions(...)        encheck_dimensions(1, __VA_ARGS__)
        #define echeck_dimensions_custom(...) encheck_dimensions_custom(1, __VA_ARGS__)
        #define echeck_compat(...)            encheck_compat(1, __VA_ARGS__)
       +#define select_print_format(...)      nselect_print_format(1, __VA_ARGS__)
        #define eread_segment(...)            enread_segment(1, __VA_ARGS__)
        #define eread_frame(...)              enread_frame(1, __VA_ARGS__)
        #define eread_row(...)                enread_row(1, __VA_ARGS__)
       @@ -128,6 +129,7 @@ void eninf_check_fd(int status, int fd, const char *file);
        void encheck_dimensions(int status, const struct stream *stream, enum dimension dimensions, const char *prefix);
        void encheck_compat(int status, const struct stream *a, const struct stream *b);
        const char *get_pixel_format(const char *specified, const char *current);
       +const char *nselect_print_format(int status, const char *format, enum encoding encoding, const char *fmt);
        int enread_segment(int status, struct stream *stream, void *buf, size_t n);
        size_t ensend_frames(int status, struct stream *stream, int outfd, size_t frames, const char *outfname);
        size_t ensend_rows(int status, struct stream *stream, int outfd, size_t rows, const char *outfname);
 (DIR) diff --git a/src/util.c b/src/util.c
       @@ -6,6 +6,7 @@ char *argv0;
        void
        weprintf(const char *fmt, ...)
        {
       +        char end;
                va_list ap;
                va_start(ap, fmt);
        
       @@ -14,9 +15,12 @@ weprintf(const char *fmt, ...)
        
                vfprintf(stderr, fmt, ap);
        
       -        if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
       +        end = *fmt ? strchr(fmt, '\0')[-1] : '\n';
       +        if (end == ':') {
                        fputc(' ', stderr);
                        perror(NULL);
       +        } else if (end != '\n') {
       +                fputc('\n', stderr);
                }
        
                va_end(ap);