/* * Copyright (c) 2001 Mark Fullmer and The Ohio State University * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: ftvar.c,v 1.5 2004/01/05 17:56:19 maf Exp $ */ #include "ftconfig.h" #include "ftlib.h" #include #include #include #if HAVE_STRINGS_H #include #endif #if HAVE_STRING_H #include #endif /* * function: ftvar_new() * * Create a new variable set. * * Caller must call ftvar_free() to release memory. * * returns: initialized struct * */ int ftvar_new(struct ftvar *ftvar) { bzero(ftvar, sizeof *ftvar); FT_SLIST_INIT(&ftvar->entries); return 0; } /* ftvar_new */ /* * function: ftvar_free() * * Free storage created by ftvar_new() and calls to ftvar_set() * */ void ftvar_free(struct ftvar *ftvar) { struct ftvar_entry *ftve; while (!FT_SLIST_EMPTY(&ftvar->entries)) { ftve = FT_SLIST_FIRST(&ftvar->entries); if (ftve->name) free(ftve->name); if (ftve->val); free(ftve->val); FT_SLIST_REMOVE_HEAD(&ftvar->entries, chain); free(ftve); } } /* ftvar_free */ /* * function: ftvar_pset() * * Parse variable binding in form VAR=VALUE and perform a set * * returns <0 error * >=0 ok */ int ftvar_pset(struct ftvar *ftvar, char *binding) { char *sm, *n, *v; int ret; ret = -1; /* preserve source string */ if (!(sm = (char*)malloc(strlen(binding)+1))) { fterr_warnx("malloc(%d)", strlen(binding)+1); return -1; } strcpy(sm, binding); for (v = sm; *v && (*v != '='); ++v); /* end of string reached? */ if (!*v) goto out; /* terminate name */ *v = 0; /* value is rest */ v++; /* name is start */ n = sm; ret = ftvar_set(ftvar, n, v); out: free(sm); return ret; } /* ftvar_pset */ /* * function: ftvar_set() * * Add or update variable name with value. * * returns <0 error * >=0 ok */ int ftvar_set(struct ftvar *ftvar, char *name, char *val) { struct ftvar_entry *ftve; int new; new = 0; if ((!*name) || (!name[0])) return -1; if ((!*val) || (!val[0])) return -1; /* if the entry exists then this is an update */ if ((ftve = ftvar_find(ftvar, name))) { free(ftve->val); ftve->val = (char*)0L; } else { new = 1; if (!(ftve = (struct ftvar_entry*)malloc(sizeof *ftve))) { fterr_warnx("malloc(ftve)"); return -1; } bzero(ftve, sizeof *ftve); if (!(ftve->name = (char*)malloc(strlen(name)+1))) { fterr_warnx("malloc(ftve->name)"); free(ftve); return -1; } strcpy(ftve->name, name); } /* always allocate the new value */ if (!(ftve->val = (char*)malloc(strlen(val)+1))) { fterr_warnx("malloc(ftve->val)"); free(ftve->name); free(ftve); return -1; } strcpy(ftve->val, val); if (new) FT_SLIST_INSERT_HEAD(&ftvar->entries, ftve, chain); return 0; } /* ftvar_set */ /* * function: ftvar_find() * * Find an entry by name * * returns entry or 0L if not found */ struct ftvar_entry *ftvar_find(struct ftvar *ftvar, char *name) { struct ftvar_entry *ftve; FT_SLIST_FOREACH(ftve, &ftvar->entries, chain) { if (!strcmp(ftve->name, name)) return ftve; } return (struct ftvar_entry*)0L; } /* ftvar_find */ /* * function: ftvar_clear() * * Clear/Remove a variable. Variables which are cleared that do not * exist will not produce an error. * */ void ftvar_clear(struct ftvar *ftvar, char *name) { struct ftvar_entry *ftve; if ((ftve = ftvar_find(ftvar, name))) { if (ftve->name) free(ftve->name); if (ftve->val) free(ftve->val); FT_SLIST_REMOVE(&ftvar->entries, ftve, ftvar_entry, chain); free(ftve); } } /* ftvar_clear */ /* * function: ftvar_evalstr() * * Perform variable substitution on string. Variables start with * @ and end with a non alphanumeric character, ie @TEST. * * If the variable set contains TEST=foo then the evaluated string * "This is a @TEST." will result in "This is a foo." * */ int ftvar_evalstr(struct ftvar *ftvar, char *src, char *dst, int dstlen) { struct ftvar_entry *ftve; char *s, *d, *v, *ve, *sm, *tmp; int len, ret, inbrace, x; char *vexp, *varname, *def; d = dst; len = 0; ret = -1; inbrace = 0; vexp = (char*)0L; sm = (char*)0L; def = (char*)0L; /* preserve source string */ if (!(sm = (char*)malloc(strlen(src)+1))) { fterr_warnx("ftvar: malloc(%d)", strlen(src)+1); return -1; } s = sm; strcpy(s, src); while (1) { /* end of source string? */ if (!*s) { ret = 0; goto done; } /* end of dst buf? */ if ((len+1) == dstlen) goto done; /* start of var? */ if (*s == '@') { /* variable starts after the @ */ v = ++s; /* end of variable initialized to start */ ve = v; /* is variable in a brace? yes then must end on a brace */ if (*v && (*v == '{')) { inbrace = 1; while (*ve && (*ve != '}')) ++ve; if (*ve != '}') fterr_errx(1, "ftvar: %s: Missing }", v); ++ve; /* else it ends on the first non alphanumeric char */ } else { while (*ve && isalnum(*ve)) ++ve; } /* copy out the variable expression, braces and all */ x = ve - v; if (!(vexp = (char*)malloc(x+1))) fterr_errx(1, "ftvar: malloc(%d)", x+1); strncpy(vexp, v, x); vexp[x] = 0; /* * either have form !inbrace VARIABLE * or * inbrace VARIABLE:-foo * * if it's the former can just proceed with substitution. * * The latter requires checking for the :, processing the * command (now only -) if the variable substitution fails * * there's also a special case of @{VAR} */ varname = vexp; if (inbrace) { /* skip over opening brace */ ++varname; /* find the first non alphanumeric */ tmp = varname; while (*tmp && isalnum(*tmp)) ++tmp; /* if its an end brace then done with special processing */ if (*tmp == '}') { *tmp = 0; goto skipspecial; } /* better be a : */ if (*tmp != ':') fterr_errx(1, "ftvar: %s: missing :", vexp); /* : to null so the variable name is null terminated */ *tmp++ = 0; /* only special type is - (default) */ if (*tmp != '-') fterr_errx(1, "ftvar: %s: expecting -", vexp); /* default expression is after the - */ def = ++tmp; /* drop the trailing } */ while (*tmp && *tmp != '}') ++tmp; *tmp = 0; } skipspecial: /* lookup var */ if ((ftve = ftvar_find(ftvar, varname))) tmp = ftve->val; /* found it, copy in the value */ else tmp = (def) ? def : (char*)0L; /* got a substitution */ if (tmp) while (*tmp && ((len+1) != dstlen)) d[len++] = *tmp++; /* end of dst buf? */ if ((len+1) == dstlen) goto done; /* skip variable name in original string */ s = ve; /* reset */ inbrace = 0; def = (char*)0L; if (vexp) free(vexp); vexp = (char*)0L; } else { d[len++] = *s++; /* no, copy */ /* end of dst buf? */ if ((len+1) == dstlen) goto done; } } ret = 0; done: dst[len] = 0; if (sm) free(sm); if (vexp) free(vexp); return ret; } /* ftvar_evalstr */