chiark / gitweb /
Switch to GPL v3
[disorder] / lib / regsub.c
... / ...
CommitLineData
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
31static 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
41static 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
51static 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
62static 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
97unsigned 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
110int 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
118const 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/*
157Local Variables:
158c-basic-offset:2
159comment-column:40
160End:
161*/