608f868130adefe7d30f0a7d9c714cca95891b7a
[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 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\n"
42         "Allowed sed scripts come in the following form:\n"
43         "\t'ADDR [!] COMMAND'\n\n"
44         "\twhere address ADDR can be:\n"
45         "\t  NUMBER    Match specified line number\n"
46         "\t  $         Match last line\n"
47         "\t  /REGEXP/  Match specified regexp\n"
48         "\t  (! inverts the meaning of the match)\n\n"
49         "\tand COMMAND can be:\n"
50         "\t  s/regexp/replacement/[igp]\n"
51         "\t     which attempt to match regexp against the pattern space\n"
52         "\t     and if successful replaces the matched portion with replacement.\n\n"
53         "\t  aTEXT\n"
54         "\t     which appends TEXT after the pattern space\n"
55         "Options:\n"
56         "-e\tadd the script to the commands to be executed\n"
57         "-n\tsuppress automatic printing of pattern space\n\n"
58 #if defined BB_REGEXP
59         "This version of sed matches full regular expresions.\n";
60 #else
61         "This version of sed matches strings (not full regular expresions).\n";
62 #endif
63
64 /* Flags & variables */
65
66 typedef enum { f_none, f_replace, f_append } sed_function;
67
68 #define NO_LINE         -2
69 #define LAST_LINE       -1
70 static int addr_line = NO_LINE;
71 static char *addr_pattern = NULL;
72 static int negated = 0;
73
74 #define SKIPSPACES(p)           do { while (isspace(*(p))) (p)++; } while (0)
75
76 #define BUFSIZE         1024
77
78 static inline int at_last(FILE * fp)
79 {
80         int res = 0;
81
82         if (feof(fp))
83                 return 1;
84         else {
85                 int ch;
86
87                 if ((ch = fgetc(fp)) == EOF)
88                         res++;
89                 ungetc(ch, fp);
90         }
91         return res;
92 }
93
94 static void do_sed_repl(FILE * fp, char *needle, char *newNeedle,
95                                                 int ignoreCase, int printFlag, int quietFlag)
96 {
97         int foundOne = FALSE;
98         char haystack[BUFSIZE];
99         int line = 1, doit;
100
101         while (fgets(haystack, BUFSIZE - 1, fp)) {
102                 doit = 0;
103                 if (addr_pattern) {
104                         doit = !find_match(haystack, addr_pattern, FALSE);
105                 } else if (addr_line == NO_LINE)
106                         doit = 1;
107                 else if (addr_line == LAST_LINE) {
108                         if (at_last(fp))
109                                 doit = 1;
110                 } else {
111                         if (line == addr_line)
112                                 doit = 1;
113                 }
114                 if (negated)
115                         doit = 1 - doit;
116                 if (doit) {
117                         foundOne =
118                                 replace_match(haystack, needle, newNeedle, ignoreCase);
119
120                         if (foundOne == TRUE && printFlag == TRUE) {
121                                 fprintf(stdout, haystack);
122                         }
123                 }
124
125                 if (quietFlag == FALSE) {
126                         fprintf(stdout, haystack);
127                 }
128
129                 line++;
130         }
131 }
132
133 static void do_sed_append(FILE * fp, char *appendline, int quietFlag)
134 {
135         char buffer[BUFSIZE];
136         int line = 1, doit;
137
138         while (fgets(buffer, BUFSIZE - 1, fp)) {
139                 doit = 0;
140                 if (addr_pattern) {
141                         doit = !find_match(buffer, addr_pattern, FALSE);
142                 } else if (addr_line == NO_LINE)
143                         doit = 1;
144                 else if (addr_line == LAST_LINE) {
145                         if (at_last(fp))
146                                 doit = 1;
147                 } else {
148                         if (line == addr_line)
149                                 doit = 1;
150                 }
151                 if (negated)
152                         doit = 1 - doit;
153                 if (quietFlag == FALSE) {
154                         fprintf(stdout, buffer);
155                 }
156                 if (doit) {
157                         fputs(appendline, stdout);
158                         fputc('\n', stdout);
159                 }
160
161                 line++;
162         }
163 }
164
165 extern int sed_main(int argc, char **argv)
166 {
167         FILE *fp;
168         char *needle = NULL, *newNeedle = NULL;
169         char *name;
170         char *cp;
171         int ignoreCase = FALSE;
172         int printFlag = FALSE;
173         int quietFlag = FALSE;
174         int stopNow;
175         char *line_s = NULL, saved;
176         char *appendline = NULL;
177         char *pos;
178         sed_function sed_f = f_none;
179
180         argc--;
181         argv++;
182         if (argc < 1) {
183                 usage(sed_usage);
184         }
185
186         while (argc > 1) {
187             if (**argv == '-') {
188                     argc--;
189                     cp = *argv++;
190                     stopNow = FALSE;
191
192                     while (*++cp && stopNow == FALSE) {
193                             switch (*cp) {
194                             case 'n':
195                                     quietFlag = TRUE;
196                                     break;
197                         case 'e':
198                                 if (*(cp + 1) == 0 && --argc < 0) {
199                                         usage(sed_usage);
200                                 }
201                                 if (*++cp != 's')
202                                         cp = *argv++;
203     
204                                 /* Read address if present */
205                                 SKIPSPACES(cp);
206                                 if (*cp == '$') {
207                                         addr_line = LAST_LINE;
208                                         cp++;
209                                 } else {
210                                         if (isdigit(*cp)) {     /* LINE ADDRESS   */
211                                                 line_s = cp;
212                                                 while (isdigit(*cp))
213                                                         cp++;
214                                                 if (cp > line_s) {
215                                                         /* numeric line */
216                                                         saved = *cp;
217                                                         *cp = '\0';
218                                                         addr_line = atoi(line_s);
219                                                         *cp = saved;
220                                                 }
221                                         } else if (*cp == '/') {        /* PATTERN ADDRESS */
222                                                 pos = addr_pattern = cp + 1;
223                                                 pos = strchr(pos, '/');
224                                                 if (!pos)
225                                                         usage(sed_usage);
226                                                 *pos = '\0';
227                                                 cp = pos + 1;
228                                         }
229                                 }
230     
231                                 SKIPSPACES(cp);
232                                 if (*cp == '!') {
233                                         negated++;
234                                         cp++;
235                                 }
236     
237                                 /* Read command */
238     
239                                 SKIPSPACES(cp);
240                                 switch (*cp) {
241                                 case 's':               /* REPLACE */
242                                         if (strlen(cp) <= 3 || *(cp + 1) != '/')
243                                                 break;
244                                         sed_f = f_replace;
245     
246                                         pos = needle = cp + 2;
247     
248                                         for (;;) {
249                                                 pos = strchr(pos, '/');
250                                                 if (pos == NULL) {
251                                                         usage(sed_usage);
252                                                 }
253                                                 if (*(pos - 1) == '\\') {
254                                                         pos++;
255                                                         continue;
256                                                 }
257                                                 break;
258                                         }
259                                         *pos = 0;
260                                         newNeedle = ++pos;
261                                         for (;;) {
262                                                 pos = strchr(pos, '/');
263                                                 if (pos == NULL) {
264                                                         usage(sed_usage);
265                                                 }
266                                                 if (*(pos - 1) == '\\') {
267                                                         pos++;
268                                                         continue;
269                                                 }
270                                                 break;
271                                         }
272                                         *pos = 0;
273                                         if (pos + 2 != 0) {
274                                                 while (*++pos) {
275                                                         switch (*pos) {
276                                                         case 'i':
277                                                                 ignoreCase = TRUE;
278                                                                 break;
279                                                         case 'p':
280                                                                 printFlag = TRUE;
281                                                                 break;
282                                                         case 'g':
283                                                                 break;
284                                                         default:
285                                                                 usage(sed_usage);
286                                                         }
287                                                 }
288                                         }
289                                         cp = pos;
290                                         /* fprintf(stderr, "replace '%s' with '%s'\n", needle, newNeedle); */
291                                         break;
292     
293                                 case 'a':               /* APPEND */
294                                         if (strlen(cp) < 2)
295                                                 break;
296                                         sed_f = f_append;
297                                         appendline = ++cp;
298                                         /* fprintf(stderr, "append '%s'\n", appendline); */
299                                         break;
300                                 }
301     
302                                 stopNow = TRUE;
303                                 break;
304     
305                         default:
306                                 usage(sed_usage);
307                         }
308                 }
309         }
310     }
311
312         if (argc == 0) {
313                 switch (sed_f) {
314                 case f_none:
315                         break;
316                 case f_replace:
317                         do_sed_repl(stdin, needle, newNeedle, ignoreCase, printFlag,
318                                                 quietFlag);
319                         break;
320                 case f_append:
321                         do_sed_append(stdin, appendline, quietFlag);
322                         break;
323                 }
324         } else {
325                 while (argc-- > 0) {
326                         name = *argv++;
327
328                         fp = fopen(name, "r");
329                         if (fp == NULL) {
330                                 perror(name);
331                                 continue;
332                         }
333
334                         switch (sed_f) {
335                         case f_none:
336                                 break;
337                         case f_replace:
338                                 do_sed_repl(fp, needle, newNeedle, ignoreCase, printFlag,
339                                                         quietFlag);
340                                 break;
341                         case f_append:
342                                 do_sed_append(fp, appendline, quietFlag);
343                                 break;
344                         }
345
346                         if (ferror(fp))
347                                 perror(name);
348
349                         fclose(fp);
350                 }
351         }
352         exit(TRUE);
353 }
354
355
356 /* END CODE */