Removed unnecessary #include "regexp.h" line from find.c as per Matt Kraai's
[oweals/busybox.git] / sed.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Mini sed implementation for busybox
4  *
5  *
6  * Copyright (C) 1999,2000 by Lineo, inc.
7  * Written by Erik Andersen <andersen@lineo.com>, <andersee@debian.org>
8  *
9  * Modifications for addresses and append command have been
10  * written by Marco Pantaleoni <panta@prosa.it>, <panta@elasticworld.org>
11  * and are:
12  * Copyright (C) 1999 Marco Pantaleoni.
13  *
14  * This program is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22  * General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program; if not, write to the Free Software
26  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27  *
28  */
29
30 #include "internal.h"
31 #include "regexp.h"
32 #include <stdio.h>
33 #include <dirent.h>
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <signal.h>
37 #include <time.h>
38 #include <ctype.h>
39
40 static const char sed_usage[] =
41         "sed [-n] -e script [file...]\n"
42 #ifndef BB_FEATURE_TRIVIAL_HELP
43         "\nAllowed sed scripts come in the following form:\n"
44         "\t'ADDR [!] COMMAND'\n\n"
45         "\twhere address ADDR can be:\n"
46         "\t  NUMBER    Match specified line number\n"
47         "\t  $         Match last line\n"
48         "\t  /REGEXP/  Match specified regexp\n"
49         "\t  (! inverts the meaning of the match)\n\n"
50         "\tand COMMAND can be:\n"
51         "\t  s/regexp/replacement/[igp]\n"
52         "\t     which attempt to match regexp against the pattern space\n"
53         "\t     and if successful replaces the matched portion with replacement.\n\n"
54         "\t  aTEXT\n"
55         "\t     which appends TEXT after the pattern space\n"
56         "Options:\n"
57         "-e\tadd the script to the commands to be executed\n"
58         "-n\tsuppress automatic printing of pattern space\n\n"
59 #if defined BB_REGEXP
60         "This version of sed matches full regular expressions.\n";
61 #else
62         "This version of sed matches strings (not full regular expressions).\n"
63 #endif
64 #endif
65         ;
66
67 /* Flags & variables */
68
69 typedef enum { f_none, f_replace, f_append } sed_function;
70
71 #define NO_LINE         -2
72 #define LAST_LINE       -1
73 static int addr_line = NO_LINE;
74 static char *addr_pattern = NULL;
75 static int negated = 0;
76
77 #define SKIPSPACES(p)           do { while (isspace(*(p))) (p)++; } while (0)
78
79 #define BUFSIZE         1024
80
81 static inline int at_last(FILE * fp)
82 {
83         int res = 0;
84
85         if (feof(fp))
86                 return 1;
87         else {
88                 int ch;
89
90                 if ((ch = fgetc(fp)) == EOF)
91                         res++;
92                 ungetc(ch, fp);
93         }
94         return res;
95 }
96
97 static void do_sed_repl(FILE * fp, char *needle, char *newNeedle,
98                                                 int ignoreCase, int printFlag, int quietFlag)
99 {
100         int foundOne = FALSE;
101         char haystack[BUFSIZE];
102         int line = 1, doit;
103
104         while (fgets(haystack, BUFSIZE - 1, fp)) {
105                 doit = 0;
106                 if (addr_pattern) {
107                         doit = !find_match(haystack, addr_pattern, FALSE);
108                 } else if (addr_line == NO_LINE)
109                         doit = 1;
110                 else if (addr_line == LAST_LINE) {
111                         if (at_last(fp))
112                                 doit = 1;
113                 } else {
114                         if (line == addr_line)
115                                 doit = 1;
116                 }
117                 if (negated)
118                         doit = 1 - doit;
119                 if (doit) {
120                         foundOne =
121                                 replace_match(haystack, needle, newNeedle, ignoreCase);
122
123                         if (foundOne == TRUE && printFlag == TRUE) {
124                                 fprintf(stdout, haystack);
125                         }
126                 }
127
128                 if (quietFlag == FALSE) {
129                         fprintf(stdout, haystack);
130                 }
131
132                 line++;
133         }
134 }
135
136 static void do_sed_append(FILE * fp, char *appendline, int quietFlag)
137 {
138         char buffer[BUFSIZE];
139         int line = 1, doit;
140
141         while (fgets(buffer, BUFSIZE - 1, fp)) {
142                 doit = 0;
143                 if (addr_pattern) {
144                         doit = !find_match(buffer, addr_pattern, FALSE);
145                 } else if (addr_line == NO_LINE)
146                         doit = 1;
147                 else if (addr_line == LAST_LINE) {
148                         if (at_last(fp))
149                                 doit = 1;
150                 } else {
151                         if (line == addr_line)
152                                 doit = 1;
153                 }
154                 if (negated)
155                         doit = 1 - doit;
156                 if (quietFlag == FALSE) {
157                         fprintf(stdout, buffer);
158                 }
159                 if (doit) {
160                         fputs(appendline, stdout);
161                         fputc('\n', stdout);
162                 }
163
164                 line++;
165         }
166 }
167
168 extern int sed_main(int argc, char **argv)
169 {
170         FILE *fp;
171         char *needle = NULL, *newNeedle = NULL;
172         char *name;
173         char *cp;
174         int ignoreCase = FALSE;
175         int printFlag = FALSE;
176         int quietFlag = FALSE;
177         int stopNow;
178         char *line_s = NULL, saved;
179         char *appendline = NULL;
180         char *pos;
181         sed_function sed_f = f_none;
182
183         argc--;
184         argv++;
185         if (argc < 1) {
186                 usage(sed_usage);
187         }
188
189         while (argc > 1) {
190                 if (**argv != '-')
191                         usage(sed_usage);
192                 argc--;
193                 cp = *argv++;
194                 stopNow = FALSE;
195
196                 while (*++cp && stopNow == FALSE) {
197                         switch (*cp) {
198                         case 'n':
199                                 quietFlag = TRUE;
200                                 break;
201                         case 'e':
202                                 if (*(cp + 1) == 0 && --argc < 0) {
203                                         usage(sed_usage);
204                                 }
205                                 if (*++cp != 's')
206                                         cp = *argv++;
207
208                                 /* Read address if present */
209                                 SKIPSPACES(cp);
210                                 if (*cp == '$') {
211                                         addr_line = LAST_LINE;
212                                         cp++;
213                                 } else {
214                                         if (isdigit(*cp)) {     /* LINE ADDRESS   */
215                                                 line_s = cp;
216                                                 while (isdigit(*cp))
217                                                         cp++;
218                                                 if (cp > line_s) {
219                                                         /* numeric line */
220                                                         saved = *cp;
221                                                         *cp = '\0';
222                                                         addr_line = atoi(line_s);
223                                                         *cp = saved;
224                                                 }
225                                         } else if (*cp == '/') {        /* PATTERN ADDRESS */
226                                                 pos = addr_pattern = cp + 1;
227                                                 pos = strchr(pos, '/');
228                                                 if (!pos)
229                                                         usage(sed_usage);
230                                                 *pos = '\0';
231                                                 cp = pos + 1;
232                                         }
233                                 }
234
235                                 SKIPSPACES(cp);
236                                 if (*cp == '!') {
237                                         negated++;
238                                         cp++;
239                                 }
240
241                                 /* Read command */
242
243                                 SKIPSPACES(cp);
244                                 switch (*cp) {
245                                 case 's':               /* REPLACE */
246                                         if (strlen(cp) <= 3 || *(cp + 1) != '/')
247                                                 break;
248                                         sed_f = f_replace;
249
250                                         pos = needle = cp + 2;
251
252                                         for (;;) {
253                                                 pos = strchr(pos, '/');
254                                                 if (pos == NULL) {
255                                                         usage(sed_usage);
256                                                 }
257                                                 if (*(pos - 1) == '\\') {
258                                                         pos++;
259                                                         continue;
260                                                 }
261                                                 break;
262                                         }
263                                         *pos = 0;
264                                         newNeedle = ++pos;
265                                         for (;;) {
266                                                 pos = strchr(pos, '/');
267                                                 if (pos == NULL) {
268                                                         usage(sed_usage);
269                                                 }
270                                                 if (*(pos - 1) == '\\') {
271                                                         pos++;
272                                                         continue;
273                                                 }
274                                                 break;
275                                         }
276                                         *pos = 0;
277                                         if (pos + 2 != 0) {
278                                                 while (*++pos) {
279                                                         switch (*pos) {
280                                                         case 'i':
281                                                                 ignoreCase = TRUE;
282                                                                 break;
283                                                         case 'p':
284                                                                 printFlag = TRUE;
285                                                                 break;
286                                                         case 'g':
287                                                                 break;
288                                                         default:
289                                                                 usage(sed_usage);
290                                                         }
291                                                 }
292                                         }
293                                         cp = pos;
294                                         /* fprintf(stderr, "replace '%s' with '%s'\n", needle, newNeedle); */
295                                         break;
296
297                                 case 'a':               /* APPEND */
298                                         if (strlen(cp) < 2)
299                                                 break;
300                                         sed_f = f_append;
301                                         appendline = ++cp;
302                                         /* fprintf(stderr, "append '%s'\n", appendline); */
303                                         break;
304                                 }
305
306                                 stopNow = TRUE;
307                                 break;
308
309                         default:
310                                 usage(sed_usage);
311                         }
312                 }
313     }
314
315         if (argc == 0) {
316                 switch (sed_f) {
317                 case f_none:
318                         break;
319                 case f_replace:
320                         do_sed_repl(stdin, needle, newNeedle, ignoreCase, printFlag,
321                                                 quietFlag);
322                         break;
323                 case f_append:
324                         do_sed_append(stdin, appendline, quietFlag);
325                         break;
326                 }
327         } else {
328                 while (argc-- > 0) {
329                         name = *argv++;
330
331                         fp = fopen(name, "r");
332                         if (fp == NULL) {
333                                 perror(name);
334                                 continue;
335                         }
336
337                         switch (sed_f) {
338                         case f_none:
339                                 break;
340                         case f_replace:
341                                 do_sed_repl(fp, needle, newNeedle, ignoreCase, printFlag,
342                                                         quietFlag);
343                                 break;
344                         case f_append:
345                                 do_sed_append(fp, appendline, quietFlag);
346                                 break;
347                         }
348
349                         if (ferror(fp))
350                                 perror(name);
351
352                         fclose(fp);
353                 }
354         }
355         return(TRUE);
356 }
357
358
359 /* END CODE */