aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile.am5
-rw-r--r--lib/envop.c484
-rw-r--r--lib/envop.h68
-rw-r--r--lib/wildmatch.c140
4 files changed, 696 insertions, 1 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 4aa9d3e..7b4eb42 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -21,6 +21,8 @@ noinst_HEADERS = libpies.h grecsasrt.h
libpies_a_SOURCES=\
addrfmt.c\
arraymember.c\
+ envop.c\
+ envop.h\
grecsasrt.c\
mkfilename.c\
netrc.c\
@@ -30,7 +32,8 @@ libpies_a_SOURCES=\
safe_strcmp.c\
split3.c\
strtotok.c\
- url.c
+ url.c\
+ wildmatch.c
libpies_a_LIBADD=\
$(LIBOBJS)\
diff --git a/lib/envop.c b/lib/envop.c
new file mode 100644
index 0000000..79083f7
--- /dev/null
+++ b/lib/envop.c
@@ -0,0 +1,484 @@
+/* Environment modification functions for GNU Pies.
+ Copyright (C) 2019 Sergey Poznyakoff
+
+ GNU Pies is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GNU Pies is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Pies. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include "envop.h"
+#include "wordsplit.h"
+
+environ_t *
+environ_create (char **def)
+{
+ size_t i;
+ environ_t *env = malloc (sizeof (*env));
+
+ if (!env)
+ return NULL;
+
+ if (!def)
+ {
+ static char *nullenv[] = { NULL };
+ def = nullenv;
+ }
+
+ for (i = 0; def[i]; i++)
+ ;
+ env->env_count = 0;
+ env->env_max = i + 1;
+ env->env_base = calloc (env->env_max, sizeof (env->env_base[0]));
+ if (!env->env_base)
+ {
+ free (env);
+ return NULL;
+ }
+
+ for (i = 0; def[i]; i++)
+ {
+ if (!(env->env_base[i] = strdup (def[i])))
+ {
+ environ_free (env);
+ return NULL;
+ }
+ env->env_count++;
+ }
+ env->env_base[i] = NULL;
+ return env;
+}
+
+void
+environ_free (environ_t *env)
+{
+ size_t i;
+ for (i = 0; i < env->env_count; i++)
+ free (env->env_base[i]);
+ free (env->env_base);
+}
+
+static ssize_t
+getenvind (environ_t *env, char const *name, char **pval)
+{
+ size_t i;
+
+ for (i = 0; i < env->env_count; i++)
+ {
+ char const *p;
+ char *q;
+
+ for (p = name, q = env->env_base[i]; *p == *q; p++, q++)
+ if (*p == '=')
+ break;
+ if ((*p == 0 || *p == '=') && *q == '=')
+ {
+ if (pval)
+ *pval = q + 1;
+ return i;
+ }
+ }
+ return -1;
+}
+
+static ssize_t
+environ_alloc (environ_t *env)
+{
+ size_t n;
+ if (env->env_count + 1 >= env->env_max)
+ {
+ char **p;
+ if (env->env_base == NULL)
+ {
+ n = 64;
+ p = calloc (n, sizeof (p[0]));
+ if (!p)
+ return -1;
+ }
+ else
+ {
+ n = env->env_max;
+ if ((size_t) -1 / 3 * 2 / sizeof (p[0]) <= n)
+ {
+ errno = ENOMEM;
+ return -1;
+ }
+ n += (n + 1) / 2;
+ p = realloc (env->env_base, n * sizeof (p[0]));
+ if (!p)
+ return -1;
+ }
+ env->env_base = p;
+ env->env_max = n;
+ }
+ n = env->env_count++;
+ env->env_base[env->env_count] = NULL;
+ return n;
+}
+
+static int
+environ_add_alloced (environ_t *env, char *def)
+{
+ ssize_t n;
+ n = getenvind (env, def, NULL);
+ if (n == -1)
+ {
+ n = environ_alloc (env);
+ if (n == -1)
+ return -1;
+ }
+ free (env->env_base[n]);
+ env->env_base[n] = def;
+ return 0;
+}
+
+int
+environ_add (environ_t *env, char const *def)
+{
+ char *defcp = strdup (def);
+ if (!defcp)
+ return -1;
+ if (environ_add_alloced (env, defcp))
+ {
+ free (defcp);
+ return -1;
+ }
+ return 0;
+}
+
+int
+environ_set (environ_t *env, char const *name, char const *value)
+{
+ size_t len;
+ char *def;
+ struct wordsplit ws;
+
+ if (!name)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ if (!value)
+ return environ_unset (env, name);
+
+ ws.ws_env = (char const **) env->env_base;
+ if (wordsplit (value, &ws,
+ WRDSF_NOSPLIT
+ | WRDSF_QUOTE
+ | WRDSF_NOCMD /* FIXME */
+ | WRDSF_SQUEEZE_DELIMS
+ | WRDSF_CESCAPES
+ | WRDSF_ENV
+ | WRDSF_PATHEXPAND))
+ {
+ int ec = errno;
+ if (ws.ws_errno != WRDSE_USAGE) /* FIXME */
+ wordsplit_free (&ws);
+ errno = ec;
+ return -1;
+ }
+
+ if (strcmp (name, ":") == 0)
+ {
+ wordsplit_free (&ws);
+ return 0;
+ }
+
+ len = strlen (name) + strlen (ws.ws_wordv[0]) + 2;
+ def = malloc (len);
+ if (!def)
+ {
+ int ec = errno;
+ wordsplit_free (&ws);
+ errno = ec;
+ return -1;
+ }
+ strcat (strcat (strcpy (def, name), "="), ws.ws_wordv[0]);
+ wordsplit_free (&ws);
+ if (environ_add_alloced (env, def))
+ {
+ free (def);
+ return -1;
+ }
+ return 0;
+}
+
+int
+environ_unset (environ_t *env, char const *name)
+{
+ ssize_t n;
+
+ if (!env || !name)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ n = getenvind (env, name, NULL);
+ if (n == -1)
+ return ENOENT;
+
+ free (env->env_base[n]);
+ memmove (env->env_base + n, env->env_base + n + 1,
+ (env->env_count - n) * sizeof (env->env_base[0]));
+ env->env_count--;
+ return 0;
+}
+
+int
+environ_unset_glob (environ_t *env, const char *pattern)
+{
+ size_t i;
+
+ if (!env || !pattern)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ for (i = 0; i < env->env_count; )
+ {
+ size_t len = strcspn (env->env_base[i], "=");
+ if (wildmatch (pattern, env->env_base[i], len) == 0)
+ {
+ free (env->env_base[i]);
+ memmove (env->env_base + i, env->env_base + i + 1,
+ (env->env_count - i) * sizeof (env->env_base[0]));
+ env->env_count--;
+ }
+ else
+ i++;
+ }
+ return 0;
+}
+
+static void
+envop_entry_insert (struct envop_entry **phead, struct envop_entry *op)
+{
+ struct envop_entry *head = *phead;
+
+ if (!head)
+ {
+ *phead = op;
+ return;
+ }
+
+ switch (op->code)
+ {
+ case envop_clear:
+ if (head->code == envop_clear)
+ free (op);
+ else
+ {
+ op->next = head;
+ *phead = op;
+ }
+ break;
+
+ case envop_keep:
+ {
+ struct envop_entry *prev = NULL;
+ while (head && head->code <= op->code)
+ {
+ prev = head;
+ head = prev->next;
+ }
+ op->next = head;
+ if (prev)
+ prev->next = op;
+ else
+ *phead = op;
+ }
+ break;
+
+ default:
+ while (head && head->next)
+ head = head->next;
+
+ head->next = op;
+ }
+}
+
+static int
+valid_envar_name (char const *name)
+{
+ if (!name)
+ return 0;
+ if (!(isalpha (*name) || *name == '_'))
+ return 0;
+ while (*++name)
+ {
+ if (!(isalnum (*name) || *name == '_'))
+ return 0;
+ }
+ return 1;
+}
+
+int
+envop_entry_add (struct envop_entry **head,
+ enum envop_code code, char const *name, char const *value)
+{
+ struct envop_entry *op;
+ size_t s;
+
+ switch (code)
+ {
+ case envop_clear:
+ break;
+
+ case envop_set:
+ if (!name || !(*name == ':' || valid_envar_name (name)))
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ break;
+
+ case envop_keep:
+ case envop_unset:
+ break;
+
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ s = sizeof (op[0]);
+ if (name)
+ {
+ s += strlen (name) + 1;
+ if (value)
+ s += strlen (value) + 1;
+ }
+ op = malloc (s);
+ if (!op)
+ return -1;
+ op->next = NULL;
+ op->code = code;
+ op->name = NULL;
+ op->value = NULL;
+ if (name)
+ {
+ op->name = (char*)(op + 1);
+ strcpy (op->name, name);
+ if (value)
+ {
+ op->value = op->name + strlen (name) + 1;
+ strcpy (op->value, value);
+ }
+ }
+ envop_entry_insert (head, op);
+ return 0;
+}
+
+static int
+envopmatch (struct envop_entry *op, char const *var, int len)
+{
+ if (op->value)
+ {
+ if (strncmp (op->name, var, len) == 0)
+ return strcmp (var + len + 1, op->value);
+ }
+ return wildmatch (op->name, var, len);
+}
+
+static int
+keep_env (char const *var, struct envop_entry *keep)
+{
+ int len = strcspn (var, "=");
+ for (; keep && keep->code == envop_keep; keep = keep->next)
+ {
+ if (envopmatch (keep, var, len) == 0)
+ return 1;
+ }
+ return 0;
+}
+
+int
+envop_exec (struct envop_entry *op, environ_t *env)
+{
+ size_t i;
+
+ if (op && op->code == envop_clear)
+ {
+ op = op->next;
+ if (op && op->code == envop_keep)
+ {
+ size_t keep_count = 0;
+ for (i = 0; i < env->env_count; i++)
+ {
+ if (keep_env (env->env_base[i], op))
+ {
+ if (i > keep_count)
+ {
+ env->env_base[keep_count] = env->env_base[i];
+ env->env_base[i] = NULL;
+ }
+ keep_count++;
+ }
+ else
+ {
+ free (env->env_base[i]);
+ env->env_base[i] = NULL;
+ }
+ }
+ env->env_count = keep_count;
+ }
+ else
+ {
+ size_t i;
+ for (i = 0; i < env->env_count; i++)
+ free (env->env_base[i]);
+ env->env_base[0] = 0;
+ env->env_count = 0;
+ }
+ }
+
+ /* Process eventual set and unset statements */
+ for (; op; op = op->next)
+ {
+ switch (op->code)
+ {
+ case envop_set:
+ if (environ_set (env, op->name, op->value))
+ return -1;
+ break;
+
+ case envop_unset:
+ environ_unset_glob (env, op->name);
+ break;
+
+ case envop_keep:
+ break;
+
+ default:
+ abort ();
+ }
+ }
+
+ return 0;
+}
+
+void
+envop_free (struct envop_entry *op)
+{
+ while (op)
+ {
+ struct envop_entry *next = op->next;
+ free (op);
+ op = next;
+ }
+}
+
diff --git a/lib/envop.h b/lib/envop.h
new file mode 100644
index 0000000..054152e
--- /dev/null
+++ b/lib/envop.h
@@ -0,0 +1,68 @@
+/* Environment and environment operation definitions for GNU Pies.
+ Copyright (C) 2019 Sergey Poznyakoff
+
+ GNU Pies is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GNU Pies is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Pies. If not, see <http://www.gnu.org/licenses/>. */
+
+/* Environment structure */
+struct environ
+{
+ char **env_base;
+ size_t env_count;
+ size_t env_max;
+};
+typedef struct environ environ_t;
+
+environ_t *environ_create (char **);
+void environ_free (environ_t *env);
+int environ_add (environ_t *env, char const *def);
+int environ_set (environ_t *env, char const *name, char const *val);
+int environ_unset (environ_t *env, char const *name);
+int environ_unset_glob (environ_t *env, const char *pattern);
+static inline char **
+environ_ptr (environ_t *env)
+{
+ return env->env_base;
+}
+
+/* Environment operation codes.
+ Elements in a oplist are sorted in that order. */
+enum envop_code
+ {
+ envop_clear, /* Clear environment */
+ envop_keep, /* Keep variable when clearing */
+ envop_set, /* Set variable */
+ envop_unset /* Unset variable */
+ };
+
+struct envop_entry /* Environment operation entry */
+{
+ struct envop_entry *next; /* Next entry in the list */
+ enum envop_code code; /* Operation code */
+ char *name; /* Variable name (or globbing pattern) */
+ char *value; /* Value of the variable */
+};
+
+typedef struct envop_entry envop_t;
+
+int wildmatch (char const *expr, char const *name, size_t len);
+
+int envop_entry_add (envop_t **head,
+ enum envop_code code,
+ char const *name, char const *value);
+
+int envop_exec (envop_t *op, environ_t *env);
+void envop_free (envop_t *op);
+
+
+
diff --git a/lib/wildmatch.c b/lib/wildmatch.c
new file mode 100644
index 0000000..2b4d7c8
--- /dev/null
+++ b/lib/wildmatch.c
@@ -0,0 +1,140 @@
+/* Environment-specific globbing pattern matching for GNU Pies.
+ Copyright (C) 2019 Sergey Poznyakoff
+
+ GNU Pies is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GNU Pies is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Pies. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdlib.h>
+
+enum
+ {
+ WILD_FALSE = 0,
+ WILD_TRUE,
+ WILD_ABORT
+ };
+
+static int
+match_char_class (char const **pexpr, char c)
+{
+ int res;
+ int rc;
+ char const *expr = *pexpr;
+
+ expr++;
+ if (*expr == '^')
+ {
+ res = 0;
+ expr++;
+ }
+ else
+ res = 1;
+
+ if (*expr == '-' || *expr == ']')
+ rc = c == *expr++;
+ else
+ rc = !res;
+
+ for (; *expr && *expr != ']'; expr++)
+ {
+ if (rc == res)
+ {
+ if (*expr == '\\' && expr[1] == ']')
+ expr++;
+ }
+ else if (expr[1] == '-')
+ {
+ if (*expr == '\\')
+ rc = *++expr == c;
+ else
+ {
+ rc = *expr <= c && c <= expr[2];
+ expr += 2;
+ }
+ }
+ else if (*expr == '\\' && expr[1] == ']')
+ rc = *++expr == c;
+ else
+ rc = *expr == c;
+ }
+ *pexpr = *expr ? expr + 1 : expr;
+ return rc == res;
+}
+
+#define END_OF_NAME(s,l) ((l) == 0 || *(s) == 0)
+#define NEXT_CHAR(s,l) (s++, l--)
+
+int
+wilder_match (char const *expr, char const *name, size_t len)
+{
+ int c;
+
+ while (expr && *expr)
+ {
+ if (END_OF_NAME (name, len) && *expr != '*')
+ return WILD_ABORT;
+ switch (*expr)
+ {
+ case '*':
+ while (*++expr == '*')
+ ;
+ if (*expr == 0)
+ return WILD_TRUE;
+ while (!END_OF_NAME (name, len))
+ {
+ int res = wilder_match (expr, name, len);
+ if (res != WILD_FALSE)
+ return res;
+ NEXT_CHAR (name, len);
+ }
+ return WILD_ABORT;
+
+ case '?':
+ expr++;
+ NEXT_CHAR (name, len);
+ break;
+
+ case '[':
+ if (!match_char_class (&expr, *name))
+ return WILD_FALSE;
+ NEXT_CHAR (name, len);
+ break;
+
+ case '\\':
+ if (expr[1])
+ {
+ c = *++expr; expr++;
+ if (*name != wordsplit_c_unquote_char (c))
+ return WILD_FALSE;
+ NEXT_CHAR (name, len);
+ break;
+ }
+ /* fall through */
+ default:
+ if (*expr != *name)
+ return WILD_FALSE;
+ expr++;
+ NEXT_CHAR (name, len);
+ }
+ }
+ return END_OF_NAME (name, len) ? WILD_TRUE : WILD_FALSE;
+}
+
+/* Return 0 if first LEN bytes of NAME match globbing pattern EXPR. */
+int
+wildmatch (char const *expr, char const *name, size_t len)
+{
+ return wilder_match (expr, name, len) != WILD_TRUE;
+}

Return to:

Send suggestions and report system problems to the System administrator.