chiark / gitweb /
examples/disorder.init.in: Read settings from `/etc/default/disorder'.
[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 /** @file lib/regsub.c
19  * @brief Regexp substitution
20  */
21 #include "common.h"
22
23 #include "regexp.h"
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
32 static inline size_t substring_start(const char attribute((unused)) *subject,
33                                      const size_t *ovector,
34                                      size_t n) {
35   switch(n) {
36   case PREMATCH: return 0;
37   case POSTMATCH: return ovector[1];
38   default: return ovector[2 * n];
39   }
40 }
41
42 static inline size_t substring_end(const char *subject,
43                                    const size_t *ovector,
44                                    size_t n) {
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
52 static void transform_append(struct dynstr *d,
53                              const char *subject,
54                              const size_t *ovector,
55                              size_t n) {
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
63 static void replace_core(struct dynstr *d,
64                          const char *subject,
65                          const char *replace,
66                          size_t rc,
67                          const size_t *ovector) {
68   size_t substr;
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
98 unsigned 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
111 int regsub_compile_options(unsigned flags) {
112   int options = 0;
113
114   if(flags & REGSUB_CASE_INDEPENDENT)
115     options |= RXF_CASELESS;
116   return options;
117 }
118
119 const char *regsub(const regexp *re, const char *subject,
120                    const char *replace, unsigned flags) {
121   int rc, matches;
122   size_t ovector[99];
123   struct dynstr d;
124
125   dynstr_init(&d);
126   matches = 0;
127   /* find the next match */
128   while((rc = regexp_match(re, subject, strlen(subject), 0,
129                            ovector, sizeof ovector / sizeof (ovector[0]))) > 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 != RXERR_NOMATCH) {
146     disorder_error(0, "regexp_match 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 /*
159 Local Variables:
160 c-basic-offset:2
161 comment-column:40
162 End:
163 */