aboutsummaryrefslogtreecommitdiff
path: root/examples/dte/replace.c
diff options
context:
space:
mode:
Diffstat (limited to 'examples/dte/replace.c')
-rw-r--r--examples/dte/replace.c256
1 files changed, 0 insertions, 256 deletions
diff --git a/examples/dte/replace.c b/examples/dte/replace.c
deleted file mode 100644
index 028d474..0000000
--- a/examples/dte/replace.c
+++ /dev/null
@@ -1,256 +0,0 @@
1#include <stdlib.h>
2#include "replace.h"
3#include "buffer.h"
4#include "change.h"
5#include "editor.h"
6#include "error.h"
7#include "regexp.h"
8#include "screen.h"
9#include "selection.h"
10#include "util/debug.h"
11#include "util/string.h"
12#include "util/xmalloc.h"
13#include "view.h"
14#include "window.h"
15
16static void build_replacement (
17 String *buf,
18 const char *line,
19 const char *format,
20 const regmatch_t *matches
21) {
22 for (size_t i = 0; format[i]; ) {
23 char ch = format[i++];
24 size_t match_idx;
25 if (ch == '\\') {
26 if (unlikely(format[i] == '\0')) {
27 break;
28 }
29 ch = format[i++];
30 if (ch < '1' || ch > '9') {
31 string_append_byte(buf, ch);
32 continue;
33 }
34 match_idx = ch - '0';
35 } else if (ch == '&') {
36 match_idx = 0;
37 } else {
38 string_append_byte(buf, ch);
39 continue;
40 }
41 const regmatch_t *match = &matches[match_idx];
42 regoff_t len = match->rm_eo - match->rm_so;
43 if (len > 0) {
44 string_append_buf(buf, line + match->rm_so, (size_t)len);
45 }
46 }
47}
48
49/*
50 * s/abc/x
51 *
52 * string to match against
53 * -------------------------------------------
54 * "foo abc bar abc baz" "foo abc bar abc baz"
55 * "foo x bar abc baz" " bar abc baz"
56 */
57static unsigned int replace_on_line (
58 View *view,
59 StringView *line,
60 regex_t *re,
61 const char *format,
62 BlockIter *bi,
63 ReplaceFlags *flagsp
64) {
65 const unsigned char *buf = line->data;
66 unsigned char *alloc = NULL;
67 EditorState *e = view->window->editor;
68 ReplaceFlags flags = *flagsp;
69 regmatch_t matches[32];
70 size_t pos = 0;
71 int eflags = 0;
72 unsigned int nr = 0;
73
74 while (regexp_exec (
75 re,
76 buf + pos,
77 line->length - pos,
78 ARRAYLEN(matches),
79 matches,
80 eflags
81 )) {
82 regoff_t match_len = matches[0].rm_eo - matches[0].rm_so;
83 bool skip = false;
84
85 // Move cursor to beginning of the text to replace
86 block_iter_skip_bytes(bi, matches[0].rm_so);
87 view->cursor = *bi;
88
89 if (flags & REPLACE_CONFIRM) {
90 switch (status_prompt(e, "Replace? [Y/n/a/q]", "ynaq")) {
91 case 'y':
92 break;
93 case 'n':
94 skip = true;
95 break;
96 case 'a':
97 flags &= ~REPLACE_CONFIRM;
98 *flagsp = flags;
99
100 // Record rest of the changes as one chain
101 begin_change_chain();
102 break;
103 case 'q':
104 case 0:
105 *flagsp = flags | REPLACE_CANCEL;
106 goto out;
107 }
108 }
109
110 if (skip) {
111 // Move cursor after the matched text
112 block_iter_skip_bytes(&view->cursor, match_len);
113 } else {
114 String b = STRING_INIT;
115 build_replacement(&b, buf + pos, format, matches);
116
117 // line ref is invalidated by modification
118 if (buf == line->data && line->length != 0) {
119 BUG_ON(alloc);
120 alloc = xmemdup(buf, line->length);
121 buf = alloc;
122 }
123
124 buffer_replace_bytes(view, match_len, b.buffer, b.len);
125 nr++;
126
127 // Update selection length
128 if (view->selection) {
129 view->sel_eo += b.len;
130 view->sel_eo -= match_len;
131 }
132
133 // Move cursor after the replaced text
134 block_iter_skip_bytes(&view->cursor, b.len);
135 string_free(&b);
136 }
137 *bi = view->cursor;
138
139 if (!match_len) {
140 break;
141 }
142
143 if (!(flags & REPLACE_GLOBAL)) {
144 break;
145 }
146
147 pos += matches[0].rm_so + match_len;
148
149 // Don't match beginning of line again
150 eflags = REG_NOTBOL;
151 }
152
153out:
154 free(alloc);
155 return nr;
156}
157
158bool reg_replace(View *view, const char *pattern, const char *format, ReplaceFlags flags)
159{
160 if (unlikely(pattern[0] == '\0')) {
161 return error_msg("Search pattern must contain at least 1 character");
162 }
163
164 int re_flags = REG_NEWLINE;
165 re_flags |= (flags & REPLACE_IGNORE_CASE) ? REG_ICASE : 0;
166 re_flags |= (flags & REPLACE_BASIC) ? 0 : DEFAULT_REGEX_FLAGS;
167
168 regex_t re;
169 if (unlikely(!regexp_compile_internal(&re, pattern, re_flags))) {
170 return false;
171 }
172
173 BlockIter bi = block_iter(view->buffer);
174 size_t nr_bytes;
175 bool swapped = false;
176 if (view->selection) {
177 SelectionInfo info;
178 init_selection(view, &info);
179 view->cursor = info.si;
180 view->sel_so = info.so;
181 view->sel_eo = info.eo;
182 swapped = info.swapped;
183 bi = view->cursor;
184 nr_bytes = info.eo - info.so;
185 } else {
186 BlockIter eof = bi;
187 block_iter_eof(&eof);
188 nr_bytes = block_iter_get_offset(&eof);
189 }
190
191 // Record multiple changes as one chain only when replacing all
192 if (!(flags & REPLACE_CONFIRM)) {
193 begin_change_chain();
194 }
195
196 unsigned int nr_substitutions = 0;
197 size_t nr_lines = 0;
198 while (1) {
199 StringView line;
200 fill_line_ref(&bi, &line);
201
202 // Number of bytes to process
203 size_t count = line.length;
204 if (line.length > nr_bytes) {
205 // End of selection is not full line
206 line.length = nr_bytes;
207 }
208
209 unsigned int nr = replace_on_line(view, &line, &re, format, &bi, &flags);
210 if (nr) {
211 nr_substitutions += nr;
212 nr_lines++;
213 }
214
215 if (flags & REPLACE_CANCEL || count + 1 >= nr_bytes) {
216 break;
217 }
218
219 nr_bytes -= count + 1;
220 block_iter_next_line(&bi);
221 }
222
223 if (!(flags & REPLACE_CONFIRM)) {
224 end_change_chain(view);
225 }
226
227 regfree(&re);
228
229 if (nr_substitutions) {
230 info_msg (
231 "%u substitution%s on %zu line%s",
232 nr_substitutions,
233 (nr_substitutions > 1) ? "s" : "",
234 nr_lines,
235 (nr_lines > 1) ? "s" : ""
236 );
237 } else if (!(flags & REPLACE_CANCEL)) {
238 error_msg("Pattern '%s' not found", pattern);
239 }
240
241 if (view->selection) {
242 // Undo what init_selection() did
243 if (view->sel_eo) {
244 view->sel_eo--;
245 }
246 if (swapped) {
247 ssize_t tmp = view->sel_so;
248 view->sel_so = view->sel_eo;
249 view->sel_eo = tmp;
250 }
251 block_iter_goto_offset(&view->cursor, view->sel_eo);
252 view->sel_eo = SEL_EO_RECALC;
253 }
254
255 return (nr_substitutions > 0);
256}