Browse Source

Allow passing a reader callback to hipack_read()

Instead of using fgetc(), ferror(), and feof() directly, allow passing a
callback function in a hipack_reader_t. Now that a pointer to a struct is
passed its fields are also used to return the error (if any), effectively
changing the API.
Adrian Perez de Castro 5 years ago
parent
commit
9cbad89347
3 changed files with 75 additions and 39 deletions
  1. 47 31
      hipack-parser.c
  2. 19 4
      hipack.h
  3. 9 4
      tools/hipack-parse.c

+ 47 - 31
hipack-parser.c

@@ -14,6 +14,9 @@
 #include <errno.h>
 
 
+const char* HIPACK_READ_ERROR = "Error reading from input";
+
+
 enum status {
     kStatusOk = 0,
     kStatusEof,
@@ -24,7 +27,8 @@ typedef enum status status_t;
 
 
 struct parser {
-    FILE       *fp;
+    int       (*getchar) (void*);
+    void       *getchar_data;
     int         look;
     unsigned    line;
     unsigned    column;
@@ -153,12 +157,12 @@ xdigit_to_int (int xdigit)
 static inline int
 nextchar_raw (P, S)
 {
-    int ch = fgetc (p->fp);
+    int ch = (*p->getchar) (p->getchar_data);
     switch (ch) {
-        case EOF:
-            if (ferror (p->fp)) {
-                *status = kStatusIoError;
-            }
+        case HIPACK_IO_ERROR:
+            *status = kStatusIoError;
+            /* fall-through */
+        case HIPACK_IO_EOF:
             break;
 
         case '\n':
@@ -179,11 +183,11 @@ nextchar (P, S)
         p->look = nextchar_raw (p, CHECK_OK);
 
         if (p->look == '#') {
-            while (p->look != '\n' && p->look != EOF) {
+            while (p->look != '\n' && p->look != HIPACK_IO_EOF) {
                 p->look = nextchar_raw (p, CHECK_OK);
             }
         }
-    } while (p->look != EOF && p->look == '#');
+    } while (p->look != HIPACK_IO_EOF && p->look == '#');
 
 error:
     /* noop */;
@@ -193,7 +197,7 @@ error:
 static inline void
 skipwhite (P, S)
 {
-    while (p->look != EOF && is_hipack_whitespace (p->look))
+    while (p->look != HIPACK_IO_EOF && is_hipack_whitespace (p->look))
         nextchar (p, status);
 }
 
@@ -292,7 +296,7 @@ parse_key (P, S)
     uint32_t alloc_size = 0;
     uint32_t size = 0;
 
-    while (p->look != EOF && is_hipack_key_character (p->look)) {
+    while (p->look != HIPACK_IO_EOF && is_hipack_key_character (p->look)) {
         hstr = string_resize (hstr, &alloc_size, size + 1);
         hstr->data[size++] = p->look;
         nextchar (p, CHECK_OK);
@@ -315,7 +319,7 @@ parse_string (P, S)
 
     matchchar (p, '"', NULL, CHECK_OK);
 
-    while (p->look != '"' && p->look != EOF) {
+    while (p->look != '"' && p->look != HIPACK_IO_EOF) {
         /* Handle escapes. */
         if (p->look == '\\') {
             int extra;
@@ -480,7 +484,7 @@ parse_number (P, S)
     /* Read the rest of the number. */
     bool dot_seen = false;
     bool exp_seen = false;
-    while (p->look != EOF && is_number_char (p->look)) {
+    while (p->look != HIPACK_IO_EOF && is_number_char (p->look)) {
         if (!is_hex && (p->look == 'e' || p->look == 'E')) {
             if (exp_seen) {
                 *status = kStatusError;
@@ -675,10 +679,8 @@ parse_message (P, S)
     nextchar (p, CHECK_OK);
     skipwhite (p, CHECK_OK);
 
-    if (p->look == EOF) {
-        if (ferror (p->fp)) {
-            *status = kStatusIoError;
-        }
+    if (p->look == HIPACK_IO_ERROR) {
+        *status = kStatusIoError;
     } else if (p->look == '{') {
         /* Input starts with a Dict marker. */
         nextchar (p, CHECK_OK);
@@ -686,7 +688,7 @@ parse_message (P, S)
         parse_keyval_items (p, result, '}', CHECK_OK);
         matchchar (p, '}', "unterminated message", CHECK_OK);
     } else {
-        parse_keyval_items (p, result, EOF, CHECK_OK);
+        parse_keyval_items (p, result, HIPACK_IO_EOF, CHECK_OK);
     }
     return result;
 
@@ -697,20 +699,24 @@ error:
 
 
 hipack_dict_t*
-hipack_read (FILE        *fp,
-             const char **error,
-             unsigned    *line,
-             unsigned    *column)
+hipack_read (hipack_reader_t *reader)
 {
-    assert (fp);
+    assert (reader);
 
-    status_t status = kStatusOk;
+    /*
+     * Copy the reader function (and its data pointer) into the parser
+     * structure. The rest of the fields are used as results, so the
+     * reader structure can be cleaned up right after.
+     */
     struct parser p = {
-        .fp = fp,
-        .line = 1,
+        .getchar      = reader->getchar,
+        .getchar_data = reader->getchar_data,
+        .line         = 1,
         0,
     };
+    memset (reader, 0x00, sizeof (hipack_reader_t));
 
+    status_t status = kStatusOk;
     hipack_dict_t *result = parse_message (&p, &status);
     switch (status) {
         case kStatusOk:
@@ -718,23 +724,33 @@ hipack_read (FILE        *fp,
             break;
         case kStatusError:
             assert (!result);
+            assert (p.error);
             break;
         case kStatusIoError:
-            assert (ferror (p.fp));
-            p.error = strerror (errno);
+            p.error = HIPACK_READ_ERROR;
             hipack_dict_free (result);
             result = NULL;
             break;
         case kStatusEof:
-            assert (feof (p.fp));
             break;
     }
 
-    if (error) *error   = p.error;
-    if (line) *line     = p.line;
-    if (column) *column = p.column;
+    reader->error        = p.error;
+    reader->error_line   = p.line;
+    reader->error_column = p.column;
 
     return result;
 }
 
 
+int
+hipack_stdio_getchar (void *fp)
+{
+    assert (fp);
+    int ch = fgetc ((FILE*) fp);
+    if (ch == EOF) {
+        return ferror ((FILE*) fp) ? HIPACK_IO_ERROR : HIPACK_IO_EOF;
+    }
+    return ch;
+}
+

+ 19 - 4
hipack.h

@@ -184,10 +184,25 @@ hipack_value_free (hipack_value_t *value)
 }
 
 
-extern hipack_dict_t* hipack_read (FILE        *fp,
-                                   const char **error,
-                                   unsigned    *line,
-                                   unsigned    *column);
+typedef struct {
+    int       (*getchar) (void*);
+    void       *getchar_data;
+    const char *error;
+    unsigned    error_line;
+    unsigned    error_column;
+} hipack_reader_t;
+
+
+enum {
+    HIPACK_IO_EOF   = -1,
+    HIPACK_IO_ERROR = -2,
+};
+
+extern const char* HIPACK_READ_ERROR;
+
+
+extern int hipack_stdio_getchar (void* fp);
+extern hipack_dict_t* hipack_read (hipack_reader_t *reader);
 
 
 #endif /* !HIPACK_H */

+ 9 - 4
tools/hipack-parse.c

@@ -27,12 +27,17 @@ main (int argc, const char *argv[])
     }
 
     int retcode = EXIT_SUCCESS;
-    const char *error = NULL;
-    unsigned line, column;
-    hipack_dict_t *message = hipack_read (fp, &error, &line, &column);
+    hipack_reader_t reader = {
+        .getchar = hipack_stdio_getchar,
+        .getchar_data = fp,
+    };
+    hipack_dict_t *message = hipack_read (&reader);
     if (!message) {
+        assert (reader.error);
         fprintf (stderr, "line %u, column %u: %s\n",
-                 line, column, error);
+                 reader.error_line, reader.error_column,
+                 (reader.error == HIPACK_READ_ERROR)
+                    ? strerror (errno) : reader.error);
         retcode = EXIT_FAILURE;
     }
     hipack_dict_free (message);