chiark / gitweb /
more doxygen
[disorder] / lib / regsub.c
CommitLineData
460b9539 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
35static 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
45static 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
55static 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
66static 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
101unsigned 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
114int 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
122const 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/*
161Local Variables:
162c-basic-offset:2
163comment-column:40
164End:
165*/