blob: 9fed6704e9f194c0f91d5383d9e1a5cf7bf00e1b [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 Andersen7ab9c7e2000-05-12 19:41:47 +000041 "sed [-n] -e script [file...]\n"
42#ifndef BB_FEATURE_TRIVIAL_HELP
43 "\nAllowed sed scripts come in the following form:\n"
Erik Andersene49d5ec2000-02-08 19:58:47 +000044 "\t'ADDR [!] COMMAND'\n\n"
45 "\twhere address ADDR can be:\n"
46 "\t NUMBER Match specified line number\n"
47 "\t $ Match last line\n"
48 "\t /REGEXP/ Match specified regexp\n"
49 "\t (! inverts the meaning of the match)\n\n"
50 "\tand COMMAND can be:\n"
51 "\t s/regexp/replacement/[igp]\n"
52 "\t which attempt to match regexp against the pattern space\n"
53 "\t and if successful replaces the matched portion with replacement.\n\n"
54 "\t aTEXT\n"
55 "\t which appends TEXT after the pattern space\n"
56 "Options:\n"
57 "-e\tadd the script to the commands to be executed\n"
58 "-n\tsuppress automatic printing of pattern space\n\n"
Eric Andersen6b6b3f61999-10-28 16:06:25 +000059#if defined BB_REGEXP
Erik Andersene49d5ec2000-02-08 19:58:47 +000060 "This version of sed matches full regular expresions.\n";
Eric Andersen6b6b3f61999-10-28 16:06:25 +000061#else
Erik Andersen7ab9c7e2000-05-12 19:41:47 +000062 "This version of sed matches strings (not full regular expresions).\n"
Eric Andersen6b6b3f61999-10-28 16:06:25 +000063#endif
Erik Andersen7ab9c7e2000-05-12 19:41:47 +000064#endif
65 ;
Eric Andersen6b6b3f61999-10-28 16:06:25 +000066
Erik Andersen1266a131999-12-29 22:19:46 +000067/* Flags & variables */
68
69typedef enum { f_none, f_replace, f_append } sed_function;
70
71#define NO_LINE -2
72#define LAST_LINE -1
73static int addr_line = NO_LINE;
74static char *addr_pattern = NULL;
75static int negated = 0;
76
77#define SKIPSPACES(p) do { while (isspace(*(p))) (p)++; } while (0)
78
79#define BUFSIZE 1024
80
81static inline int at_last(FILE * fp)
Eric Andersen50d63601999-11-09 01:47:36 +000082{
Erik Andersene49d5ec2000-02-08 19:58:47 +000083 int res = 0;
Eric Andersen6b6b3f61999-10-28 16:06:25 +000084
Erik Andersene49d5ec2000-02-08 19:58:47 +000085 if (feof(fp))
86 return 1;
87 else {
Erik Andersen4d054312000-02-10 07:31:15 +000088 int ch;
Erik Andersene49d5ec2000-02-08 19:58:47 +000089
90 if ((ch = fgetc(fp)) == EOF)
91 res++;
92 ungetc(ch, fp);
93 }
94 return res;
Erik Andersen1266a131999-12-29 22:19:46 +000095}
96
97static void do_sed_repl(FILE * fp, char *needle, char *newNeedle,
Erik Andersene49d5ec2000-02-08 19:58:47 +000098 int ignoreCase, int printFlag, int quietFlag)
Erik Andersen1266a131999-12-29 22:19:46 +000099{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000100 int foundOne = FALSE;
101 char haystack[BUFSIZE];
102 int line = 1, doit;
Erik Andersen1266a131999-12-29 22:19:46 +0000103
Erik Andersene49d5ec2000-02-08 19:58:47 +0000104 while (fgets(haystack, BUFSIZE - 1, fp)) {
105 doit = 0;
106 if (addr_pattern) {
107 doit = !find_match(haystack, addr_pattern, FALSE);
108 } else if (addr_line == NO_LINE)
109 doit = 1;
110 else if (addr_line == LAST_LINE) {
111 if (at_last(fp))
112 doit = 1;
113 } else {
114 if (line == addr_line)
115 doit = 1;
116 }
117 if (negated)
118 doit = 1 - doit;
119 if (doit) {
120 foundOne =
121 replace_match(haystack, needle, newNeedle, ignoreCase);
122
123 if (foundOne == TRUE && printFlag == TRUE) {
124 fprintf(stdout, haystack);
125 }
126 }
127
128 if (quietFlag == FALSE) {
129 fprintf(stdout, haystack);
130 }
131
132 line++;
Erik Andersen1266a131999-12-29 22:19:46 +0000133 }
Eric Andersen50d63601999-11-09 01:47:36 +0000134}
Eric Andersen6b6b3f61999-10-28 16:06:25 +0000135
Erik Andersen1266a131999-12-29 22:19:46 +0000136static void do_sed_append(FILE * fp, char *appendline, int quietFlag)
137{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000138 char buffer[BUFSIZE];
139 int line = 1, doit;
Erik Andersen1266a131999-12-29 22:19:46 +0000140
Erik Andersene49d5ec2000-02-08 19:58:47 +0000141 while (fgets(buffer, BUFSIZE - 1, fp)) {
142 doit = 0;
143 if (addr_pattern) {
144 doit = !find_match(buffer, addr_pattern, FALSE);
145 } else if (addr_line == NO_LINE)
146 doit = 1;
147 else if (addr_line == LAST_LINE) {
148 if (at_last(fp))
149 doit = 1;
150 } else {
151 if (line == addr_line)
152 doit = 1;
153 }
154 if (negated)
155 doit = 1 - doit;
156 if (quietFlag == FALSE) {
157 fprintf(stdout, buffer);
158 }
159 if (doit) {
160 fputs(appendline, stdout);
161 fputc('\n', stdout);
162 }
Erik Andersen1266a131999-12-29 22:19:46 +0000163
Erik Andersene49d5ec2000-02-08 19:58:47 +0000164 line++;
165 }
Erik Andersen1266a131999-12-29 22:19:46 +0000166}
167
168extern int sed_main(int argc, char **argv)
Eric Andersen6b6b3f61999-10-28 16:06:25 +0000169{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000170 FILE *fp;
171 char *needle = NULL, *newNeedle = NULL;
172 char *name;
173 char *cp;
174 int ignoreCase = FALSE;
175 int printFlag = FALSE;
176 int quietFlag = FALSE;
177 int stopNow;
178 char *line_s = NULL, saved;
179 char *appendline = NULL;
180 char *pos;
181 sed_function sed_f = f_none;
Eric Andersen6b6b3f61999-10-28 16:06:25 +0000182
Eric Andersen6b6b3f61999-10-28 16:06:25 +0000183 argc--;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000184 argv++;
185 if (argc < 1) {
Eric Andersenc1525e81999-10-29 00:07:31 +0000186 usage(sed_usage);
Eric Andersen50d63601999-11-09 01:47:36 +0000187 }
Eric Andersen6b6b3f61999-10-28 16:06:25 +0000188
Erik Andersen4d054312000-02-10 07:31:15 +0000189 while (argc > 1) {
Erik Andersene916d242000-03-06 19:20:35 +0000190 if (**argv != '-')
191 usage(sed_usage);
192 argc--;
193 cp = *argv++;
194 stopNow = FALSE;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000195
Erik Andersene916d242000-03-06 19:20:35 +0000196 while (*++cp && stopNow == FALSE) {
197 switch (*cp) {
198 case 'n':
199 quietFlag = TRUE;
200 break;
201 case 'e':
202 if (*(cp + 1) == 0 && --argc < 0) {
203 usage(sed_usage);
204 }
205 if (*++cp != 's')
206 cp = *argv++;
207
208 /* Read address if present */
209 SKIPSPACES(cp);
210 if (*cp == '$') {
211 addr_line = LAST_LINE;
212 cp++;
213 } else {
214 if (isdigit(*cp)) { /* LINE ADDRESS */
215 line_s = cp;
216 while (isdigit(*cp))
217 cp++;
218 if (cp > line_s) {
219 /* numeric line */
220 saved = *cp;
221 *cp = '\0';
222 addr_line = atoi(line_s);
223 *cp = saved;
224 }
225 } else if (*cp == '/') { /* PATTERN ADDRESS */
226 pos = addr_pattern = cp + 1;
227 pos = strchr(pos, '/');
228 if (!pos)
229 usage(sed_usage);
230 *pos = '\0';
231 cp = pos + 1;
232 }
233 }
234
235 SKIPSPACES(cp);
236 if (*cp == '!') {
237 negated++;
238 cp++;
239 }
240
241 /* Read command */
242
243 SKIPSPACES(cp);
244 switch (*cp) {
245 case 's': /* REPLACE */
246 if (strlen(cp) <= 3 || *(cp + 1) != '/')
247 break;
248 sed_f = f_replace;
249
250 pos = needle = cp + 2;
251
252 for (;;) {
253 pos = strchr(pos, '/');
254 if (pos == NULL) {
255 usage(sed_usage);
256 }
257 if (*(pos - 1) == '\\') {
258 pos++;
259 continue;
260 }
261 break;
262 }
263 *pos = 0;
264 newNeedle = ++pos;
265 for (;;) {
266 pos = strchr(pos, '/');
267 if (pos == NULL) {
268 usage(sed_usage);
269 }
270 if (*(pos - 1) == '\\') {
271 pos++;
272 continue;
273 }
274 break;
275 }
276 *pos = 0;
277 if (pos + 2 != 0) {
278 while (*++pos) {
279 switch (*pos) {
280 case 'i':
281 ignoreCase = TRUE;
282 break;
283 case 'p':
284 printFlag = TRUE;
285 break;
286 case 'g':
287 break;
288 default:
289 usage(sed_usage);
290 }
291 }
292 }
293 cp = pos;
294 /* fprintf(stderr, "replace '%s' with '%s'\n", needle, newNeedle); */
295 break;
296
297 case 'a': /* APPEND */
298 if (strlen(cp) < 2)
299 break;
300 sed_f = f_append;
301 appendline = ++cp;
302 /* fprintf(stderr, "append '%s'\n", appendline); */
303 break;
304 }
305
306 stopNow = TRUE;
307 break;
308
309 default:
310 usage(sed_usage);
311 }
312 }
Erik Andersen4d054312000-02-10 07:31:15 +0000313 }
Eric Andersen6b6b3f61999-10-28 16:06:25 +0000314
Erik Andersene49d5ec2000-02-08 19:58:47 +0000315 if (argc == 0) {
316 switch (sed_f) {
317 case f_none:
318 break;
319 case f_replace:
320 do_sed_repl(stdin, needle, newNeedle, ignoreCase, printFlag,
321 quietFlag);
322 break;
323 case f_append:
324 do_sed_append(stdin, appendline, quietFlag);
325 break;
326 }
327 } else {
328 while (argc-- > 0) {
329 name = *argv++;
Eric Andersen50d63601999-11-09 01:47:36 +0000330
Erik Andersene49d5ec2000-02-08 19:58:47 +0000331 fp = fopen(name, "r");
332 if (fp == NULL) {
333 perror(name);
334 continue;
335 }
Eric Andersen50d63601999-11-09 01:47:36 +0000336
Erik Andersene49d5ec2000-02-08 19:58:47 +0000337 switch (sed_f) {
338 case f_none:
339 break;
340 case f_replace:
341 do_sed_repl(fp, needle, newNeedle, ignoreCase, printFlag,
342 quietFlag);
343 break;
344 case f_append:
345 do_sed_append(fp, appendline, quietFlag);
346 break;
347 }
Eric Andersen50d63601999-11-09 01:47:36 +0000348
Erik Andersene49d5ec2000-02-08 19:58:47 +0000349 if (ferror(fp))
350 perror(name);
351
352 fclose(fp);
353 }
Eric Andersen6b6b3f61999-10-28 16:06:25 +0000354 }
Erik Andersene49d5ec2000-02-08 19:58:47 +0000355 exit(TRUE);
Eric Andersen6b6b3f61999-10-28 16:06:25 +0000356}
357
358
359/* END CODE */