chiark / gitweb /
Merge branch 'master' of git.distorted.org.uk:~mdw/publish/public-git/disorder
[disorder] / lib / regsub.c
CommitLineData
460b9539 1/*
2 * This file is part of DisOrder
5aff007d 3 * Copyright (C) 2004, 2005, 2007, 20008 Richard Kettlewell
460b9539 4 *
e7eb3a27 5 * This program is free software: you can redistribute it and/or modify
460b9539 6 * it under the terms of the GNU General Public License as published by
e7eb3a27 7 * the Free Software Foundation, either version 3 of the License, or
460b9539 8 * (at your option) any later version.
e7eb3a27
RK
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 *
460b9539 15 * You should have received a copy of the GNU General Public License
e7eb3a27 16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
460b9539 17 */
132a5a4a
RK
18/** @file lib/regsub.c
19 * @brief Regexp substitution
20 */
05b75f8d 21#include "common.h"
460b9539 22
a2e9d147 23#include "regexp.h"
460b9539 24#include "regsub.h"
25#include "mem.h"
26#include "vector.h"
27#include "log.h"
28
29#define PREMATCH (-1) /* fictitious pre-match substring */
30#define POSTMATCH (-2) /* fictitious post-match substring */
31
a2e9d147
MW
32static inline size_t substring_start(const char attribute((unused)) *subject,
33 const size_t *ovector,
34 size_t n) {
460b9539 35 switch(n) {
36 case PREMATCH: return 0;
37 case POSTMATCH: return ovector[1];
38 default: return ovector[2 * n];
39 }
40}
41
a2e9d147
MW
42static inline size_t substring_end(const char *subject,
43 const size_t *ovector,
44 size_t n) {
460b9539 45 switch(n) {
46 case PREMATCH: return ovector[0];
47 case POSTMATCH: return strlen(subject);
48 default: return ovector[2 * n + 1];
49 }
50}
51
52static void transform_append(struct dynstr *d,
53 const char *subject,
a2e9d147
MW
54 const size_t *ovector,
55 size_t n) {
460b9539 56 int start = substring_start(subject, ovector, n);
57 int end = substring_end(subject, ovector, n);
58
59 if(start != -1)
60 dynstr_append_bytes(d, subject + start, end - start);
61}
62
63static void replace_core(struct dynstr *d,
64 const char *subject,
65 const char *replace,
a2e9d147
MW
66 size_t rc,
67 const size_t *ovector) {
68 size_t substr;
460b9539 69
70 while(*replace) {
71 if(*replace == '$')
72 switch(replace[1]) {
73 case '&':
74 transform_append(d, subject, ovector, 0);
75 replace += 2;
76 break;
77 case '1': case '2': case '3':
78 case '4': case '5': case '6':
79 case '7': case '8': case '9':
80 substr = replace[1] - '0';
81 if(substr < rc)
82 transform_append(d, subject, ovector, substr);
83 replace += 2;
84 break;
85 case '$':
86 dynstr_append(d, '$');
87 replace += 2;
88 break;
89 default:
90 dynstr_append(d, *replace++);
91 break;
92 }
93 else
94 dynstr_append(d, *replace++);
95 }
96}
97
98unsigned regsub_flags(const char *flags) {
99 unsigned f = 0;
100
101 while(*flags) {
102 switch(*flags++) {
103 case 'g': f |= REGSUB_GLOBAL; break;
104 case 'i': f |= REGSUB_CASE_INDEPENDENT; break;
105 default: break;
106 }
107 }
108 return f;
109}
110
111int regsub_compile_options(unsigned flags) {
112 int options = 0;
113
114 if(flags & REGSUB_CASE_INDEPENDENT)
a2e9d147 115 options |= RXF_CASELESS;
460b9539 116 return options;
117}
118
a2e9d147
MW
119const char *regsub(const regexp *re, const char *subject,
120 const char *replace, unsigned flags) {
121 int rc, matches;
122 size_t ovector[99];
460b9539 123 struct dynstr d;
124
125 dynstr_init(&d);
126 matches = 0;
127 /* find the next match */
a2e9d147
MW
128 while((rc = regexp_match(re, subject, strlen(subject), 0,
129 ovector, sizeof ovector / sizeof (ovector[0]))) > 0) {
460b9539 130 /* text just before the match */
131 if(!(flags & REGSUB_REPLACE))
132 transform_append(&d, subject, ovector, PREMATCH);
133 /* the replacement text */
134 replace_core(&d, subject, replace, rc, ovector);
135 ++matches;
136 if(!*subject) /* end of subject */
137 break;
138 if(flags & REGSUB_REPLACE) /* replace subject entirely */
139 break;
140 /* step over the matched substring */
141 subject += substring_start(subject, ovector, POSTMATCH);
142 if(!(flags & REGSUB_GLOBAL))
143 break;
144 }
a2e9d147
MW
145 if(rc <= 0 && rc != RXERR_NOMATCH) {
146 disorder_error(0, "regexp_match returned %d, subject '%s'", rc, subject);
460b9539 147 return 0;
148 }
149 if((flags & REGSUB_MUST_MATCH) && matches == 0)
150 return 0;
151 /* append the remainder of the subject */
152 if(!(flags & REGSUB_REPLACE))
153 dynstr_append_string(&d, subject);
154 dynstr_terminate(&d);
155 return d.vec;
156}
157
158/*
159Local Variables:
160c-basic-offset:2
161comment-column:40
162End:
163*/