/* GNU SED, a batch stream editor. Copyright (C) 1999, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. This program 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. This program 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 this program; if not, write to the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "sed.h" #include <ctype.h> #include <string.h> #include <stdio.h> #ifdef HAVE_STDLIB_H # include <stdlib.h> #endif #ifdef gettext_noop # define N_(String) gettext_noop(String) #else # define N_(String) (String) #endif extern bool use_extended_syntax_p; static const char errors[] = "no previous regular expression\0" "cannot specify modifiers on empty regexp"; #define NO_REGEX (errors) #define BAD_MODIF (NO_REGEX + sizeof(N_("no previous regular expression"))) #define END_ERRORS (BAD_MODIF + sizeof(N_("cannot specify modifiers on empty regexp"))) static void compile_regex_1 (new_regex, needed_sub) struct regex *new_regex; int needed_sub; { #ifdef REG_PERL int errcode; errcode = regncomp(&new_regex->pattern, new_regex->re, new_regex->sz, (needed_sub ? 0 : REG_NOSUB) | new_regex->flags | extended_regexp_flags); if (errcode) { char errorbuf[200]; regerror(errcode, NULL, errorbuf, 200); bad_prog(gettext(errorbuf)); } #else const char *error; int syntax = ((extended_regexp_flags & REG_EXTENDED) ? RE_SYNTAX_POSIX_EXTENDED : RE_SYNTAX_POSIX_BASIC); syntax &= ~RE_DOT_NOT_NULL; syntax |= RE_NO_POSIX_BACKTRACKING; switch (posixicity) { case POSIXLY_EXTENDED: syntax &= ~RE_UNMATCHED_RIGHT_PAREN_ORD; break; case POSIXLY_CORRECT: syntax |= RE_UNMATCHED_RIGHT_PAREN_ORD; break; case POSIXLY_BASIC: syntax |= RE_UNMATCHED_RIGHT_PAREN_ORD | RE_LIMITED_OPS | RE_NO_GNU_OPS; break; } #ifdef RE_ICASE syntax |= (new_regex->flags & REG_ICASE) ? RE_ICASE : 0; #endif #ifdef RE_NO_SUB syntax |= needed_sub ? 0 : RE_NO_SUB; #endif new_regex->pattern.fastmap = malloc (1 << (sizeof (char) * 8)); /* If REG_NEWLINE is set, newlines are treated differently. */ if (new_regex->flags & REG_NEWLINE) { /* REG_NEWLINE implies neither . nor [^...] match newline. */ syntax &= ~RE_DOT_NEWLINE; syntax |= RE_HAT_LISTS_NOT_NEWLINE; } re_set_syntax (syntax); error = re_compile_pattern (new_regex->re, new_regex->sz, &new_regex->pattern); new_regex->pattern.newline_anchor = (new_regex->flags & REG_NEWLINE) != 0; new_regex->pattern.translate = NULL; #ifndef RE_ICASE if (new_regex->flags & REG_ICASE) { static char translate[1 << (sizeof(char) * 8)]; int i; for (i = 0; i < sizeof(translate) / sizeof(char); i++) translate[i] = tolower (i); new_regex->pattern.translate = translate; } #endif if (error) bad_prog(error); #endif /* Just to be sure, I mark this as not POSIXLY_CORRECT behavior */ if (needed_sub && new_regex->pattern.re_nsub < needed_sub - 1 && posixicity == POSIXLY_EXTENDED) { char buf[200]; sprintf(buf, _("invalid reference \\%d on `s' command's RHS"), needed_sub - 1); bad_prog(buf); } } struct regex * compile_regex(b, flags, needed_sub) struct buffer *b; int flags; int needed_sub; { struct regex *new_regex; size_t re_len; /* // matches the last RE */ if (size_buffer(b) == 0) { if (flags > 0) bad_prog(_(BAD_MODIF)); return NULL; } re_len = size_buffer(b); new_regex = ck_malloc(sizeof (struct regex) + re_len - 1); new_regex->flags = flags; memcpy (new_regex->re, get_buffer(b), re_len); #ifdef REG_PERL new_regex->sz = re_len; #else /* GNU regex does not process \t & co. */ new_regex->sz = normalize_text(new_regex->re, re_len, TEXT_REGEX); #endif compile_regex_1 (new_regex, needed_sub); return new_regex; } #ifdef REG_PERL static void copy_regs (regs, pmatch, nregs) struct re_registers *regs; regmatch_t *pmatch; int nregs; { int i; int need_regs = nregs + 1; /* We need one extra element beyond `num_regs' for the `-1' marker GNU code uses. */ /* Have the register data arrays been allocated? */ if (!regs->start) { /* No. So allocate them with malloc. */ regs->start = MALLOC (need_regs, regoff_t); regs->end = MALLOC (need_regs, regoff_t); regs->num_regs = need_regs; } else if (need_regs > regs->num_regs) { /* Yes. We also need more elements than were already allocated, so reallocate them. */ regs->start = REALLOC (regs->start, need_regs, regoff_t); regs->end = REALLOC (regs->end, need_regs, regoff_t); regs->num_regs = need_regs; } /* Copy the regs. */ for (i = 0; i < nregs; ++i) { regs->start[i] = pmatch[i].rm_so; regs->end[i] = pmatch[i].rm_eo; } for ( ; i < regs->num_regs; ++i) regs->start[i] = regs->end[i] = -1; } #endif int match_regex(regex, buf, buflen, buf_start_offset, regarray, regsize) struct regex *regex; char *buf; size_t buflen; size_t buf_start_offset; struct re_registers *regarray; int regsize; { int ret; static struct regex *regex_last; #ifdef REG_PERL regmatch_t rm[10], *regmatch = rm; if (regsize > 10) regmatch = (regmatch_t *) alloca (sizeof (regmatch_t) * regsize); #endif /* printf ("Matching from %d/%d\n", buf_start_offset, buflen); */ /* Keep track of the last regexp matched. */ if (!regex) { regex = regex_last; if (!regex_last) bad_prog(_(NO_REGEX)); } else regex_last = regex; #ifdef REG_PERL regmatch[0].rm_so = CAST(int)buf_start_offset; regmatch[0].rm_eo = CAST(int)buflen; ret = regexec (®ex->pattern, buf, regsize, regmatch, REG_STARTEND); if (regsize) copy_regs (regarray, regmatch, regsize); return (ret == 0); #else if (regex->pattern.no_sub && regsize) compile_regex_1 (regex, regsize); regex->pattern.regs_allocated = REGS_REALLOCATE; ret = re_search (®ex->pattern, buf, buflen, buf_start_offset, buflen - buf_start_offset, regsize ? regarray : NULL); return (ret > -1); #endif } #ifdef DEBUG_LEAKS void release_regex(regex) struct regex *regex; { regfree(®ex->pattern); FREE(regex); } #endif /*DEBUG_LEAKS*/