From 481a3f5110e03c2301e53837da2666114b5efa5c Mon Sep 17 00:00:00 2001 From: giraffedata Date: Wed, 9 Dec 2009 03:52:08 +0000 Subject: 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 --- lib/util/Makefile | 8 ++++- lib/util/io.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/util/io.h | 11 +++++++ lib/util/shhopt.c | 88 ++++++++++++++++++++++++++++++----------------------- lib/util/shhopt.h | 1 + lib/util/token.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/util/token.h | 11 +++++++ 7 files changed, 249 insertions(+), 39 deletions(-) create mode 100644 lib/util/io.c create mode 100644 lib/util/io.h create mode 100644 lib/util/token.c create mode 100644 lib/util/token.h (limited to 'lib/util') 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 +#include +#include +#include + +#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 + +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 | @@ -478,6 +481,15 @@ optExecute(optEntry const opt, char *arg, int lng) if (opt.arg) 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 +#include + +#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 -- cgit 1.4.1