1 /*  GNU SED, a batch stream editor.
2     Copyright (C) 1989-2019 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 #define INITIAL_BUFFER_SIZE	50
18 #define FREAD_BUFFER_SIZE	8192
19 
20 #include "sed.h"
21 
22 #include <stddef.h>
23 #include <stdio.h>
24 #include <ctype.h>
25 #include <unistd.h>
26 #include <errno.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include "stat-macros.h"
32 
33 #include <selinux/selinux.h>
34 #include <selinux/context.h>
35 #include "acl.h"
36 #include "ignore-value.h"
37 #include "progname.h"
38 #include "xalloc.h"
39 
40 /* The number of extra bytes that must be allocated/usable, beyond
41    the declared "end" of each line buffer that may be passed to
42    match_regex.  This is imposed by its use of dfaexec.  */
43 #define DFA_SLOP 1
44 
45 /* Sed operates a line at a time. */
46 struct line {
47   char *text;		/* Pointer to line allocated by malloc. */
48   char *active;		/* Pointer to non-consumed part of text. */
49   size_t length;	/* Length of text (or active, if used). */
50   size_t alloc;		/* Allocated space for active. */
51   bool chomped;		/* Was a trailing newline dropped? */
52   mbstate_t mbstate;
53 };
54 
55 #define SIZEOF_LINE	offsetof (struct line, mbstate)
56 
57 /* A queue of text to write out at the end of a cycle
58    (filled by the "a", "r" and "R" commands.) */
59 struct append_queue {
60   const char *fname;
61   char *text;
62   size_t textlen;
63   struct append_queue *next;
64   bool free;
65 };
66 
67 /* State information for the input stream. */
68 struct input {
69   /* The list of yet-to-be-opened files.  It is invalid for file_list
70      to be NULL.  When *file_list is NULL we are currently processing
71      the last file.  */
72 
73   char **file_list;
74 
75   /* Count of files we failed to open. */
76   countT bad_count;
77 
78   /* Current input line number (over all files).  */
79   countT line_number;
80 
81   /* True if we'll reset line numbers and addresses before
82      starting to process the next (possibly the first) file.  */
83   bool reset_at_next_file;
84 
85   /* Function to read one line.  If FP is NULL, read_fn better not
86      be one which uses fp; in particular, read_always_fail() is
87      recommended. */
88   bool (*read_fn) (struct input *);	/* read one line */
89 
90   char *out_file_name;
91 
92   const char *in_file_name;
93 
94   /* Owner and mode to be set just before closing the file.  */
95   struct stat st;
96 
97   /* if NULL, none of the following are valid */
98   FILE *fp;
99 
100   bool no_buffering;
101 };
102 
103 
104 /* Have we done any replacements lately?  This is used by the `t' command. */
105 static bool replaced = false;
106 
107 /* The current output file (stdout if -i is not being used. */
108 static struct output output_file;
109 
110 /* The `current' input line. */
111 static struct line line;
112 
113 /* An input line used to accumulate the result of the s and e commands. */
114 static struct line s_accum;
115 
116 /* An input line that's been stored by later use by the program */
117 static struct line hold;
118 
119 /* The buffered input look-ahead.  The only field that should be
120    used outside of read_mem_line() or line_init() is buffer.length. */
121 static struct line buffer;
122 
123 static struct append_queue *append_head = NULL;
124 static struct append_queue *append_tail = NULL;
125 
126 /* increase a struct line's length, making some attempt at
127    keeping realloc() calls under control by padding for future growth.  */
128 static void
resize_line(struct line *lb, size_t len)129 resize_line (struct line *lb, size_t len)
130 {
131   int inactive;
132   inactive = lb->active - lb->text;
133 
134   /* If the inactive part has got to more than two thirds of the buffer,
135    * remove it. */
136   if (inactive > lb->alloc * 2)
137     {
138       memmove (lb->text, lb->active, lb->length);
139       lb->alloc += lb->active - lb->text;
140       lb->active = lb->text;
141       inactive = 0;
142 
143       if (lb->alloc > len)
144         return;
145     }
146 
147   lb->alloc *= 2;
148   if (lb->alloc < len)
149     lb->alloc = len;
150   if (lb->alloc < INITIAL_BUFFER_SIZE)
151     lb->alloc = INITIAL_BUFFER_SIZE;
152 
153   lb->text = REALLOC (lb->text, inactive + lb->alloc + DFA_SLOP, char);
154   lb->active = lb->text + inactive;
155 }
156 
157 /* Append LENGTH bytes from STRING to the line, TO.  */
158 static void
str_append(struct line *to, const char *string, size_t length)159 str_append (struct line *to, const char *string, size_t length)
160 {
161   size_t new_length = to->length + length;
162 
163   if (to->alloc < new_length)
164     resize_line (to, new_length);
165   memcpy (to->active + to->length, string, length);
166   to->length = new_length;
167 
168   if (mb_cur_max > 1 && !is_utf8)
169     while (length)
170       {
171         size_t n = MBRLEN (string, length, &to->mbstate);
172 
173         /* Treat an invalid or incomplete sequence like a
174            single-byte character.  */
175         if (n == (size_t) -1 || n == (size_t) -2)
176           {
177             memset (&to->mbstate, 0, sizeof (to->mbstate));
178             n = 1;
179           }
180 
181         if (n == 0)
182           break;
183 
184         string += n;
185         length -= n;
186       }
187 }
188 
189 static void
str_append_modified(struct line *to, const char *string, size_t length, enum replacement_types type)190 str_append_modified (struct line *to, const char *string, size_t length,
191                      enum replacement_types type)
192 {
193   mbstate_t from_stat;
194 
195   if (type == REPL_ASIS)
196     {
197       str_append (to, string, length);
198       return;
199     }
200 
201   if (to->alloc - to->length < length * mb_cur_max)
202     resize_line (to, to->length + length * mb_cur_max);
203 
204   memcpy (&from_stat, &to->mbstate, sizeof (mbstate_t));
205   while (length)
206     {
207       wchar_t wc;
208       size_t n = MBRTOWC (&wc, string, length, &from_stat);
209 
210       /* Treat an invalid sequence like a single-byte character.  */
211       if (n == (size_t) -1)
212         {
213           type &= ~(REPL_LOWERCASE_FIRST | REPL_UPPERCASE_FIRST);
214           if (type == REPL_ASIS)
215             {
216               str_append (to, string, length);
217               return;
218             }
219 
220           str_append (to, string, 1);
221           memset (&to->mbstate, 0, sizeof (from_stat));
222           n = 1;
223           string += n, length -= n;
224           continue;
225         }
226 
227       if (n == 0 || n == (size_t) -2)
228         {
229           /* L'\0' or an incomplete sequence: copy it manually.  */
230           str_append (to, string, length);
231           return;
232         }
233 
234       string += n, length -= n;
235 
236       /* Convert the first character specially... */
237       if (type & (REPL_UPPERCASE_FIRST | REPL_LOWERCASE_FIRST))
238         {
239           if (type & REPL_UPPERCASE_FIRST)
240             wc = towupper (wc);
241           else
242             wc = towlower (wc);
243 
244           type &= ~(REPL_LOWERCASE_FIRST | REPL_UPPERCASE_FIRST);
245           if (type == REPL_ASIS)
246             {
247               /* Copy the new wide character to the end of the string. */
248               n = WCRTOMB (to->active + to->length, wc, &to->mbstate);
249               to->length += n;
250               if (n == (size_t) -1 || n == (size_t) -2)
251                 {
252                   fprintf (stderr,
253                            _("case conversion produced an invalid character"));
254                   abort ();
255                 }
256               str_append (to, string, length);
257               return;
258             }
259         }
260       else if (type & REPL_UPPERCASE)
261         wc = towupper (wc);
262       else
263         wc = towlower (wc);
264 
265       /* Copy the new wide character to the end of the string. */
266       n = WCRTOMB (to->active + to->length, wc, &to->mbstate);
267       to->length += n;
268       if (n == -1 || n == -2)
269         {
270           fprintf (stderr, _("case conversion produced an invalid character"));
271           abort ();
272         }
273     }
274 }
275 
276 /* Initialize a "struct line" buffer.  Copy multibyte state from `state'
277    if not null.  */
278 static void
line_init(struct line *buf, struct line *state, size_t initial_size)279 line_init (struct line *buf, struct line *state, size_t initial_size)
280 {
281   buf->text = XCALLOC (initial_size + DFA_SLOP, char);
282   buf->active = buf->text;
283   buf->alloc = initial_size;
284   buf->length = 0;
285   buf->chomped = true;
286 
287   if (state)
288     memcpy (&buf->mbstate, &state->mbstate, sizeof (buf->mbstate));
289   else
290     memset (&buf->mbstate, 0, sizeof (buf->mbstate));
291 }
292 
293 /* Reset a "struct line" buffer to length zero.  Copy multibyte state from
294    `state' if not null.  */
295 static void
line_reset(struct line *buf, struct line *state)296 line_reset (struct line *buf, struct line *state)
297 {
298   if (buf->alloc == 0)
299     line_init (buf, state, INITIAL_BUFFER_SIZE);
300   else
301     {
302       buf->length = 0;
303       if (state)
304         memcpy (&buf->mbstate, &state->mbstate, sizeof (buf->mbstate));
305       else
306         memset (&buf->mbstate, 0, sizeof (buf->mbstate));
307     }
308 }
309 
310 /* Copy the contents of the line `from' into the line `to'.
311    This destroys the old contents of `to'.
312    Copy the multibyte state if `state' is true. */
313 static void
line_copy(struct line *from, struct line *to, int state)314 line_copy (struct line *from, struct line *to, int state)
315 {
316   /* Remove the inactive portion in the destination buffer. */
317   to->alloc += to->active - to->text;
318 
319   if (to->alloc < from->length)
320     {
321       to->alloc *= 2;
322       if (to->alloc < from->length)
323         to->alloc = from->length;
324       if (to->alloc < INITIAL_BUFFER_SIZE)
325         to->alloc = INITIAL_BUFFER_SIZE;
326       /* Use free()+MALLOC() instead of REALLOC() to
327          avoid unnecessary copying of old text. */
328       free (to->text);
329       to->text = XCALLOC (to->alloc + DFA_SLOP, char);
330     }
331 
332   to->active = to->text;
333   to->length = from->length;
334   to->chomped = from->chomped;
335   memcpy (to->active, from->active, from->length);
336 
337   if (state)
338     memcpy (&to->mbstate, &from->mbstate, sizeof (from->mbstate));
339 }
340 
341 /* Append the contents of the line `from' to the line `to'.
342    Copy the multibyte state if `state' is true. */
343 static void
line_append(struct line *from, struct line *to, int state)344 line_append (struct line *from, struct line *to, int state)
345 {
346   str_append (to, &buffer_delimiter, 1);
347   str_append (to, from->active, from->length);
348   to->chomped = from->chomped;
349 
350   if (state)
351     memcpy (&to->mbstate, &from->mbstate, sizeof (from->mbstate));
352 }
353 
354 /* Exchange two "struct line" buffers.
355    Copy the multibyte state if `state' is true. */
356 static void
line_exchange(struct line *a, struct line *b, int state)357 line_exchange (struct line *a, struct line *b, int state)
358 {
359   struct line t;
360 
361   if (state)
362     {
363       memcpy (&t,  a, sizeof (struct line));
364       memcpy ( a,  b, sizeof (struct line));
365       memcpy ( b, &t, sizeof (struct line));
366     }
367   else
368     {
369       memcpy (&t,  a, SIZEOF_LINE);
370       memcpy ( a,  b, SIZEOF_LINE);
371       memcpy ( b, &t, SIZEOF_LINE);
372     }
373 }
374 
375 /* dummy function to simplify read_pattern_space() */
376 static bool
read_always_fail(struct input *input _GL_UNUSED)377 read_always_fail (struct input *input _GL_UNUSED)
378 {
379   return false;
380 }
381 
382 static bool
read_file_line(struct input *input)383 read_file_line (struct input *input)
384 {
385   static char *b;
386   static size_t blen;
387 
388   long result = ck_getdelim (&b, &blen, buffer_delimiter, input->fp);
389   if (result <= 0)
390     return false;
391 
392   /* Remove the trailing new-line that is left by getline. */
393   if (b[result - 1] == buffer_delimiter)
394     --result;
395   else
396     line.chomped = false;
397 
398   str_append (&line, b, result);
399   return true;
400 }
401 
402 static inline void
output_missing_newline(struct output *outf)403 output_missing_newline (struct output *outf)
404 {
405   if (outf->missing_newline)
406     {
407       ck_fwrite (&buffer_delimiter, 1, 1, outf->fp);
408       outf->missing_newline = false;
409     }
410 }
411 
412 static inline void
flush_output(FILE *fp)413 flush_output (FILE *fp)
414 {
415   if (unbuffered)
416     ck_fflush (fp);
417 }
418 
419 static void
output_line(const char *text, size_t length, int nl, struct output *outf)420 output_line (const char *text, size_t length, int nl, struct output *outf)
421 {
422   if (!text)
423     return;
424 
425   output_missing_newline (outf);
426   if (length)
427     ck_fwrite (text, 1, length, outf->fp);
428   if (nl)
429     ck_fwrite (&buffer_delimiter, 1, 1, outf->fp);
430   else
431     outf->missing_newline = true;
432 
433   flush_output (outf->fp);
434 }
435 
436 static struct append_queue *
next_append_slot(void)437 next_append_slot (void)
438 {
439   struct append_queue *n = XCALLOC (1, struct append_queue);
440 
441   n->fname = NULL;
442   n->text = NULL;
443   n->textlen = 0;
444   n->next = NULL;
445   n->free = false;
446 
447   if (append_tail)
448       append_tail->next = n;
449   else
450       append_head = n;
451   return append_tail = n;
452 }
453 
454 static void
release_append_queue(void)455 release_append_queue (void)
456 {
457   struct append_queue *p, *q;
458 
459   for (p=append_head; p; p=q)
460     {
461       if (p->free)
462         free (p->text);
463 
464       q = p->next;
465       free (p);
466     }
467   append_head = append_tail = NULL;
468 }
469 
470 static void
dump_append_queue(void)471 dump_append_queue (void)
472 {
473   struct append_queue *p;
474 
475   output_missing_newline (&output_file);
476   for (p=append_head; p; p=p->next)
477     {
478       if (p->text)
479         ck_fwrite (p->text, 1, p->textlen, output_file.fp);
480 
481       if (p->fname)
482         {
483           char buf[FREAD_BUFFER_SIZE];
484           size_t cnt;
485           FILE *fp;
486 
487           /* "If _fname_ does not exist or cannot be read, it shall
488              be treated as if it were an empty file, causing no error
489              condition."  IEEE Std 1003.2-1992
490              So, don't fail. */
491           fp = ck_fopen (p->fname, read_mode, false);
492           if (fp)
493             {
494               while ((cnt = ck_fread (buf, 1, sizeof buf, fp)) > 0)
495                 ck_fwrite (buf, 1, cnt, output_file.fp);
496               ck_fclose (fp);
497             }
498         }
499     }
500 
501   flush_output (output_file.fp);
502   release_append_queue ();
503 }
504 
505 /* Compute the name of the backup file for in-place editing */
506 static char *
get_backup_file_name(const char *name)507 get_backup_file_name (const char *name)
508 {
509   char *old_asterisk, *asterisk, *backup, *p;
510   int name_length = strlen (name), backup_length = strlen (in_place_extension);
511 
512   /* Compute the length of the backup file */
513   for (asterisk = in_place_extension - 1, old_asterisk = asterisk + 1;
514        (asterisk = strchr (old_asterisk, '*'));
515        old_asterisk = asterisk + 1)
516     backup_length += name_length - 1;
517 
518   p = backup = xmalloc (backup_length + 1);
519 
520   /* Each iteration gobbles up to an asterisk */
521   for (asterisk = in_place_extension - 1, old_asterisk = asterisk + 1;
522        (asterisk = strchr (old_asterisk, '*'));
523        old_asterisk = asterisk + 1)
524     {
525       memcpy (p, old_asterisk, asterisk - old_asterisk);
526       p += asterisk - old_asterisk;
527       strcpy (p, name);
528       p += name_length;
529     }
530 
531   /* Tack on what's after the last asterisk */
532   strcpy (p, old_asterisk);
533   return backup;
534 }
535 
536 /* Initialize a struct input for the named file. */
537 static void
open_next_file(const char *name, struct input *input)538 open_next_file (const char *name, struct input *input)
539 {
540   buffer.length = 0;
541 
542   input->in_file_name = name;
543   if (name[0] == '-' && name[1] == '\0' && !in_place_extension)
544     {
545       clearerr (stdin);	/* clear any stale EOF indication */
546 #if defined WIN32 || defined _WIN32 || defined __CYGWIN__ \
547   || defined MSDOS || defined __EMX__
548       input->fp = ck_fdopen (fileno (stdin), "stdin", read_mode, false);
549 #else
550       input->fp = stdin;
551 #endif
552     }
553   else
554     {
555       if (follow_symlinks)
556         input->in_file_name = follow_symlink (name);
557 
558       if ( ! (input->fp = ck_fopen (name, read_mode, false)) )
559         {
560           const char *ptr = strerror (errno);
561           fprintf (stderr, _("%s: can't read %s: %s\n"), program_name,
562                    name, ptr);
563           input->read_fn = read_always_fail; /* a redundancy */
564           ++input->bad_count;
565           return;
566         }
567     }
568 
569   input->read_fn = read_file_line;
570 
571   if (in_place_extension)
572     {
573       int input_fd;
574       char *tmpdir, *p;
575       security_context_t old_fscreatecon;
576       int reset_fscreatecon = 0;
577       memset (&old_fscreatecon, 0, sizeof (old_fscreatecon));
578 
579       /* get the base name */
580       tmpdir = xstrdup (input->in_file_name);
581       if ((p = strrchr (tmpdir, '/')))
582         *p = 0;
583       else
584         strcpy (tmpdir, ".");
585 
586       if (isatty (fileno (input->fp)))
587         panic (_("couldn't edit %s: is a terminal"), input->in_file_name);
588 
589       input_fd = fileno (input->fp);
590       fstat (input_fd, &input->st);
591       if (!S_ISREG (input->st.st_mode))
592         panic (_("couldn't edit %s: not a regular file"), input->in_file_name);
593 
594       if (is_selinux_enabled () > 0)
595         {
596           security_context_t con;
597           if (lgetfilecon (input->in_file_name, &con) != -1)
598             {
599               /* Save and restore the old context for the sake of w and W
600                  commands.  */
601               reset_fscreatecon = getfscreatecon (&old_fscreatecon) >= 0;
602               if (setfscreatecon (con) < 0)
603                 fprintf (stderr, _("%s: warning: failed to set default" \
604                                    " file creation context to %s: %s"),
605                          program_name, con, strerror (errno));
606               freecon (con);
607             }
608           else
609             {
610               if (errno != ENOSYS)
611                 fprintf (stderr, _("%s: warning: failed to get" \
612                                    " security context of %s: %s"),
613                          program_name, input->in_file_name, strerror (errno));
614             }
615         }
616 
617       output_file.fp = ck_mkstemp (&input->out_file_name, tmpdir, "sed",
618                                    write_mode);
619       register_cleanup_file (input->out_file_name);
620       output_file.missing_newline = false;
621       free (tmpdir);
622 
623       if (reset_fscreatecon)
624         {
625           setfscreatecon (old_fscreatecon);
626           freecon (old_fscreatecon);
627         }
628 
629       if (!output_file.fp)
630         panic (_("couldn't open temporary file %s: %s"), input->out_file_name,
631                strerror (errno));
632     }
633   else
634     {
635       if (input->fp && unbuffered)
636         setvbuf (input->fp, NULL, _IONBF, 0);
637       output_file.fp = stdout;
638     }
639 }
640 
641 
642 /* Clean up an input stream that we are done with. */
643 static void
closedown(struct input *input)644 closedown (struct input *input)
645 {
646   input->read_fn = read_always_fail;
647   if (!input->fp)
648     return;
649 
650   if (in_place_extension && output_file.fp != NULL)
651     {
652       const char *target_name;
653       int input_fd, output_fd;
654 
655       target_name = input->in_file_name;
656       input_fd = fileno (input->fp);
657       output_fd = fileno (output_file.fp);
658 #ifdef HAVE_FCHOWN
659       /* Try to set both UID and GID, but if that fails,
660          try to set only the GID.  Ignore failure.  */
661       if (fchown (output_fd, input->st.st_uid, input->st.st_gid) == -1)
662         ignore_value (fchown (output_fd, -1, input->st.st_gid));
663 #endif
664       copy_acl (input->in_file_name, input_fd,
665                 input->out_file_name, output_fd,
666                 input->st.st_mode);
667 
668       ck_fclose (input->fp);
669       ck_fclose (output_file.fp);
670       if (strcmp (in_place_extension, "*") != 0)
671         {
672           char *backup_file_name = get_backup_file_name (target_name);
673           ck_rename (target_name, backup_file_name, input->out_file_name);
674           free (backup_file_name);
675         }
676 
677       ck_rename (input->out_file_name, target_name, input->out_file_name);
678       cancel_cleanup ();
679       free (input->out_file_name);
680     }
681   else
682     ck_fclose (input->fp);
683 
684   input->fp = NULL;
685 }
686 
687 /* Reset range commands so that they are marked as non-matching */
688 static void
reset_addresses(struct vector *vec)689 reset_addresses (struct vector *vec)
690 {
691   struct sed_cmd *cur_cmd;
692   int n;
693 
694   for (cur_cmd = vec->v, n = vec->v_length; n--; cur_cmd++)
695     if (cur_cmd->a1
696         && cur_cmd->a1->addr_type == ADDR_IS_NUM
697         && cur_cmd->a1->addr_number == 0)
698       cur_cmd->range_state = RANGE_ACTIVE;
699     else
700       cur_cmd->range_state = RANGE_INACTIVE;
701 }
702 
703 /* Read in the next line of input, and store it in the pattern space.
704    Return zero if there is nothing left to input. */
705 static bool
read_pattern_space(struct input *input, struct vector *the_program, int append)706 read_pattern_space (struct input *input, struct vector *the_program, int append)
707 {
708   if (append_head) /* redundant test to optimize for common case */
709     dump_append_queue ();
710   replaced = false;
711   if (!append)
712     line.length = 0;
713   line.chomped = true;  /* default, until proved otherwise */
714 
715   while ( ! (*input->read_fn)(input) )
716     {
717       closedown (input);
718 
719       if (!*input->file_list)
720         return false;
721 
722       if (input->reset_at_next_file)
723         {
724           input->line_number = 0;
725           hold.length = 0;
726           reset_addresses (the_program);
727           rewind_read_files ();
728 
729           /* If doing in-place editing, we will never append the
730              new-line to this file; but if the output goes to stdout,
731              we might still have to output the missing new-line.  */
732           if (in_place_extension)
733             output_file.missing_newline = false;
734 
735           input->reset_at_next_file = separate_files;
736         }
737 
738       open_next_file (*input->file_list++, input);
739     }
740 
741   ++input->line_number;
742   return true;
743 }
744 
745 static bool
last_file_with_data_p(struct input *input)746 last_file_with_data_p (struct input *input)
747 {
748   for (;;)
749     {
750       int ch;
751 
752       closedown (input);
753       if (!*input->file_list)
754         return true;
755       open_next_file (*input->file_list++, input);
756       if (input->fp)
757         {
758           if ((ch = getc (input->fp)) != EOF)
759             {
760               ungetc (ch, input->fp);
761               return false;
762             }
763         }
764     }
765 }
766 
767 /* Determine if we match the `$' address. */
768 static bool
test_eof(struct input *input)769 test_eof (struct input *input)
770 {
771   int ch;
772 
773   if (buffer.length)
774     return false;
775   if (!input->fp)
776     return separate_files || last_file_with_data_p (input);
777   if (feof (input->fp))
778     return separate_files || last_file_with_data_p (input);
779   if ((ch = getc (input->fp)) == EOF)
780     return separate_files || last_file_with_data_p (input);
781   ungetc (ch, input->fp);
782   return false;
783 }
784 
785 /* Return non-zero if the current line matches the address
786    pointed to by `addr'. */
787 static bool
match_an_address_p(struct addr *addr, struct input *input)788 match_an_address_p (struct addr *addr, struct input *input)
789 {
790   switch (addr->addr_type)
791     {
792     case ADDR_IS_NULL:
793       return true;
794 
795     case ADDR_IS_REGEX:
796       return match_regex (addr->addr_regex, line.active, line.length, 0,
797                          NULL, 0);
798 
799     case ADDR_IS_NUM_MOD:
800       return (input->line_number >= addr->addr_number
801               && ((input->line_number - addr->addr_number)
802                   % addr->addr_step) == 0);
803 
804     case ADDR_IS_STEP:
805     case ADDR_IS_STEP_MOD:
806       /* reminder: these are only meaningful for a2 addresses */
807       /* a2->addr_number needs to be recomputed each time a1 address
808          matches for the step and step_mod types */
809       return (addr->addr_number <= input->line_number);
810 
811     case ADDR_IS_LAST:
812       return test_eof (input);
813 
814     case ADDR_IS_NUM:
815       /* reminder: these are only meaningful for a1 addresses */
816       return (addr->addr_number == input->line_number);
817 
818     default:
819       panic ("INTERNAL ERROR: bad address type");
820     }
821   /*NOTREACHED*/
822   return false;
823 }
824 
825 /* return non-zero if current address is valid for cmd */
826 static bool
match_address_p(struct sed_cmd *cmd, struct input *input)827 match_address_p (struct sed_cmd *cmd, struct input *input)
828 {
829   if (!cmd->a1)
830     return true;
831 
832   if (cmd->range_state != RANGE_ACTIVE)
833     {
834       if (!cmd->a2)
835         return match_an_address_p (cmd->a1, input);
836 
837       /* Find if we are going to activate a range.  Handle ADDR_IS_NUM
838          specially: it represent an "absolute" state, it should not
839          be computed like regexes.  */
840       if (cmd->a1->addr_type == ADDR_IS_NUM)
841         {
842           if (cmd->range_state == RANGE_CLOSED
843               || input->line_number < cmd->a1->addr_number)
844             return false;
845         }
846       else
847         {
848           if (!match_an_address_p (cmd->a1, input))
849             return false;
850         }
851 
852       /* Ok, start a new range.  */
853       cmd->range_state = RANGE_ACTIVE;
854       switch (cmd->a2->addr_type)
855         {
856         case ADDR_IS_REGEX:
857           /* Always include at least two lines.  */
858           return true;
859         case ADDR_IS_NUM:
860           /* Same handling as below, but always include at least one line.  */
861           if (input->line_number >= cmd->a2->addr_number)
862             cmd->range_state = RANGE_CLOSED;
863           return (input->line_number <= cmd->a2->addr_number
864                   || match_an_address_p (cmd->a1, input));
865         case ADDR_IS_STEP:
866           cmd->a2->addr_number = input->line_number + cmd->a2->addr_step;
867           return true;
868         case ADDR_IS_STEP_MOD:
869           cmd->a2->addr_number = input->line_number + cmd->a2->addr_step
870                                  - (input->line_number%cmd->a2->addr_step);
871           return true;
872         default:
873           break;
874         }
875     }
876 
877   /* cmd->range_state == RANGE_ACTIVE.  Check if the range is
878      ending; also handle ADDR_IS_NUM specially in this case.  */
879 
880   if (cmd->a2->addr_type == ADDR_IS_NUM)
881     {
882       /* If the second address is a line number, and if we got past
883          that line, fail to match (it can happen when you jump
884          over such addresses with `b' and `t'.  Use RANGE_CLOSED
885          so that the range is not re-enabled anymore.  */
886       if (input->line_number >= cmd->a2->addr_number)
887         cmd->range_state = RANGE_CLOSED;
888 
889       return (input->line_number <= cmd->a2->addr_number);
890    }
891 
892   /* Other addresses are treated as usual.  */
893   if (match_an_address_p (cmd->a2, input))
894     cmd->range_state = RANGE_CLOSED;
895 
896   return true;
897 }
898 
899 static void
do_list(int line_len)900 do_list (int line_len)
901 {
902   unsigned char *p = (unsigned char *)line.active;
903   countT len = line.length;
904   countT width = 0;
905   char obuf[180];	/* just in case we encounter a 512-bit char (;-) */
906   char *o;
907   size_t olen;
908   FILE *fp = output_file.fp;
909 
910   output_missing_newline (&output_file);
911   for (; len--; ++p) {
912       o = obuf;
913 
914       /* Some locales define 8-bit characters as printable.  This makes the
915          testsuite fail at 8to7.sed because the `l' command in fact will not
916          convert the 8-bit characters. */
917 #if defined isascii || defined HAVE_ISASCII
918       if (isascii (*p) && ISPRINT (*p)) {
919 #else
920       if (ISPRINT (*p)) {
921 #endif
922           *o++ = *p;
923           if (*p == '\\')
924             *o++ = '\\';
925       } else {
926           *o++ = '\\';
927           switch (*p) {
928 #if defined __STDC__ && __STDC__-0
929             case '\a': *o++ = 'a'; break;
930 #else /* Not STDC; we'll just assume ASCII */
931             case 007:  *o++ = 'a'; break;
932 #endif
933             case '\b': *o++ = 'b'; break;
934             case '\f': *o++ = 'f'; break;
935             case '\n': *o++ = 'n'; break;
936             case '\r': *o++ = 'r'; break;
937             case '\t': *o++ = 't'; break;
938             case '\v': *o++ = 'v'; break;
939             default:
940               sprintf (o, "%03o", *p);
941               o += strlen (o);
942               break;
943             }
944       }
945       olen = o - obuf;
946       if (width+olen >= line_len && line_len > 0) {
947           ck_fwrite ("\\", 1, 1, fp);
948           ck_fwrite (&buffer_delimiter, 1, 1, fp);
949           width = 0;
950       }
951       ck_fwrite (obuf, 1, olen, fp);
952       width += olen;
953   }
954   ck_fwrite ("$", 1, 1, fp);
955   ck_fwrite (&buffer_delimiter, 1, 1, fp);
956   flush_output (fp);
957 }
958 
959 
960 static void append_replacement (struct line *buf, struct replacement *p,
961                                 struct re_registers *regs)
962 {
963   enum replacement_types repl_mod = 0;
964 
965   for (; p; p=p->next)
966     {
967       int i = p->subst_id;
968       enum replacement_types curr_type;
969 
970       /* Apply a \[lu] modifier that was given earlier, but which we
971          have not had yet the occasion to apply.  But don't do it
972          if this replacement has a modifier of its own. */
973       curr_type = (p->repl_type & REPL_MODIFIERS)
974         ? p->repl_type
975         : p->repl_type | repl_mod;
976 
977       repl_mod = 0;
978       if (p->prefix_length)
979         {
980           str_append_modified (buf, p->prefix, p->prefix_length,
981                                curr_type);
982           curr_type &= ~REPL_MODIFIERS;
983         }
984 
985       if (0 <= i && i < regs->num_regs)
986         {
987           if (regs->end[i] == regs->start[i] && p->repl_type & REPL_MODIFIERS)
988             /* Save this modifier, we shall apply it later.
989                e.g. in s/()([a-z])/\u\1\2/
990                the \u modifier is applied to \2, not \1 */
991             repl_mod = curr_type & REPL_MODIFIERS;
992 
993           else if (regs->end[i] != regs->start[i])
994             str_append_modified (buf, line.active + regs->start[i],
995                                  (size_t)(regs->end[i] - regs->start[i]),
996                                  curr_type);
997         }
998     }
999 }
1000 
1001 static void
1002 do_subst (struct subst *sub)
1003 {
1004   size_t start = 0;	/* where to start scan for (next) match in LINE */
1005   size_t last_end = 0;  /* where did the last successful match end in LINE */
1006   countT count = 0;	/* number of matches found */
1007   bool again = true;
1008 
1009   static struct re_registers regs;
1010 
1011   line_reset (&s_accum, &line);
1012 
1013   /* The first part of the loop optimizes s/xxx// when xxx is at the
1014      start, and s/xxx$// */
1015   if (!match_regex (sub->regx, line.active, line.length, start,
1016                     &regs, sub->max_id + 1))
1017     return;
1018 
1019   if (debug)
1020     {
1021       if (regs.num_regs>0 && regs.start[0] != -1)
1022         puts ("MATCHED REGEX REGISTERS");
1023 
1024       for (int i = 0; i < regs.num_regs; ++i)
1025         {
1026           if (regs.start[i] == -1)
1027             break;
1028 
1029           printf ("  regex[%d] = %d-%d '", i,
1030                   (int)regs.start[i], (int)regs.end[i]);
1031 
1032           if (regs.start[i] != regs.end[i])
1033             fwrite (line.active + regs.start[i], regs.end[i] -regs.start[i],
1034                     1, stdout);
1035 
1036           puts ("'");
1037         }
1038     }
1039 
1040   if (!sub->replacement && sub->numb <= 1)
1041     {
1042       if (regs.start[0] == 0 && !sub->global)
1043         {
1044           /* We found a match, set the `replaced' flag. */
1045           replaced = true;
1046 
1047           line.active += regs.end[0];
1048           line.length -= regs.end[0];
1049           line.alloc -= regs.end[0];
1050           goto post_subst;
1051         }
1052       else if (regs.end[0] == line.length)
1053         {
1054           /* We found a match, set the `replaced' flag. */
1055           replaced = true;
1056 
1057           line.length = regs.start[0];
1058           goto post_subst;
1059         }
1060     }
1061 
1062   do
1063     {
1064       size_t offset = regs.start[0];
1065       size_t matched = regs.end[0] - regs.start[0];
1066 
1067       /* Copy stuff to the left of this match into the output string. */
1068       if (start < offset)
1069         {
1070           str_append (&s_accum, line.active + start, offset - start);
1071           start = offset;
1072         }
1073 
1074       /* If we're counting up to the Nth match, are we there yet?
1075          And even if we are there, there is another case we have to
1076          skip: are we matching an empty string immediately following
1077          another match?
1078 
1079          This latter case avoids that baaaac, when passed through
1080          s,a*,x,g, gives `xbxxcx' instead of xbxcx.  This behavior is
1081          unacceptable because it is not consistently applied (for
1082          example, `baaaa' gives `xbx', not `xbxx'). */
1083       if ((matched > 0 || count == 0 || offset > last_end)
1084           && ++count >= sub->numb)
1085         {
1086           /* We found a match, set the `replaced' flag. */
1087           replaced = true;
1088 
1089           /* Now expand the replacement string into the output string. */
1090           append_replacement (&s_accum, sub->replacement, &regs);
1091           again = sub->global;
1092         }
1093       else
1094         {
1095           /* The match was not replaced.  Copy the text until its
1096              end; if it was vacuous, skip over one character and
1097              add that character to the output.  */
1098           if (matched == 0)
1099             {
1100               if (start < line.length)
1101                 matched = 1;
1102               else
1103                 break;
1104             }
1105 
1106           str_append (&s_accum, line.active + offset, matched);
1107         }
1108 
1109       /* Start after the match.  last_end is the real end of the matched
1110          substring, excluding characters that were skipped in case the RE
1111          matched the empty string.  */
1112       start = offset + matched;
1113       last_end = regs.end[0];
1114     }
1115   while (again
1116          && start <= line.length
1117          && match_regex (sub->regx, line.active, line.length, start,
1118                          &regs, sub->max_id + 1));
1119 
1120   /* Copy stuff to the right of the last match into the output string. */
1121   if (start < line.length)
1122     str_append (&s_accum, line.active + start, line.length-start);
1123   s_accum.chomped = line.chomped;
1124 
1125   /* Exchange line and s_accum.  This can be much cheaper
1126      than copying s_accum.active into line.text (for huge lines). */
1127   line_exchange (&line, &s_accum, false);
1128 
1129   /* Finish up. */
1130   if (count < sub->numb)
1131     return;
1132 
1133  post_subst:
1134   if (sub->print & 1)
1135     output_line (line.active, line.length, line.chomped, &output_file);
1136 
1137   if (sub->eval)
1138     {
1139 #ifdef HAVE_POPEN
1140       FILE *pipe_fp;
1141       line_reset (&s_accum, NULL);
1142 
1143       str_append (&line, "", 1);
1144       pipe_fp = popen (line.active, "r");
1145 
1146       if (pipe_fp != NULL)
1147         {
1148           while (!feof (pipe_fp))
1149             {
1150               char buf[4096];
1151               int n = fread (buf, sizeof (char), 4096, pipe_fp);
1152               if (n > 0)
1153                 str_append (&s_accum, buf, n);
1154             }
1155 
1156           pclose (pipe_fp);
1157 
1158           /* Exchange line and s_accum.  This can be much cheaper than copying
1159              s_accum.active into line.text (for huge lines).  See comment above
1160              for 'g' as to while the third argument is incorrect anyway.  */
1161           line_exchange (&line, &s_accum, true);
1162           if (line.length
1163               && line.active[line.length - 1] == buffer_delimiter)
1164             line.length--;
1165         }
1166       else
1167         panic (_("error in subprocess"));
1168 #else
1169       panic (_("option `e' not supported"));
1170 #endif
1171     }
1172 
1173   if (sub->print & 2)
1174     output_line (line.active, line.length, line.chomped, &output_file);
1175   if (sub->outf)
1176     output_line (line.active, line.length, line.chomped, sub->outf);
1177 }
1178 
1179 /* Translate the global input LINE via TRANS.
1180    This function handles the multi-byte case.  */
1181 static void
1182 translate_mb (char *const *trans)
1183 {
1184   size_t idx; /* index in the input line.  */
1185   mbstate_t mbstate = { 0, };
1186   for (idx = 0; idx < line.length;)
1187     {
1188       unsigned int i;
1189       size_t mbclen = MBRLEN (line.active + idx,
1190                               line.length - idx, &mbstate);
1191       /* An invalid sequence, or a truncated multibyte
1192          character.  Treat it as a single-byte character.  */
1193       if (mbclen == (size_t) -1 || mbclen == (size_t) -2 || mbclen == 0)
1194         mbclen = 1;
1195 
1196       /* `i' indicate i-th translate pair.  */
1197       for (i = 0; trans[2*i] != NULL; i++)
1198         {
1199           if (STREQ_LEN (line.active + idx, trans[2*i], mbclen))
1200             {
1201               bool move_remain_buffer = false;
1202               const char *tr = trans[2*i+1];
1203               size_t trans_len = *tr == '\0' ? 1 : strlen (tr);
1204 
1205               if (mbclen < trans_len)
1206                 {
1207                   size_t new_len = (line.length + 1
1208                                     + trans_len - mbclen);
1209                   /* We must extend the line buffer.  */
1210                   if (line.alloc < new_len)
1211                     {
1212                       /* And we must resize the buffer.  */
1213                       resize_line (&line, new_len);
1214                     }
1215                   move_remain_buffer = true;
1216                 }
1217               else if (mbclen > trans_len)
1218                 {
1219                   /* We must truncate the line buffer.  */
1220                   move_remain_buffer = true;
1221                 }
1222               size_t prev_idx = idx;
1223               if (move_remain_buffer)
1224                 {
1225                   /* Move the remaining with \0.  */
1226                   char const *move_from = (line.active + idx + mbclen);
1227                   char *move_to = line.active + idx + trans_len;
1228                   size_t move_len = line.length + 1 - idx - mbclen;
1229                   size_t move_offset = trans_len - mbclen;
1230                   memmove (move_to, move_from, move_len);
1231                   line.length += move_offset;
1232                   idx += move_offset;
1233                 }
1234               memcpy (line.active + prev_idx, trans[2*i+1],
1235                      trans_len);
1236               break;
1237             }
1238         }
1239       idx += mbclen;
1240     }
1241 }
1242 
1243 static void
1244 debug_print_end_of_cycle (void)
1245 {
1246   puts ("END-OF-CYCLE:");
1247 }
1248 
1249 static void
1250 debug_print_input (const struct input *input)
1251 {
1252   bool is_stdin = (input->fp && fileno (input->fp) == 0);
1253 
1254   printf ("INPUT:   '%s' line %lu\n",
1255           is_stdin?"STDIN":input->in_file_name,
1256           input->line_number);
1257 }
1258 
1259 static void
1260 debug_print_line (struct line *ln)
1261 {
1262   const char *src = ln->active ? ln->active : ln->text;
1263   size_t l = ln->length;
1264   const char *p = src;
1265 
1266   fputs ( (ln == &hold) ? "HOLD:    ":"PATTERN: ", stdout);
1267   while (l--)
1268     debug_print_char (*p++);
1269   putchar ('\n');
1270 }
1271 
1272 /* Execute the program `vec' on the current input line.
1273    Return exit status if caller should quit, -1 otherwise. */
1274 static int
1275 execute_program (struct vector *vec, struct input *input)
1276 {
1277   struct sed_cmd *cur_cmd;
1278   struct sed_cmd *end_cmd;
1279 
1280   cur_cmd = vec->v;
1281   end_cmd = vec->v + vec->v_length;
1282   while (cur_cmd < end_cmd)
1283     {
1284       if (debug)
1285         {
1286           fputs ("COMMAND: ", stdout);
1287           debug_print_command (vec, cur_cmd);
1288         }
1289 
1290       if (match_address_p (cur_cmd, input) != cur_cmd->addr_bang)
1291         {
1292           switch (cur_cmd->cmd)
1293             {
1294             case 'a':
1295               {
1296                 struct append_queue *aq = next_append_slot ();
1297                 aq->text = cur_cmd->x.cmd_txt.text;
1298                 aq->textlen = cur_cmd->x.cmd_txt.text_length;
1299               }
1300               break;
1301 
1302             case '{':
1303             case 'b':
1304               cur_cmd = vec->v + cur_cmd->x.jump_index;
1305               continue;
1306 
1307             case '}':
1308             case '#':
1309             case ':':
1310               /* Executing labels and block-ends are easy. */
1311               break;
1312 
1313             case 'c':
1314               if (cur_cmd->range_state != RANGE_ACTIVE)
1315                 output_line (cur_cmd->x.cmd_txt.text,
1316                             cur_cmd->x.cmd_txt.text_length - 1, true,
1317                             &output_file);
1318               /* POSIX.2 is silent about c starting a new cycle,
1319                  but it seems to be expected (and make sense). */
1320               FALLTHROUGH;
1321             case 'd':
1322               if (debug)
1323                 debug_print_end_of_cycle ();
1324               return -1;
1325 
1326             case 'D':
1327               {
1328                 char *p = memchr (line.active, buffer_delimiter, line.length);
1329                 if (!p)
1330                   return -1;
1331 
1332                 ++p;
1333                 line.alloc -= p - line.active;
1334                 line.length -= p - line.active;
1335                 line.active += p - line.active;
1336 
1337                 /* reset to start next cycle without reading a new line: */
1338                 cur_cmd = vec->v;
1339 
1340                 if (debug)
1341                   debug_print_line (&line);
1342                 continue;
1343               }
1344 
1345             case 'e': {
1346 #ifndef HAVE_POPEN
1347               panic (_("`e' command not supported"));
1348 #else
1349               FILE *pipe_fp;
1350               int cmd_length = cur_cmd->x.cmd_txt.text_length;
1351               line_reset (&s_accum, NULL);
1352 
1353               if (!cmd_length)
1354                 {
1355                   str_append (&line, "", 1);
1356                   pipe_fp = popen (line.active, "r");
1357                 }
1358               else
1359                 {
1360                   cur_cmd->x.cmd_txt.text[cmd_length - 1] = 0;
1361                   pipe_fp = popen (cur_cmd->x.cmd_txt.text, "r");
1362                   output_missing_newline (&output_file);
1363                 }
1364 
1365               if (pipe_fp == NULL)
1366                 panic (_("error in subprocess"));
1367 
1368               {
1369                 char buf[4096];
1370                 int n;
1371                 while (!feof (pipe_fp))
1372                   if ((n = fread (buf, sizeof (char), 4096, pipe_fp)) > 0)
1373                     {
1374                       if (!cmd_length)
1375                         str_append (&s_accum, buf, n);
1376                       else
1377                         ck_fwrite (buf, 1, n, output_file.fp);
1378                     }
1379 
1380                 pclose (pipe_fp);
1381                 if (!cmd_length)
1382                   {
1383                     /* Store into pattern space for plain `e' commands */
1384                     if (s_accum.length
1385                         && (s_accum.active[s_accum.length - 1]
1386                             == buffer_delimiter))
1387                       s_accum.length--;
1388 
1389                     /* Exchange line and s_accum.  This can be much
1390                        cheaper than copying s_accum.active into line.text
1391                        (for huge lines).  See comment above for 'g' as
1392                        to while the third argument is incorrect anyway.  */
1393                     line_exchange (&line, &s_accum, true);
1394                   }
1395                 else
1396                   flush_output (output_file.fp);
1397               }
1398 #endif
1399               break;
1400             }
1401 
1402             case 'g':
1403               /* We do not have a really good choice for the third parameter.
1404                  The problem is that hold space and the input file might as
1405                  well have different states; copying it from hold space means
1406                  that subsequent input might be read incorrectly, while
1407                  keeping it as in pattern space means that commands operating
1408                  on the moved buffer might consider a wrong character set.
1409                  We keep it true because it's what sed <= 4.1.5 did.  */
1410               line_copy (&hold, &line, true);
1411               if (debug)
1412                 debug_print_line (&hold);
1413               break;
1414 
1415             case 'G':
1416               /* We do not have a really good choice for the third parameter.
1417                  The problem is that hold space and pattern space might as
1418                  well have different states.  So, true is as wrong as false.
1419                  We keep it true because it's what sed <= 4.1.5 did, but
1420                  we could consider having line_ap.  */
1421               line_append (&hold, &line, true);
1422               if (debug)
1423                 debug_print_line (&line);
1424               break;
1425 
1426             case 'h':
1427               /* Here, it is ok to have true.  */
1428               line_copy (&line, &hold, true);
1429               if (debug)
1430                 debug_print_line (&hold);
1431               break;
1432 
1433             case 'H':
1434               /* See comment above for 'G' regarding the third parameter.  */
1435               line_append (&line, &hold, true);
1436               if (debug)
1437                 debug_print_line (&hold);
1438               break;
1439 
1440             case 'i':
1441               output_line (cur_cmd->x.cmd_txt.text,
1442                           cur_cmd->x.cmd_txt.text_length - 1,
1443                           true, &output_file);
1444               break;
1445 
1446             case 'l':
1447               do_list (cur_cmd->x.int_arg == -1
1448                       ? lcmd_out_line_len
1449                       : cur_cmd->x.int_arg);
1450               break;
1451 
1452             case 'n':
1453               if (!no_default_output)
1454                 output_line (line.active, line.length, line.chomped,
1455                              &output_file);
1456               if (test_eof (input) || !read_pattern_space (input, vec, false))
1457                 {
1458                   if (debug)
1459                     debug_print_end_of_cycle ();
1460                   return -1;
1461                 }
1462 
1463               if (debug)
1464                 debug_print_line (&line);
1465               break;
1466 
1467             case 'N':
1468               str_append (&line, &buffer_delimiter, 1);
1469 
1470               if (test_eof (input) || !read_pattern_space (input, vec, true))
1471                 {
1472                   if (debug)
1473                     debug_print_end_of_cycle ();
1474                   line.length--;
1475                   if (posixicity == POSIXLY_EXTENDED && !no_default_output)
1476                      output_line (line.active, line.length, line.chomped,
1477                                   &output_file);
1478                   return -1;
1479                 }
1480               if (debug)
1481                 debug_print_line (&line);
1482               break;
1483 
1484             case 'p':
1485               output_line (line.active, line.length, line.chomped,
1486                            &output_file);
1487               break;
1488 
1489             case 'P':
1490               {
1491                 char *p = memchr (line.active, buffer_delimiter, line.length);
1492                 output_line (line.active, p ? p - line.active : line.length,
1493                              p ? true : line.chomped, &output_file);
1494               }
1495               break;
1496 
1497             case 'q':
1498               if (!no_default_output)
1499                 output_line (line.active, line.length, line.chomped,
1500                             &output_file);
1501               dump_append_queue ();
1502               FALLTHROUGH;
1503 
1504             case 'Q':
1505               return cur_cmd->x.int_arg == -1 ? 0 : cur_cmd->x.int_arg;
1506 
1507             case 'r':
1508               if (cur_cmd->x.fname)
1509                 {
1510                   struct append_queue *aq = next_append_slot ();
1511                   aq->fname = cur_cmd->x.fname;
1512                 }
1513               break;
1514 
1515             case 'R':
1516               if (cur_cmd->x.inf->fp && !feof (cur_cmd->x.inf->fp))
1517                 {
1518                   struct append_queue *aq;
1519                   size_t buflen;
1520                   char *text = NULL;
1521                   int result;
1522 
1523                   result = ck_getdelim (&text, &buflen, buffer_delimiter,
1524                                         cur_cmd->x.inf->fp);
1525                   if (result != EOF)
1526                     {
1527                       aq = next_append_slot ();
1528                       aq->free = true;
1529                       aq->text = text;
1530                       aq->textlen = result;
1531                     }
1532                   else
1533                     {
1534                       /* The external input file (for R command) reached EOF,
1535                       the 'text' buffer will not be added to the append queue
1536                       so release it */
1537                       free (text);
1538                     }
1539                 }
1540               break;
1541 
1542             case 's':
1543               do_subst (cur_cmd->x.cmd_subst);
1544               if (debug)
1545                 debug_print_line (&line);
1546               break;
1547 
1548             case 't':
1549               if (replaced)
1550                 {
1551                   replaced = false;
1552                   cur_cmd = vec->v + cur_cmd->x.jump_index;
1553                   continue;
1554                 }
1555               break;
1556 
1557             case 'T':
1558               if (!replaced)
1559                 {
1560                   cur_cmd = vec->v + cur_cmd->x.jump_index;
1561                   continue;
1562                 }
1563               else
1564                 replaced = false;
1565               break;
1566 
1567             case 'w':
1568               if (cur_cmd->x.outf->fp)
1569                 output_line (line.active, line.length,
1570                             line.chomped, cur_cmd->x.outf);
1571               break;
1572 
1573             case 'W':
1574               if (cur_cmd->x.outf->fp)
1575                 {
1576                   char *p = memchr (line.active, buffer_delimiter, line.length);
1577                   output_line (line.active, p ? p - line.active : line.length,
1578                                p ? true : line.chomped, cur_cmd->x.outf);
1579                 }
1580               break;
1581 
1582             case 'x':
1583               /* See comment above for 'g' regarding the third parameter.  */
1584               line_exchange (&line, &hold, false);
1585               if (debug)
1586                 {
1587                   debug_print_line (&line);
1588                   debug_print_line (&hold);
1589                 }
1590               break;
1591 
1592             case 'y':
1593               if (mb_cur_max > 1)
1594                 translate_mb (cur_cmd->x.translatemb);
1595               else
1596                 {
1597                   unsigned char *p, *e;
1598                   p = (unsigned char *)line.active;
1599                   for (e=p+line.length; p<e; ++p)
1600                     *p = cur_cmd->x.translate[*p];
1601                 }
1602               if (debug)
1603                 debug_print_line (&line);
1604               break;
1605 
1606             case 'z':
1607               line.length = 0;
1608               if (debug)
1609                 debug_print_line (&line);
1610               break;
1611 
1612             case '=':
1613               output_missing_newline (&output_file);
1614               fprintf (output_file.fp, "%lu%c",
1615                        (unsigned long)input->line_number,
1616                        buffer_delimiter);
1617               flush_output (output_file.fp);
1618              break;
1619 
1620            case 'F':
1621               output_missing_newline (&output_file);
1622               fprintf (output_file.fp, "%s%c",
1623                        input->in_file_name,
1624                        buffer_delimiter);
1625               flush_output (output_file.fp);
1626              break;
1627 
1628            default:
1629              panic ("INTERNAL ERROR: Bad cmd %c", cur_cmd->cmd);
1630            }
1631         }
1632 
1633       /* this is buried down here so that a "continue" statement can skip it */
1634       ++cur_cmd;
1635     }
1636 
1637     if (debug)
1638       debug_print_end_of_cycle ();
1639     if (!no_default_output)
1640       output_line (line.active, line.length, line.chomped, &output_file);
1641     return -1;
1642 }
1643 
1644 
1645 /* Apply the compiled script to all the named files. */
1646 int
1647 process_files (struct vector *the_program, char **argv)
1648 {
1649   static char dash[] = "-";
1650   static char *stdin_argv[2] = { dash, NULL };
1651   struct input input;
1652   int status;
1653 
1654   line_init (&line, NULL, INITIAL_BUFFER_SIZE);
1655   line_init (&hold, NULL, 0);
1656   line_init (&buffer, NULL, 0);
1657 
1658   input.reset_at_next_file = true;
1659   if (argv && *argv)
1660     input.file_list = argv;
1661   else if (in_place_extension)
1662     panic (_("no input files"));
1663   else
1664     input.file_list = stdin_argv;
1665 
1666   input.bad_count = 0;
1667   input.line_number = 0;
1668   input.read_fn = read_always_fail;
1669   input.fp = NULL;
1670 
1671   status = EXIT_SUCCESS;
1672   while (read_pattern_space (&input, the_program, false))
1673     {
1674       if (debug)
1675         {
1676           debug_print_input (&input);
1677           debug_print_line (&line);
1678         }
1679 
1680       status = execute_program (the_program, &input);
1681       if (status == -1)
1682         status = EXIT_SUCCESS;
1683       else
1684         break;
1685     }
1686   closedown (&input);
1687 
1688 #ifdef lint
1689   /* We're about to exit, so these free()s are redundant.
1690      But if we're running under a memory-leak detecting
1691      implementation of malloc(), we want to explicitly
1692      deallocate in order to avoid extraneous noise from
1693      the allocator. */
1694   release_append_queue ();
1695   free (buffer.text);
1696   free (hold.text);
1697   free (line.text);
1698   free (s_accum.text);
1699 #endif /* lint */
1700 
1701   if (input.bad_count)
1702     status = EXIT_BAD_INPUT;
1703 
1704   return status;
1705 }
1706