Changeset 1770 in MondoRescue for branches/stable/mindi-busybox/editors/sed.c
- Timestamp:
- Nov 6, 2007, 11:01:53 AM (16 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/stable/mindi-busybox/editors/sed.c
r902 r1770 11 11 * MAINTAINER: Rob Landley <rob@landley.net> 12 12 * 13 * Licensed under GPL v2 or later, see file LICENSE in this tarball for details.13 * Licensed under GPL version 2, see file LICENSE in this tarball for details. 14 14 */ 15 15 … … 22 22 the command line). It calls get_address() and parse_cmd_args(). The 23 23 resulting sed_cmd_t structures are appended to a linked list 24 ( bbg.sed_cmd_head/bbg.sed_cmd_tail).24 (G.sed_cmd_head/G.sed_cmd_tail). 25 25 26 26 add_input_file() adds a FILE * to the list of input files. We need to … … 59 59 */ 60 60 61 #include " busybox.h"61 #include "libbb.h" 62 62 #include "xregex.h" 63 63 64 64 /* Each sed command turns into one of these structures. */ 65 65 typedef struct sed_cmd_s { 66 /* Ordered by alignment requirements: currently 36 bytes on x86 */ 67 68 /* address storage */ 69 regex_t *beg_match; /* sed -e '/match/cmd' */ 70 regex_t *end_match; /* sed -e '/match/,/end_match/cmd' */ 71 regex_t *sub_match; /* For 's/sub_match/string/' */ 72 int beg_line; /* 'sed 1p' 0 == apply commands to all lines */ 73 int end_line; /* 'sed 1,3p' 0 == one line only. -1 = last line ($) */ 74 75 FILE *file; /* File (sw) command writes to, -1 for none. */ 76 char *string; /* Data string for (saicytb) commands. */ 77 78 unsigned short which_match; /* (s) Which match to replace (0 for all) */ 79 80 /* Bitfields (gcc won't group them if we don't) */ 81 unsigned int invert:1; /* the '!' after the address */ 82 unsigned int in_match:1; /* Next line also included in match? */ 83 unsigned int no_newline:1; /* Last line written by (sw) had no '\n' */ 84 unsigned int sub_p:1; /* (s) print option */ 85 86 /* GENERAL FIELDS */ 87 char cmd; /* The command char: abcdDgGhHilnNpPqrstwxy:={} */ 88 struct sed_cmd_s *next; /* Next command (linked list, NULL terminated) */ 66 /* Ordered by alignment requirements: currently 36 bytes on x86 */ 67 struct sed_cmd_s *next; /* Next command (linked list, NULL terminated) */ 68 69 /* address storage */ 70 regex_t *beg_match; /* sed -e '/match/cmd' */ 71 regex_t *end_match; /* sed -e '/match/,/end_match/cmd' */ 72 regex_t *sub_match; /* For 's/sub_match/string/' */ 73 int beg_line; /* 'sed 1p' 0 == apply commands to all lines */ 74 int end_line; /* 'sed 1,3p' 0 == one line only. -1 = last line ($) */ 75 76 FILE *sw_file; /* File (sw) command writes to, -1 for none. */ 77 char *string; /* Data string for (saicytb) commands. */ 78 79 unsigned short which_match; /* (s) Which match to replace (0 for all) */ 80 81 /* Bitfields (gcc won't group them if we don't) */ 82 unsigned invert:1; /* the '!' after the address */ 83 unsigned in_match:1; /* Next line also included in match? */ 84 unsigned sub_p:1; /* (s) print option */ 85 86 char sw_last_char; /* Last line written by (sw) had no '\n' */ 87 88 /* GENERAL FIELDS */ 89 char cmd; /* The command char: abcdDgGhHilnNpPqrstwxy:={} */ 89 90 } sed_cmd_t; 90 91 91 static const char *const semicolon_whitespace = "; \n\r\t\v"; 92 93 struct sed_globals 94 { 92 static const char semicolon_whitespace[] ALIGN1 = "; \n\r\t\v"; 93 94 struct globals { 95 95 /* options */ 96 int be_quiet, in_place,regex_type;96 int be_quiet, regex_type; 97 97 FILE *nonstdout; 98 98 char *outname, *hold_space; 99 99 100 100 /* List of input files */ 101 int input_file_count, current_input_file;101 int input_file_count, current_input_file; 102 102 FILE **input_file_list; 103 103 104 104 regmatch_t regmatch[10]; 105 105 regex_t *previous_regex_ptr; 106 106 107 107 /* linked list of sed commands */ 108 108 sed_cmd_t sed_cmd_head, *sed_cmd_tail; … … 118 118 int len; /* Space allocated */ 119 119 } pipeline; 120 } bbg; 121 122 123 void sed_free_and_close_stuff(void); 120 }; 121 #define G (*(struct globals*)&bb_common_bufsiz1) 122 void BUG_sed_globals_too_big(void); 123 #define INIT_G() do { \ 124 if (sizeof(struct globals) > COMMON_BUFSIZE) \ 125 BUG_sed_globals_too_big(); \ 126 G.sed_cmd_tail = &G.sed_cmd_head; \ 127 } while (0) 128 129 124 130 #if ENABLE_FEATURE_CLEAN_UP 125 void sed_free_and_close_stuff(void)126 { 127 sed_cmd_t *sed_cmd = bbg.sed_cmd_head.next;128 129 llist_free( bbg.append_head, free);131 static void sed_free_and_close_stuff(void) 132 { 133 sed_cmd_t *sed_cmd = G.sed_cmd_head.next; 134 135 llist_free(G.append_head, free); 130 136 131 137 while (sed_cmd) { 132 138 sed_cmd_t *sed_cmd_next = sed_cmd->next; 133 139 134 if (sed_cmd->file)135 bb_xprint_and_close_file(sed_cmd->file);140 if (sed_cmd->sw_file) 141 xprint_and_close_file(sed_cmd->sw_file); 136 142 137 143 if (sed_cmd->beg_match) { … … 152 158 } 153 159 154 if(bbg.hold_space) free(bbg.hold_space); 155 156 while(bbg.current_input_file<bbg.input_file_count) 157 fclose(bbg.input_file_list[bbg.current_input_file++]); 158 } 160 if (G.hold_space) free(G.hold_space); 161 162 while (G.current_input_file < G.input_file_count) 163 fclose(G.input_file_list[G.current_input_file++]); 164 } 165 #else 166 void sed_free_and_close_stuff(void); 159 167 #endif 160 168 … … 163 171 static void cleanup_outname(void) 164 172 { 165 if(bbg.outname) unlink(bbg.outname);173 if (G.outname) unlink(G.outname); 166 174 } 167 175 168 176 /* strdup, replacing "\n" with '\n', and "\delimiter" with 'delimiter' */ 169 177 170 static void parse_escapes(char *dest, c har *string, int len, char from, char to)171 { 172 int i =0;173 174 while (i<len) {175 if (string[i] == '\\') {176 if (!to || string[i+1] == from) {177 * (dest++)= to ? to : string[i+1];178 i +=2;178 static void parse_escapes(char *dest, const char *string, int len, char from, char to) 179 { 180 int i = 0; 181 182 while (i < len) { 183 if (string[i] == '\\') { 184 if (!to || string[i+1] == from) { 185 *dest++ = to ? to : string[i+1]; 186 i += 2; 179 187 continue; 180 } else *(dest++)=string[i++]; 181 } 182 *(dest++) = string[i++]; 183 } 184 *dest=0; 185 } 186 187 static char *copy_parsing_escapes(char *string, int len) 188 { 189 char *dest=xmalloc(len+1); 190 191 parse_escapes(dest,string,len,'n','\n'); 188 } 189 *dest++ = string[i++]; 190 } 191 *dest++ = string[i++]; 192 } 193 *dest = 0; 194 } 195 196 static char *copy_parsing_escapes(const char *string, int len) 197 { 198 char *dest = xmalloc(len + 1); 199 200 parse_escapes(dest, string, len, 'n', '\n'); 192 201 return dest; 193 202 } … … 200 209 * a backslash ('\'). A negative delimiter disables square bracket checking. 201 210 */ 202 static int index_of_next_unescaped_regexp_delim(int delimiter, c har *str)211 static int index_of_next_unescaped_regexp_delim(int delimiter, const char *str) 203 212 { 204 213 int bracket = -1; … … 209 218 if (delimiter < 0) { 210 219 bracket--; 211 delimiter *= -1;220 delimiter = -delimiter; 212 221 } 213 222 … … 228 237 229 238 /* if we make it to here, we've hit the end of the string */ 230 bb_error_msg_and_die("unmatched '%c'", delimiter);239 bb_error_msg_and_die("unmatched '%c'", delimiter); 231 240 } 232 241 … … 234 243 * Returns the index of the third delimiter 235 244 */ 236 static int parse_regex_delim(c har *cmdstr, char **match, char **replace)237 { 238 c har *cmdstr_ptr = cmdstr;245 static int parse_regex_delim(const char *cmdstr, char **match, char **replace) 246 { 247 const char *cmdstr_ptr = cmdstr; 239 248 char delimiter; 240 249 int idx = 0; … … 244 253 if (*cmdstr == '\0') 245 254 bb_error_msg_and_die("bad format in substitution expression"); 246 delimiter = * (cmdstr_ptr++);255 delimiter = *cmdstr_ptr++; 247 256 248 257 /* save the match string */ … … 261 270 * returns the index in the string just past where the address ends. 262 271 */ 263 static int get_address(c har *my_str, int *linenum, regex_t ** regex)264 { 265 c har *pos = my_str;272 static int get_address(const char *my_str, int *linenum, regex_t ** regex) 273 { 274 const char *pos = my_str; 266 275 267 276 if (isdigit(*my_str)) { 268 *linenum = strtol(my_str, &pos, 10);277 *linenum = strtol(my_str, (char**)&pos, 10); 269 278 /* endstr shouldnt ever equal NULL */ 270 279 } else if (*my_str == '$') { … … 276 285 char *temp; 277 286 278 if (*my_str == '\\') delimiter = *(++pos);279 else delimiter = '/';287 delimiter = '/'; 288 if (*my_str == '\\') delimiter = *++pos; 280 289 next = index_of_next_unescaped_regexp_delim(delimiter, ++pos); 281 temp = copy_parsing_escapes(pos, next);282 *regex = (regex_t *)xmalloc(sizeof(regex_t));283 xregcomp(*regex, temp, bbg.regex_type|REG_NEWLINE);290 temp = copy_parsing_escapes(pos, next); 291 *regex = xmalloc(sizeof(regex_t)); 292 xregcomp(*regex, temp, G.regex_type|REG_NEWLINE); 284 293 free(temp); 285 294 /* Move position to next character after last delimiter */ … … 290 299 291 300 /* Grab a filename. Whitespace at start is skipped, then goes to EOL. */ 292 static int parse_file_cmd(sed_cmd_t *sed_cmd, c har *filecmdstr, char **retval)293 { 294 int start = 0, idx, hack =0;301 static int parse_file_cmd(sed_cmd_t *sed_cmd, const char *filecmdstr, char **retval) 302 { 303 int start = 0, idx, hack = 0; 295 304 296 305 /* Skip whitespace, then grab filename to end of line */ 297 while (isspace(filecmdstr[start])) start++; 298 idx=start; 299 while(filecmdstr[idx] && filecmdstr[idx]!='\n') idx++; 306 while (isspace(filecmdstr[start])) 307 start++; 308 idx = start; 309 while (filecmdstr[idx] && filecmdstr[idx] != '\n') 310 idx++; 311 300 312 /* If lines glued together, put backslash back. */ 301 if(filecmdstr[idx]=='\n') hack=1; 302 if(idx==start) bb_error_msg_and_die("Empty filename"); 303 *retval = bb_xstrndup(filecmdstr+start, idx-start+hack+1); 304 if(hack) *(idx+*retval)='\\'; 313 if (filecmdstr[idx] == '\n') 314 hack = 1; 315 if (idx == start) 316 bb_error_msg_and_die("empty filename"); 317 *retval = xstrndup(filecmdstr+start, idx-start+hack+1); 318 if (hack) 319 (*retval)[idx] = '\\'; 305 320 306 321 return idx; 307 322 } 308 323 309 static int parse_subst_cmd(sed_cmd_t *sed_cmd, c har *substr)310 { 311 int cflags = bbg.regex_type;324 static int parse_subst_cmd(sed_cmd_t *sed_cmd, const char *substr) 325 { 326 int cflags = G.regex_type; 312 327 char *match; 313 int idx = 0;328 int idx; 314 329 315 330 /* … … 328 343 /* process the flags */ 329 344 330 sed_cmd->which_match =1;345 sed_cmd->which_match = 1; 331 346 while (substr[++idx]) { 332 347 /* Parse match number */ 333 if (isdigit(substr[idx])) {334 if (match[0]!='^') {348 if (isdigit(substr[idx])) { 349 if (match[0] != '^') { 335 350 /* Match 0 treated as all, multiple matches we take the last one. */ 336 char *pos=substr+idx; 337 sed_cmd->which_match=(unsigned short)strtol(substr+idx,&pos,10); 338 idx=pos-substr; 351 const char *pos = substr + idx; 352 /* FIXME: error check? */ 353 sed_cmd->which_match = (unsigned short)strtol(substr+idx, (char**) &pos, 10); 354 idx = pos - substr; 339 355 } 340 356 continue; 341 357 } 342 358 /* Skip spaces */ 343 if (isspace(substr[idx])) continue;359 if (isspace(substr[idx])) continue; 344 360 345 361 switch (substr[idx]) { 346 /* Replace all occurrences */ 347 case 'g': 348 if (match[0] != '^') sed_cmd->which_match = 0; 349 break; 350 /* Print pattern space */ 351 case 'p': 352 sed_cmd->sub_p = 1; 353 break; 354 /* Write to file */ 355 case 'w': 356 { 357 char *temp; 358 idx+=parse_file_cmd(sed_cmd,substr+idx,&temp); 359 360 break; 361 } 362 /* Ignore case (gnu exension) */ 363 case 'I': 364 cflags |= REG_ICASE; 365 break; 366 /* Comment */ 367 case '#': 368 while(substr[++idx]); 369 /* Fall through */ 370 /* End of command */ 371 case ';': 372 case '}': 373 goto out; 374 default: 375 bb_error_msg_and_die("bad option in substitution expression"); 362 /* Replace all occurrences */ 363 case 'g': 364 if (match[0] != '^') sed_cmd->which_match = 0; 365 break; 366 /* Print pattern space */ 367 case 'p': 368 sed_cmd->sub_p = 1; 369 break; 370 /* Write to file */ 371 case 'w': 372 { 373 char *temp; 374 idx += parse_file_cmd(sed_cmd, substr+idx, &temp); 375 break; 376 } 377 /* Ignore case (gnu exension) */ 378 case 'I': 379 cflags |= REG_ICASE; 380 break; 381 /* Comment */ 382 case '#': 383 while (substr[++idx]) /*skip all*/; 384 /* Fall through */ 385 /* End of command */ 386 case ';': 387 case '}': 388 goto out; 389 default: 390 bb_error_msg_and_die("bad option in substitution expression"); 376 391 } 377 392 } … … 380 395 if (*match != '\0') { 381 396 /* If match is empty, we use last regex used at runtime */ 382 sed_cmd->sub_match = (regex_t *)xmalloc(sizeof(regex_t));397 sed_cmd->sub_match = xmalloc(sizeof(regex_t)); 383 398 xregcomp(sed_cmd->sub_match, match, cflags); 384 399 } … … 391 406 * Process the commands arguments 392 407 */ 393 static c har *parse_cmd_args(sed_cmd_t *sed_cmd,char *cmdstr)408 static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr) 394 409 { 395 410 /* handle (s)ubstitution command */ 396 if (sed_cmd->cmd == 's') cmdstr += parse_subst_cmd(sed_cmd, cmdstr); 411 if (sed_cmd->cmd == 's') 412 cmdstr += parse_subst_cmd(sed_cmd, cmdstr); 397 413 /* handle edit cmds: (a)ppend, (i)nsert, and (c)hange */ 398 414 else if (strchr("aic", sed_cmd->cmd)) { … … 400 416 bb_error_msg_and_die 401 417 ("only a beginning address can be specified for edit commands"); 402 for (;;) {403 if (*cmdstr=='\n' || *cmdstr=='\\') {418 for (;;) { 419 if (*cmdstr == '\n' || *cmdstr == '\\') { 404 420 cmdstr++; 405 421 break; 406 } else if(isspace(*cmdstr)) cmdstr++; 407 else break; 408 } 409 sed_cmd->string = bb_xstrdup(cmdstr); 410 parse_escapes(sed_cmd->string,sed_cmd->string,strlen(cmdstr),0,0); 422 } else if (isspace(*cmdstr)) 423 cmdstr++; 424 else 425 break; 426 } 427 sed_cmd->string = xstrdup(cmdstr); 428 parse_escapes(sed_cmd->string, sed_cmd->string, strlen(cmdstr), 0, 0); 411 429 cmdstr += strlen(cmdstr); 412 430 /* handle file cmds: (r)ead */ 413 } else if (strchr("rw", sed_cmd->cmd)) {431 } else if (strchr("rw", sed_cmd->cmd)) { 414 432 if (sed_cmd->end_line || sed_cmd->end_match) 415 bb_error_msg_and_die(" Command only uses one address");433 bb_error_msg_and_die("command only uses one address"); 416 434 cmdstr += parse_file_cmd(sed_cmd, cmdstr, &sed_cmd->string); 417 if(sed_cmd->cmd=='w') 418 sed_cmd->file=bb_xfopen(sed_cmd->string,"w"); 435 if (sed_cmd->cmd == 'w') { 436 sed_cmd->sw_file = xfopen(sed_cmd->string, "w"); 437 sed_cmd->sw_last_char = '\n'; 438 } 419 439 /* handle branch commands */ 420 440 } else if (strchr(":btT", sed_cmd->cmd)) { 421 441 int length; 422 442 423 while(isspace(*cmdstr)) cmdstr++;443 cmdstr = skip_whitespace(cmdstr); 424 444 length = strcspn(cmdstr, semicolon_whitespace); 425 445 if (length) { 426 sed_cmd->string = bb_xstrndup(cmdstr, length);446 sed_cmd->string = xstrndup(cmdstr, length); 427 447 cmdstr += length; 428 448 } … … 431 451 else if (sed_cmd->cmd == 'y') { 432 452 char *match, *replace; 433 int i =cmdstr[0];434 435 cmdstr +=parse_regex_delim(cmdstr, &match, &replace)+1;453 int i = cmdstr[0]; 454 455 cmdstr += parse_regex_delim(cmdstr, &match, &replace)+1; 436 456 /* \n already parsed, but \delimiter needs unescaping. */ 437 parse_escapes(match, match,strlen(match),i,i);438 parse_escapes(replace, replace,strlen(replace),i,i);457 parse_escapes(match, match, strlen(match), i, i); 458 parse_escapes(replace, replace, strlen(replace), i, i); 439 459 440 460 sed_cmd->string = xzalloc((strlen(match) + 1) * 2); 441 461 for (i = 0; match[i] && replace[i]; i++) { 442 sed_cmd->string[i *2] = match[i];443 sed_cmd->string[ (i * 2) +1] = replace[i];462 sed_cmd->string[i*2] = match[i]; 463 sed_cmd->string[i*2+1] = replace[i]; 444 464 } 445 465 free(match); … … 450 470 */ 451 471 else if (strchr("dDgGhHlnNpPqx={}", sed_cmd->cmd) == 0) { 452 bb_error_msg_and_die(" Unsupported command %c", sed_cmd->cmd);472 bb_error_msg_and_die("unsupported command %c", sed_cmd->cmd); 453 473 } 454 474 455 475 /* give back whatever's left over */ 456 return (cmdstr);476 return cmdstr; 457 477 } 458 478 … … 460 480 /* Parse address+command sets, skipping comment lines. */ 461 481 462 static void add_cmd(c har *cmdstr)482 static void add_cmd(const char *cmdstr) 463 483 { 464 484 sed_cmd_t *sed_cmd; … … 466 486 467 487 /* Append this line to any unfinished line from last time. */ 468 if ( bbg.add_cmd_line) {469 c mdstr = bb_xasprintf("%s\n%s", bbg.add_cmd_line, cmdstr);470 free( bbg.add_cmd_line);471 bbg.add_cmd_line = cmdstr;488 if (G.add_cmd_line) { 489 char *tp = xasprintf("%s\n%s", G.add_cmd_line, cmdstr); 490 free(G.add_cmd_line); 491 cmdstr = G.add_cmd_line = tp; 472 492 } 473 493 474 494 /* If this line ends with backslash, request next line. */ 475 temp=strlen(cmdstr); 476 if(temp && cmdstr[temp-1]=='\\') { 477 if (!bbg.add_cmd_line) bbg.add_cmd_line = bb_xstrdup(cmdstr); 478 bbg.add_cmd_line[temp-1] = 0; 495 temp = strlen(cmdstr); 496 if (temp && cmdstr[--temp] == '\\') { 497 if (!G.add_cmd_line) 498 G.add_cmd_line = xstrdup(cmdstr); 499 G.add_cmd_line[temp] = '\0'; 479 500 return; 480 501 } 481 502 482 503 /* Loop parsing all commands in this line. */ 483 while (*cmdstr) {504 while (*cmdstr) { 484 505 /* Skip leading whitespace and semicolons */ 485 506 cmdstr += strspn(cmdstr, semicolon_whitespace); 486 507 487 508 /* If no more commands, exit. */ 488 if (!*cmdstr) break;509 if (!*cmdstr) break; 489 510 490 511 /* if this is a comment, jump past it and keep going */ 491 512 if (*cmdstr == '#') { 492 513 /* "#n" is the same as using -n on the command line */ 493 if (cmdstr[1] == 'n') bbg.be_quiet++; 494 if(!(cmdstr=strpbrk(cmdstr, "\n\r"))) break; 514 if (cmdstr[1] == 'n') 515 G.be_quiet++; 516 cmdstr = strpbrk(cmdstr, "\n\r"); 517 if (!cmdstr) break; 495 518 continue; 496 519 } … … 513 536 cmdstr++; 514 537 idx = get_address(cmdstr, &sed_cmd->end_line, &sed_cmd->end_match); 515 if (!idx) bb_error_msg_and_die("no address after comma\n"); 538 if (!idx) 539 bb_error_msg_and_die("no address after comma"); 516 540 cmdstr += idx; 517 541 } 518 542 519 543 /* skip whitespace before the command */ 520 while (isspace(*cmdstr)) cmdstr++;544 cmdstr = skip_whitespace(cmdstr); 521 545 522 546 /* Check for inversion flag */ … … 526 550 527 551 /* skip whitespace before the command */ 528 while (isspace(*cmdstr)) cmdstr++;552 cmdstr = skip_whitespace(cmdstr); 529 553 } 530 554 531 555 /* last part (mandatory) will be a command */ 532 if (!*cmdstr) bb_error_msg_and_die("missing command"); 556 if (!*cmdstr) 557 bb_error_msg_and_die("missing command"); 533 558 sed_cmd->cmd = *(cmdstr++); 534 559 cmdstr = parse_cmd_args(sed_cmd, cmdstr); 535 560 536 561 /* Add the command to the command array */ 537 bbg.sed_cmd_tail->next = sed_cmd;538 bbg.sed_cmd_tail = bbg.sed_cmd_tail->next;562 G.sed_cmd_tail->next = sed_cmd; 563 G.sed_cmd_tail = G.sed_cmd_tail->next; 539 564 } 540 565 541 566 /* If we glued multiple lines together, free the memory. */ 542 free( bbg.add_cmd_line);543 bbg.add_cmd_line = NULL;567 free(G.add_cmd_line); 568 G.add_cmd_line = NULL; 544 569 } 545 570 … … 550 575 static void pipe_putc(char c) 551 576 { 552 if (bbg.pipeline.idx==bbg.pipeline.len) {553 bbg.pipeline.buf = xrealloc(bbg.pipeline.buf,554 bbg.pipeline.len + PIPE_GROW);555 bbg.pipeline.len+=PIPE_GROW;556 } 557 bbg.pipeline.buf[bbg.pipeline.idx++] = c;577 if (G.pipeline.idx == G.pipeline.len) { 578 G.pipeline.buf = xrealloc(G.pipeline.buf, 579 G.pipeline.len + PIPE_GROW); 580 G.pipeline.len += PIPE_GROW; 581 } 582 G.pipeline.buf[G.pipeline.idx++] = c; 558 583 } 559 584 … … 565 590 for (i = 0; replace[i]; i++) { 566 591 /* if we find a backreference (\1, \2, etc.) print the backref'ed * text */ 567 if (replace[i] == '\\' && replace[i+1]>='0' && replace[i+1]<='9') { 568 int backref=replace[++i]-'0'; 569 570 /* print out the text held in bbg.regmatch[backref] */ 571 if(bbg.regmatch[backref].rm_so != -1) 572 for (j = bbg.regmatch[backref].rm_so; 573 j < bbg.regmatch[backref].rm_eo; j++) pipe_putc(line[j]); 574 } 575 576 /* if we find a backslash escaped character, print the character */ 577 else if (replace[i] == '\\') pipe_putc(replace[++i]); 578 592 if (replace[i] == '\\') { 593 unsigned backref = replace[++i] - '0'; 594 if (backref <= 9) { 595 /* print out the text held in G.regmatch[backref] */ 596 if (G.regmatch[backref].rm_so != -1) { 597 j = G.regmatch[backref].rm_so; 598 while (j < G.regmatch[backref].rm_eo) 599 pipe_putc(line[j++]); 600 } 601 continue; 602 } 603 /* I _think_ it is impossible to get '\' to be 604 * the last char in replace string. Thus we dont check 605 * for replace[i] == NUL. (counterexample anyone?) */ 606 /* if we find a backslash escaped character, print the character */ 607 pipe_putc(replace[i]); 608 continue; 609 } 579 610 /* if we find an unescaped '&' print out the whole matched text. */ 580 else if (replace[i] == '&') 581 for (j = bbg.regmatch[0].rm_so; j < bbg.regmatch[0].rm_eo; j++) 582 pipe_putc(line[j]); 611 if (replace[i] == '&') { 612 j = G.regmatch[0].rm_so; 613 while (j < G.regmatch[0].rm_eo) 614 pipe_putc(line[j++]); 615 continue; 616 } 583 617 /* Otherwise just output the character. */ 584 elsepipe_putc(replace[i]);618 pipe_putc(replace[i]); 585 619 } 586 620 } … … 590 624 char *oldline = *line; 591 625 int altered = 0; 592 int match_count =0;626 int match_count = 0; 593 627 regex_t *current_regex; 594 628 595 629 /* Handle empty regex. */ 596 630 if (sed_cmd->sub_match == NULL) { 597 current_regex = bbg.previous_regex_ptr; 598 if(!current_regex) 599 bb_error_msg_and_die("No previous regexp."); 600 } else bbg.previous_regex_ptr = current_regex = sed_cmd->sub_match; 631 current_regex = G.previous_regex_ptr; 632 if (!current_regex) 633 bb_error_msg_and_die("no previous regexp"); 634 } else 635 G.previous_regex_ptr = current_regex = sed_cmd->sub_match; 601 636 602 637 /* Find the first match */ 603 if (REG_NOMATCH==regexec(current_regex, oldline, 10, bbg.regmatch, 0))638 if (REG_NOMATCH == regexec(current_regex, oldline, 10, G.regmatch, 0)) 604 639 return 0; 605 640 606 641 /* Initialize temporary output buffer. */ 607 bbg.pipeline.buf=xmalloc(PIPE_GROW);608 bbg.pipeline.len=PIPE_GROW;609 bbg.pipeline.idx=0;642 G.pipeline.buf = xmalloc(PIPE_GROW); 643 G.pipeline.len = PIPE_GROW; 644 G.pipeline.idx = 0; 610 645 611 646 /* Now loop through, substituting for matches */ … … 617 652 The match_count check is so not to break 618 653 echo "hi" | busybox sed 's/^/!/g' */ 619 if (!bbg.regmatch[0].rm_so && !bbg.regmatch[0].rm_eo && match_count) {620 pipe_putc(* (oldline++));654 if (!G.regmatch[0].rm_so && !G.regmatch[0].rm_eo && match_count) { 655 pipe_putc(*oldline++); 621 656 continue; 622 657 } … … 626 661 /* If we aren't interested in this match, output old line to 627 662 end of match and continue */ 628 if (sed_cmd->which_match && sed_cmd->which_match!=match_count) {629 for (i=0;i<bbg.regmatch[0].rm_eo;i++)630 pipe_putc(* (oldline++));663 if (sed_cmd->which_match && sed_cmd->which_match != match_count) { 664 for (i = 0; i < G.regmatch[0].rm_eo; i++) 665 pipe_putc(*oldline++); 631 666 continue; 632 667 } 633 668 634 669 /* print everything before the match */ 635 for (i = 0; i < bbg.regmatch[0].rm_so; i++) pipe_putc(oldline[i]); 670 for (i = 0; i < G.regmatch[0].rm_so; i++) 671 pipe_putc(oldline[i]); 636 672 637 673 /* then print the substitution string */ … … 639 675 640 676 /* advance past the match */ 641 oldline += bbg.regmatch[0].rm_eo;677 oldline += G.regmatch[0].rm_eo; 642 678 /* flag that something has changed */ 643 679 altered++; … … 645 681 /* if we're not doing this globally, get out now */ 646 682 if (sed_cmd->which_match) break; 647 } while (*oldline && (regexec(current_regex, oldline, 10, bbg.regmatch, 0) != REG_NOMATCH));683 } while (*oldline && (regexec(current_regex, oldline, 10, G.regmatch, 0) != REG_NOMATCH)); 648 684 649 685 /* Copy rest of string into output pipeline */ 650 686 651 while(*oldline) pipe_putc(*(oldline++)); 687 while (*oldline) 688 pipe_putc(*oldline++); 652 689 pipe_putc(0); 653 690 654 691 free(*line); 655 *line = bbg.pipeline.buf;692 *line = G.pipeline.buf; 656 693 return altered; 657 694 } … … 662 699 sed_cmd_t *sed_cmd; 663 700 664 for (sed_cmd = bbg.sed_cmd_head.next; sed_cmd; sed_cmd = sed_cmd->next) {665 if ( (sed_cmd->cmd == ':') && (sed_cmd->string) && (strcmp(sed_cmd->string, label) == 0)) {666 return (sed_cmd);667 } 668 } 669 bb_error_msg_and_die(" Can't find label for jump to `%s'", label);701 for (sed_cmd = G.sed_cmd_head.next; sed_cmd; sed_cmd = sed_cmd->next) { 702 if (sed_cmd->cmd == ':' && sed_cmd->string && !strcmp(sed_cmd->string, label)) { 703 return sed_cmd; 704 } 705 } 706 bb_error_msg_and_die("can't find label for jump to '%s'", label); 670 707 } 671 708 672 709 static void append(char *s) 673 710 { 674 llist_add_to_end(& bbg.append_head, bb_xstrdup(s));711 llist_add_to_end(&G.append_head, xstrdup(s)); 675 712 } 676 713 … … 680 717 681 718 /* Output appended lines. */ 682 while ((data = (char *)llist_pop(&bbg.append_head))) {683 fprintf( bbg.nonstdout,"%s\n",data);719 while ((data = (char *)llist_pop(&G.append_head))) { 720 fprintf(G.nonstdout, "%s\n", data); 684 721 free(data); 685 722 } … … 688 725 static void add_input_file(FILE *file) 689 726 { 690 bbg.input_file_list=xrealloc(bbg.input_file_list,691 ( bbg.input_file_count + 1) * sizeof(FILE *));692 bbg.input_file_list[bbg.input_file_count++] = file;693 } 694 695 /* Get next line of input from bbg.input_file_list, flushing append buffer and727 G.input_file_list = xrealloc(G.input_file_list, 728 (G.input_file_count + 1) * sizeof(FILE *)); 729 G.input_file_list[G.input_file_count++] = file; 730 } 731 732 /* Get next line of input from G.input_file_list, flushing append buffer and 696 733 * noting if we ran out of files without a newline on the last line we read. 697 734 */ 698 static char *get_next_line(int *no_newline) 699 { 700 char *temp=NULL; 735 enum { 736 NO_EOL_CHAR = 1, 737 LAST_IS_NUL = 2, 738 }; 739 static char *get_next_line(char *gets_char) 740 { 741 char *temp = NULL; 701 742 int len; 743 char gc; 702 744 703 745 flush_append(); 704 while (bbg.current_input_file<bbg.input_file_count) { 705 temp = bb_get_chunk_from_file(bbg.input_file_list[bbg.current_input_file],&len); 746 747 /* will be returned if last line in the file 748 * doesn't end with either '\n' or '\0' */ 749 gc = NO_EOL_CHAR; 750 while (G.current_input_file < G.input_file_count) { 751 FILE *fp = G.input_file_list[G.current_input_file]; 752 /* Read line up to a newline or NUL byte, inclusive, 753 * return malloc'ed char[]. length of the chunk read 754 * is stored in len. NULL if EOF/error */ 755 temp = bb_get_chunk_from_file(fp, &len); 706 756 if (temp) { 707 *no_newline = !(len && temp[len-1]=='\n'); 708 if (!*no_newline) temp[len-1] = 0; 757 /* len > 0 here, it's ok to do temp[len-1] */ 758 char c = temp[len-1]; 759 if (c == '\n' || c == '\0') { 760 temp[len-1] = '\0'; 761 gc = c; 762 if (c == '\0') { 763 int ch = fgetc(fp); 764 if (ch != EOF) 765 ungetc(ch, fp); 766 else 767 gc = LAST_IS_NUL; 768 } 769 } 770 /* else we put NO_EOL_CHAR into *gets_char */ 709 771 break; 710 // Close this file and advance to next one 711 } else fclose(bbg.input_file_list[bbg.current_input_file++]); 712 } 713 772 773 /* NB: I had the idea of peeking next file(s) and returning 774 * NO_EOL_CHAR only if it is the *last* non-empty 775 * input file. But there is a case where this won't work: 776 * file1: "a woo\nb woo" 777 * file2: "c no\nd no" 778 * sed -ne 's/woo/bang/p' input1 input2 => "a bang\nb bang" 779 * (note: *no* newline after "b bang"!) */ 780 } 781 /* Close this file and advance to next one */ 782 fclose(fp); 783 G.current_input_file++; 784 } 785 *gets_char = gc; 714 786 return temp; 715 787 } 716 788 717 /* Output line of text. missing_newline means the last line output did not 718 end with a newline. no_newline means this line does not end with a 719 newline. */ 720 721 static int puts_maybe_newline(char *s, FILE *file, int missing_newline, int no_newline) 722 { 723 if(missing_newline) fputc('\n',file); 724 fputs(s,file); 725 if(!no_newline) fputc('\n',file); 726 727 if(ferror(file)) { 728 bb_default_error_retval = 4; /* It's what gnu sed exits with... */ 789 /* Output line of text. */ 790 /* Note: 791 * The tricks with NO_EOL_CHAR and last_puts_char are there to emulate gnu sed. 792 * Without them, we had this: 793 * echo -n thingy >z1 794 * echo -n again >z2 795 * >znull 796 * sed "s/i/z/" z1 z2 znull | hexdump -vC 797 * output: 798 * gnu sed 4.1.5: 799 * 00000000 74 68 7a 6e 67 79 0a 61 67 61 7a 6e |thzngy.agazn| 800 * bbox: 801 * 00000000 74 68 7a 6e 67 79 61 67 61 7a 6e |thzngyagazn| 802 */ 803 static void puts_maybe_newline(char *s, FILE *file, char *last_puts_char, char last_gets_char) 804 { 805 char lpc = *last_puts_char; 806 807 /* Need to insert a '\n' between two files because first file's 808 * last line wasn't terminated? */ 809 if (lpc != '\n' && lpc != '\0') { 810 fputc('\n', file); 811 lpc = '\n'; 812 } 813 fputs(s, file); 814 815 /* 'x' - just something which is not '\n', '\0' or NO_EOL_CHAR */ 816 if (s[0]) 817 lpc = 'x'; 818 819 /* had trailing '\0' and it was last char of file? */ 820 if (last_gets_char == LAST_IS_NUL) { 821 fputc('\0', file); 822 lpc = 'x'; /* */ 823 } else 824 /* had trailing '\n' or '\0'? */ 825 if (last_gets_char != NO_EOL_CHAR) { 826 fputc(last_gets_char, file); 827 lpc = last_gets_char; 828 } 829 830 if (ferror(file)) { 831 xfunc_error_retval = 4; /* It's what gnu sed exits with... */ 729 832 bb_error_msg_and_die(bb_msg_write_error); 730 833 } 731 732 return no_newline; 733 } 734 735 #define sed_puts(s,n) missing_newline=puts_maybe_newline(s,bbg.nonstdout,missing_newline,n) 834 *last_puts_char = lpc; 835 } 836 837 #define sed_puts(s, n) (puts_maybe_newline(s, G.nonstdout, &last_puts_char, n)) 838 839 static int beg_match(sed_cmd_t *sed_cmd, const char *pattern_space) 840 { 841 int retval = sed_cmd->beg_match && !regexec(sed_cmd->beg_match, pattern_space, 0, NULL, 0); 842 if (retval) 843 G.previous_regex_ptr = sed_cmd->beg_match; 844 return retval; 845 } 736 846 737 847 /* Process all the lines in all the files */ … … 740 850 { 741 851 char *pattern_space, *next_line; 742 int linenum = 0, missing_newline=0; 743 int no_newline,next_no_newline=0; 852 int linenum = 0; 853 char last_puts_char = '\n'; 854 char last_gets_char, next_gets_char; 855 sed_cmd_t *sed_cmd; 856 int substituted; 744 857 745 858 /* Prime the pump */ 746 next_line = get_next_line(&next_ no_newline);859 next_line = get_next_line(&next_gets_char); 747 860 748 861 /* go through every line in each file */ 749 for(;;) { 750 sed_cmd_t *sed_cmd;751 int substituted=0; 752 753 /* Advance to next line. Stop if out of lines. */754 if(!(pattern_space=next_line)) break;755 no_newline=next_no_newline;756 757 758 759 next_line = get_next_line(&next_no_newline);760 862 again: 863 substituted = 0; 864 865 /* Advance to next line. Stop if out of lines. */ 866 pattern_space = next_line; 867 if (!pattern_space) return; 868 last_gets_char = next_gets_char; 869 870 /* Read one line in advance so we can act on the last line, 871 * the '$' address */ 872 next_line = get_next_line(&next_gets_char); 873 linenum++; 761 874 restart: 762 /* for every line, go through all the commands */ 763 for (sed_cmd = bbg.sed_cmd_head.next; sed_cmd; sed_cmd = sed_cmd->next) 764 { 765 int old_matched, matched; 766 767 old_matched = sed_cmd->in_match; 768 769 /* Determine if this command matches this line: */ 770 771 /* Are we continuing a previous multi-line match? */ 772 773 sed_cmd->in_match = sed_cmd->in_match 774 875 /* for every line, go through all the commands */ 876 for (sed_cmd = G.sed_cmd_head.next; sed_cmd; sed_cmd = sed_cmd->next) { 877 int old_matched, matched; 878 879 old_matched = sed_cmd->in_match; 880 881 /* Determine if this command matches this line: */ 882 883 /* Are we continuing a previous multi-line match? */ 884 sed_cmd->in_match = sed_cmd->in_match 775 885 /* Or is no range necessary? */ 776 || (!sed_cmd->beg_line && !sed_cmd->end_line 777 && !sed_cmd->beg_match && !sed_cmd->end_match) 778 886 || (!sed_cmd->beg_line && !sed_cmd->end_line 887 && !sed_cmd->beg_match && !sed_cmd->end_match) 779 888 /* Or did we match the start of a numerical range? */ 780 || (sed_cmd->beg_line > 0 && (sed_cmd->beg_line == linenum)) 781 889 || (sed_cmd->beg_line > 0 && (sed_cmd->beg_line == linenum)) 782 890 /* Or does this line match our begin address regex? */ 783 || (sed_cmd->beg_match && 784 !regexec(sed_cmd->beg_match, pattern_space, 0, NULL, 0)) 785 891 || (beg_match(sed_cmd, pattern_space)) 786 892 /* Or did we match last line of input? */ 787 || (sed_cmd->beg_line == -1 && next_line == NULL); 788 789 /* Snapshot the value */ 790 791 matched = sed_cmd->in_match; 792 793 /* Is this line the end of the current match? */ 794 795 if(matched) { 796 sed_cmd->in_match = !( 797 /* has the ending line come, or is this a single address command? */ 798 (sed_cmd->end_line ? 799 sed_cmd->end_line==-1 ? 800 !next_line 801 : sed_cmd->end_line<=linenum 802 : !sed_cmd->end_match) 803 /* or does this line matches our last address regex */ 804 || (sed_cmd->end_match && old_matched && (regexec(sed_cmd->end_match, pattern_space, 0, NULL, 0) == 0)) 805 ); 806 } 807 808 /* Skip blocks of commands we didn't match. */ 809 if (sed_cmd->cmd == '{') { 810 if(sed_cmd->invert ? matched : !matched) 811 while(sed_cmd && sed_cmd->cmd!='}') sed_cmd=sed_cmd->next; 812 if(!sed_cmd) bb_error_msg_and_die("Unterminated {"); 813 continue; 814 } 815 816 /* Okay, so did this line match? */ 817 if (sed_cmd->invert ? !matched : matched) { 818 /* Update last used regex in case a blank substitute BRE is found */ 819 if (sed_cmd->beg_match) { 820 bbg.previous_regex_ptr = sed_cmd->beg_match; 893 || (sed_cmd->beg_line == -1 && next_line == NULL); 894 895 /* Snapshot the value */ 896 897 matched = sed_cmd->in_match; 898 899 /* Is this line the end of the current match? */ 900 901 if (matched) { 902 sed_cmd->in_match = !( 903 /* has the ending line come, or is this a single address command? */ 904 (sed_cmd->end_line ? 905 sed_cmd->end_line == -1 ? 906 !next_line 907 : (sed_cmd->end_line <= linenum) 908 : !sed_cmd->end_match 909 ) 910 /* or does this line matches our last address regex */ 911 || (sed_cmd->end_match && old_matched 912 && (regexec(sed_cmd->end_match, 913 pattern_space, 0, NULL, 0) == 0)) 914 ); 915 } 916 917 /* Skip blocks of commands we didn't match. */ 918 if (sed_cmd->cmd == '{') { 919 if (sed_cmd->invert ? matched : !matched) { 920 while (sed_cmd->cmd != '}') { 921 sed_cmd = sed_cmd->next; 922 if (!sed_cmd) 923 bb_error_msg_and_die("unterminated {"); 821 924 } 822 823 /* actual sedding */ 824 switch (sed_cmd->cmd) { 825 826 /* Print line number */ 827 case '=': 828 fprintf(bbg.nonstdout,"%d\n", linenum); 829 break; 830 831 /* Write the current pattern space up to the first newline */ 832 case 'P': 833 { 834 char *tmp = strchr(pattern_space, '\n'); 835 836 if (tmp) { 837 *tmp = '\0'; 838 sed_puts(pattern_space,1); 839 *tmp = '\n'; 925 } 926 continue; 927 } 928 929 /* Okay, so did this line match? */ 930 if (sed_cmd->invert ? !matched : matched) { 931 /* Update last used regex in case a blank substitute BRE is found */ 932 if (sed_cmd->beg_match) { 933 G.previous_regex_ptr = sed_cmd->beg_match; 934 } 935 936 /* actual sedding */ 937 switch (sed_cmd->cmd) { 938 939 /* Print line number */ 940 case '=': 941 fprintf(G.nonstdout, "%d\n", linenum); 942 break; 943 944 /* Write the current pattern space up to the first newline */ 945 case 'P': 946 { 947 char *tmp = strchr(pattern_space, '\n'); 948 949 if (tmp) { 950 *tmp = '\0'; 951 /* TODO: explain why '\n' below */ 952 sed_puts(pattern_space, '\n'); 953 *tmp = '\n'; 954 break; 955 } 956 /* Fall Through */ 957 } 958 959 /* Write the current pattern space to output */ 960 case 'p': 961 /* NB: we print this _before_ the last line 962 * (of current file) is printed. Even if 963 * that line is nonterminated, we print 964 * '\n' here (gnu sed does the same) */ 965 sed_puts(pattern_space, '\n'); 966 break; 967 /* Delete up through first newline */ 968 case 'D': 969 { 970 char *tmp = strchr(pattern_space, '\n'); 971 972 if (tmp) { 973 tmp = xstrdup(tmp+1); 974 free(pattern_space); 975 pattern_space = tmp; 976 goto restart; 977 } 978 } 979 /* discard this line. */ 980 case 'd': 981 goto discard_line; 982 983 /* Substitute with regex */ 984 case 's': 985 if (!do_subst_command(sed_cmd, &pattern_space)) 986 break; 987 substituted |= 1; 988 989 /* handle p option */ 990 if (sed_cmd->sub_p) 991 sed_puts(pattern_space, last_gets_char); 992 /* handle w option */ 993 if (sed_cmd->sw_file) 994 puts_maybe_newline( 995 pattern_space, sed_cmd->sw_file, 996 &sed_cmd->sw_last_char, last_gets_char); 997 break; 998 999 /* Append line to linked list to be printed later */ 1000 case 'a': 1001 append(sed_cmd->string); 1002 break; 1003 1004 /* Insert text before this line */ 1005 case 'i': 1006 sed_puts(sed_cmd->string, '\n'); 1007 break; 1008 1009 /* Cut and paste text (replace) */ 1010 case 'c': 1011 /* Only triggers on last line of a matching range. */ 1012 if (!sed_cmd->in_match) 1013 sed_puts(sed_cmd->string, NO_EOL_CHAR); 1014 goto discard_line; 1015 1016 /* Read file, append contents to output */ 1017 case 'r': 1018 { 1019 FILE *rfile; 1020 1021 rfile = fopen(sed_cmd->string, "r"); 1022 if (rfile) { 1023 char *line; 1024 1025 while ((line = xmalloc_getline(rfile)) 1026 != NULL) 1027 append(line); 1028 xprint_and_close_file(rfile); 1029 } 1030 1031 break; 1032 } 1033 1034 /* Write pattern space to file. */ 1035 case 'w': 1036 puts_maybe_newline( 1037 pattern_space, sed_cmd->sw_file, 1038 &sed_cmd->sw_last_char, last_gets_char); 1039 break; 1040 1041 /* Read next line from input */ 1042 case 'n': 1043 if (!G.be_quiet) 1044 sed_puts(pattern_space, last_gets_char); 1045 if (next_line) { 1046 free(pattern_space); 1047 pattern_space = next_line; 1048 last_gets_char = next_gets_char; 1049 next_line = get_next_line(&next_gets_char); 1050 linenum++; 1051 break; 1052 } 1053 /* fall through */ 1054 1055 /* Quit. End of script, end of input. */ 1056 case 'q': 1057 /* Exit the outer while loop */ 1058 free(next_line); 1059 next_line = NULL; 1060 goto discard_commands; 1061 1062 /* Append the next line to the current line */ 1063 case 'N': 1064 { 1065 int len; 1066 /* If no next line, jump to end of script and exit. */ 1067 if (next_line == NULL) { 1068 /* Jump to end of script and exit */ 1069 free(next_line); 1070 next_line = NULL; 1071 goto discard_line; 1072 /* append next_line, read new next_line. */ 1073 } 1074 len = strlen(pattern_space); 1075 pattern_space = realloc(pattern_space, len + strlen(next_line) + 2); 1076 pattern_space[len] = '\n'; 1077 strcpy(pattern_space + len+1, next_line); 1078 last_gets_char = next_gets_char; 1079 next_line = get_next_line(&next_gets_char); 1080 linenum++; 1081 break; 1082 } 1083 1084 /* Test/branch if substitution occurred */ 1085 case 't': 1086 if (!substituted) break; 1087 substituted = 0; 1088 /* Fall through */ 1089 /* Test/branch if substitution didn't occur */ 1090 case 'T': 1091 if (substituted) break; 1092 /* Fall through */ 1093 /* Branch to label */ 1094 case 'b': 1095 if (!sed_cmd->string) goto discard_commands; 1096 else sed_cmd = branch_to(sed_cmd->string); 1097 break; 1098 /* Transliterate characters */ 1099 case 'y': 1100 { 1101 int i, j; 1102 1103 for (i = 0; pattern_space[i]; i++) { 1104 for (j = 0; sed_cmd->string[j]; j += 2) { 1105 if (pattern_space[i] == sed_cmd->string[j]) { 1106 pattern_space[i] = sed_cmd->string[j + 1]; 840 1107 break; 841 1108 } 842 /* Fall Through */843 }844 845 /* Write the current pattern space to output */846 case 'p':847 sed_puts(pattern_space,no_newline);848 break;849 /* Delete up through first newline */850 case 'D':851 {852 char *tmp = strchr(pattern_space,'\n');853 854 if(tmp) {855 tmp=bb_xstrdup(tmp+1);856 free(pattern_space);857 pattern_space=tmp;858 goto restart;859 }860 }861 /* discard this line. */862 case 'd':863 goto discard_line;864 865 /* Substitute with regex */866 case 's':867 if(do_subst_command(sed_cmd, &pattern_space)) {868 substituted|=1;869 870 /* handle p option */871 if(sed_cmd->sub_p)872 sed_puts(pattern_space,no_newline);873 /* handle w option */874 if(sed_cmd->file)875 sed_cmd->no_newline=puts_maybe_newline(pattern_space, sed_cmd->file, sed_cmd->no_newline, no_newline);876 877 }878 break;879 880 /* Append line to linked list to be printed later */881 case 'a':882 {883 append(sed_cmd->string);884 break;885 }886 887 /* Insert text before this line */888 case 'i':889 sed_puts(sed_cmd->string,1);890 break;891 892 /* Cut and paste text (replace) */893 case 'c':894 /* Only triggers on last line of a matching range. */895 if (!sed_cmd->in_match) sed_puts(sed_cmd->string,0);896 goto discard_line;897 898 /* Read file, append contents to output */899 case 'r':900 {901 FILE *rfile;902 903 rfile = fopen(sed_cmd->string, "r");904 if (rfile) {905 char *line;906 907 while ((line = bb_get_chomped_line_from_file(rfile))908 != NULL)909 append(line);910 bb_xprint_and_close_file(rfile);911 }912 913 break;914 }915 916 /* Write pattern space to file. */917 case 'w':918 sed_cmd->no_newline=puts_maybe_newline(pattern_space,sed_cmd->file, sed_cmd->no_newline,no_newline);919 break;920 921 /* Read next line from input */922 case 'n':923 if (!bbg.be_quiet)924 sed_puts(pattern_space,no_newline);925 if (next_line) {926 free(pattern_space);927 pattern_space = next_line;928 no_newline=next_no_newline;929 next_line = get_next_line(&next_no_newline);930 linenum++;931 break;932 }933 /* fall through */934 935 /* Quit. End of script, end of input. */936 case 'q':937 /* Exit the outer while loop */938 free(next_line);939 next_line = NULL;940 goto discard_commands;941 942 /* Append the next line to the current line */943 case 'N':944 {945 /* If no next line, jump to end of script and exit. */946 if (next_line == NULL) {947 /* Jump to end of script and exit */948 free(next_line);949 next_line = NULL;950 goto discard_line;951 /* append next_line, read new next_line. */952 } else {953 int len=strlen(pattern_space);954 955 pattern_space = realloc(pattern_space, len + strlen(next_line) + 2);956 pattern_space[len]='\n';957 strcpy(pattern_space+len+1, next_line);958 no_newline=next_no_newline;959 next_line = get_next_line(&next_no_newline);960 linenum++;961 }962 break;963 }964 965 /* Test/branch if substitution occurred */966 case 't':967 if(!substituted) break;968 substituted=0;969 /* Fall through */970 /* Test/branch if substitution didn't occur */971 case 'T':972 if (substituted) break;973 /* Fall through */974 /* Branch to label */975 case 'b':976 if (!sed_cmd->string) goto discard_commands;977 else sed_cmd = branch_to(sed_cmd->string);978 break;979 /* Transliterate characters */980 case 'y':981 {982 int i;983 984 for (i = 0; pattern_space[i]; i++) {985 int j;986 987 for (j = 0; sed_cmd->string[j]; j += 2) {988 if (pattern_space[i] == sed_cmd->string[j]) {989 pattern_space[i] = sed_cmd->string[j + 1];990 break;991 }992 }993 }994 995 break;996 }997 case 'g': /* Replace pattern space with hold space */998 free(pattern_space);999 pattern_space = bb_xstrdup(bbg.hold_space ? bbg.hold_space : "");1000 break;1001 case 'G': /* Append newline and hold space to pattern space */1002 {1003 int pattern_space_size = 2;1004 int hold_space_size = 0;1005 1006 if (pattern_space)1007 pattern_space_size += strlen(pattern_space);1008 if (bbg.hold_space)1009 hold_space_size = strlen(bbg.hold_space);1010 pattern_space = xrealloc(pattern_space,1011 pattern_space_size + hold_space_size);1012 if (pattern_space_size == 2) pattern_space[0]=0;1013 strcat(pattern_space, "\n");1014 if (bbg.hold_space)1015 strcat(pattern_space, bbg.hold_space);1016 no_newline=0;1017 1018 break;1019 }1020 case 'h': /* Replace hold space with pattern space */1021 free(bbg.hold_space);1022 bbg.hold_space = bb_xstrdup(pattern_space);1023 break;1024 case 'H': /* Append newline and pattern space to hold space */1025 {1026 int hold_space_size = 2;1027 int pattern_space_size = 0;1028 1029 if (bbg.hold_space)1030 hold_space_size += strlen(bbg.hold_space);1031 if (pattern_space)1032 pattern_space_size = strlen(pattern_space);1033 bbg.hold_space = xrealloc(bbg.hold_space,1034 hold_space_size + pattern_space_size);1035 1036 if (hold_space_size == 2) *bbg.hold_space=0;1037 strcat(bbg.hold_space, "\n");1038 if (pattern_space) strcat(bbg.hold_space, pattern_space);1039 1040 break;1041 }1042 case 'x': /* Exchange hold and pattern space */1043 {1044 char *tmp = pattern_space;1045 pattern_space = bbg.hold_space ? : xzalloc(1);1046 no_newline=0;1047 bbg.hold_space = tmp;1048 break;1049 1109 } 1050 1110 } 1051 } 1052 } 1053 1054 /* 1055 * exit point from sedding... 1056 */ 1057 discard_commands: 1058 /* we will print the line unless we were told to be quiet ('-n') 1059 or if the line was suppressed (ala 'd'elete) */ 1060 if (!bbg.be_quiet) sed_puts(pattern_space,no_newline); 1061 1062 /* Delete and such jump here. */ 1063 discard_line: 1064 flush_append(); 1065 free(pattern_space); 1066 } 1111 1112 break; 1113 } 1114 case 'g': /* Replace pattern space with hold space */ 1115 free(pattern_space); 1116 pattern_space = xstrdup(G.hold_space ? G.hold_space : ""); 1117 break; 1118 case 'G': /* Append newline and hold space to pattern space */ 1119 { 1120 int pattern_space_size = 2; 1121 int hold_space_size = 0; 1122 1123 if (pattern_space) 1124 pattern_space_size += strlen(pattern_space); 1125 if (G.hold_space) 1126 hold_space_size = strlen(G.hold_space); 1127 pattern_space = xrealloc(pattern_space, 1128 pattern_space_size + hold_space_size); 1129 if (pattern_space_size == 2) 1130 pattern_space[0] = 0; 1131 strcat(pattern_space, "\n"); 1132 if (G.hold_space) 1133 strcat(pattern_space, G.hold_space); 1134 last_gets_char = '\n'; 1135 1136 break; 1137 } 1138 case 'h': /* Replace hold space with pattern space */ 1139 free(G.hold_space); 1140 G.hold_space = xstrdup(pattern_space); 1141 break; 1142 case 'H': /* Append newline and pattern space to hold space */ 1143 { 1144 int hold_space_size = 2; 1145 int pattern_space_size = 0; 1146 1147 if (G.hold_space) 1148 hold_space_size += strlen(G.hold_space); 1149 if (pattern_space) 1150 pattern_space_size = strlen(pattern_space); 1151 G.hold_space = xrealloc(G.hold_space, 1152 hold_space_size + pattern_space_size); 1153 1154 if (hold_space_size == 2) 1155 *G.hold_space = 0; 1156 strcat(G.hold_space, "\n"); 1157 if (pattern_space) 1158 strcat(G.hold_space, pattern_space); 1159 1160 break; 1161 } 1162 case 'x': /* Exchange hold and pattern space */ 1163 { 1164 char *tmp = pattern_space; 1165 pattern_space = G.hold_space ? : xzalloc(1); 1166 last_gets_char = '\n'; 1167 G.hold_space = tmp; 1168 break; 1169 } 1170 } 1171 } 1172 } 1173 1174 /* 1175 * exit point from sedding... 1176 */ 1177 discard_commands: 1178 /* we will print the line unless we were told to be quiet ('-n') 1179 or if the line was suppressed (ala 'd'elete) */ 1180 if (!G.be_quiet) 1181 sed_puts(pattern_space, last_gets_char); 1182 1183 /* Delete and such jump here. */ 1184 discard_line: 1185 flush_append(); 1186 free(pattern_space); 1187 1188 goto again; 1067 1189 } 1068 1190 1069 1191 /* It is possible to have a command line argument with embedded 1070 newlines. This counts as multiple command lines. */ 1192 * newlines. This counts as multiple command lines. 1193 * However, newline can be escaped: 's/e/z\<newline>z/' 1194 * We check for this. 1195 */ 1071 1196 1072 1197 static void add_cmd_block(char *cmdstr) 1073 1198 { 1074 int go=1; 1075 char *temp=bb_xstrdup(cmdstr),*temp2=temp; 1076 1077 while(go) { 1078 int len=strcspn(temp2,"\n"); 1079 if(!temp2[len]) go=0; 1080 else temp2[len]=0; 1081 add_cmd(temp2); 1082 temp2+=len+1; 1083 } 1084 free(temp); 1085 } 1086 1199 char *sv, *eol; 1200 1201 cmdstr = sv = xstrdup(cmdstr); 1202 do { 1203 eol = strchr(cmdstr, '\n'); 1204 next: 1205 if (eol) { 1206 /* Count preceding slashes */ 1207 int slashes = 0; 1208 char *sl = eol; 1209 1210 while (sl != cmdstr && *--sl == '\\') 1211 slashes++; 1212 /* Odd number of preceding slashes - newline is escaped */ 1213 if (slashes & 1) { 1214 strcpy(eol-1, eol); 1215 eol = strchr(eol, '\n'); 1216 goto next; 1217 } 1218 *eol = '\0'; 1219 } 1220 add_cmd(cmdstr); 1221 cmdstr = eol + 1; 1222 } while (eol); 1223 free(sv); 1224 } 1225 1226 int sed_main(int argc, char **argv); 1087 1227 int sed_main(int argc, char **argv) 1088 1228 { 1089 int status = EXIT_SUCCESS, opt, getpat = 1; 1090 1091 bbg.sed_cmd_tail=&bbg.sed_cmd_head; 1229 enum { 1230 OPT_in_place = 1 << 0, 1231 }; 1232 unsigned opt; 1233 llist_t *opt_e, *opt_f; 1234 int status = EXIT_SUCCESS; 1235 1236 INIT_G(); 1092 1237 1093 1238 /* destroy command strings on exit */ … … 1095 1240 1096 1241 /* Lie to autoconf when it starts asking stupid questions. */ 1097 if (argc==2 && !strcmp(argv[1],"--version")) {1098 p rintf("This is not GNU sed version 4.0\n");1099 exit(0);1242 if (argc == 2 && !strcmp(argv[1], "--version")) { 1243 puts("This is not GNU sed version 4.0"); 1244 return 0; 1100 1245 } 1101 1246 1102 1247 /* do normal option parsing */ 1103 while ((opt = getopt(argc, argv, "irne:f:")) > 0) { 1104 switch (opt) { 1105 case 'i': 1106 bbg.in_place++; 1107 atexit(cleanup_outname); 1108 break; 1109 case 'r': 1110 bbg.regex_type|=REG_EXTENDED; 1111 break; 1112 case 'n': 1113 bbg.be_quiet++; 1114 break; 1115 case 'e': 1116 add_cmd_block(optarg); 1117 getpat=0; 1118 break; 1119 case 'f': 1120 { 1121 FILE *cmdfile; 1122 char *line; 1123 1124 cmdfile = bb_xfopen(optarg, "r"); 1125 1126 while ((line = bb_get_chomped_line_from_file(cmdfile)) != NULL) { 1127 add_cmd(line); 1128 getpat=0; 1129 free(line); 1130 } 1131 bb_xprint_and_close_file(cmdfile); 1132 1133 break; 1134 } 1135 default: 1248 opt_e = opt_f = NULL; 1249 opt_complementary = "e::f::" /* can occur multiple times */ 1250 "nn"; /* count -n */ 1251 opt = getopt32(argv, "irne:f:", &opt_e, &opt_f, 1252 &G.be_quiet); /* counter for -n */ 1253 argc -= optind; 1254 argv += optind; 1255 if (opt & OPT_in_place) { // -i 1256 atexit(cleanup_outname); 1257 } 1258 if (opt & 0x2) G.regex_type |= REG_EXTENDED; // -r 1259 //if (opt & 0x4) G.be_quiet++; // -n 1260 while (opt_e) { // -e 1261 add_cmd_block(opt_e->data); 1262 opt_e = opt_e->link; 1263 /* we leak opt_e here... */ 1264 } 1265 while (opt_f) { // -f 1266 char *line; 1267 FILE *cmdfile; 1268 cmdfile = xfopen(opt_f->data, "r"); 1269 while ((line = xmalloc_getline(cmdfile)) != NULL) { 1270 add_cmd(line); 1271 free(line); 1272 } 1273 fclose(cmdfile); 1274 opt_f = opt_f->link; 1275 /* we leak opt_f here... */ 1276 } 1277 /* if we didn't get a pattern from -e or -f, use argv[0] */ 1278 if (!(opt & 0x18)) { 1279 if (!argc) 1136 1280 bb_show_usage(); 1137 } 1138 } 1139 1140 /* if we didn't get a pattern from -e or -f, use argv[optind] */ 1141 if(getpat) { 1142 if (argv[optind] == NULL) 1143 bb_show_usage(); 1144 else 1145 add_cmd_block(argv[optind++]); 1281 add_cmd_block(*argv++); 1282 argc--; 1146 1283 } 1147 1284 /* Flush any unfinished commands. */ … … 1149 1286 1150 1287 /* By default, we write to stdout */ 1151 bbg.nonstdout=stdout;1152 1153 /* argv[ (optind)..(argc-1)] should be names of file to process. If no1288 G.nonstdout = stdout; 1289 1290 /* argv[0..(argc-1)] should be names of file to process. If no 1154 1291 * files were specified or '-' was specified, take input from stdin. 1155 1292 * Otherwise, we process all the files specified. */ 1156 if (argv[optind] == NULL) { 1157 if(bbg.in_place) bb_error_msg_and_die(bb_msg_requires_arg, "-i"); 1293 if (argv[0] == NULL) { 1294 if (opt & OPT_in_place) 1295 bb_error_msg_and_die(bb_msg_requires_arg, "-i"); 1158 1296 add_input_file(stdin); 1159 1297 process_files(); … … 1162 1300 FILE *file; 1163 1301 1164 for (i = optind; i < argc; i++) { 1165 if(!strcmp(argv[i], "-") && !bbg.in_place) { 1302 for (i = 0; i < argc; i++) { 1303 struct stat statbuf; 1304 int nonstdoutfd; 1305 1306 if (LONE_DASH(argv[i]) && !(opt & OPT_in_place)) { 1166 1307 add_input_file(stdin); 1167 1308 process_files(); 1168 } else { 1169 file = bb_wfopen(argv[i], "r"); 1170 if (file) { 1171 if(bbg.in_place) { 1172 struct stat statbuf; 1173 int nonstdoutfd; 1174 1175 bbg.outname=bb_xstrndup(argv[i],strlen(argv[i])+6); 1176 strcat(bbg.outname,"XXXXXX"); 1177 if(-1==(nonstdoutfd=mkstemp(bbg.outname))) 1178 bb_error_msg_and_die("no temp file"); 1179 bbg.nonstdout=fdopen(nonstdoutfd,"w"); 1180 1181 /* Set permissions of output file */ 1182 1183 fstat(fileno(file),&statbuf); 1184 fchmod(nonstdoutfd,statbuf.st_mode); 1185 add_input_file(file); 1186 process_files(); 1187 fclose(bbg.nonstdout); 1188 1189 bbg.nonstdout=stdout; 1190 unlink(argv[i]); 1191 rename(bbg.outname,argv[i]); 1192 free(bbg.outname); 1193 bbg.outname=0; 1194 } else add_input_file(file); 1195 } else { 1196 status = EXIT_FAILURE; 1197 } 1198 } 1199 } 1200 if(bbg.input_file_count>bbg.current_input_file) process_files(); 1309 continue; 1310 } 1311 file = fopen_or_warn(argv[i], "r"); 1312 if (!file) { 1313 status = EXIT_FAILURE; 1314 continue; 1315 } 1316 if (!(opt & OPT_in_place)) { 1317 add_input_file(file); 1318 continue; 1319 } 1320 1321 G.outname = xasprintf("%sXXXXXX", argv[i]); 1322 nonstdoutfd = mkstemp(G.outname); 1323 if (-1 == nonstdoutfd) 1324 bb_perror_msg_and_die("cannot create temp file %s", G.outname); 1325 G.nonstdout = fdopen(nonstdoutfd, "w"); 1326 1327 /* Set permissions of output file */ 1328 1329 fstat(fileno(file), &statbuf); 1330 fchmod(nonstdoutfd, statbuf.st_mode); 1331 add_input_file(file); 1332 process_files(); 1333 fclose(G.nonstdout); 1334 1335 G.nonstdout = stdout; 1336 /* unlink(argv[i]); */ 1337 // FIXME: error check / message? 1338 rename(G.outname, argv[i]); 1339 free(G.outname); 1340 G.outname = 0; 1341 } 1342 if (G.input_file_count > G.current_input_file) 1343 process_files(); 1201 1344 } 1202 1345
Note:
See TracChangeset
for help on using the changeset viewer.