chiark / gitweb /
bad6034560eb4669ab3438a35fca3052354aee0e
[disorder] / lib / regsub.c
1 /*
2  * This file is part of DisOrder
3  * Copyright (C) 2004, 2005 Richard Kettlewell
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18  * USA
19  */
20
21 #include <config.h>
22 #include "types.h"
23
24 #include <string.h>
25 #include <pcre.h>
26
27 #include "regsub.h"
28 #include "mem.h"
29 #include "vector.h"
30 #include "log.h"
31
32 #define PREMATCH (-1)                   /* fictitious pre-match substring */
33 #define POSTMATCH (-2)                  /* fictitious post-match substring */
34
35 static inline int substring_start(const char attribute((unused)) *subject,
36                                   const int *ovector,
37                                   int n) {
38   switch(n) {
39   case PREMATCH: return 0;
40   case POSTMATCH: return ovector[1];
41   default: return ovector[2 * n];
42   }
43 }
44
45 static inline int substring_end(const char *subject,
46                                 const int *ovector,
47                                 int n) {
48   switch(n) {
49   case PREMATCH: return ovector[0];
50   case POSTMATCH: return strlen(subject);
51   default: return ovector[2 * n + 1];
52   }
53 }
54
55 static void transform_append(struct dynstr *d,
56                              const char *subject,
57                              const int *ovector,
58                              int n) {
59   int start = substring_start(subject, ovector, n);
60   int end = substring_end(subject, ovector, n);
61
62   if(start != -1)
63     dynstr_append_bytes(d, subject + start, end - start);
64 }
65
66 static void replace_core(struct dynstr *d,
67                          const char *subject,
68                          const char *replace,
69                          int rc,
70                          const int *ovector) {
71   int substr;
72   
73   while(*replace) {
74     if(*replace == '$')
75       switch(replace[1]) {
76       case '&':
77         transform_append(d, subject, ovector, 0);
78         replace += 2;
79         break;
80       case '1': case '2': case '3':
81       case '4': case '5': case '6':
82       case '7': case '8': case '9':
83         substr = replace[1] - '0';
84         if(substr < rc)
85           transform_append(d, subject, ovector, substr);
86         replace += 2;
87         break;
88       case '$':
89         dynstr_append(d, '$');
90         replace += 2;
91         break;
92       default:
93         dynstr_append(d, *replace++);
94         break;
95       }
96     else
97       dynstr_append(d, *replace++);
98   }
99 }
100
101 unsigned regsub_flags(const char *flags) {
102   unsigned f = 0;
103
104   while(*flags) {
105     switch(*flags++) {
106     case 'g': f |= REGSUB_GLOBAL; break;
107     case 'i': f |= REGSUB_CASE_INDEPENDENT; break;
108     default: break;
109     }
110   }
111   return f;
112 }
113
114 int regsub_compile_options(unsigned flags) {
115   int options = 0;
116
117   if(flags & REGSUB_CASE_INDEPENDENT)
118     options |= PCRE_CASELESS;
119   return options;
120 }
121
122 const char *regsub(const pcre *re, const char *subject, const char *replace,
123                    unsigned flags) {
124   int rc, ovector[99], matches;
125   struct dynstr d;
126
127   dynstr_init(&d);
128   matches = 0;
129   /* find the next match */
130   while((rc = pcre_exec(re, 0, subject, strlen(subject), 0,
131                      0, ovector, sizeof ovector / sizeof (int))) > 0) {
132     /* text just before the match */
133     if(!(flags & REGSUB_REPLACE))
134       transform_append(&d, subject, ovector, PREMATCH);
135     /* the replacement text */
136     replace_core(&d, subject, replace, rc, ovector);
137     ++matches;
138     if(!*subject)                       /* end of subject */
139       break;
140     if(flags & REGSUB_REPLACE)          /* replace subject entirely */
141       break;
142     /* step over the matched substring */
143     subject += substring_start(subject, ovector, POSTMATCH);
144     if(!(flags & REGSUB_GLOBAL))
145       break;
146   }
147   if(rc <= 0 && rc != PCRE_ERROR_NOMATCH) {
148     error(0, "pcre_exec returned %d, subject '%s'", rc, subject);
149     return 0;
150   }
151   if((flags & REGSUB_MUST_MATCH) && matches == 0)
152     return 0;
153   /* append the remainder of the subject */
154   if(!(flags & REGSUB_REPLACE))
155     dynstr_append_string(&d, subject);
156   dynstr_terminate(&d);
157   return d.vec;
158 }
159
160 /*
161 Local Variables:
162 c-basic-offset:2
163 comment-column:40
164 End:
165 */
166 /* arch-tag:69b3b3dafcbaa25619a77a33c44ad699 */