X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=editors%2Fsed.c;h=45574e81dd5138039159a8032e8a3649ee37a44b;hb=21d7d61de164b25bc6a70e49b12567e80caed953;hp=a0d0cf74b7eb696f8448845406565c8bf199e256;hpb=40ec4aeb8e26199a076627060a888c80146ab753;p=oweals%2Fbusybox.git diff --git a/editors/sed.c b/editors/sed.c index a0d0cf74b..45574e81d 100644 --- a/editors/sed.c +++ b/editors/sed.c @@ -5,7 +5,8 @@ * Copyright (C) 1999,2000,2001 by Lineo, inc. and Mark Whitley * Copyright (C) 1999,2000,2001 by Mark Whitley * Copyright (C) 2002 Matt Kraai - * Copyright (C) 2003 by Glenn McGrath + * Copyright (C) 2003 by Glenn McGrath + * Copyright (C) 2003,2004 by Rob Landley * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -111,7 +112,10 @@ typedef struct sed_cmd_s { /* globals */ /* options */ -static int be_quiet = 0; +static int be_quiet = 0, in_place=0; +FILE *nonstdout; +char *outname; + static const char bad_format_in_subst[] = "bad format in substitution expression"; @@ -168,6 +172,13 @@ static void free_and_close_stuff(void) } #endif +/* If something bad happens during -i operation, delete temp file */ + +static void cleanup_outname(void) +{ + if(outname) unlink(outname); +} + /* strdup, replacing "\n" with '\n', and "\delimiter" with 'delimiter' */ static void parse_escapes(char *dest, const char *string, int len, char from, char to) @@ -199,7 +210,7 @@ static char *copy_parsing_slashn(const char *string, int len) /* * index_of_next_unescaped_regexp_delim - walks left to right through a string * beginning at a specified index and returns the index of the next regular - * expression delimiter (typically a forward * slash ('/')) not preceeded by + * expression delimiter (typically a forward * slash ('/')) not preceded by * a backslash ('\'). */ static int index_of_next_unescaped_regexp_delim(const char delimiter, @@ -284,7 +295,7 @@ static int get_address(char *my_str, int *linenum, regex_t ** regex) next = index_of_next_unescaped_regexp_delim(delimiter, ++pos); if (next == -1) bb_error_msg_and_die("unterminated match expression"); - + temp=copy_parsing_slashn(pos,next); *regex = (regex_t *) xmalloc(sizeof(regex_t)); xregcomp(*regex, temp, REG_NEWLINE); @@ -362,7 +373,7 @@ static int parse_subst_cmd(sed_cmd_t * const sed_cmd, char *substr) { char *temp; idx+=parse_file_cmd(sed_cmd,substr+idx,&temp); - + break; } /* Ignore case (gnu exension) */ @@ -400,7 +411,13 @@ static char *parse_cmd_args(sed_cmd_t *sed_cmd, char *cmdstr) if ((sed_cmd->end_line || sed_cmd->end_match) && sed_cmd->cmd != 'c') bb_error_msg_and_die ("only a beginning address can be specified for edit commands"); - while(isspace(*cmdstr)) cmdstr++; + for(;;) { + if(*cmdstr=='\n' || *cmdstr=='\\') { + cmdstr++; + break; + } else if(isspace(*cmdstr)) cmdstr++; + else break; + } sed_cmd->string = bb_xstrdup(cmdstr); parse_escapes(sed_cmd->string,sed_cmd->string,strlen(cmdstr),0,0); cmdstr += strlen(cmdstr); @@ -618,6 +635,15 @@ static int do_subst_command(sed_cmd_t * sed_cmd, char **line) do { int i; + /* Work around bug in glibc regexec, demonstrated by: + echo " a.b" | busybox sed 's [^ .]* x g' + The match_count check is so not to break + echo "hi" | busybox sed 's/^/!/g' */ + if(!regmatch[0].rm_so && !regmatch[0].rm_eo && match_count) { + pipe_putc(*(oldline++)); + continue; + } + match_count++; /* If we aren't interested in this match, output old line to @@ -681,7 +707,7 @@ static void flush_append(void) { /* Output appended lines. */ while(append_head) { - puts(append_head->string); + fprintf(nonstdout,"%s\n",append_head->string); append_tail=append_head->next; free(append_head->string); free(append_head); @@ -719,12 +745,17 @@ static int puts_maybe_newline(char *s, FILE *file, int missing_newline, int no_n fputs(s,file); if(!no_newline) fputc('\n',file); + if(ferror(file)) { + fprintf(stderr,"Write failed.\n"); + exit(4); /* It's what gnu sed exits with... */ + } + return no_newline; } -#define sed_puts(s,n) missing_newline=puts_maybe_newline(s,stdout,missing_newline,n) +#define sed_puts(s,n) missing_newline=puts_maybe_newline(s,nonstdout,missing_newline,n) -static void process_file(FILE * file) +static void process_file(FILE *file) { char *pattern_space, *next_line, *hold_space=NULL; static int linenum = 0, missing_newline=0; @@ -810,7 +841,7 @@ restart: /* Print line number */ case '=': - printf("%d\n", linenum); + fprintf(nonstdout,"%d\n", linenum); break; /* Write the current pattern space up to the first newline */ @@ -1065,7 +1096,9 @@ static void add_cmd_block(char *cmdstr) extern int sed_main(int argc, char **argv) { - int opt, status = EXIT_SUCCESS; + int status = EXIT_SUCCESS; + int opt; + uint8_t getpat = 1; #ifdef CONFIG_FEATURE_CLEAN_UP /* destroy command strings on exit */ @@ -1073,14 +1106,27 @@ extern int sed_main(int argc, char **argv) bb_perror_msg_and_die("atexit"); #endif +#define LIE_TO_AUTOCONF +#ifdef LIE_TO_AUTOCONF + if(argc==2 && !strcmp(argv[1],"--version")) { + printf("This is not GNU sed version 4.0\n"); + exit(0); + } +#endif + /* do normal option parsing */ - while ((opt = getopt(argc, argv, "ne:f:")) > 0) { + while ((opt = getopt(argc, argv, "ine:f:")) > 0) { switch (opt) { + case 'i': + in_place++; + atexit(cleanup_outname); + break; case 'n': be_quiet++; break; case 'e': add_cmd_block(optarg); + getpat=0; break; case 'f': { @@ -1092,6 +1138,7 @@ extern int sed_main(int argc, char **argv) while ((line = bb_get_chomped_line_from_file(cmdfile)) != NULL) { add_cmd(line); + getpat=0; free(line); } bb_xprint_and_close_file(cmdfile); @@ -1105,7 +1152,7 @@ extern int sed_main(int argc, char **argv) /* if we didn't get a pattern from a -e and no command file was specified, * argv[optind] should be the pattern. no pattern, no worky */ - if (sed_cmd_head.next == NULL) { + if(getpat) { if (argv[optind] == NULL) bb_show_usage(); else @@ -1114,23 +1161,51 @@ extern int sed_main(int argc, char **argv) /* Flush any unfinished commands. */ add_cmd(""); + /* By default, we write to stdout */ + nonstdout=stdout; + /* argv[(optind)..(argc-1)] should be names of file to process. If no * files were specified or '-' was specified, take input from stdin. * Otherwise, we process all the files specified. */ if (argv[optind] == NULL) { + if(in_place) { + fprintf(stderr,"sed: Filename required for -i\n"); + exit(1); + } process_file(stdin); } else { int i; FILE *file; for (i = optind; i < argc; i++) { - if(!strcmp(argv[i], "-")) { + if(!strcmp(argv[i], "-") && !in_place) { process_file(stdin); } else { file = bb_wfopen(argv[i], "r"); if (file) { + if(in_place) { + struct stat statbuf; + outname=bb_xstrndup(argv[i],strlen(argv[i])+6); + strcat(outname,"XXXXXX"); + /* Set permissions of output file */ + fstat(fileno(file),&statbuf); + mkstemp(outname); + nonstdout=bb_wfopen(outname,"w"); + /* Set permissions of output file */ + fstat(fileno(file),&statbuf); + fchmod(fileno(nonstdout),statbuf.st_mode); + atexit(cleanup_outname); + } process_file(file); fclose(file); + if(in_place) { + fclose(nonstdout); + nonstdout=stdout; + unlink(argv[i]); + rename(outname,argv[i]); + free(outname); + outname=0; + } } else { status = EXIT_FAILURE; }