diff options
author | Mark Whitley <markw@lineo.com> | 2000-07-14 00:00:15 +0000 |
---|---|---|
committer | Mark Whitley <markw@lineo.com> | 2000-07-14 00:00:15 +0000 |
commit | 94074a980c3f72e3765fcb43f1056b509ec800b5 (patch) | |
tree | 122b1a641314cb1cb56846d67245a1010f4ed967 | |
parent | 496e33feeb2d0eb4aa2bf3cc2f34f6e29987c1bd (diff) | |
download | busybox-w32-94074a980c3f72e3765fcb43f1056b509ec800b5.tar.gz busybox-w32-94074a980c3f72e3765fcb43f1056b509ec800b5.tar.bz2 busybox-w32-94074a980c3f72e3765fcb43f1056b509ec800b5.zip |
Added support for (a)ppend, (i)nsert, and (c)hange commands to sed.
-rw-r--r-- | editors/sed.c | 95 | ||||
-rw-r--r-- | sed.c | 95 |
2 files changed, 176 insertions, 14 deletions
diff --git a/editors/sed.c b/editors/sed.c index bf8ca1b29..1eea128d5 100644 --- a/editors/sed.c +++ b/editors/sed.c | |||
@@ -24,8 +24,9 @@ | |||
24 | Supported features and commands in this version of sed: | 24 | Supported features and commands in this version of sed: |
25 | 25 | ||
26 | - comments ('#') | 26 | - comments ('#') |
27 | - Address matching: num|/matchstr/[,num|/matchstr/|$]command | 27 | - address matching: num|/matchstr/[,num|/matchstr/|$]command |
28 | - Commands: p, d, s/match/replace/[g] | 28 | - commands: (p)rint, (d)elete, (s)ubstitue (with g & I flags) |
29 | - edit commands: (a)ppend, (i)nsert, (c)hange | ||
29 | 30 | ||
30 | (Note: Specifying an address (range) to match is *optional*; commands | 31 | (Note: Specifying an address (range) to match is *optional*; commands |
31 | default to the whole pattern space if no specific address match was | 32 | default to the whole pattern space if no specific address match was |
@@ -73,6 +74,9 @@ struct sed_cmd { | |||
73 | regex_t *sub_match; /* sed -e 's/sub_match/replace/' */ | 74 | regex_t *sub_match; /* sed -e 's/sub_match/replace/' */ |
74 | char *replace; /* sed -e 's/sub_match/replace/' XXX: who will hold the \1 \2 \3s? */ | 75 | char *replace; /* sed -e 's/sub_match/replace/' XXX: who will hold the \1 \2 \3s? */ |
75 | unsigned int sub_g:1; /* sed -e 's/foo/bar/g' (global) */ | 76 | unsigned int sub_g:1; /* sed -e 's/foo/bar/g' (global) */ |
77 | |||
78 | /* edit command (a,i,c) speicific field */ | ||
79 | char *editline; | ||
76 | }; | 80 | }; |
77 | 81 | ||
78 | /* globals */ | 82 | /* globals */ |
@@ -274,6 +278,59 @@ static void parse_subst_cmd(struct sed_cmd *sed_cmd, const char *substr) | |||
274 | free(match); | 278 | free(match); |
275 | } | 279 | } |
276 | 280 | ||
281 | static void parse_edit_cmd(struct sed_cmd *sed_cmd, const char *editstr) | ||
282 | { | ||
283 | int idx = 0; | ||
284 | char *ptr; /* shorthand */ | ||
285 | |||
286 | /* | ||
287 | * the string that gets passed to this function should look like this: | ||
288 | * | ||
289 | * need one of these | ||
290 | * | | ||
291 | * | this backslash (immediately following the edit command) is mandatory | ||
292 | * | | | ||
293 | * [aic]\ | ||
294 | * TEXT1\ | ||
295 | * TEXT2\ | ||
296 | * TEXTN | ||
297 | * | ||
298 | * as soon as we hit a TEXT line that has no trailing '\', we're done. | ||
299 | * this means a command like: | ||
300 | * | ||
301 | * i\ | ||
302 | * INSERTME | ||
303 | * | ||
304 | * is a-ok. | ||
305 | * | ||
306 | */ | ||
307 | |||
308 | if (editstr[1] != '\\' && (editstr[2] != '\n' || editstr[2] != '\r')) | ||
309 | fatalError("bad format in edit expression\n"); | ||
310 | |||
311 | /* store the edit line text */ | ||
312 | sed_cmd->editline = strdup(&editstr[3]); | ||
313 | ptr = sed_cmd->editline; | ||
314 | |||
315 | /* now we need to go through * and: s/\\[\r\n]$/\n/g on the edit line */ | ||
316 | while (ptr[idx]) { | ||
317 | while (ptr[idx] != '\\' && (ptr[idx+1] != '\n' || ptr[idx+1] != '\r')) { | ||
318 | idx++; | ||
319 | if (!ptr[idx]) { | ||
320 | ptr[idx] = '\n'; | ||
321 | ptr[idx+1] = 0; | ||
322 | return; | ||
323 | } | ||
324 | } | ||
325 | /* move the newline over the '\' before it (effectively eats the '\') */ | ||
326 | memmove(&ptr[idx], &ptr[idx+1], strlen(&ptr[idx+1])); | ||
327 | ptr[strlen(ptr)-1] = 0; | ||
328 | /* substitue \r for \n if needed */ | ||
329 | if (ptr[idx] == '\r') | ||
330 | ptr[idx] = '\n'; | ||
331 | } | ||
332 | } | ||
333 | |||
277 | static void parse_cmd_str(struct sed_cmd *sed_cmd, const char *cmdstr) | 334 | static void parse_cmd_str(struct sed_cmd *sed_cmd, const char *cmdstr) |
278 | { | 335 | { |
279 | int idx = 0; | 336 | int idx = 0; |
@@ -295,13 +352,17 @@ static void parse_cmd_str(struct sed_cmd *sed_cmd, const char *cmdstr) | |||
295 | /* last part (mandatory) will be a command */ | 352 | /* last part (mandatory) will be a command */ |
296 | if (cmdstr[idx] == '\0') | 353 | if (cmdstr[idx] == '\0') |
297 | fatalError("missing command\n"); | 354 | fatalError("missing command\n"); |
298 | if (!strchr("pds", cmdstr[idx])) /* <-- XXX add new commands here */ | 355 | if (!strchr("pdsaic", cmdstr[idx])) /* <-- XXX add new commands here */ |
299 | fatalError("invalid command\n"); | 356 | fatalError("invalid command\n"); |
300 | sed_cmd->cmd = cmdstr[idx]; | 357 | sed_cmd->cmd = cmdstr[idx]; |
301 | 358 | ||
302 | /* special-case handling for (s)ubstitution */ | 359 | /* special-case handling for (s)ubstitution */ |
303 | if (sed_cmd->cmd == 's') | 360 | if (sed_cmd->cmd == 's') |
304 | parse_subst_cmd(sed_cmd, &cmdstr[idx]); | 361 | parse_subst_cmd(sed_cmd, &cmdstr[idx]); |
362 | |||
363 | /* special-case handling for (a)ppend, (i)nsert, and (c)hange */ | ||
364 | if (strchr("aic", cmdstr[idx])) | ||
365 | parse_edit_cmd(sed_cmd, &cmdstr[idx]); | ||
305 | } | 366 | } |
306 | 367 | ||
307 | static void add_cmd_str(const char *cmdstr) | 368 | static void add_cmd_str(const char *cmdstr) |
@@ -327,13 +388,19 @@ static void load_cmd_file(char *filename) | |||
327 | { | 388 | { |
328 | FILE *cmdfile; | 389 | FILE *cmdfile; |
329 | char *line; | 390 | char *line; |
391 | char *nextline; | ||
330 | 392 | ||
331 | cmdfile = fopen(filename, "r"); | 393 | cmdfile = fopen(filename, "r"); |
332 | if (cmdfile == NULL) | 394 | if (cmdfile == NULL) |
333 | fatalError(strerror(errno)); | 395 | fatalError(strerror(errno)); |
334 | 396 | ||
335 | while ((line = get_line_from_file(cmdfile)) != NULL) { | 397 | while ((line = get_line_from_file(cmdfile)) != NULL) { |
336 | line[strlen(line)-1] = 0; /* eat newline */ | 398 | /* if a line ends with '\' it needs the next line appended to it */ |
399 | while (line[strlen(line)-2] == '\\' && | ||
400 | (nextline = get_line_from_file(cmdfile)) != NULL) { | ||
401 | line = realloc(line, strlen(line) + strlen(nextline) + 1); | ||
402 | strcat(line, nextline); | ||
403 | } | ||
337 | add_cmd_str(line); | 404 | add_cmd_str(line); |
338 | free(line); | 405 | free(line); |
339 | } | 406 | } |
@@ -359,8 +426,7 @@ static int do_subst_command(const struct sed_cmd *sed_cmd, const char *line) | |||
359 | fputs(sed_cmd->replace, stdout); | 426 | fputs(sed_cmd->replace, stdout); |
360 | /* then advance past the match */ | 427 | /* then advance past the match */ |
361 | ptr += regmatch.rm_eo; | 428 | ptr += regmatch.rm_eo; |
362 | /* and let the calling function know that something | 429 | /* and flag that something has changed */ |
363 | * has been changed */ | ||
364 | altered++; | 430 | altered++; |
365 | 431 | ||
366 | /* if we're not doing this globally... */ | 432 | /* if we're not doing this globally... */ |
@@ -398,6 +464,21 @@ static int do_sed_command(const struct sed_cmd *sed_cmd, const char *line) | |||
398 | case 's': | 464 | case 's': |
399 | altered = do_subst_command(sed_cmd, line); | 465 | altered = do_subst_command(sed_cmd, line); |
400 | break; | 466 | break; |
467 | |||
468 | case 'a': | ||
469 | fputs(line, stdout); | ||
470 | fputs(sed_cmd->editline, stdout); | ||
471 | altered++; | ||
472 | break; | ||
473 | |||
474 | case 'i': | ||
475 | fputs(sed_cmd->editline, stdout); | ||
476 | break; | ||
477 | |||
478 | case 'c': | ||
479 | fputs(sed_cmd->editline, stdout); | ||
480 | altered++; | ||
481 | break; | ||
401 | } | 482 | } |
402 | 483 | ||
403 | return altered; | 484 | return altered; |
@@ -483,7 +564,7 @@ extern int sed_main(int argc, char **argv) | |||
483 | while ((opt = getopt(argc, argv, "Vhne:f:")) > 0) { | 564 | while ((opt = getopt(argc, argv, "Vhne:f:")) > 0) { |
484 | switch (opt) { | 565 | switch (opt) { |
485 | case 'V': | 566 | case 'V': |
486 | printf("Print Busybox version here\n"); | 567 | printf("BusyBox v%s (%s)\n", BB_VER, BB_BT); |
487 | exit(0); | 568 | exit(0); |
488 | break; | 569 | break; |
489 | case 'h': | 570 | case 'h': |
@@ -24,8 +24,9 @@ | |||
24 | Supported features and commands in this version of sed: | 24 | Supported features and commands in this version of sed: |
25 | 25 | ||
26 | - comments ('#') | 26 | - comments ('#') |
27 | - Address matching: num|/matchstr/[,num|/matchstr/|$]command | 27 | - address matching: num|/matchstr/[,num|/matchstr/|$]command |
28 | - Commands: p, d, s/match/replace/[g] | 28 | - commands: (p)rint, (d)elete, (s)ubstitue (with g & I flags) |
29 | - edit commands: (a)ppend, (i)nsert, (c)hange | ||
29 | 30 | ||
30 | (Note: Specifying an address (range) to match is *optional*; commands | 31 | (Note: Specifying an address (range) to match is *optional*; commands |
31 | default to the whole pattern space if no specific address match was | 32 | default to the whole pattern space if no specific address match was |
@@ -73,6 +74,9 @@ struct sed_cmd { | |||
73 | regex_t *sub_match; /* sed -e 's/sub_match/replace/' */ | 74 | regex_t *sub_match; /* sed -e 's/sub_match/replace/' */ |
74 | char *replace; /* sed -e 's/sub_match/replace/' XXX: who will hold the \1 \2 \3s? */ | 75 | char *replace; /* sed -e 's/sub_match/replace/' XXX: who will hold the \1 \2 \3s? */ |
75 | unsigned int sub_g:1; /* sed -e 's/foo/bar/g' (global) */ | 76 | unsigned int sub_g:1; /* sed -e 's/foo/bar/g' (global) */ |
77 | |||
78 | /* edit command (a,i,c) speicific field */ | ||
79 | char *editline; | ||
76 | }; | 80 | }; |
77 | 81 | ||
78 | /* globals */ | 82 | /* globals */ |
@@ -274,6 +278,59 @@ static void parse_subst_cmd(struct sed_cmd *sed_cmd, const char *substr) | |||
274 | free(match); | 278 | free(match); |
275 | } | 279 | } |
276 | 280 | ||
281 | static void parse_edit_cmd(struct sed_cmd *sed_cmd, const char *editstr) | ||
282 | { | ||
283 | int idx = 0; | ||
284 | char *ptr; /* shorthand */ | ||
285 | |||
286 | /* | ||
287 | * the string that gets passed to this function should look like this: | ||
288 | * | ||
289 | * need one of these | ||
290 | * | | ||
291 | * | this backslash (immediately following the edit command) is mandatory | ||
292 | * | | | ||
293 | * [aic]\ | ||
294 | * TEXT1\ | ||
295 | * TEXT2\ | ||
296 | * TEXTN | ||
297 | * | ||
298 | * as soon as we hit a TEXT line that has no trailing '\', we're done. | ||
299 | * this means a command like: | ||
300 | * | ||
301 | * i\ | ||
302 | * INSERTME | ||
303 | * | ||
304 | * is a-ok. | ||
305 | * | ||
306 | */ | ||
307 | |||
308 | if (editstr[1] != '\\' && (editstr[2] != '\n' || editstr[2] != '\r')) | ||
309 | fatalError("bad format in edit expression\n"); | ||
310 | |||
311 | /* store the edit line text */ | ||
312 | sed_cmd->editline = strdup(&editstr[3]); | ||
313 | ptr = sed_cmd->editline; | ||
314 | |||
315 | /* now we need to go through * and: s/\\[\r\n]$/\n/g on the edit line */ | ||
316 | while (ptr[idx]) { | ||
317 | while (ptr[idx] != '\\' && (ptr[idx+1] != '\n' || ptr[idx+1] != '\r')) { | ||
318 | idx++; | ||
319 | if (!ptr[idx]) { | ||
320 | ptr[idx] = '\n'; | ||
321 | ptr[idx+1] = 0; | ||
322 | return; | ||
323 | } | ||
324 | } | ||
325 | /* move the newline over the '\' before it (effectively eats the '\') */ | ||
326 | memmove(&ptr[idx], &ptr[idx+1], strlen(&ptr[idx+1])); | ||
327 | ptr[strlen(ptr)-1] = 0; | ||
328 | /* substitue \r for \n if needed */ | ||
329 | if (ptr[idx] == '\r') | ||
330 | ptr[idx] = '\n'; | ||
331 | } | ||
332 | } | ||
333 | |||
277 | static void parse_cmd_str(struct sed_cmd *sed_cmd, const char *cmdstr) | 334 | static void parse_cmd_str(struct sed_cmd *sed_cmd, const char *cmdstr) |
278 | { | 335 | { |
279 | int idx = 0; | 336 | int idx = 0; |
@@ -295,13 +352,17 @@ static void parse_cmd_str(struct sed_cmd *sed_cmd, const char *cmdstr) | |||
295 | /* last part (mandatory) will be a command */ | 352 | /* last part (mandatory) will be a command */ |
296 | if (cmdstr[idx] == '\0') | 353 | if (cmdstr[idx] == '\0') |
297 | fatalError("missing command\n"); | 354 | fatalError("missing command\n"); |
298 | if (!strchr("pds", cmdstr[idx])) /* <-- XXX add new commands here */ | 355 | if (!strchr("pdsaic", cmdstr[idx])) /* <-- XXX add new commands here */ |
299 | fatalError("invalid command\n"); | 356 | fatalError("invalid command\n"); |
300 | sed_cmd->cmd = cmdstr[idx]; | 357 | sed_cmd->cmd = cmdstr[idx]; |
301 | 358 | ||
302 | /* special-case handling for (s)ubstitution */ | 359 | /* special-case handling for (s)ubstitution */ |
303 | if (sed_cmd->cmd == 's') | 360 | if (sed_cmd->cmd == 's') |
304 | parse_subst_cmd(sed_cmd, &cmdstr[idx]); | 361 | parse_subst_cmd(sed_cmd, &cmdstr[idx]); |
362 | |||
363 | /* special-case handling for (a)ppend, (i)nsert, and (c)hange */ | ||
364 | if (strchr("aic", cmdstr[idx])) | ||
365 | parse_edit_cmd(sed_cmd, &cmdstr[idx]); | ||
305 | } | 366 | } |
306 | 367 | ||
307 | static void add_cmd_str(const char *cmdstr) | 368 | static void add_cmd_str(const char *cmdstr) |
@@ -327,13 +388,19 @@ static void load_cmd_file(char *filename) | |||
327 | { | 388 | { |
328 | FILE *cmdfile; | 389 | FILE *cmdfile; |
329 | char *line; | 390 | char *line; |
391 | char *nextline; | ||
330 | 392 | ||
331 | cmdfile = fopen(filename, "r"); | 393 | cmdfile = fopen(filename, "r"); |
332 | if (cmdfile == NULL) | 394 | if (cmdfile == NULL) |
333 | fatalError(strerror(errno)); | 395 | fatalError(strerror(errno)); |
334 | 396 | ||
335 | while ((line = get_line_from_file(cmdfile)) != NULL) { | 397 | while ((line = get_line_from_file(cmdfile)) != NULL) { |
336 | line[strlen(line)-1] = 0; /* eat newline */ | 398 | /* if a line ends with '\' it needs the next line appended to it */ |
399 | while (line[strlen(line)-2] == '\\' && | ||
400 | (nextline = get_line_from_file(cmdfile)) != NULL) { | ||
401 | line = realloc(line, strlen(line) + strlen(nextline) + 1); | ||
402 | strcat(line, nextline); | ||
403 | } | ||
337 | add_cmd_str(line); | 404 | add_cmd_str(line); |
338 | free(line); | 405 | free(line); |
339 | } | 406 | } |
@@ -359,8 +426,7 @@ static int do_subst_command(const struct sed_cmd *sed_cmd, const char *line) | |||
359 | fputs(sed_cmd->replace, stdout); | 426 | fputs(sed_cmd->replace, stdout); |
360 | /* then advance past the match */ | 427 | /* then advance past the match */ |
361 | ptr += regmatch.rm_eo; | 428 | ptr += regmatch.rm_eo; |
362 | /* and let the calling function know that something | 429 | /* and flag that something has changed */ |
363 | * has been changed */ | ||
364 | altered++; | 430 | altered++; |
365 | 431 | ||
366 | /* if we're not doing this globally... */ | 432 | /* if we're not doing this globally... */ |
@@ -398,6 +464,21 @@ static int do_sed_command(const struct sed_cmd *sed_cmd, const char *line) | |||
398 | case 's': | 464 | case 's': |
399 | altered = do_subst_command(sed_cmd, line); | 465 | altered = do_subst_command(sed_cmd, line); |
400 | break; | 466 | break; |
467 | |||
468 | case 'a': | ||
469 | fputs(line, stdout); | ||
470 | fputs(sed_cmd->editline, stdout); | ||
471 | altered++; | ||
472 | break; | ||
473 | |||
474 | case 'i': | ||
475 | fputs(sed_cmd->editline, stdout); | ||
476 | break; | ||
477 | |||
478 | case 'c': | ||
479 | fputs(sed_cmd->editline, stdout); | ||
480 | altered++; | ||
481 | break; | ||
401 | } | 482 | } |
402 | 483 | ||
403 | return altered; | 484 | return altered; |
@@ -483,7 +564,7 @@ extern int sed_main(int argc, char **argv) | |||
483 | while ((opt = getopt(argc, argv, "Vhne:f:")) > 0) { | 564 | while ((opt = getopt(argc, argv, "Vhne:f:")) > 0) { |
484 | switch (opt) { | 565 | switch (opt) { |
485 | case 'V': | 566 | case 'V': |
486 | printf("Print Busybox version here\n"); | 567 | printf("BusyBox v%s (%s)\n", BB_VER, BB_BT); |
487 | exit(0); | 568 | exit(0); |
488 | break; | 569 | break; |
489 | case 'h': | 570 | case 'h': |