source: trunk/src/sed/sed/debug.c

Last change on this file was 3613, checked in by bird, 10 months ago

src/sed: Merged in changes between 4.1.5 and 4.9 from the vendor branch. (svn merge /vendor/sed/4.1.5 /vendor/sed/current .)

File size: 9.1 KB
RevLine 
[3611]1/* GNU SED, a batch stream editor.
2 Copyright (C) 2018-2022 Free Software Foundation, Inc.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; If not, see <https://www.gnu.org/licenses/>. */
16
17/* Written by Assaf Gordon. */
18
19/* debug.c: debugging functions */
20
21#include "sed.h"
22#include "basicdefs.h"
23#include <stdio.h>
24#include <ctype.h>
25#include <string.h>
26#include <stdlib.h>
27#include <sys/types.h>
28#include <assert.h>
29
30/* indentation level when printing the program */
31static int block_level = 0;
32
33
34void
35debug_print_char (char c)
36{
37 if (ISPRINT (c) && c != '\\')
38 {
39 putchar (c);
40 return;
41 }
42
43 putchar ('\\');
44 switch (c)
45 {
46 case '\a':
47 putchar ('a');
48 break;
49 case '\f':
50 putchar ('f');
51 break;
52 case '\r':
53 putchar ('r');
54 break;
55 case '\t':
56 putchar ('t');
57 break;
58 case '\v':
59 putchar ('v');
60 break;
61 case '\n':
62 putchar ('n');
63 break;
64 case '\\':
65 putchar ('\\');
66 break;
67
68 default:
69 printf ("o%03o", (unsigned int) c);
70 }
71}
72
73static void
74debug_print_regex_pattern (const char *pat, size_t len)
75{
76 const char *p = pat;
77 while (len--)
78 {
79 if (*p == '/')
80 fputs ("\\/", stdout);
81 else
82 debug_print_char (*p);
83 ++p;
84 }
85}
86
87static void
88debug_print_regex_flags (const struct regex *r, bool addr)
89{
90 if (!r)
91 return;
92
93#ifdef REG_PERL
94 if (r->flags & REG_DOTALL) /* REG_PERL */
95 putchar ('s');
96 if (r->flags & REG_EXTENDED) /* REG_PERL */
97 putchar ('x');
98#endif
99
100 if (r->flags & REG_ICASE)
101 putchar (addr ? 'I' : 'i');
102 if (r->flags & REG_NEWLINE)
103 putchar (addr ? 'M' : 'm');
104}
105
106static void
107debug_print_regex (const struct regex *r)
108{
109 if (!r)
110 {
111 /* Previous Regex */
112 fputs ("//", stdout);
113 return;
114 }
115
116 putchar ('/');
117 debug_print_regex_pattern (r->re, r->sz);
118 putchar ('/');
119}
120
121static void
122debug_print_addr (const struct addr *a)
123{
124 if (!a)
125 return;
126 switch (a->addr_type)
127 {
128 case ADDR_IS_NULL:
129 fputs ("[ADDR-NULL]", stdout);
130 break;
131 case ADDR_IS_REGEX:
132 debug_print_regex (a->addr_regex);
133 debug_print_regex_flags (a->addr_regex, true);
134 break;
135 case ADDR_IS_NUM:
136 printf ("%lu", a->addr_number);
137 break;
138 case ADDR_IS_NUM_MOD:
139 printf ("%lu~%lu", a->addr_number, a->addr_step);
140 break;
141 case ADDR_IS_STEP:
142 printf ("+%lu", a->addr_step);
143 break;
144 case ADDR_IS_STEP_MOD:
145 printf ("~%lu", a->addr_step);
146 break;
147 case ADDR_IS_LAST:
148 putchar ('$');
149 break;
150 }
151}
152
153static void
154debug_print_subst_replacement (const struct replacement *r)
155{
156 enum replacement_types last_repl_type = REPL_ASIS;
157
158 if (!r)
159 return;
160
161 const struct replacement *p = r;
162 while (p)
163 {
164 if (p->repl_type != last_repl_type)
165 {
166 /* Special GNU replacements \E\U\u\L\l should be printed
167 BEFORE the 'prefix' .... the 'prefix' refers to being
168 before the backreference. */
169 putchar ('\\');
170 if (p->repl_type == 0)
171 putchar ('E');
172 else if (p->repl_type == REPL_UPPERCASE)
173 putchar ('U');
174 else if (p->repl_type == REPL_LOWERCASE)
175 putchar ('L');
176 else if ((p->repl_type & REPL_MODIFIERS) == REPL_UPPERCASE_FIRST)
177 putchar ('u');
178 else if ((p->repl_type & REPL_MODIFIERS) == REPL_LOWERCASE_FIRST)
179 putchar ('l');
180
181 last_repl_type = p->repl_type;
182 }
183
184 if (p->prefix_length)
185 fwrite (p->prefix, 1, p->prefix_length, stdout);
186
187 if (p->subst_id != -1)
188 {
189 if (p->subst_id == 0)
190 putchar ('&');
191 else
192 printf ("\\%d", p->subst_id);
193 }
194
195 p = p->next;
196 }
197}
198
199static void
200debug_print_output_file (const struct output *o)
201{
202 if (!o)
203 return;
204
205 fputs (o->name, stdout);
206}
207
208static void
209debug_print_subst (const struct subst *s)
210{
211 if (!s)
212 return;
213
214 debug_print_regex (s->regx);
215 debug_print_subst_replacement (s->replacement);
216 putchar ('/');
217
218 debug_print_regex_flags (s->regx, false);
219
220 if (s->global)
221 putchar ('g');
222 if (s->eval)
223 putchar ('e');
224 if (s->print)
225 putchar ('p');
226 if (s->numb)
227 printf ("%lu", s->numb);
228 if (s->outf)
229 {
230 putchar ('w');
231 debug_print_output_file (s->outf);
232 }
233}
234
235static void
236debug_print_translation (const struct sed_cmd *sc)
237{
238 unsigned int i;
239
240 if (mb_cur_max > 1)
241 {
242 /* multibyte translation */
243 putchar ('/');
244 for (i = 0; sc->x.translatemb[2 * i] != NULL; i++)
245 fputs (sc->x.translatemb[2 * i], stdout);
246 putchar ('/');
247 for (i = 0; sc->x.translatemb[2 * i] != NULL; i++)
248 fputs (sc->x.translatemb[2 * i + 1], stdout);
249 putchar ('/');
250 }
251 else
252 {
253 /* unibyte translation */
254 putchar ('/');
255 for (i = 0; i < 256; ++i)
256 if (sc->x.translate[i] != (unsigned char) i)
257 putchar ((unsigned char) i);
258 putchar ('/');
259 for (i = 0; i < 256; ++i)
260 if (sc->x.translate[i] != (unsigned char) i)
261 putchar (sc->x.translate[i]);
262 putchar ('/');
263 }
264}
265
266static void
267debug_print_function (const struct vector *program, const struct sed_cmd *sc)
268{
269 if (!sc)
270 return;
271
272 putchar (sc->cmd);
273
274 switch (sc->cmd) /* LCOV_EXCL_BR */
275 {
276 case '=':
277 break;
278
279 case ':':
280 printf ("%s", sc->x.label_name);
281 break;
282
283 case '{':
284 break;
285
286 case '}':
287 break;
288
289 case '#': /* LCOV_EXCL_LINE */
290 /* should not happen - discarded during compilation. */
291 assert (0); /* LCOV_EXCL_LINE */
292
293 case 'a':
294 case 'c':
295 case 'i':
296 fputs ("\\", stdout);
297 if (sc->x.cmd_txt.text_length)
298 fwrite (sc->x.cmd_txt.text, 1, sc->x.cmd_txt.text_length, stdout);
299 break;
300
301 case 'b':
302 case 't':
303 case 'T':
304 {
305 if (sc->x.jump_index < program->v_length)
306 {
307 const char *label_name = program->v[sc->x.jump_index].x.label_name;
308 if (label_name)
309 printf (" %s", label_name);
310 }
311 }
312 break;
313
314 case 'D':
315 break;
316
317 case 'd':
318 break;
319
320 case 'e':
321 putchar (' ');
322 fwrite (sc->x.cmd_txt.text, 1, sc->x.cmd_txt.text_length, stdout);
323 break;
324
325 case 'F':
326 break;
327
328 case 'g':
329 break;
330
331 case 'G':
332 break;
333
334 case 'h':
335 break;
336
337 case 'H':
338 break;
339
340 /* 'i' is lumped above with 'a' and 'c' */
341
342 case 'L':
343 case 'l':
344 case 'q':
345 case 'Q':
346 if (sc->x.int_arg != -1)
347 printf (" %d", sc->x.int_arg);
348 break;
349
350 case 'n':
351 break;
352
353 case 'N':
354 break;
355
356 case 'P':
357 break;
358
359 case 'p':
360 break;
361
362 /* 'q','Q' are lumped above with 'L' and 'l' */
363
364 case 'r':
365 putchar (' ');
366 fputs (sc->x.readcmd.fname, stdout);
367 break;
368
369 case 'R':
370 putchar (' ');
371 fputs (sc->x.inf->name, stdout);
372 break;
373
374 case 's':
375 debug_print_subst (sc->x.cmd_subst);
376 break;
377
378 /* 't','T' are lumped above with 'b' */
379
380 case 'v': /* LCOV_EXCL_LINE */
381 /* should not happen - handled during compilation then discarded. */
382 assert (0); /* LCOV_EXCL_LINE */
383
384 case 'W':
385 debug_print_output_file (sc->x.outf);
386 break;
387
388 case 'w':
389 debug_print_output_file (sc->x.outf);
390 break;
391
392 case 'x':
393 break;
394
395 case 'y':
396 debug_print_translation (sc);
397 break;
398
399 case 'z':
400 break;
401
402 default: /* LCOV_EXCL_LINE */
403 /* should not happen - unless missed a sed command. */
404 assert (0); /* LCOV_EXCL_LINE */
405 }
406}
407
408void
409debug_print_command (const struct vector *program, const struct sed_cmd *sc)
410{
411 bool addr_bang;
412 if (!program)
413 return;
414
415 if (sc->cmd == '}')
416 --block_level;
417
418 for (int j = 0; j < block_level; ++j)
419 fputs (" ", stdout);
420
421 debug_print_addr (sc->a1);
422 if (sc->a2)
423 putchar (',');
424 debug_print_addr (sc->a2);
425
426 addr_bang = sc->addr_bang;
427 /* Implmentation detail: GNU Sed implements beginning of block
428 by negating the matched address and jumping if there's no match. */
429 if (sc->cmd == '{')
430 addr_bang = !addr_bang;
431 if (addr_bang)
432 putchar ('!');
433
434 if (sc->a1 || sc->a2)
435 putchar (' ');
436
437 debug_print_function (program, sc);
438
439 putchar ('\n');
440
441 if (sc->cmd == '{')
442 ++block_level;
443}
444
445void
446debug_print_program (const struct vector *program)
447{
448 if (!program)
449 return;
450
451 block_level = 1;
452 puts ("SED PROGRAM:");
453 for (size_t i = 0; i < program->v_length; ++i)
454 debug_print_command (program, &program->v[i]);
455 block_level = 0;
456}
Note: See TracBrowser for help on using the repository browser.