chiark / gitweb /
Doxygen-clean
[disorder] / lib / regsub.c
1 /*
2  * This file is part of DisOrder
3  * Copyright (C) 2004, 2005, 2007, 20008 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 3 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,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU 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, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include "common.h"
20
21 #include <pcre.h>
22
23 #include "regsub.h"
24 #include "mem.h"
25 #include "vector.h"
26 #include "log.h"
27
28 #define PREMATCH (-1)                   /* fictitious pre-match substring */
29 #define POSTMATCH (-2)                  /* fictitious post-match substring */
30
31 static inline int substring_start(const char attribute((unused)) *subject,
32                                   const int *ovector,
33                                   int n) {
34   switch(n) {
35   case PREMATCH: return 0;
36   case POSTMATCH: return ovector[1];
37   default: return ovector[2 * n];
38   }
39 }
40
41 static inline int substring_end(const char *subject,
42                                 const int *ovector,
43                                 int n) {
44   switch(n) {
45   case PREMATCH: return ovector[0];
46   case POSTMATCH: return strlen(subject);
47   default: return ovector[2 * n + 1];
48   }
49 }
50
51 static void transform_append(struct dynstr *d,
52                              const char *subject,
53                              const int *ovector,
54                              int n) {
55   int start = substring_start(subject, ovector, n);
56   int end = substring_end(subject, ovector, n);
57
58   if(start != -1)
59     dynstr_append_bytes(d, subject + start, end - start);
60 }
61
62 static void replace_core(struct dynstr *d,
63                          const char *subject,
64                          const char *replace,
65                          int rc,
66                          const int *ovector) {
67   int substr;
68   
69   while(*replace) {
70     if(*replace == '$')
71       switch(replace[1]) {
72       case '&':
73         transform_append(d, subject, ovector, 0);
74         replace += 2;
75         break;
76       case '1': case '2': case '3':
77       case '4': case '5': case '6':
78       case '7': case '8': case '9':
79         substr = replace[1] - '0';
80         if(substr < rc)
81           transform_append(d, subject, ovector, substr);
82         replace += 2;
83         break;
84       case '$':
85         dynstr_append(d, '$');
86         replace += 2;
87         break;
88       default:
89         dynstr_append(d, *replace++);
90         break;
91       }
92     else
93       dynstr_append(d, *replace++);
94   }
95 }
96
97 unsigned regsub_flags(const char *flags) {
98   unsigned f = 0;
99
100   while(*flags) {
101     switch(*flags++) {
102     case 'g': f |= REGSUB_GLOBAL; break;
103     case 'i': f |= REGSUB_CASE_INDEPENDENT; break;
104     default: break;
105     }
106   }
107   return f;
108 }
109
110 int regsub_compile_options(unsigned flags) {
111   int options = 0;
112
113   if(flags & REGSUB_CASE_INDEPENDENT)
114     options |= PCRE_CASELESS;
115   return options;
116 }
117
118 const char *regsub(const pcre *re, const char *subject, const char *replace,
119                    unsigned flags) {
120   int rc, ovector[99], matches;
121   struct dynstr d;
122
123   dynstr_init(&d);
124   matches = 0;
125   /* find the next match */
126   while((rc = pcre_exec(re, 0, subject, strlen(subject), 0,
127                      0, ovector, sizeof ovector / sizeof (int))) > 0) {
128     /* text just before the match */
129     if(!(flags & REGSUB_REPLACE))
130       transform_append(&d, subject, ovector, PREMATCH);
131     /* the replacement text */
132     replace_core(&d, subject, replace, rc, ovector);
133     ++matches;
134     if(!*subject)                       /* end of subject */
135       break;
136     if(flags & REGSUB_REPLACE)          /* replace subject entirely */
137       break;
138     /* step over the matched substring */
139     subject += substring_start(subject, ovector, POSTMATCH);
140     if(!(flags & REGSUB_GLOBAL))
141       break;
142   }
143   if(rc <= 0 && rc != PCRE_ERROR_NOMATCH) {
144     error(0, "pcre_exec returned %d, subject '%s'", rc, subject);
145     return 0;
146   }
147   if((flags & REGSUB_MUST_MATCH) && matches == 0)
148     return 0;
149   /* append the remainder of the subject */
150   if(!(flags & REGSUB_REPLACE))
151     dynstr_append_string(&d, subject);
152   dynstr_terminate(&d);
153   return d.vec;
154 }
155
156 /*
157 Local Variables:
158 c-basic-offset:2
159 comment-column:40
160 End:
161 */