Some formatting updates (ran the code through indent)
[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                 char 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         if (**argv == '-') {
187                 argc--;
188                 cp = *argv++;
189                 stopNow = FALSE;
190
191                 while (*++cp && stopNow == FALSE) {
192                         switch (*cp) {
193                         case 'n':
194                                 quietFlag = TRUE;
195                                 break;
196                         case 'e':
197                                 if (*(cp + 1) == 0 && --argc < 0) {
198                                         usage(sed_usage);
199                                 }
200                                 if (*++cp != 's')
201                                         cp = *argv++;
202
203                                 /* Read address if present */
204                                 SKIPSPACES(cp);
205                                 if (*cp == '$') {
206                                         addr_line = LAST_LINE;
207                                         cp++;
208                                 } else {
209                                         if (isdigit(*cp)) {     /* LINE ADDRESS   */
210                                                 line_s = cp;
211                                                 while (isdigit(*cp))
212                                                         cp++;
213                                                 if (cp > line_s) {
214                                                         /* numeric line */
215                                                         saved = *cp;
216                                                         *cp = '\0';
217                                                         addr_line = atoi(line_s);
218                                                         *cp = saved;
219                                                 }
220                                         } else if (*cp == '/') {        /* PATTERN ADDRESS */
221                                                 pos = addr_pattern = cp + 1;
222                                                 pos = strchr(pos, '/');
223                                                 if (!pos)
224                                                         usage(sed_usage);
225                                                 *pos = '\0';
226                                                 cp = pos + 1;
227                                         }
228                                 }
229
230                                 SKIPSPACES(cp);
231                                 if (*cp == '!') {
232                                         negated++;
233                                         cp++;
234                                 }
235
236                                 /* Read command */
237
238                                 SKIPSPACES(cp);
239                                 switch (*cp) {
240                                 case 's':               /* REPLACE */
241                                         if (strlen(cp) <= 3 || *(cp + 1) != '/')
242                                                 break;
243                                         sed_f = f_replace;
244
245                                         pos = needle = cp + 2;
246
247                                         for (;;) {
248                                                 pos = strchr(pos, '/');
249                                                 if (pos == NULL) {
250                                                         usage(sed_usage);
251                                                 }
252                                                 if (*(pos - 1) == '\\') {
253                                                         pos++;
254                                                         continue;
255                                                 }
256                                                 break;
257                                         }
258                                         *pos = 0;
259                                         newNeedle = ++pos;
260                                         for (;;) {
261                                                 pos = strchr(pos, '/');
262                                                 if (pos == NULL) {
263                                                         usage(sed_usage);
264                                                 }
265                                                 if (*(pos - 1) == '\\') {
266                                                         pos++;
267                                                         continue;
268                                                 }
269                                                 break;
270                                         }
271                                         *pos = 0;
272                                         if (pos + 2 != 0) {
273                                                 while (*++pos) {
274                                                         switch (*pos) {
275                                                         case 'i':
276                                                                 ignoreCase = TRUE;
277                                                                 break;
278                                                         case 'p':
279                                                                 printFlag = TRUE;
280                                                                 break;
281                                                         case 'g':
282                                                                 break;
283                                                         default:
284                                                                 usage(sed_usage);
285                                                         }
286                                                 }
287                                         }
288                                         cp = pos;
289                                         /* fprintf(stderr, "replace '%s' with '%s'\n", needle, newNeedle); */
290                                         break;
291
292                                 case 'a':               /* APPEND */
293                                         if (strlen(cp) < 2)
294                                                 break;
295                                         sed_f = f_append;
296                                         appendline = ++cp;
297                                         /* fprintf(stderr, "append '%s'\n", appendline); */
298                                         break;
299                                 }
300
301                                 stopNow = TRUE;
302                                 break;
303
304                         default:
305                                 usage(sed_usage);
306                         }
307                 }
308         }
309
310         if (argc == 0) {
311                 switch (sed_f) {
312                 case f_none:
313                         break;
314                 case f_replace:
315                         do_sed_repl(stdin, needle, newNeedle, ignoreCase, printFlag,
316                                                 quietFlag);
317                         break;
318                 case f_append:
319                         do_sed_append(stdin, appendline, quietFlag);
320                         break;
321                 }
322         } else {
323                 while (argc-- > 0) {
324                         name = *argv++;
325
326                         fp = fopen(name, "r");
327                         if (fp == NULL) {
328                                 perror(name);
329                                 continue;
330                         }
331
332                         switch (sed_f) {
333                         case f_none:
334                                 break;
335                         case f_replace:
336                                 do_sed_repl(fp, needle, newNeedle, ignoreCase, printFlag,
337                                                         quietFlag);
338                                 break;
339                         case f_append:
340                                 do_sed_append(fp, appendline, quietFlag);
341                                 break;
342                         }
343
344                         if (ferror(fp))
345                                 perror(name);
346
347                         fclose(fp);
348                 }
349         }
350         exit(TRUE);
351 }
352
353
354 /* END CODE */