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