blob: 0d57a4a06e765ceb37dc17c820b847390d81c713 [file] [log] [blame]
Erik Andersene49d5ec2000-02-08 19:58:47 +00001/* vi: set sw=4 ts=4: */
Eric Andersen6b6b3f61999-10-28 16:06:25 +00002/*
3 * Mini sed implementation for busybox
4 *
5 *
Erik Andersen61677fe2000-04-13 01:18:56 +00006 * Copyright (C) 1999,2000 by Lineo, inc.
Eric Andersen6b6b3f61999-10-28 16:06:25 +00007 * Written by Erik Andersen <andersen@lineo.com>, <andersee@debian.org>
8 *
Erik Andersen1266a131999-12-29 22:19:46 +00009 * 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 *
Eric Andersen6b6b3f61999-10-28 16:06:25 +000014 * 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
Erik Andersen1266a131999-12-29 22:19:46 +000040static const char sed_usage[] =
Erik Andersene49d5ec2000-02-08 19:58:47 +000041 "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"
Eric Andersen6b6b3f61999-10-28 16:06:25 +000058#if defined BB_REGEXP
Erik Andersene49d5ec2000-02-08 19:58:47 +000059 "This version of sed matches full regular expresions.\n";
Eric Andersen6b6b3f61999-10-28 16:06:25 +000060#else
Erik Andersene49d5ec2000-02-08 19:58:47 +000061 "This version of sed matches strings (not full regular expresions).\n";
Eric Andersen6b6b3f61999-10-28 16:06:25 +000062#endif
63
Erik Andersen1266a131999-12-29 22:19:46 +000064/* Flags & variables */
65
66typedef enum { f_none, f_replace, f_append } sed_function;
67
68#define NO_LINE -2
69#define LAST_LINE -1
70static int addr_line = NO_LINE;
71static char *addr_pattern = NULL;
72static int negated = 0;
73
74#define SKIPSPACES(p) do { while (isspace(*(p))) (p)++; } while (0)
75
76#define BUFSIZE 1024
77
78static inline int at_last(FILE * fp)
Eric Andersen50d63601999-11-09 01:47:36 +000079{
Erik Andersene49d5ec2000-02-08 19:58:47 +000080 int res = 0;
Eric Andersen6b6b3f61999-10-28 16:06:25 +000081
Erik Andersene49d5ec2000-02-08 19:58:47 +000082 if (feof(fp))
83 return 1;
84 else {
Erik Andersen4d054312000-02-10 07:31:15 +000085 int ch;
Erik Andersene49d5ec2000-02-08 19:58:47 +000086
87 if ((ch = fgetc(fp)) == EOF)
88 res++;
89 ungetc(ch, fp);
90 }
91 return res;
Erik Andersen1266a131999-12-29 22:19:46 +000092}
93
94static void do_sed_repl(FILE * fp, char *needle, char *newNeedle,
Erik Andersene49d5ec2000-02-08 19:58:47 +000095 int ignoreCase, int printFlag, int quietFlag)
Erik Andersen1266a131999-12-29 22:19:46 +000096{
Erik Andersene49d5ec2000-02-08 19:58:47 +000097 int foundOne = FALSE;
98 char haystack[BUFSIZE];
99 int line = 1, doit;
Erik Andersen1266a131999-12-29 22:19:46 +0000100
Erik Andersene49d5ec2000-02-08 19:58:47 +0000101 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++;
Erik Andersen1266a131999-12-29 22:19:46 +0000130 }
Eric Andersen50d63601999-11-09 01:47:36 +0000131}
Eric Andersen6b6b3f61999-10-28 16:06:25 +0000132
Erik Andersen1266a131999-12-29 22:19:46 +0000133static void do_sed_append(FILE * fp, char *appendline, int quietFlag)
134{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000135 char buffer[BUFSIZE];
136 int line = 1, doit;
Erik Andersen1266a131999-12-29 22:19:46 +0000137
Erik Andersene49d5ec2000-02-08 19:58:47 +0000138 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 }
Erik Andersen1266a131999-12-29 22:19:46 +0000160
Erik Andersene49d5ec2000-02-08 19:58:47 +0000161 line++;
162 }
Erik Andersen1266a131999-12-29 22:19:46 +0000163}
164
165extern int sed_main(int argc, char **argv)
Eric Andersen6b6b3f61999-10-28 16:06:25 +0000166{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000167 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;
Eric Andersen6b6b3f61999-10-28 16:06:25 +0000179
Eric Andersen6b6b3f61999-10-28 16:06:25 +0000180 argc--;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000181 argv++;
182 if (argc < 1) {
Eric Andersenc1525e81999-10-29 00:07:31 +0000183 usage(sed_usage);
Eric Andersen50d63601999-11-09 01:47:36 +0000184 }
Eric Andersen6b6b3f61999-10-28 16:06:25 +0000185
Erik Andersen4d054312000-02-10 07:31:15 +0000186 while (argc > 1) {
Erik Andersene916d242000-03-06 19:20:35 +0000187 if (**argv != '-')
188 usage(sed_usage);
189 argc--;
190 cp = *argv++;
191 stopNow = FALSE;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000192
Erik Andersene916d242000-03-06 19:20:35 +0000193 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 }
Erik Andersen4d054312000-02-10 07:31:15 +0000310 }
Eric Andersen6b6b3f61999-10-28 16:06:25 +0000311
Erik Andersene49d5ec2000-02-08 19:58:47 +0000312 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++;
Eric Andersen50d63601999-11-09 01:47:36 +0000327
Erik Andersene49d5ec2000-02-08 19:58:47 +0000328 fp = fopen(name, "r");
329 if (fp == NULL) {
330 perror(name);
331 continue;
332 }
Eric Andersen50d63601999-11-09 01:47:36 +0000333
Erik Andersene49d5ec2000-02-08 19:58:47 +0000334 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 }
Eric Andersen50d63601999-11-09 01:47:36 +0000345
Erik Andersene49d5ec2000-02-08 19:58:47 +0000346 if (ferror(fp))
347 perror(name);
348
349 fclose(fp);
350 }
Eric Andersen6b6b3f61999-10-28 16:06:25 +0000351 }
Erik Andersene49d5ec2000-02-08 19:58:47 +0000352 exit(TRUE);
Eric Andersen6b6b3f61999-10-28 16:06:25 +0000353}
354
355
356/* END CODE */