1 /*
2  * pcre_rewrite.c
3  * (C)1999-2011 Marc Huber <Marc.Huber@web.de>
4  * All rights reserved.
5  *
6  * $Id: pcre_rewrite.c,v 1.16 2020/03/05 19:48:48 marc Exp marc $
7  *
8  */
9 
10 /*
11  * This code was tested with version 2.08 of the PCRE library,
12  * available from:
13  * ftp://ftp.cus.cam.ac.uk/pub/software/programs/pcre/
14  */
15 
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #ifdef WITH_PCRE
20 # include <pcre.h>
21 #endif
22 #ifdef WITH_PCRE2
23 # include <pcre2.h>
24 #endif
25 #include "headers.h"
26 
27 static const char rcsid[] __attribute__ ((used)) = "$Id: pcre_rewrite.c,v 1.16 2020/03/05 19:48:48 marc Exp marc $";
28 
29 #ifdef WITH_PCRE
30 struct pcre_rule {
31     pcre *p;
32     pcre_extra *pe;
33     struct pcre_rule *next;
34     char *flags;
35     char replacement[1];
36 };
37 #endif
38 #ifdef WITH_PCRE2
39 struct pcre_rule {
40     pcre2_code *p;
41     PCRE2_SIZE rlength;		/* strlen(replacement string) */
42     struct pcre_rule *next;
43     char *flags;
44     char replacement[1];
45 };
46 #endif
47 
48 static struct pcre_rule *pcre_start = NULL, *pcre_last = NULL;
49 
PCRE_add(char * regex,char * replacement,char * flags)50 int PCRE_add(char *regex, char *replacement, char *flags)
51 {
52     struct pcre_rule *pr;
53 #ifdef WITH_PCRE
54     const char *errptr;
55     int erroffset;
56 #endif
57 #ifdef WITH_PCRE2
58     int errorcode;
59     PCRE2_SIZE erroffset;
60 #endif
61 
62     if (!flags || !*flags)
63 	flags = "L";
64 
65     pr = Xcalloc(1, sizeof(struct pcre_rule)
66 		 + strlen(replacement) + strlen(flags) + 1);
67 
68 #ifdef WITH_PCRE
69     pr->p = pcre_compile(regex, 0, &errptr, &erroffset, NULL);
70     if (!pr->p) {
71 	logmsg("pcre_compile: %s: %s", regex, errptr ? errptr : "(NULL)");
72 	free(pr);
73 	return -1;
74     }
75 
76     pr->pe = pcre_study(pr->p, 0, &errptr);
77 #endif
78 #ifdef WITH_PCRE2
79     pr->p = pcre2_compile((PCRE2_SPTR) regex, PCRE2_ZERO_TERMINATED, PCRE2_UTF, &errorcode, &erroffset, NULL);
80     if (!pr->p) {
81 	PCRE2_UCHAR buffer[256];
82 	pcre2_get_error_message(errorcode, buffer, sizeof(buffer));
83 	logmsg("pcre2_compile: %s: %s", regex, buffer);
84 	free(pr);
85 	return -1;
86     }
87 #endif
88 
89     strcpy(pr->replacement, replacement);
90     pr->flags = pr->replacement + strlen(pr->replacement) + 2;
91     strcpy(pr->flags, flags);
92     pr->next = NULL;
93 
94     if (!pcre_start)
95 	pcre_start = pcre_last = pr;
96     else {
97 	pcre_last->next = pr;
98 	pcre_last = pr;
99     }
100 
101     Debug((DEBUG_PROC, " PCRE_add(%s, %s, %s)\n", regex, replacement, flags));
102     return 0;
103 }
104 
PCRE_exec(const char * inbuf,char * outbuf,size_t outlen)105 int PCRE_exec(const char *inbuf, char *outbuf, size_t outlen)
106 {
107     int loopmax = 20;
108 #ifdef WITH_PCRE
109 #define OVECSIZE 100
110     int ovector[3 * (OVECSIZE + 1)];
111 #endif
112     struct pcre_rule *pr = pcre_start;
113     size_t tbuflen = 2 * outlen;
114     char *tbuf = alloca(tbuflen);
115 
116     if (!pcre_start)
117 	return 0;		/* no match */
118 
119     strncpy(tbuf, inbuf, tbuflen);
120     tbuf[tbuflen - 1] = 0;
121 
122     while (loopmax--) {
123 	int m;
124 #ifdef WITH_PCRE2
125 	pcre2_match_data *match_data = NULL;
126 	PCRE2_SIZE outlength = (PCRE2_SIZE) outlen - 1;
127 #endif
128 	if (pr == NULL)
129 	    pr = pcre_start;
130 #ifdef WITH_PCRE
131 	m = pcre_exec(pr->p, pr->pe, tbuf, (int) strlen(tbuf), 0, 0, ovector, OVECSIZE);
132 	if (m > -1) {
133 	    char *o, *t;
134 
135 	    for (o = outbuf, t = pr->replacement; *t && o < outbuf + tbuflen; t++)
136 		switch (*t) {
137 		case '\\':	/* escape next character */
138 		    if (*(t + 1))
139 			*o++ = *t++;
140 		    break;
141 		case '$':
142 		    if (*(t + 1)) {
143 			int num = 0;
144 
145 			if (isdigit((int) *++t))
146 			    num = *t - '0';
147 			else if (*t == '{')
148 			    for (t++; *t && *t != '}'; t++) {
149 				if (isdigit((int) *t))
150 				    num = num * 10 + *t - '0';
151 			} else
152 			    continue;
153 
154 			pcre_copy_substring(tbuf, ovector, m, num, o, (int) (outbuf + outlen - o - 1));
155 			while (*o)
156 			    o++;
157 		    }
158 		    break;
159 		default:
160 		    *o++ = *t;
161 		}		/* switch */
162 
163 	    *o = 0;
164 	    if (!strcmp(pr->flags, "N")) {	/* Next */
165 		pr = pcre_start;
166 		strncpy(tbuf, outbuf, tbuflen);
167 		tbuf[tbuflen - 1] = 0;
168 		continue;
169 	    }
170 	    if (!strcmp(pr->flags, "R"))	/* Reject */
171 		*outbuf = 0;
172 	    Debug((DEBUG_PROC, " PCRE_exec(%s) = %s\n", inbuf, outbuf));
173 	    return -1;
174 	}
175 #endif
176 #ifdef WITH_PCRE2
177 	// caveat emptor: compiles, but untested
178 	match_data = pcre2_match_data_create_from_pattern(pr->p, NULL);
179 	m = pcre2_substitute(pr->p, (PCRE2_SPTR) tbuf, PCRE2_ZERO_TERMINATED, PCRE2_SUBSTITUTE_EXTENDED | PCRE2_UTF, 0, match_data,
180 			     NULL, (PCRE2_SPTR) pr->replacement, PCRE2_ZERO_TERMINATED, (PCRE2_UCHAR8 *) outbuf, &outlength);
181 
182 	if (match_data) {
183 	    pcre2_match_data_free(match_data);
184 	    match_data = NULL;
185 	}
186 
187 	if (m > -1) {
188 	    if (outlength > 1 && outlength < (PCRE2_SIZE) outlen)
189 		outbuf[outlen] = 0;
190 
191 	    if (!strcmp(pr->flags, "N")) {	/* Next */
192 		pr = pcre_start;
193 		strncpy(tbuf, outbuf, tbuflen);
194 		tbuf[tbuflen - 1] = 0;
195 		continue;
196 	    }
197 	    if (!strcmp(pr->flags, "R"))	/* Reject */
198 		*outbuf = 0;
199 	    Debug((DEBUG_PROC, " PCRE_exec(%s) = %s\n", inbuf, outbuf));
200 	    return -1;
201 	}
202 #endif				/* if */
203 	pr = pr->next;
204     }				/* while */
205     return 0;
206 }
207