chiark / gitweb /
Merge from disorder.dev.
[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
460b9539 23#include <pcre.h>
24
25#include "regsub.h"
26#include "mem.h"
27#include "vector.h"
28#include "log.h"
29
30#define PREMATCH (-1) /* fictitious pre-match substring */
31#define POSTMATCH (-2) /* fictitious post-match substring */
32
33static inline int substring_start(const char attribute((unused)) *subject,
34 const int *ovector,
35 int n) {
36 switch(n) {
37 case PREMATCH: return 0;
38 case POSTMATCH: return ovector[1];
39 default: return ovector[2 * n];
40 }
41}
42
43static inline int substring_end(const char *subject,
44 const int *ovector,
45 int n) {
46 switch(n) {
47 case PREMATCH: return ovector[0];
48 case POSTMATCH: return strlen(subject);
49 default: return ovector[2 * n + 1];
50 }
51}
52
53static void transform_append(struct dynstr *d,
54 const char *subject,
55 const int *ovector,
56 int n) {
57 int start = substring_start(subject, ovector, n);
58 int end = substring_end(subject, ovector, n);
59
60 if(start != -1)
61 dynstr_append_bytes(d, subject + start, end - start);
62}
63
64static void replace_core(struct dynstr *d,
65 const char *subject,
66 const char *replace,
67 int rc,
68 const int *ovector) {
69 int substr;
70
71 while(*replace) {
72 if(*replace == '$')
73 switch(replace[1]) {
74 case '&':
75 transform_append(d, subject, ovector, 0);
76 replace += 2;
77 break;
78 case '1': case '2': case '3':
79 case '4': case '5': case '6':
80 case '7': case '8': case '9':
81 substr = replace[1] - '0';
82 if(substr < rc)
83 transform_append(d, subject, ovector, substr);
84 replace += 2;
85 break;
86 case '$':
87 dynstr_append(d, '$');
88 replace += 2;
89 break;
90 default:
91 dynstr_append(d, *replace++);
92 break;
93 }
94 else
95 dynstr_append(d, *replace++);
96 }
97}
98
99unsigned regsub_flags(const char *flags) {
100 unsigned f = 0;
101
102 while(*flags) {
103 switch(*flags++) {
104 case 'g': f |= REGSUB_GLOBAL; break;
105 case 'i': f |= REGSUB_CASE_INDEPENDENT; break;
106 default: break;
107 }
108 }
109 return f;
110}
111
112int regsub_compile_options(unsigned flags) {
113 int options = 0;
114
115 if(flags & REGSUB_CASE_INDEPENDENT)
116 options |= PCRE_CASELESS;
117 return options;
118}
119
120const char *regsub(const pcre *re, const char *subject, const char *replace,
121 unsigned flags) {
122 int rc, ovector[99], matches;
123 struct dynstr d;
124
125 dynstr_init(&d);
126 matches = 0;
127 /* find the next match */
128 while((rc = pcre_exec(re, 0, subject, strlen(subject), 0,
129 0, ovector, sizeof ovector / sizeof (int))) > 0) {
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 }
145 if(rc <= 0 && rc != PCRE_ERROR_NOMATCH) {
146 error(0, "pcre_exec returned %d, subject '%s'", rc, subject);
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*/