shithub: sox

Download patch

ref: b190c3d473a96a48c27c01186b1740ecd6753b87
parent: 79f515914c96e04a3be3b20bbc5c71459e4b8915
author: Mans Rullgard <mans@mansr.com>
date: Sat Aug 8 07:19:39 EDT 2020

formats: add lsx_read_fields() function

Add a function for reading multiple fields according to a spec string
with size checking, aborting on read error.  This simplifies error
checking, which is sorely lacking, in format handlers.

--- a/src/formats_i.c
+++ b/src/formats_i.c
@@ -409,6 +409,66 @@
   return SOX_EOF;
 }
 
+int lsx_read_fields(sox_format_t *ft, uint32_t *len,  const char *spec, ...)
+{
+    int err = SOX_SUCCESS;
+    va_list ap;
+
+#define do_read(type, f, n) do {                                \
+        size_t nr;                                              \
+        if (*len < n * sizeof(type)) {                          \
+            err = SOX_EOF;                                      \
+            goto end;                                           \
+        }                                                       \
+        nr = lsx_read_##f##_buf(ft, va_arg(ap, type *), r);     \
+        if (nr != n)                                            \
+            err = SOX_EOF;                                      \
+        *len -= nr * sizeof(type);                              \
+    } while (0)
+
+    va_start(ap, spec);
+
+    while (*spec) {
+        unsigned long r = 1;
+        char c = *spec;
+
+        if (c >= '0' && c <= '9') {
+            char *next;
+            r = strtoul(spec, &next, 10);
+            spec = next;
+            c = *spec;
+        } else if (c == '*') {
+            r = va_arg(ap, int);
+            c = *++spec;
+        }
+
+        switch (c) {
+        case 'b': do_read(uint8_t, b, r);       break;
+        case 'h': do_read(uint16_t, w, r);      break;
+        case 'i': do_read(uint32_t, dw, r);     break;
+        case 'q': do_read(uint64_t, qw, r);     break;
+        case 'x': err = lsx_skipbytes(ft, r);   break;
+
+        default:
+            lsx_fail("lsx_read_fields: invalid format character '%c'", c);
+            err = SOX_EOF;
+            break;
+        }
+
+        if (err)
+            break;
+
+        spec++;
+    }
+
+end:
+    va_end(ap);
+
+#undef do_read
+
+    return err;
+}
+
 /* N.B. This macro doesn't work for unaligned types (e.g. 3-byte
    types). */
 #define WRITE_FUNC(type, size, ctype, twiddle) \
--- a/src/sox_i.h
+++ b/src/sox_i.h
@@ -183,6 +183,8 @@
 #define lsx_readsw(ft, sw) lsx_readb(ft, (uint16_t *)sw)
 #endif
 
+int lsx_read_fields(sox_format_t *ft, uint32_t *len,  const char *spec, ...);
+
 int lsx_write3(sox_format_t * ft, unsigned u3);
 int lsx_writeb(sox_format_t * ft, unsigned ub);
 int lsx_writedf(sox_format_t * ft, double d);