diff options
author | pgen <p.gen.progs@gmail.com> | 2019-09-15 23:40:37 +0200 |
---|---|---|
committer | pgen <p.gen.progs@gmail.com> | 2019-10-01 00:32:56 +0200 |
commit | 24037bdfe6aaf3ba6fa3453869466b6fd7b2579d (patch) | |
tree | 5fc9a1e3eb2d9998d64cfc7b6f0d63e2042ed695 | |
parent | a2adfc0f3c304aec4a573478838ea769c3f590a8 (diff) |
Use ctxopt.[ch] for options management
The old getopt, derived from egetopt, was too restrictive, so I developed
ctxopt to better manage smenu options.
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | Makefile.in | 6 | ||||
-rw-r--r-- | ctxopt.c | 3525 | ||||
-rw-r--r-- | ctxopt.h | 71 | ||||
-rw-r--r-- | getopt.c | 211 | ||||
-rw-r--r-- | getopt.h | 35 | ||||
-rw-r--r-- | smenu.c | 2434 | ||||
-rw-r--r-- | tests/initial_selection/t0001.good | 2 | ||||
-rw-r--r-- | tests/initial_selection/t0001.tst | 2 | ||||
-rw-r--r-- | tests/rows_cols_in-ex-clusions/t0001.good | 2 | ||||
-rw-r--r-- | tests/rows_cols_in-ex-clusions/t0001.tst | 2 | ||||
-rw-r--r-- | tests/rows_cols_in-ex-clusions/t0002.good | 2 | ||||
-rw-r--r-- | tests/rows_cols_in-ex-clusions/t0002.tst | 2 | ||||
-rw-r--r-- | tests/rows_cols_in-ex-clusions/t0003.good | 2 | ||||
-rw-r--r-- | tests/rows_cols_in-ex-clusions/t0003.tst | 2 | ||||
-rw-r--r-- | tests/special_levels/t0003.good | 12 | ||||
-rw-r--r-- | tests/special_levels/t0003.tst | 2 | ||||
-rw-r--r-- | tests/tagging/t0013.good | 2 | ||||
-rw-r--r-- | tests/tagging/t0013.tst | 2 | ||||
-rw-r--r-- | usage.c | 207 | ||||
-rw-r--r-- | usage.h | 4 |
21 files changed, 5022 insertions, 1507 deletions
diff --git a/Makefile.am b/Makefile.am index 9518f43..0dfba2a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,7 +1,7 @@ bin_PROGRAMS = smenu smenu_SOURCES = smenu.c smenu.h list.c list.h xmalloc.c xmalloc.h \ index.c index.h utf8.c utf8.h fgetc.c fgetc.h \ - utils.c utils.h getopt.c getopt.h usage.c usage.h + utils.c utils.h usage.c usage.h ctxopt.h ctxopt.c dist_man_MANS = smenu.1 EXTRA_DIST = smenu.spec.in smenu.spec ChangeLog build.sh version \ COPYRIGHT LICENSE.rst README.rst examples build-aux tests \ diff --git a/Makefile.in b/Makefile.in index a45cb86..645e44d 100644 --- a/Makefile.in +++ b/Makefile.in @@ -104,7 +104,7 @@ am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)" PROGRAMS = $(bin_PROGRAMS) am_smenu_OBJECTS = smenu.$(OBJEXT) list.$(OBJEXT) xmalloc.$(OBJEXT) \ index.$(OBJEXT) utf8.$(OBJEXT) fgetc.$(OBJEXT) utils.$(OBJEXT) \ - getopt.$(OBJEXT) usage.$(OBJEXT) + usage.$(OBJEXT) ctxopt.$(OBJEXT) smenu_OBJECTS = $(am_smenu_OBJECTS) smenu_LDADD = $(LDADD) AM_V_P = $(am__v_P_@AM_V@) @@ -309,7 +309,7 @@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ smenu_SOURCES = smenu.c smenu.h list.c list.h xmalloc.c xmalloc.h \ index.c index.h utf8.c utf8.h fgetc.c fgetc.h \ - utils.c utils.h getopt.c getopt.h usage.c usage.h + utils.c utils.h usage.c usage.h ctxopt.h ctxopt.c dist_man_MANS = smenu.1 EXTRA_DIST = smenu.spec.in smenu.spec ChangeLog build.sh version \ @@ -425,8 +425,8 @@ mostlyclean-compile: distclean-compile: -rm -f *.tab.c +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctxopt.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fgetc.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getopt.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/index.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/list.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/smenu.Po@am__quote@ diff --git a/ctxopt.c b/ctxopt.c new file mode 100644 index 0000000..3ff3fae --- /dev/null +++ b/ctxopt.c @@ -0,0 +1,3525 @@ +//#include "config.h" +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <ctype.h> +#include <sys/types.h> +#include <regex.h> +#include <stdarg.h> +#include <string.h> +#include "ctxopt.h" + +errors ctxopterrno = CTXOPTNOERR; + +/* *********************** */ +/* Static global variables */ +/* *********************** */ +static void * contexts_bst; +static void * options_bst; + +/* Prototypes */ + +/* ****************** */ +/* Messages interface */ +/* ****************** */ + +static void (**err_functions)(char *, char *, char *, char *, char *); + +static void +fatal(errors e, char * opt_par, char * opt_params, char * opt_name, + char * ctx_par, char * ctx_name, const char * format, ...); + +static void +error(const char * format, ...); + +static int user_rc; /* Used by various callback functions */ +static int user_value; /* Used by various callback functions */ +static char * user_string; /* Used by various callback functions */ +static void * user_object; /* Used by various callback functions */ + +/* *************************** */ +/* Memory management interface */ +/* *************************** */ + +static void * +xmalloc(size_t size); + +static void * +xcalloc(size_t num, size_t size); + +static void * +xrealloc(void * ptr, size_t size); + +static char * +xstrdup(const char * p); + +static char * +xstrndup(const char * str, size_t len); + +/* ************* */ +/* BST interface */ +/* ************* */ +typedef struct bst_s bst_t; + +typedef enum +{ + preorder, + postorder, + endorder, + leaf +} walk_order_e; + +static void * +bst_delete(const void * vkey, void ** vrootp, + int (*compar)(const void *, const void *)); + +static void +bst_destroy_recurse(bst_t * root, void (*free_action)(void *)); + +static void +bst_destroy(void * vrootp, void (*freefct)(void *)); + +static void * +bst_find(const void * vkey, void * const * vrootp, + int (*compar)(const void *, const void *)); + +static void * +bst_search(const void * vkey, void ** vrootp, + int (*compar)(const void *, const void *)); + +static void +bst_walk_recurse(const bst_t * root, + void (*action)(const void *, walk_order_e, int), int level); + +static void +bst_walk(const void * vroot, void (*action)(const void *, walk_order_e, int)); + +/* ********************* */ +/* Linked list Interface */ +/* ********************* */ + +typedef struct ll_node_s ll_node_t; +typedef struct ll_s ll_t; + +static void +ll_append(ll_t * const list, void * const data); + +static void +ll_prepend(ll_t * const list, void * const data); + +static void +ll_insert_after(ll_t * const list, ll_node_t * node, void * const data); + +static void +ll_insert_before(ll_t * const list, ll_node_t * node, void * const data); + +static int +ll_delete(ll_t * const list, ll_node_t * node); + +static ll_node_t * +ll_find(ll_t * const, void * const, int (*)(const void *, const void *)); + +static void +ll_init(ll_t * list); + +static ll_node_t * +ll_new_node(void); + +static ll_t * +ll_new(void); + +static int +ll_strarray(ll_t * list, ll_node_t * start_node, int * count, char *** array); + +/* ****************** */ +/* various interfaces */ +/* ****************** */ + +static void +ltrim(char * str, const char * trim_str); + +static void +rtrim(char * str, const char * trim_str, size_t min); + +static int +strchrcount(char * str, char c); + +int +strpref(char * s1, char * s2); + +static char * +xstrtok_r(char * str, const char * delim, char ** end); + +/* ************* */ +/* cop interface */ +/* ************* */ + +typedef struct opt_s opt_t; +typedef struct par_s par_t; +typedef struct ctx_s ctx_t; +typedef struct constraint_s constraint_t; +typedef struct ctx_inst_s ctx_inst_t; +typedef struct opt_inst_s opt_inst_t; +typedef struct seen_opt_s seen_opt_t; + +static char * +strtoken(char * s, char * token, size_t tok_len, char * pattern, int * pos); + +static int +ctx_compare(const void * c1, const void * c2); + +static int +opt_compare(const void * o1, const void * o2); + +static int +par_compare(const void * a1, const void * a2); + +static int +seen_opt_compare(const void * so1, const void * so2); + +static int +str_compare(const void * s1, const void * s2); + +static ctx_t * +locate_ctx(char * name); + +static opt_t * +locate_opt(char * name); + +static par_t * +locate_par(char * name, ctx_t * ctx); + +static void +bst_seen_opt_cb(const void * node, walk_order_e kind, int level); + +static void +bst_seen_opt_seen_cb(const void * node, walk_order_e kind, int level); + +static void +bst_print_ctx_cb(const void * node, walk_order_e kind, int level); + +static void +match_prefix_cb(const void * node, walk_order_e kind, int level); + +static int +has_unseen_mandatory_opt(ctx_inst_t * ctx_inst); + +static int +opt_parse(char * s, opt_t ** opt); + +static int +init_opts(char * spec, ctx_t * ctx); + +static int +ctxopt_build_cmdline_list(int nb_words, char ** words); + +static int +opt_set_parms(char * opt_name, char * par_str); + +static ctx_inst_t * +new_ctx_inst(ctx_t * ctx, ctx_inst_t * prev_ctx_inst); + +static void +evaluate_ctx_inst(ctx_inst_t * ctx_inst); + +/* ====================== */ +/* Message implementation */ +/* ====================== */ +static void +fatal(errors e, char * opt_par, char * opt_params, char * opt_name, + char * ctx_par, char * ctx_name, const char * format, ...) +{ + va_list args; + + ctxopterrno = e; + + if (e == CTXOPTINTERNAL) + fprintf(stderr, "ctxopt internal: "); + + if (err_functions[e] != NULL) + err_functions[e](opt_par, opt_params, opt_name, ctx_par, ctx_name); + else + { + va_start(args, format); + vfprintf(stderr, format, args); + fprintf(stderr, "\n"); + } + + if (opt_par != NULL) + ctxopt_disp_usage(continue_after); + + va_end(args); + + exit(EXIT_FAILURE); +} + +static void +error(const char * format, ...) +{ + va_list args; + + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + fprintf(stderr, "\n"); + ctxopt_disp_usage(exit_after); +} + +/* ******************************** */ +/* Memory management implementation */ +/* ******************************** */ + +/* ================= */ +/* Customized malloc */ +/* ================= */ +static void * +xmalloc(size_t size) +{ + void * allocated; + size_t real_size; + + real_size = (size > 0) ? size : 1; + allocated = malloc(real_size); + if (allocated == NULL) + fatal(CTXOPTINTERNAL, NULL, NULL, NULL, NULL, NULL, + "Insufficient memory (attempt to malloc %lu bytes)\n", + (unsigned long int)size); + + return allocated; +} + +/* ================= */ +/* Customized calloc */ +/* ================= */ +static void * +xcalloc(size_t n, size_t size) +{ + void * allocated; + + n = (n > 0) ? n : 1; + size = (size > 0) ? size : 1; + allocated = calloc(n, size); + if (allocated == NULL) + fatal(CTXOPTINTERNAL, NULL, NULL, NULL, NULL, NULL, + "Insufficient memory (attempt to calloc %lu bytes)\n", + (unsigned long int)size); + + return allocated; +} + +/* ================== */ +/* Customized realloc */ +/* ================== */ +static void * +xrealloc(void * p, size_t size) +{ + void * allocated; + + allocated = realloc(p, size); + if (allocated == NULL && size > 0) + fatal(CTXOPTINTERNAL, NULL, NULL, NULL, NULL, NULL, + "Insufficient memory (attempt to xrealloc %lu bytes)\n", + (unsigned long int)size); + + return allocated; +} + +/* =================================== */ +/* strdup implementation using xmalloc */ +/* =================================== */ +static char * +xstrdup(const char * p) +{ + char * allocated; + + allocated = xmalloc(strlen(p) + 1); + strcpy(allocated, p); + + return allocated; +} + +/* ================================================== */ +/* strndup implementation using xmalloc */ +/* This version guarantees that there is a final '\0' */ +/* ================================================== */ +static char * +xstrndup(const char * str, size_t len) +{ + char * p; + + p = memchr(str, '\0', len); + + if (p) + len = p - str; + + p = xmalloc(len + 1); + memcpy(p, str, len); + p[len] = '\0'; + + return p; +} + +/* ************************** */ +/* Linked list implementation */ +/* ************************** */ + +/* Linked list node structure */ +/* """""""""""""""""""""""""" */ +struct ll_node_s +{ + void * data; + struct ll_node_s * next; + struct ll_node_s * prev; +}; + +/* Linked List structure */ +/* """"""""""""""""""""" */ +struct ll_s +{ + ll_node_t * head; + ll_node_t * tail; + long len; +}; + +/* ======================== */ +/* Create a new linked list */ +/* ======================== */ +static ll_t * +ll_new(void) +{ + ll_t * ret = xmalloc(sizeof(ll_t)); + ll_init(ret); + + return ret; +} + +/* ======================== */ +/* Initialize a linked list */ +/* ======================== */ +static void +ll_init(ll_t * list) +{ + list->head = NULL; + list->tail = NULL; + list->len = 0; +} + +/* ==================================================== */ +/* Allocate the space for a new node in the linked list */ +/* ==================================================== */ +static ll_node_t * +ll_new_node(void) +{ + ll_node_t * ret = xmalloc(sizeof(ll_node_t)); + + return ret; +} + +/* ==================================================================== */ +/* Append a new node filled with its data at the end of the linked list */ +/* The user is responsible for the memory management of the data */ +/* ==================================================================== */ +static void +ll_append(ll_t * const list, void * const data) +{ + ll_node_t * node; + + node = ll_new_node(); /* ll_new_node cannot return NULL because it * + | uses xmalloc which does not return if there * + | is an allocation error. */ + + node->data = data; + node->next = NULL; + + node->prev = list->tail; + if (list->tail) + list->tail->next = node; + else + list->head = node; + + list->tail = node; + + ++list->len; +} + +/* =================================================================== */ +/* Put a new node filled with its data at the beginning of the linked */ +/* list. The user is responsible for the memory management of the data */ +/* =================================================================== */ +static void +ll_prepend(ll_t * const list, void * const data) +{ + ll_node_t * node; + + node = ll_new_node(); /* ll_new_node cannot return NULL because it * + | uses xmalloc which does not return if there * + | is an allocation error. */ + + node->data = data; + node->prev = NULL; + + node->next = list->head; + if (list->head) + list->head->prev = node; + else + list->tail = node; + + list->head = node; + + ++list->len; +} + +/* ======================================================= */ +/* Insert a new node before the specified node in the list */ +/* ======================================================= */ +static void +ll_insert_before(ll_t * const list, ll_node_t * node, void * const data) +{ + ll_node_t * new_node; + + if (node->prev == NULL) + ll_prepend(list, data); + else + { + new_node = ll_new_node(); /* ll_new_node cannot return NULL because it * + | uses xmalloc which does not return if there * + | is an allocation error. */ + + new_node->data = data; + new_node->next = node; + new_node->prev = node->prev; + node->prev->next = new_node; + node->prev = new_node; + + ++list->len; + } +} + +/* ====================================================== */ +/* Insert a new node after the specified node in the list */ +/* ====================================================== */ +void +ll_insert_after(ll_t * const list, ll_node_t * node, void * const data) +{ + ll_node_t * new_node; + + if (node->next == NULL) + ll_append(list, data); + else + { + new_node = ll_new_node(); /* ll_new_node cannot return NULL because it * + | uses xmalloc which does not return if there * + | is an allocation error. */ + + new_node->data = data; + new_node->prev = node; + new_node->next = node->next; + node->next->prev = new_node; + node->next = new_node; + + ++list->len; + } +} + +/* ================================================================ */ +/* Remove a node from a linked list */ +/* The memory taken by the deleted node must be freed by the caller */ +/* ================================================================ */ +static int +ll_delete(ll_t * const list, ll_node_t * node) +{ + if (list->head == list->tail) + { + if (list->head != NULL) + list->head = list->tail = NULL; + else + return 0; + } + else if (node->prev == NULL) + { + list->head = node->next; + list->head->prev = NULL; + } + else if (node->next == NULL) + { + list->tail = node->prev; + list->tail->next = NULL; + } + else + { + node->next->prev = node->prev; + node->prev->next = node->next; + } + + --list->len; + + return 1; +} + +/* =========================================================================*/ +/* Find a node in the list containing data. Return the node pointer or NULL */ +/* if not found. */ +/* A comparison function must be provided to compare a and b (strcmp like). */ +/* =========================================================================*/ +static ll_node_t * +ll_find(ll_t * const list, void * const data, + int (*cmpfunc)(const void * a, const void * b)) +{ + ll_node_t * node; + + if (NULL == (node = list->head)) + return NULL; + + do + { + if (0 == cmpfunc(node->data, data)) + return node; + } while (NULL != (node = node->next)); + + return NULL; +} + +/* ==================================================================== */ +/* Allocates and fills an array of strings from a list */ +/* WARNINGS: */ +/* 1) The list node must contain strings (char *) */ +/* 2) The strings in the resulting array MUST NOT be freed as the are */ +/* NOT copied from the strings of the list. */ +/* */ +/* IN list : The list from which the array is generated */ +/* IN start_node : The node of the list which will be the first node to */ +/* consider to create the array */ +/* OUT: count : The number of elements of the resulting array. */ +/* OUT: array : The resulting array. */ +/* RC : : The number of elements of the resulting array. */ +/* ==================================================================== */ +static int +ll_strarray(ll_t * list, ll_node_t * start_node, int * count, char *** array) +{ + int n = 0; + ll_node_t * node; + + *count = 0; + + node = start_node; + + if (list == NULL || node == NULL) + return 0; + + *array = xmalloc((list->len + 1) * sizeof(char **)); + while (node != NULL) + { + (*array)[n++] = (char *)(node->data); + (*count)++; + + node = node->next; + } + + (*array)[*count] = NULL; + *array = xrealloc(*array, ((*count) + 1) * sizeof(char **)); + + return *count; +} + +/* ******************************************************************* */ +/* BST (search.h compatible) implementation */ +/* */ +/* Tree search generalized from Knuth (6.2.2) Algorithm T just like */ +/* the AT&T man page says. */ +/* */ +/* Written by reading the System V Interface Definition, not the code. */ +/* */ +/* Totally public domain. */ +/* ******************************************************************* */ + +struct bst_s +{ + void * key; + struct bst_s * llink; + struct bst_s * rlink; +}; + +/* ========================== */ +/* delete node with given key */ +/* ========================== */ +static void * +bst_delete(const void * vkey, void ** vrootp, + int (*compar)(const void *, const void *)) +{ + bst_t ** rootp = (bst_t **)vrootp; + bst_t * p, *q, *r; + int cmp; + + if (rootp == NULL || (p = *rootp) == NULL) + return NULL; + + while ((cmp = (*compar)(vkey, (*rootp)->key)) != 0) + { + p = *rootp; + rootp = (cmp < 0) ? &(*rootp)->llink /* follow llink branch */ + : &(*rootp)->rlink; /* follow rlink branch */ + if (*rootp == NULL) + return NULL; /* key not found */ + } + r = (*rootp)->rlink; /* D1: */ + if ((q = (*rootp)->llink) == NULL) /* Left NULL? */ + q = r; + else if (r != NULL) + { /* Right link is NULL? */ + if (r->llink == NULL) + { /* D2: Find successor */ + r->llink = q; + q = r; + } + else + { /* D3: Find NULL link */ + for (q = r->llink; q->llink != NULL; q = r->llink) + r = q; + r->llink = q->rlink; + q->llink = (*rootp)->llink; + q->rlink = (*rootp)->rlink; + } + } + if (p != *rootp) + free(*rootp); /* D4: Free node */ + *rootp = q; /* link parent to new node */ + return p; +} + +/* ============== */ +/* destroy a tree */ +/* ============== */ +static void +bst_destroy_recurse(bst_t * root, void (*free_action)(void *)) +{ + if (root->llink != NULL) + bst_destroy_recurse(root->llink, free_action); + if (root->rlink != NULL) + bst_destroy_recurse(root->rlink, free_action); + + (*free_action)((void *)root->key); + free(root); +} + +static void +bst_destroy(void * vrootp, void (*freefct)(void *)) +{ + bst_t * root = (bst_t *)vrootp; + + if (root != NULL) + bst_destroy_recurse(root, freefct); +} + +/* ======================== */ +/* find a node, or return 0 */ +/* ======================== */ +static void * +bst_find(const void * vkey, void * const * vrootp, + int (*compar)(const void *, const void *)) +{ + bst_t * const * rootp = (bst_t * const *)vrootp; + + if (rootp == NULL) + return NULL; + + while (*rootp != NULL) + { /* T1: */ + int r; + + if ((r = (*compar)(vkey, (*rootp)->key)) == 0) /* T2: */ + return *rootp; /* key found */ + rootp = (r < 0) ? &(*rootp)->llink /* T3: follow left branch */ + : &(*rootp)->rlink; /* T4: follow right branch */ + } + return NULL; +} + +/* ===================================== */ +/* find or insert datum into search tree */ +/* ===================================== */ +static void * +bst_search(const void * vkey, void ** vrootp, + int (*compar)(const void *, const void *)) +{ + bst_t * q; + bst_t ** rootp = (bst_t **)vrootp; + + if (rootp == NULL) + return NULL; + + while (*rootp != NULL) + { /* Knuth's T1: */ + int r; + + if ((r = (*compar)(vkey, (*rootp)->key)) == 0) /* T2: */ + return *rootp; /* we found it! */ + + rootp = (r < 0) ? &(*rootp)->llink /* T3: follow left branch */ + : &(*rootp)->rlink; /* T4: follow right branch */ + } + + q = xmalloc(sizeof(bst_t)); /* T5: key not found */ + if (q != 0) + { /* make new node */ + *rootp = q; /* link new node to old */ + q->key = (void *)vkey; /* initialize new node */ + q->llink = q->rlink = NULL; + } + return q; +} + +/* ======================== */ +/* Walk the nodes of a tree */ +/* ======================== */ +static void +bst_walk_recurse(const bst_t * root, + void (*action)(const void *, walk_order_e, int), int level) +{ + if (root->llink == NULL && root->rlink == NULL) + (*action)(root, leaf, level); + else + { + (*action)(root, preorder, level); + if (root->llink != NULL) + bst_walk_recurse(root->llink, action, level + 1); + (*action)(root, postorder, level); + if (root->rlink != NULL) + bst_walk_recurse(root->rlink, action, level + 1); + (*action)(root, endorder, level); + } +} + +static void +bst_walk(const void * vroot, void (*action)(const void *, walk_order_e, int)) +{ + if (vroot != NULL && action != NULL) + bst_walk_recurse(vroot, action, 0); +} + +/* *********************** */ +/* various implementations */ +/* *********************** */ + +/* ======================= */ +/* Trim leading characters */ +/* ======================= */ +static void +ltrim(char * str, const char * trim_str) +{ + size_t len = strlen(str); + size_t begin = strspn(str, trim_str); + size_t i; + + if (begin > 0) + for (i = begin; i <= len; ++i) + str[i - begin] = str[i]; +} + +/* ================================================= */ +/* Trim trailing characters */ +/* The resulting string will have at least min bytes */ +/* even if trailing spaces remain. */ +/* ================================================= */ +static void +rtrim(char * str, const char * trim_str, size_t min) +{ + size_t len = strlen(str); + while (len > min && strchr(trim_str, str[len - 1])) + str[--len] = '\0'; +} + +/* ================================================== */ +/* Count the number of occurrences of the character c */ +/* in the string str. */ +/* The str pointer is assumed to be not NULL */ +/* ================================================== */ +static int +strchrcount(char * str, char c) +{ + int count = 0; + + while (*str) + if (*str++ == c) + count++; + + return count; +} + +/* =============================================== */ +/* Is the string str2 a prefix of the string str1? */ +/* =============================================== */ +int +strpref(char * str1, char * str2) +{ + while (*str1 != '\0' && *str1 == *str2) + { + str1++; + str2++; + } + + return *str2 == '\0'; +} + +/*===================================================================== */ +/* Allocate memory and safely concatenate strings. Stolen from a public */ +/* domain implementation which can be found here: */ +/* http://openwall.info/wiki/people/solar/software/\ */ +/* public-domain-source-code */ +/* ==================================================================== */ +static char * +concat(const char * s1, ...) +{ + va_list args; + const char * s; + char * p, *result; + size_t l, m, n; + + m = n = strlen(s1); + va_start(args, s1); + while ((s = va_arg(args, char *))) + { + l = strlen(s); + if ((m += l) < l) + break; + } + va_end(args); + if (s || m >= INT_MAX) + return NULL; + + result = (char *)xmalloc(m + 1); + + memcpy(p = result, s1, n); + p += n; + va_start(args, s1); + while ((s = va_arg(args, char *))) + { + l = strlen(s); + if ((n += l) < l || n > m) + break; + memcpy(p, s, l); + p += l; + } + va_end(args); + if (s || m != n || p != result + n) + { + free(result); + return NULL; + } + + *p = 0; + return result; +} + +/* ====================================================================== */ +/* public domain strtok_r() by Charlie Gordon */ +/* from comp.lang.c 9/14/2007 */ +/* http://groups.google.com/group/comp.lang.c/msg/2ab1ecbb86646684 */ +/* */ +/* (Declaration that it's public domain): */ +/* http://groups.google.com/group/comp.lang.c/msg/7c7b39328fefab9c */ +/* */ +/* Also, fixed by Fletcher T. Penney --- added the "return NULL" when */ +/* *end == NULL */ +/* ====================================================================== */ +static char * +xstrtok_r(char * str, const char * delim, char ** end) +{ + char * ret; + + if (str == NULL) + str = *end; + + if (str == NULL) + return NULL; + + str += strspn(str, delim); + + if (*str == '\0') + return NULL; + + ret = str; + + str += strcspn(str, delim); + + if (*str) + *str++ = '\0'; + + *end = str; + + return ret; +} + +/* =========================================================== */ +/* Fills an array of strings from the words composing a string */ +/* */ +/* str: initial string which will be altered */ +/* args: array of pointers to the start of the words in str */ +/* max: maximum number of words used before giving up */ +/* return: the number of words (<=max) */ +/* =========================================================== */ +static int +str2argv(char * str, char ** args, int max) +{ + int nb_args = 0; + + while (*str) + { + if (nb_args >= max) + return nb_args; + + while (*str == ' ' || *str == '\t') + *(str++) = '\0'; + + if (!*str) + return nb_args; + + args[nb_args] = str; + nb_args++; + + while (*str && (*str != ' ') && (*str != '\t')) + str++; + } |