aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2009-09-29 21:22:05 +0300
committerSergey Poznyakoff <gray@gnu.org.ua>2009-09-29 21:22:05 +0300
commitc978fe2baba8344e3392de7f680cf491bd4b0b7e (patch)
tree956b60427d513142f1da41f79b4b5172ed56472b
parent1774f4233e097ed7f0e745fc41aed2ebb72d7591 (diff)
downloadmailfromd-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.mf6
-rw-r--r--lib/Makefile.am1
-rw-r--r--lib/libmf.h35
-rw-r--r--lib/symtab.c179
-rw-r--r--lib/transform.c666
-rw-r--r--mfd/db.c3
-rw-r--r--mfd/drivers.c12
-rw-r--r--mfd/engine.c3
-rw-r--r--mfd/gram.y142
-rw-r--r--mfd/lex.l19
-rw-r--r--mfd/mailfromd.h100
-rw-r--r--mfd/main.c4
-rw-r--r--mfd/pp.c65
-rw-r--r--mfd/pragma.c4
-rw-r--r--mfd/prog.c16
-rw-r--r--mfd/spf.c3
-rw-r--r--mfd/symbols.c560
-rw-r--r--mflib/heloarg_test.mf4
-rw-r--r--mflib/match_dnsbl.mf4
-rw-r--r--mflib/match_rhsbl.mf4
-rw-r--r--mflib/spf.mf2
-rw-r--r--mflib/valid_domain.mf2
-rw-r--r--tests/etc/catch.rc2
-rw-r--r--tests/etc/catch01.rc2
-rw-r--r--tests/etc/cidr.rc2
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