about summary refs log tree commit diff
diff options
context:
space:
mode:
authorgiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2009-12-09 03:52:08 +0000
committergiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2009-12-09 03:52:08 +0000
commit481a3f5110e03c2301e53837da2666114b5efa5c (patch)
tree77e1d1272c4252785583794324e5e7007da1d60e
parentca1c1c7c4ca76728b3134cf685e8b8a6142e663b (diff)
downloadnetpbm-mirror-481a3f5110e03c2301e53837da2666114b5efa5c.tar.gz
netpbm-mirror-481a3f5110e03c2301e53837da2666114b5efa5c.tar.xz
netpbm-mirror-481a3f5110e03c2301e53837da2666114b5efa5c.zip
new facilities needed by recently committed Pnmconvol updates: string list option type, token parsing, file line I/O
git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@1031 9d0c8265-081b-0410-96cb-a4ca84ce46f8
-rw-r--r--lib/util/Makefile8
-rw-r--r--lib/util/io.c90
-rw-r--r--lib/util/io.h11
-rw-r--r--lib/util/shhopt.c88
-rw-r--r--lib/util/shhopt.h1
-rw-r--r--lib/util/token.c79
-rw-r--r--lib/util/token.h11
7 files changed, 249 insertions, 39 deletions
diff --git a/lib/util/Makefile b/lib/util/Makefile
index f5f0e04c..8f16e432 100644
--- a/lib/util/Makefile
+++ b/lib/util/Makefile
@@ -9,7 +9,13 @@ include $(BUILDDIR)/config.mk
 
 # nstring is required for asprintf(), etc.  Also some systems don't have
 # snprintf(), e.g. Solaris 2.5.1.  2002.03.29.
-UTILOBJECTS = shhopt.o nstring.o vasprintf.o filename.o nsleep.o
+UTILOBJECTS = \
+  filename.o \
+  io.o \
+  nsleep.o \
+  shhopt.o \
+  token.o \
+  vasprintf.o \
 
 MERGE_OBJECTS =
 
diff --git a/lib/util/io.c b/lib/util/io.c
new file mode 100644
index 00000000..edec4a7c
--- /dev/null
+++ b/lib/util/io.c
@@ -0,0 +1,90 @@
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "mallocvar.h"
+#include "nstring.h"
+
+#include "io.h"
+
+
+void
+pm_freadline(FILE *        const fileP,
+             const char ** const lineP,
+             const char ** const errorP) {
+/*----------------------------------------------------------------------------
+   Read a line (assuming the file is text with lines delimited by newlines)
+   for file *fileP.  Return that line in newly malloced storage as
+   *lineP.
+
+   The newline delimiter is not part of the line.
+
+   As a special case, if the file doesn't end in a newline, the characters
+   after the last newline are a line.
+
+   If there are no more lines in the file, return *lineP == NULL.
+-----------------------------------------------------------------------------*/
+    char * buffer;
+    size_t bufferSize;
+    size_t cursor;
+    bool gotLine;
+    bool eof;
+
+    bufferSize = 1024;  /* initial value */
+    *errorP = NULL; /* initial value */
+    
+    MALLOCARRAY(buffer, bufferSize);
+
+    for (cursor = 0, gotLine = false, eof = false;
+         !gotLine && !eof && !*errorP; ) {
+        if (cursor + 1 >= bufferSize) {
+            if (bufferSize > INT_MAX/2) {
+                free(buffer);
+                buffer = NULL;
+            } else {
+                bufferSize *= 2;
+                REALLOCARRAY(buffer, bufferSize);
+            }
+        }
+
+        if (!buffer)
+            asprintfN(errorP,
+                      "Couldn't get memory for a %u-byte file read buffer.",
+                      (unsigned int)bufferSize);
+        else {
+            int const rc = getc(fileP);
+        
+            if (rc < 0) {
+                if (feof(fileP))
+                    eof = true;
+                else
+                    asprintfN(errorP, "Failed to read a character from file.  "
+                              "Errno = %d (%s)",
+                              errno, strerror(errno));
+            } else {
+                char const c = (char)rc;
+
+                if (c == '\n')
+                    gotLine = true;
+                else {
+                    buffer[cursor++] = c;
+                }
+            }
+        }
+    }
+    if (*errorP) {
+        if (buffer)
+            free(buffer);
+    } else {
+        if (eof && cursor == 0) {
+            *lineP = NULL;
+            free(buffer);
+        } else {
+            assert(cursor < bufferSize);
+            buffer[cursor++] = '\0';
+
+            *lineP = buffer;
+        }
+    }
+}
diff --git a/lib/util/io.h b/lib/util/io.h
new file mode 100644
index 00000000..39ccd14b
--- /dev/null
+++ b/lib/util/io.h
@@ -0,0 +1,11 @@
+#ifndef IO_H_INCLUDED
+#define IO_H_INCLUDED
+
+#include <stdio.h>
+
+void
+pm_freadline(FILE *        const fileP,
+             const char ** const lineP,
+             const char ** const errorP);
+
+#endif
diff --git a/lib/util/shhopt.c b/lib/util/shhopt.c
index 718186fa..7f6e2899 100644
--- a/lib/util/shhopt.c
+++ b/lib/util/shhopt.c
@@ -31,6 +31,7 @@
 
 #include "mallocvar.h"
 #include "nstring.h"
+#include "token.h"
 #include "shhopt.h"
 
 /*-----------------------------------------------------------------------+
@@ -237,6 +238,7 @@ optNeedsArgument(const optEntry opt)
 	|| opt.type == OPT_ULONG
     || opt.type == OPT_FLOAT
     || opt.type == OPT_NAMELIST
+    || opt.type == OPT_STRINGLIST
         ;
 }
 
@@ -281,46 +283,12 @@ getToken(const char *  const tokenStart,
    we return an empty string and *nextP == tokenStart, i.e. *nextP
    doesn't necessarily advance.
 -----------------------------------------------------------------------------*/
-    char * token;
-    const char * cursor;
-    unsigned int charCount;
-
-    /* Run through the token, counting characters */
-
-    charCount = 0;
-    cursor = tokenStart;
-
-    while (*cursor != delimiter && *cursor != '\0') {
-        if (*cursor == '\\') {
-            ++cursor;
-            if (*cursor == '\0')
-                optFatal("string ends with an escape character (\\)");
-        }
-        ++cursor;
-        ++charCount;
-    }
+    const char * error;
     
-    token = malloc(charCount + 1);
-    if (token == NULL)
-        optFatal("Could not allocate %u bytes of memory to parse a string",
-                 charCount + 1);
-
-    /* Go back and do it again, this time copying the characters */
-    charCount = 0;
-    cursor = tokenStart;
-
-    while (*cursor != delimiter && *cursor != '\0') {
-        if (*cursor == '\\')
-            ++cursor;
-
-        assert(*cursor != '\0');
+    pm_gettoken(tokenStart, delimiter, tokenP, nextP, &error);
 
-        token[charCount++] = *cursor++;
-    }
-    token[charCount] = '\0';
-
-    *tokenP = token;
-    *nextP = cursor;
+    if (error)
+        optFatal("error parsing a token: %s", error);
 }
 
 
@@ -375,6 +343,41 @@ parseNameList(const char *           const listText,
 
 
 
+static void
+parseStringList(const char *   const listText,
+                const char *** const listP) {
+
+    unsigned int const maxStringCount = 100;
+
+    const char * cursor;
+    unsigned int stringCount;
+    const char ** list;
+
+    MALLOCARRAY_NOFAIL(list, maxStringCount+1);
+
+    cursor = &listText[0];  /* initial value */
+
+    stringCount = 0;  /* initial value */
+
+    while (stringCount < maxStringCount && *cursor != '\0') {
+        const char * next;
+
+        getToken(cursor, ',', &list[stringCount++], &next);
+
+        cursor = next;
+
+        if (*cursor != '\0') {
+            assert(*cursor == ',');
+            ++cursor;
+        }
+    }
+    list[stringCount] = NULL;
+
+    *listP = list;
+}
+
+
+
 /*------------------------------------------------------------------------
  |  NAME          optExecute
  |
@@ -479,6 +482,15 @@ optExecute(optEntry  const opt, char *arg, int lng)
             parseNameList(arg, (struct optNameValue **)opt.arg);
 
     } break;
+    case OPT_STRINGLIST: {
+        if (arg == NULL)
+            optFatal("internal error: optExecute() called with NULL argument "
+                     "'%s'", optString(opt, lng));
+
+        if (opt.arg)
+            parseStringList(arg, (const char ***)opt.arg);
+
+    } break;
     default:
         break;
     }
diff --git a/lib/util/shhopt.h b/lib/util/shhopt.h
index 99096a76..2175fc5d 100644
--- a/lib/util/shhopt.h
+++ b/lib/util/shhopt.h
@@ -90,6 +90,7 @@ typedef enum {
     OPT_LONG,              /* signed long integer argument. */
     OPT_ULONG,             /* unsigned long integer argument. */
     OPT_FLOAT,             /* floating point argument. */
+    OPT_STRINGLIST,        /* list like "arg1,arg2.arg3" */
     OPT_NAMELIST           /* list like "key1=val1,key2=val2" */
 } optArgType;
 
diff --git a/lib/util/token.c b/lib/util/token.c
new file mode 100644
index 00000000..c4fe0aed
--- /dev/null
+++ b/lib/util/token.c
@@ -0,0 +1,79 @@
+#include <stdlib.h>
+#include <assert.h>
+
+#include "nstring.h"
+
+#include "token.h"
+
+
+void
+pm_gettoken(const char *  const tokenStart,
+            char          const delimiter,
+            const char ** const tokenP,
+            const char ** const nextP,
+            const char ** const errorP) {
+/*----------------------------------------------------------------------------
+   Find the token starting at 'tokenStart' up to but not including
+   the first 'delimiter' character or end of string.  Return it in newly
+   malloced memory as *tokenP, NUL-terminated.
+
+   Make *nextP point just past the token, i.e. to the delimiter or
+   end of string NUL character.
+
+   Note that if the string is empty, or starts with the delimiter,
+   we return an empty string and *nextP == tokenStart, i.e. *nextP
+   doesn't necessarily advance.
+-----------------------------------------------------------------------------*/
+    char * token;
+    const char * cursor;
+    unsigned int charCount;
+
+    /* Run through the token, counting characters */
+
+    charCount = 0;        /* initial value */
+    cursor = tokenStart;  /* initial value */
+    *errorP = NULL;       /* initial value */
+
+    while (*cursor != delimiter && *cursor != '\0' && !*errorP) {
+        if (*cursor == '\\') {
+            ++cursor;
+            if (*cursor == '\0')
+                asprintfN(errorP, "string ends with an escape character (\\)");
+        } else {
+            ++cursor;
+            ++charCount;
+        }
+    }
+    if (!*errorP) {
+        token = malloc(charCount + 1);
+        if (token == NULL)
+            asprintfN(errorP, "Could not allocate %u bytes of memory "
+                      "to parse a string",
+                      charCount + 1);
+        else {
+            /* Go back and do it again, this time copying the characters */
+            charCount = 0;
+            cursor = tokenStart;
+
+            while (*cursor != delimiter && *cursor != '\0') {
+                if (*cursor == '\\')
+                    ++cursor;
+
+                assert(*cursor != '\0');
+
+                token[charCount++] = *cursor++;
+            }
+            token[charCount] = '\0';
+            
+            *tokenP = token;
+            *nextP = cursor;
+        }
+    }
+}
+
+
+
+
+
+
+
diff --git a/lib/util/token.h b/lib/util/token.h
new file mode 100644
index 00000000..292a9164
--- /dev/null
+++ b/lib/util/token.h
@@ -0,0 +1,11 @@
+#ifndef TOKEN_H_INCLUDE
+#define TOKEN_H_INCLUDE
+
+void
+pm_gettoken(const char *  const tokenStart,
+            char          const delimiter,
+            const char ** const tokenP,
+            const char ** const nextP,
+            const char ** const errorP);
+
+#endif