Upates to include copyright 2000 to everything
[platform/upstream/busybox.git] / editors / 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\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                         usage(sed_usage);
189                 argc--;
190                 cp = *argv++;
191                 stopNow = FALSE;
192
193                 while (*++cp && stopNow == FALSE) {
194                         switch (*cp) {
195                         case 'n':
196                                 quietFlag = TRUE;
197                                 break;
198                         case 'e':
199                                 if (*(cp + 1) == 0 && --argc < 0) {
200                                         usage(sed_usage);
201                                 }
202                                 if (*++cp != 's')
203                                         cp = *argv++;
204
205                                 /* Read address if present */
206                                 SKIPSPACES(cp);
207                                 if (*cp == '$') {
208                                         addr_line = LAST_LINE;
209                                         cp++;
210                                 } else {
211                                         if (isdigit(*cp)) {     /* LINE ADDRESS   */
212                                                 line_s = cp;
213                                                 while (isdigit(*cp))
214                                                         cp++;
215                                                 if (cp > line_s) {
216                                                         /* numeric line */
217                                                         saved = *cp;
218                                                         *cp = '\0';
219                                                         addr_line = atoi(line_s);
220                                                         *cp = saved;
221                                                 }
222                                         } else if (*cp == '/') {        /* PATTERN ADDRESS */
223                                                 pos = addr_pattern = cp + 1;
224                                                 pos = strchr(pos, '/');
225                                                 if (!pos)
226                                                         usage(sed_usage);
227                                                 *pos = '\0';
228                                                 cp = pos + 1;
229                                         }
230                                 }
231
232                                 SKIPSPACES(cp);
233                                 if (*cp == '!') {
234                                         negated++;
235                                         cp++;
236                                 }
237
238                                 /* Read command */
239
240                                 SKIPSPACES(cp);
241                                 switch (*cp) {
242                                 case 's':               /* REPLACE */
243                                         if (strlen(cp) <= 3 || *(cp + 1) != '/')
244                                                 break;
245                                         sed_f = f_replace;
246
247                                         pos = needle = cp + 2;
248
249                                         for (;;) {
250                                                 pos = strchr(pos, '/');
251                                                 if (pos == NULL) {
252                                                         usage(sed_usage);
253                                                 }
254                                                 if (*(pos - 1) == '\\') {
255                                                         pos++;
256                                                         continue;
257                                                 }
258                                                 break;
259                                         }
260                                         *pos = 0;
261                                         newNeedle = ++pos;
262                                         for (;;) {
263                                                 pos = strchr(pos, '/');
264                                                 if (pos == NULL) {
265                                                         usage(sed_usage);
266                                                 }
267                                                 if (*(pos - 1) == '\\') {
268                                                         pos++;
269                                                         continue;
270                                                 }
271                                                 break;
272                                         }
273                                         *pos = 0;
274                                         if (pos + 2 != 0) {
275                                                 while (*++pos) {
276                                                         switch (*pos) {
277                                                         case 'i':
278                                                                 ignoreCase = TRUE;
279                                                                 break;
280                                                         case 'p':
281                                                                 printFlag = TRUE;
282                                                                 break;
283                                                         case 'g':
284                                                                 break;
285                                                         default:
286                                                                 usage(sed_usage);
287                                                         }
288                                                 }
289                                         }
290                                         cp = pos;
291                                         /* fprintf(stderr, "replace '%s' with '%s'\n", needle, newNeedle); */
292                                         break;
293
294                                 case 'a':               /* APPEND */
295                                         if (strlen(cp) < 2)
296                                                 break;
297                                         sed_f = f_append;
298                                         appendline = ++cp;
299                                         /* fprintf(stderr, "append '%s'\n", appendline); */
300                                         break;
301                                 }
302
303                                 stopNow = TRUE;
304                                 break;
305
306                         default:
307                                 usage(sed_usage);
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 */