forked from panda3d/panda3d
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsedCommand.cxx
More file actions
341 lines (299 loc) · 9.21 KB
/
sedCommand.cxx
File metadata and controls
341 lines (299 loc) · 9.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
// Filename: sedCommand.cxx
// Created by: drose (24Oct00)
//
////////////////////////////////////////////////////////////////////
#include "sedCommand.h"
#include "sedAddress.h"
#include "sedContext.h"
#include "sedScript.h"
////////////////////////////////////////////////////////////////////
// Function: SedCommand::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
SedCommand::
SedCommand() {
_addr1 = (SedAddress *)NULL;
_addr2 = (SedAddress *)NULL;
_command = '\0';
_flags = 0;
_active = false;
}
////////////////////////////////////////////////////////////////////
// Function: SedCommand::Destructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
SedCommand::
~SedCommand() {
if (_addr1 != (SedAddress *)NULL) {
delete _addr1;
}
if (_addr2 != (SedAddress *)NULL) {
delete _addr2;
}
if ((_flags & F_have_re) != 0) {
regfree(&_re);
}
}
////////////////////////////////////////////////////////////////////
// Function: SedCommand::parse_command
// Access: Public
// Description: Scans the indicated string at the given character
// position for a legal command. If a legal command is
// found, stores it and increments p to the first
// non-whitespace character after the command, returning
// true. Otherwise, returns false.
////////////////////////////////////////////////////////////////////
bool SedCommand::
parse_command(const string &line, size_t &p) {
// First, skip initial whitespace.
while (p < line.length() && isspace(line[p])) {
p++;
}
// Now, check for an address.
if (p < line.length() &&
(isdigit(line[p]) || line[p] == '/' || line[p] == '\\')) {
_addr1 = new SedAddress;
if (!_addr1->parse_address(line, p)) {
return false;
}
if (p < line.length() && line[p] == ',') {
// Another address.
// Skip the comma and more whitespace.
p++;
while (p < line.length() && isspace(line[p])) {
p++;
}
_addr2 = new SedAddress;
if (!_addr2->parse_address(line, p)) {
return false;
}
}
}
if (p >= line.length()) {
// It's a null command, which is acceptable; ignore it.
return true;
}
_command = line[p];
// Skip more whitespace after the command letter.
p++;
while (p < line.length() && isspace(line[p])) {
p++;
}
// At the moment, we only accept a small subset of sed commands. We
// can add more later as we see the need.
switch (_command) {
case 'd':
// No arguments.
return true;
case 's':
// /regexp/repl/flags
return parse_s_params(line, p);
default:
cerr << "Unknown command: " << _command << "\n";
return false;
}
}
////////////////////////////////////////////////////////////////////
// Function: SedCommand::run
// Access: Public
// Description: Runs the script command, modifying the context and/or
// the script position as appropriate.
////////////////////////////////////////////////////////////////////
void SedCommand::
run(SedScript &script, SedContext &context) {
// First, see if this command matches the pattern space.
bool matches = false;
if (_addr1 != (SedAddress *)NULL && _addr2 != (SedAddress *)NULL) {
// If the user supplied two addresses, all lines inclusive between
// the lines matched by the two addresses are considered matching.
if (_active) {
// We have previously matched _addr1. Therefore this line is
// in, but are the rest of the lines following this one?
matches = true;
if (_addr2->matches(context)) {
// If this line matches addr2, that's the end of our range for
// next time.
_active = false;
}
} else {
// We have not yet matched _addr1. This line and subsequent
// lines are in only if we match now.
if (_addr1->matches(context)) {
matches = true;
if (!_addr2->precedes(context)) {
_active = true;
}
}
}
} else if (_addr1 != (SedAddress *)NULL) {
// If the user supplied only one address, only those lines that
// exactly match the address are considered matching.
matches = _addr1->matches(context);
} else {
// If the user supplied no addresses, all lines are considered
// matching.
matches = true;
}
if (matches) {
do_command(script, context);
}
}
////////////////////////////////////////////////////////////////////
// Function: SedCommand::parse_s_params
// Access: Private
// Description: Parses the /regexp/replacement/flags parameters that
// follow the 's' command.
////////////////////////////////////////////////////////////////////
bool SedCommand::
parse_s_params(const string &line, size_t &p) {
size_t p0 = p;
char delimiter = line[p];
p++;
if (p < line.length() && delimiter == '\\') {
// A backslash might escape the opening character.
delimiter = line[p];
p++;
}
size_t begin = p;
while (p < line.length() && line[p] != delimiter) {
if (line[p] == '\\') {
p++;
// A backslash could escape the closing character.
}
p++;
}
if (p >= line.length()) {
cerr << "Could not find terminating character '" << delimiter
<< "' in regular expression: " << line.substr(p0) << "\n";
return false;
}
string re = line.substr(begin, p - begin);
p++;
int error = regcomp(&_re, re.c_str(), 0);
if (error != 0) {
static const int errbuf_size = 512;
char errbuf[errbuf_size];
regerror(error, &_re, errbuf, errbuf_size);
cerr << "Invalid regular expression: " << re << "\n"
<< errbuf << "\n";
return false;
}
_flags |= F_have_re;
// Get the replacement string.
begin = p;
while (p < line.length() && line[p] != delimiter) {
if (line[p] == '\\') {
p++;
// A backslash could escape the closing character.
}
p++;
}
if (p >= line.length()) {
cerr << "Could not find terminating character '" << delimiter
<< "' in replacement string: " << line.substr(p0) << "\n";
return false;
}
_string2 = line.substr(begin, p - begin);
// Skip the final delimiter.
p++;
if (p < line.length() && line[p] == 'g') {
// Global flag.
p++;
_flags |= F_g;
}
// Skip any more whitespace after the parameters.
while (p < line.length() && isspace(line[p])) {
p++;
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: SedCommand::do_command
// Access: Private
// Description: Actually invokes the command, once it has been
// determined that the command applied to the current
// pattern space.
////////////////////////////////////////////////////////////////////
void SedCommand::
do_command(SedScript &script, SedContext &context) {
switch (_command) {
case '\0':
// Null command.
return;
case 'd':
// Delete line.
context._deleted = true;
script._next_command = script._commands.end();
return;
case 's':
// Substitute.
do_s_command(context);
return;
}
cerr << "Undefined command: " << _command << "\n";
}
////////////////////////////////////////////////////////////////////
// Function: SedCommand::do_s_command
// Access: Private
// Description: Invokes the s command, which performs a
// pattern/replacement substitution.
////////////////////////////////////////////////////////////////////
void SedCommand::
do_s_command(SedContext &context) {
size_t nmatch = _re.re_nsub + 1;
regmatch_t *pmatch = new regmatch_t[nmatch];
string result;
const char *str = context._pattern_space.c_str();
int error = regexec(&_re, str, nmatch, pmatch, 0);
while (error == 0) {
// Here's a match. Determine the replacement.
string repl;
size_t p = 0;
while (p < _string2.length()) {
if (_string2[p] == '\\') {
p++;
if (p < _string2.length()) {
if (isdigit(_string2[p])) {
// Here's a subexpression reference.
const char *numstr = _string2.c_str() + p;
char *numend;
int ref = strtol(numstr, &numend, 10);
p += (numend - numstr);
if (ref <= 0 || ref >= (int)nmatch) {
cerr << "Invalid subexpression number: " << ref << "\n";
} else {
repl += string(str + pmatch[ref].rm_so,
pmatch[ref].rm_eo - pmatch[ref].rm_so);
}
} else {
// Here's an escaped character.
repl += _string2[p];
p++;
}
}
} else {
// Here's a normal character.
repl += _string2[p];
p++;
}
}
// Store the result so far.
result += string(str, pmatch[0].rm_so);
result += repl;
str += pmatch[0].rm_eo;
if ((_flags & F_g) == 0) {
// If we don't have the global flag set, stop after the first iteration.
result += str;
context._pattern_space = result;
delete[] pmatch;
return;
}
error = regexec(&_re, str, nmatch, pmatch, 0);
}
// All done.
result += str;
context._pattern_space = result;
delete[] pmatch;
}