diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2009-09-29 21:22:05 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2009-09-29 21:22:05 +0300 |
commit | c978fe2baba8344e3392de7f680cf491bd4b0b7e (patch) | |
tree | 956b60427d513142f1da41f79b4b5172ed56472b | |
parent | 1774f4233e097ed7f0e745fc41aed2ebb72d7591 (diff) | |
download | mailfromd-c978fe2baba8344e3392de7f680cf491bd4b0b7e.tar.gz mailfromd-c978fe2baba8344e3392de7f680cf491bd4b0b7e.tar.bz2 |
Implement module system.
* lib/transform.c: New file.
* lib/Makefile.am (libmf_a_SOURCES): Add transform.c
* lib/libmf.h (struct syment): Remove field `data',
add `refcnt'.
(syment_free_hook): New extern.
(symtab_lookup_or_install_entry): New function.
(symtab_free): Rename to symtab_destroy.
(symtab_selfun, symtab_errfun, symtab_confun)
(symtab_cpyfun): New typedefs.
(symtab_import): New proto.
(transform_t): New typedef.
(transform_append_t, transform_reduce_t): New typedefs.
(transform_error_string, transform_compile)
(transform_free, transform_string): New functions.
* lib/symtab.c (syment_alloc): New function.
(symtab_free_syment): Honor reference count.
(symtab_rehash): Bugfix.
(symtab_get_index): New function.
(symtab_lookup_or_install): Rewrite using symtab_get_index.
(symtab_lookup_or_install_entry): New function.
(symtab_create): Allocate tab.
(symtab_free): Rename to symtab_destroy.
(symtab_import): New function.
* mfd/gram.y: Move inclusion of obstack.h to mailfromd.h
Reflect changes to symbol tables.
(%union): New member import_rule_list
(REQUIRE, IMPORT) New tokens.
(require): New rule.
(builtin_variable_install): Move to symbols.c
* mfd/lex.l: Move inclusion of obstack.h to mailfromd.h
(lex_buffer_pop): Do not increment line number after popping.
It is done before pushing the context.
(lex_new_source_0): Argument is const.
(lex_new_source): First argument is const.
(advance_line): External.
(#require): Deprecated.
(require,import): New keywords.
(from): New keyword (was in <ONBLOCK> before).
(yywrap): Pop module.
* mfd/mailfromd.h: Include obstack.h
(SYMENT_STRUCT): New define.
(struct mf_symbol): New structure.
(struct builtin, struct literal, struct pragma): Use SYMENT_STRUCT.
(struct function, struct variable)
(struct constant): Inherit from mf_symbol.
(stab_module): New extern.
(stab_function, stab_variable, stab_constant): Remove. These
are now per-module tables.
(symbol_resolve_alias): New proto.
(enum import_type, struct import_rule)
(struct import_rule_list): New data types.
(import_rule_create): New function.
(enum module_namespace, struct module)
(struct module_list): New data types.
(top_module): New extern.
(MODULE_SYMTAB, TOP_MODULE_SYMTAB): New defines.
(require_module): New function.
(assign_locus, lex_new_source): Change signature.
* mfd/pp.c: Move inclusion of obstack.h to mailfromd.h
(begin_module): New function.
(parse_include): Include_once is synonimous to #require.
Use begin_module to actually switch contents in this case.
(parse_require): Call begin_module.
(require_module): New function.
(assign_locus): Change signature.
* mfd/prog.c: Move inclusion of obstack.h to mailfromd.h
Reflect changes to symbol tables.
* mfd/symbols.c (alloc_entry): Set refcnt to 1
(free_entry): New function.
(import_rule_create, import_rules_free)
(import_rules_eval): New functions.
(stab_function, stab_variable, stab_constant): Remove. These
are now per-module tables.
(import_builtin_variables, module_import_symbols): New functions.
(top_module, module_stack): New globals.
(module_init, module_create)
(module_free, module_add_submodule)
(module_has_submodule, module_push)
(module_pop, set_top_module, pop_top_module): New functions.
(init_symbols): Set syment_free_hook. Initialize top_module.
(free_symbols, init_variable)
(function_lookup_or_install,function_install
(install_alias, literal_lookup, constant_lookup): Reflect changes to
symbol tables. Handle aliases.
(builtin_variable_install): New function (from gram.y)
* mfd/db.c, mfd/engine.c, mfd/main.c,
mfd/pragma.c, mfd/spf.c : Move inclusion of obstack.h to mailfromd.h
* mfd/drivers.c: Reflect changes to symbol tables.
* etc/mailfromd.mf, mflib/heloarg_test.mf,
* mflib/match_dnsbl.mf, mflib/match_rhsbl.mf,
* mflib/spf.mf, mflib/valid_domain.mf,
tests/etc/catch.rc, tests/etc/catch01.rc,
tests/etc/cidr.rc: Use `require', without #.
-rw-r--r-- | etc/mailfromd.mf | 6 | ||||
-rw-r--r-- | lib/Makefile.am | 1 | ||||
-rw-r--r-- | lib/libmf.h | 35 | ||||
-rw-r--r-- | lib/symtab.c | 179 | ||||
-rw-r--r-- | lib/transform.c | 666 | ||||
-rw-r--r-- | mfd/db.c | 3 | ||||
-rw-r--r-- | mfd/drivers.c | 12 | ||||
-rw-r--r-- | mfd/engine.c | 3 | ||||
-rw-r--r-- | mfd/gram.y | 142 | ||||
-rw-r--r-- | mfd/lex.l | 19 | ||||
-rw-r--r-- | mfd/mailfromd.h | 100 | ||||
-rw-r--r-- | mfd/main.c | 4 | ||||
-rw-r--r-- | mfd/pp.c | 65 | ||||
-rw-r--r-- | mfd/pragma.c | 4 | ||||
-rw-r--r-- | mfd/prog.c | 16 | ||||
-rw-r--r-- | mfd/spf.c | 3 | ||||
-rw-r--r-- | mfd/symbols.c | 560 | ||||
-rw-r--r-- | mflib/heloarg_test.mf | 4 | ||||
-rw-r--r-- | mflib/match_dnsbl.mf | 4 | ||||
-rw-r--r-- | mflib/match_rhsbl.mf | 4 | ||||
-rw-r--r-- | mflib/spf.mf | 2 | ||||
-rw-r--r-- | mflib/valid_domain.mf | 2 | ||||
-rw-r--r-- | tests/etc/catch.rc | 2 | ||||
-rw-r--r-- | tests/etc/catch01.rc | 2 | ||||
-rw-r--r-- | tests/etc/cidr.rc | 2 |
25 files changed, 1613 insertions, 227 deletions
diff --git a/etc/mailfromd.mf b/etc/mailfromd.mf index 1856c8ee..bbba9a23 100644 --- a/etc/mailfromd.mf +++ b/etc/mailfromd.mf @@ -18,9 +18,9 @@ #pragma regex +extended +icase #include_once <status.mfh> -#require dns -#require localdomain -#require rateok +require dns +require localdomain +require rateok set mailfrom_address "<>" # If mailfromd does not determine local domain correctly, uncomment this diff --git a/lib/Makefile.am b/lib/Makefile.am index f8e4c578..6e056053 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -27,6 +27,7 @@ libmf_a_SOURCES=\ parsetime.c\ proctitle.c\ symtab.c\ + transform.c\ userprivs.c\ utils.c\ version.c diff --git a/lib/libmf.h b/lib/libmf.h index 6f775e8b..c0cf7596 100644 --- a/lib/libmf.h +++ b/lib/libmf.h @@ -68,7 +68,7 @@ void mf_proctitle_format (const char *fmt, ...); struct symtab; struct syment { char *name; - char data[1]; + unsigned refcnt; }; typedef int (*symtab_enumerator_t)(void *sym, void *data); @@ -77,21 +77,52 @@ typedef int (*symtab_enumerator_t)(void *sym, void *data); #define SYMTAB_ICASE 0x2 extern void *(*syment_alloc_hook)(size_t); +extern void (*syment_free_hook)(void *ptr); const char * symtab_strerror(int rc); int symtab_lookup_or_install(struct syment **ent, struct symtab *st, const char *name, int *install); +int symtab_lookup_or_install_entry(struct syment **elp, + struct symtab *st, struct syment *ent, + int *install); void symtab_clear(struct symtab *st); struct symtab *symtab_create(size_t elsize, int flags, void (*free_fun)(void *)); -void symtab_free(struct symtab **pst); +void symtab_destroy(struct symtab **pst); int symtab_remove(struct symtab *st, const char *name); int symtab_replace(struct symtab *st, struct syment *ent, struct syment **old_ent); int symtab_enumerate(struct symtab *st, symtab_enumerator_t fun, void *data); +typedef int (*symtab_selfun)(const struct syment *ent, void *closure); +typedef int (*symtab_errfun)(int rc, struct symtab *tab, const char *name, + void *closure); +typedef int (*symtab_confun)(struct symtab *dst, + struct syment *dstent, struct syment *srcent, + void *closure); +typedef int (*symtab_cpyfun)(struct syment **pdst, struct syment *src, + size_t len, void *closure); + +int symtab_import(struct symtab *dst, struct symtab *src, + symtab_selfun selfun, symtab_errfun errfun, + symtab_confun confun, symtab_cpyfun cpyfun, + void *closure); + +typedef struct transform *transform_t; + +typedef int (*transform_append_t)(void *, const char *s, size_t len); +typedef char *(*transform_reduce_t)(void *); + +char *transform_error_string (void); +transform_t transform_compile(const char *expr); +void transform_free(transform_t); +char *transform_string (transform_t tf, const char *input, + void *slist, + transform_append_t append, + transform_reduce_t reduce); + diff --git a/lib/symtab.c b/lib/symtab.c index a96cefec..3e69c4cf 100644 --- a/lib/symtab.c +++ b/lib/symtab.c @@ -35,15 +35,8 @@ static unsigned int hash_size[] = { static unsigned int max_rehash = sizeof (hash_size) / sizeof (hash_size[0]); void *(*syment_alloc_hook)(size_t) = NULL; - -static void * -syment_alloc(size_t size) -{ - if (!syment_alloc_hook) - return malloc(size); - return syment_alloc_hook(size); -} - +void (*syment_free_hook)(void *ptr) = NULL; + struct symtab { int flags; unsigned int hash_num; /* Index to hash_size table */ @@ -52,10 +45,39 @@ struct symtab { void (*free_syment) (void *); }; -struct _symtab_align { - char c; - struct syment x; -}; +static void +syment_free(void *ptr) +{ + if (!syment_free_hook) + free(ptr); + else + syment_free_hook(ptr); +} + +static struct syment * +syment_alloc(struct symtab *st, const char *name) +{ + struct syment *ent; + + if (!syment_alloc_hook) + ent = malloc(st->elsize); + else + ent = syment_alloc_hook(st->elsize); + if (ent) { + memset(ent, 0, st->elsize); + ent->refcnt = 1; + if (st->flags & SYMTAB_COPY_KEY) + ent->name = (char *) name; + else { + ent->name = strdup(name); + if (!ent->name) { + syment_free(ent); + return NULL; + } + } + } + return ent; +} static unsigned @@ -73,11 +95,13 @@ hash(const char *name, unsigned long hash_num) static void symtab_free_syment(struct symtab *st, struct syment *elem) { + if (--elem->refcnt) + return; if (st->free_syment) - st->free_syment(elem->data); + st->free_syment(elem); if (!(st->flags & SYMTAB_COPY_KEY)) free(elem->name); - elem->name = NULL; + syment_free(elem); } static unsigned @@ -131,7 +155,7 @@ symtab_rehash(struct symtab *st) return E2BIG; new_tab = calloc(hash_size[hash_num], sizeof(*new_tab)); - if (!symtab_rehash) + if (!new_tab) return ENOMEM; st->tab = new_tab; if (old_tab) { @@ -199,8 +223,9 @@ symtab_remove(struct symtab *st, const char *name) } int -symtab_lookup_or_install(struct syment **elp, - struct symtab *st, const char *name, int *install) +symtab_get_index(unsigned *idx, + struct symtab *st, const char *name, + int *install) { int rc; unsigned i, pos; @@ -221,7 +246,7 @@ symtab_lookup_or_install(struct syment **elp, if (name_cmp(st, elem->name, name) == 0) { if (install) *install = 0; - *elp = elem; + *idx = i; return 0; } @@ -236,25 +261,51 @@ symtab_lookup_or_install(struct syment **elp, if (!elem) { *install = 1; - st->tab[i] = syment_alloc(st->elsize); - if (!st->tab[i]) - return ENOMEM; - elem = st->tab[i]; - if (st->flags & SYMTAB_COPY_KEY) - elem->name = (char *) name; - else { - elem->name = strdup(name); - if (!elem->name) - return ENOMEM; - } - *elp = elem; - return 0; + *idx = i; + return 0; } if ((rc = symtab_rehash(st)) != 0) return rc; - return symtab_lookup_or_install(elp, st, name, install); + return symtab_get_index(idx, st, name, install); +} + +int +symtab_lookup_or_install(struct syment **elp, + struct symtab *st, const char *name, int *install) +{ + unsigned i; + int rc = symtab_get_index(&i, st, name, install); + if (rc == 0) { + if (install && *install == 1) { + struct syment *ent = syment_alloc(st, name); + if (!ent) + return ENOMEM; + st->tab[i] = ent; + *elp = ent; + } else + *elp = st->tab[i]; + } + return rc; +} + +int +symtab_lookup_or_install_entry(struct syment **elp, + struct symtab *st, struct syment *ent, + int *install) +{ + unsigned i; + int rc = symtab_get_index(&i, st, ent->name, install); + if (rc == 0) { + if (install && *install == 1) { + st->tab[i] = ent; + ent->refcnt++; + *elp = ent; + } else + *elp = st->tab[i]; + } + return rc; } void @@ -278,17 +329,23 @@ symtab_clear(struct symtab *st) struct symtab * symtab_create(size_t elsize, int flags, void (*free_fun)(void *)) { - struct symtab *st = calloc(1, sizeof (*st)); + struct symtab *st = malloc(sizeof(*st)); if (st) { + memset(st, 0, sizeof(*st)); st->flags = flags; st->elsize = elsize; st->free_syment = free_fun; + st->tab = calloc(hash_size[st->hash_num], sizeof(*st->tab)); + if (!st->tab) { + free(st); + st = NULL; + } } return st; } void -symtab_free(struct symtab **pst) +symtab_destroy(struct symtab **pst) { if (pst && *pst) { struct symtab *st = *pst; @@ -315,7 +372,57 @@ symtab_enumerate(struct symtab *st, symtab_enumerator_t fun, void *data) return 0; } - +int +symtab_import(struct symtab *dst, struct symtab *src, + symtab_selfun selfun, symtab_errfun errfun, + symtab_confun confun, symtab_cpyfun cpyfun, + void *closure) +{ + unsigned i; + + for (i = 0; i < hash_size[src->hash_num]; i++) { + struct syment *srcent = src->tab[i]; + if (srcent && (!selfun || selfun(srcent, closure))) { + struct syment *dstent; + int install; + unsigned j; + int rc; + + if (cpyfun) { + if (cpyfun(&dstent, srcent, dst->elsize, + closure)) + continue; + } else { + dstent = src->tab[i]; + dstent->refcnt++; + } + + rc = symtab_get_index(&j, dst, dstent->name, + &install); + if (rc) { + if (!errfun + || errfun(rc, dst, srcent->name, + closure)) + return rc; + else + continue; + } + if (!install) { + if (!confun) + return EBUSY; + dstent = dst->tab[j]; + rc = confun(dst, dstent, srcent, closure); + if (rc) + return rc; + continue; + } + dst->tab[j] = dstent; + } + } + return 0; +} + + diff --git a/lib/transform.c b/lib/transform.c new file mode 100644 index 00000000..79d87610 --- /dev/null +++ b/lib/transform.c @@ -0,0 +1,666 @@ +/* This file is part of Mailfromd. + Copyright (C) 2006, 2007, 2008, 2009 Sergey Poznyakoff. + (using my implementation for the GNU tar and rush). + + Mailfromd 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. + + Mailfromd 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 Mailfromd. If not, see <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include <stdlib.h> +#include <regex.h> +#include "xalloc.h" +#include "c-ctype.h" +#include <libmf.h> + +enum transform_type + { + transform_incomplete, + transform_first, + transform_global + }; + +enum replace_segm_type + { + segm_literal, /* Literal segment */ + segm_backref, /* Back-reference segment */ + segm_case_ctl /* Case control segment (GNU extension) */ + }; + +enum case_ctl_type + { + ctl_stop, /* Stop case conversion */ + ctl_upcase_next,/* Turn the next character to uppercase */ + ctl_locase_next,/* Turn the next character to lowercase */ + ctl_upcase, /* Turn the replacement to uppercase until ctl_stop */ + ctl_locase /* Turn the replacement to lowercase until ctl_stop */ + }; + +struct replace_segm +{ + struct replace_segm *next; + enum replace_segm_type type; + union + { + struct + { + char *ptr; + size_t size; + } literal; /* type == segm_literal */ + size_t ref; /* type == segm_backref */ + enum case_ctl_type ctl; /* type == segm_case_ctl */ + } v; +}; + +struct transform +{ + struct transform *next; + enum transform_type transform_type; + unsigned match_number; + regex_t regex; + /* Compiled replacement expression */ + struct replace_segm *repl_head, *repl_tail; + size_t segm_count; /* Number of elements in the above list */ +}; + +struct transform_list +{ + struct transform *head, *tail; +}; + +struct transform_error +{ + const char *diag; + int pos; + const char *arg; + void *mem; +}; + +struct transform_error last_transform_error; + +static void +init_transform_error () +{ + if (last_transform_error.mem) + free(last_transform_error.mem); + memset(&last_transform_error, 0, sizeof(last_transform_error)); +} + +char * +transform_error_string () +{ + char *str; + if (!last_transform_error.diag) + return NULL; + if (last_transform_error.arg) + asprintf (&str, + "%s, in \"%s\" pos. %d", + last_transform_error.diag, + last_transform_error.arg, + last_transform_error.pos); + else + str = xstrdup (last_transform_error.diag); + init_transform_error (); + return str; +} + + +static struct transform * +new_transform (struct transform_list *tlist) +{ + struct transform *p = xzalloc (sizeof *p); + p->transform_type = transform_incomplete; + if (tlist->tail) + tlist->tail->next = p; + else + tlist->head = p; + tlist->tail = p; + return p; +} + +static struct replace_segm * +add_segment (struct transform *tf) +{ + struct replace_segm *segm = xmalloc (sizeof *segm); + segm->next = NULL; + if (tf->repl_tail) + tf->repl_tail->next = segm; + else + tf->repl_head = segm; + tf->repl_tail = segm; + tf->segm_count++; + return segm; +} + +static void +add_literal_segment (struct transform *tf, char *str, char *end) +{ + size_t len = end - str; + if (len) + { + struct replace_segm *segm = add_segment (tf); + segm->type = segm_literal; + segm->v.literal.ptr = xmalloc (len + 1); + memcpy (segm->v.literal.ptr, str, len); + segm->v.literal.ptr[len] = 0; + segm->v.literal.size = len; + } +} + +static void +add_char_segment (struct transform *tf, int chr) +{ + struct replace_segm *segm = add_segment (tf); + segm->type = segm_literal; + segm->v.literal.ptr = xmalloc (2); + segm->v.literal.ptr[0] = chr; + segm->v.literal.ptr[1] = 0; + segm->v.literal.size = 1; +} + +static void +add_backref_segment (struct transform *tf, size_t ref) +{ + struct replace_segm *segm = add_segment (tf); + segm->type = segm_backref; + segm->v.ref = ref; +} + +static void +add_case_ctl_segment (struct transform *tf, enum case_ctl_type ctl) +{ + struct replace_segm *segm = add_segment (tf); + segm->type = segm_case_ctl; + segm->v.ctl = ctl; +} + +void +replace_segm_free (struct replace_segm *segm) +{ + while (segm) + { + struct replace_segm *next = segm->next; + switch (segm->type) + { + case segm_literal: + free (segm->v.literal.ptr); + break; + + case segm_backref: + case segm_case_ctl: + break; + } + free (segm); + segm = next; + } +} + +void +transform_free(struct transform *xform) +{ + while (xform) + { + struct transform *next = xform->next; + if (xform->transform_type != transform_incomplete) + regfree (&xform->regex); + replace_segm_free (xform->repl_head); + free (xform); + xform = next; + } +} + +static int +parse_transform_expr (struct transform_list *tlist, const char *expr, + const char **endp) +{ + int delim; + int i, j, rc; + char *str, *beg, *cur; + const char *p; + int cflags = 0; + struct transform *tf = new_transform (tlist); + enum transform_type transform_type; + + if (expr[0] != 's') + { + last_transform_error.diag = _("invalid transform expression"); + last_transform_error.pos = 0; + last_transform_error.arg = expr; + return 1; + } + + delim = expr[1]; + + /* Scan regular expression */ + for (i = 2; expr[i] && expr[i] != delim; i++) + if (expr[i] == '\\' && expr[i+1]) + i++; + + if (expr[i] != delim) + { + last_transform_error.diag = _("missing 2nd delimiter"); + last_transform_error.pos = i; + last_transform_error.arg = expr; + return 1; + } + + /* Scan replacement expression */ + for (j = i + 1; expr[j] && expr[j] != delim; j++) + if (expr[j] == '\\' && expr[j+1]) + j++; + + if (expr[j] != delim) + { + last_transform_error.diag = _("missing trailing delimiter"); + last_transform_error.pos = j; + last_transform_error.arg = expr; + return 1; + } + + /* Check flags */ + transform_type = transform_first; + for (p = expr + j + 1; *p && *p != ';'; p++) + switch (*p) + { + case 'g': + transform_type = transform_global; + break; + + case 'i': + cflags |= REG_ICASE; + break; + + case 'x': + cflags |= REG_EXTENDED; + break; + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + tf->match_number = strtoul (p, (char**) &p, 0); + p--; + break; + + default: + last_transform_error.diag = _("unknown flag"); + last_transform_error.pos = p - expr; + last_transform_error.arg = expr; + return 1; + } + + if (*p == ';') + p++; + + /* Extract and compile regex */ + str = xmalloc (i - 1); + memcpy (str, expr + 2, i - 2); + str[i - 2] = 0; + + rc = regcomp (&tf->regex, str, cflags); + tf->transform_type = transform_type; + if (rc) + { + char errbuf[512]; + regerror (rc, &tf->regex, errbuf, sizeof (errbuf)); + last_transform_error.diag = _("invalid transform expression"); + last_transform_error.pos = 0; + last_transform_error.mem = xstrdup(errbuf); + last_transform_error.arg = last_transform_error.mem; + free (str); + return 1; + } + + if (str[0] == '^' || str[strlen (str) - 1] == '$') + tf->transform_type = transform_first; + + free (str); + + /* Extract and compile replacement expr */ + i++; + str = xmalloc (j - i + 1); + memcpy (str, expr + i, j - i); + str[j - i] = 0; + + for (cur = beg = str; *cur;) + { + if (*cur == '\\') + { + size_t n; + + add_literal_segment (tf, beg, cur); + switch (*++cur) + { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + n = strtoul (cur, &cur, 10); + if (n > tf->regex.re_nsub) + { + last_transform_error.diag = _("back reference out of range"); + last_transform_error.pos = cur - str; + last_transform_error.mem = str; + last_transform_error.arg = last_transform_error.mem; + return 1; + } + add_backref_segment (tf, n); + break; + + case '\\': + add_char_segment (tf, '\\'); + cur++; + break; + + case 'a': + add_char_segment (tf, '\a'); + cur++; + break; + + case 'b': + add_char_segment (tf, '\b'); + cur++; + break; + + case 'f': + add_char_segment (tf, '\f'); + cur++; + break; + + case 'n': + add_char_segment (tf, '\n'); + cur++; + break; + + case 'r': + add_char_segment (tf, '\r'); + cur++; + break; + + case 't': + add_char_segment (tf, '\t'); + cur++; + break; + + case 'v': + add_char_segment (tf, '\v'); + cur++; + break; + + case '&': + add_char_segment (tf, '&'); + cur++; + break; + + case 'L': + /* Turn the replacement to lowercase until a `\U' or `\E' + is found, */ + add_case_ctl_segment (tf, ctl_locase); + cur++; + break; + + case 'l': + /* Turn the next character to lowercase, */ + add_case_ctl_segment (tf, ctl_locase_next); + cur++; + break; + + case 'U': + /* Turn the replacement to uppercase until a `\L' or `\E' + is found, */ + add_case_ctl_segment (tf, ctl_upcase); + cur++; + break; + + case 'u': + /* Turn the next character to uppercase, */ + add_case_ctl_segment (tf, ctl_upcase_next); + cur++; + break; + + case 'E': + /* Stop case conversion started by `\L' or `\U'. */ + add_case_ctl_segment (tf, ctl_stop); + cur++; + break; + + default: + /* Try to be nice */ + { + char buf[2]; + buf[0] = '\\'; + buf[1] = *cur; + add_literal_segment (tf, buf, buf + 2); + } + cur++; + break; + } + beg = cur; + } + else if (*cur == '&') + { + add_literal_segment (tf, beg, cur); + add_backref_segment (tf, 0); + beg = ++cur; + } + else + cur++; + } + add_literal_segment (tf, beg, cur); + *endp = p; + return 0; +} + +transform_t +transform_compile (const char *expr) +{ + struct transform_list tlist = { NULL, NULL }; + + init_transform_error (); + + while (*expr) + if (parse_transform_expr (&tlist, expr, &expr)) + { + transform_free (tlist.head); + return NULL; + } + return tlist.head; +} + +/* Run case conversion specified by CASE_CTL on array PTR of SIZE + characters. Returns pointer to statically allocated storage. */ +static const char * +run_case_conv (enum case_ctl_type case_ctl, const char *ptr, size_t size) +{ + static char *case_ctl_buffer; + static size_t case_ctl_bufsize; + char *p; + + if (case_ctl_bufsize < size) + { + case_ctl_bufsize = size; + case_ctl_buffer = xrealloc (case_ctl_buffer, case_ctl_bufsize); + } + memcpy (case_ctl_buffer, ptr, size); + switch (case_ctl) + { + case ctl_upcase_next: + case_ctl_buffer[0] = c_toupper (case_ctl_buffer[0]); + break; + + case ctl_locase_next: + case_ctl_buffer[0] = c_tolower (case_ctl_buffer[0]); + break; + + case ctl_upcase: + for (p = case_ctl_buffer; p < case_ctl_buffer + size; p++) + *p = c_toupper (*p); + break; + + case ctl_locase: + for (p = case_ctl_buffer; p < case_ctl_buffer + size; p++) + *p = c_tolower (*p); + break; + + case ctl_stop: + break; + } + return case_ctl_buffer; +} + + +void +_single_transform_name_to_slist (struct transform *tf, + const char *input, + void *slist, + transform_append_t append) +{ + regmatch_t *rmp; + int rc; + size_t nmatches = 0; + enum case_ctl_type case_ctl = ctl_stop, /* Current case conversion op */ + save_ctl = ctl_stop; /* Saved case_ctl for \u and \l */ + + /* Reset case conversion after a single-char operation */ +#define CASE_CTL_RESET() if (case_ctl == ctl_upcase_next \ + || case_ctl == ctl_locase_next) \ + { \ + case_ctl = save_ctl; \ + save_ctl = ctl_stop; \ + } + + rmp = xmalloc ((tf->regex.re_nsub + 1) * sizeof (*rmp)); + + while (*input) + { + size_t disp; + const char *ptr; + + rc = regexec (&tf->regex, input, tf->regex.re_nsub + 1, rmp, 0); + + if (rc == 0) + { + struct replace_segm *segm; + + disp = rmp[0].rm_eo; + + if (rmp[0].rm_so) + append (slist, input, rmp[0].rm_so); + + nmatches++; + if (tf->match_number && nmatches < tf->match_number) + { + append (slist, input, disp); + input += disp; + continue; + } + + for (segm = tf->repl_head; segm; segm = segm->next) + { + switch (segm->type) + { + case segm_lite |