int dc_main(int argc, char **argv) { /* take stuff from stdin if no args are given */ if (argc <= 1) { int i, len; char *line = NULL; char *cursor = NULL; char *token = NULL; while ((line = xmalloc_getline(stdin))) { cursor = line; len = number_of_tokens(line); for (i = 0; i < len; i++) { token = get_token(&cursor); *cursor++ = 0; stack_machine(token); } free(line); } } else { if (*argv[1] == '-') bb_show_usage(); while (argc >= 2) { stack_machine(argv[1]); argv++; argc--; } } return EXIT_SUCCESS; }
/* state->flags is already checked to be nonzero */ static void load_history(const char *fromfile) { FILE *fp; int hi; /* cleanup old */ for (hi = state->cnt_history; hi > 0;) { hi--; free(state->history[hi]); } fp = fopen(fromfile, "r"); if (fp) { for (hi = 0; hi < MAX_HISTORY;) { char *hl = xmalloc_getline(fp); int l; if (!hl) break; l = strlen(hl); if (l >= MAX_LINELEN) hl[MAX_LINELEN-1] = '\0'; if (l == 0 || hl[0] == ' ') { free(hl); continue; } state->history[hi++] = hl; } fclose(fp); } state->cur_history = state->cnt_history = hi; }
void load_history(const char *fromfile) { FILE *fp; int hi; /* cleanup old */ for (hi = n_history; hi > 0;) { hi--; free(history[hi]); } fp = fopen(fromfile, "r"); if (fp) { for (hi = 0; hi < MAX_HISTORY;) { char * hl = xmalloc_getline(fp); int l; if (!hl) break; l = strlen(hl); if (l >= BUFSIZ) hl[BUFSIZ-1] = 0; if (l == 0 || hl[0] == ' ') { free(hl); continue; } history[hi++] = hl; } fclose(fp); } cur_history = n_history = hi; }
static llist_t *get_block_backed_filesystems(void) { static const char filesystems[2][sizeof("/proc/filesystems")] = { "/etc/filesystems", "/proc/filesystems", }; char *fs, *buf; llist_t *list = 0; int i; FILE *f; for (i = 0; i < 2; i++) { f = fopen(filesystems[i], "r"); if (!f) continue; while ((buf = xmalloc_getline(f)) != 0) { if (!strncmp(buf, "nodev", 5) && isspace(buf[5])) continue; fs = skip_whitespace(buf); if (*fs=='#' || *fs=='*' || !*fs) continue; llist_add_to_end(&list, xstrdup(fs)); free(buf); } if (ENABLE_FEATURE_CLEAN_UP) fclose(f); } return list; }
int cryptpw_main(int argc, char **argv) { char salt[sizeof("$N$XXXXXXXX")]; if (!getopt32(argc, argv, "a:", NULL) || argv[optind - 1][0] != 'd') { strcpy(salt, "$1$"); /* Too ugly, and needs even more magic to handle endianness: */ //((uint32_t*)&salt)[0] = '$' + '1'*0x100 + '$'*0x10000; /* Hope one day gcc will do it itself (inlining strcpy) */ crypt_make_salt(salt + 3, 4); /* md5 */ } else { crypt_make_salt(salt, 1); /* des */ } puts(pw_encrypt(argv[optind] ? argv[optind] : xmalloc_getline(stdin), salt)); return 0; }
static void bb_dump_addfile(char *name) { char *p; FILE *fp; char *buf; fp = xfopen(name, "r"); while ((buf = xmalloc_getline(fp)) != NULL) { p = skip_whitespace(buf); if (*p && (*p != '#')) { bb_dump_add(p); } free(buf); } fclose(fp); }
static void load_regexes_from_file(llist_t *fopt) { char *line; FILE *f; while (fopt) { llist_t *cur = fopt; char *ffile = cur->data; fopt = cur->link; free(cur); f = xfopen(ffile, "r"); while ((line = xmalloc_getline(f)) != NULL) { llist_add_to(&pattern_head, new_grep_list_data(line, PATTERN_MEM_A)); } } }
static llist_t *append_file_list_to_list(llist_t *list) { FILE *src_stream; llist_t *cur = list; llist_t *tmp; char *line; llist_t *newlist = NULL; while (cur) { src_stream = xfopen(cur->data, "r"); tmp = cur; cur = cur->link; free(tmp); while ((line = xmalloc_getline(src_stream)) != NULL) { /* kill trailing '/' unless the string is just "/" */ char *cp = last_char_is(line, '/'); if (cp > line) *cp = '\0'; llist_add_to(&newlist, line); } fclose(src_stream); } return newlist; }
static void cut_file(FILE * file) { char *line = NULL; unsigned int linenum = 0; /* keep these zero-based to be consistent */ /* go through every line in the file */ while ((line = xmalloc_getline(file)) != NULL) { /* set up a list so we can keep track of what's been printed */ char * printed = xzalloc(strlen(line) * sizeof(char)); char * orig_line = line; unsigned int cl_pos = 0; int spos; /* cut based on chars/bytes XXX: only works when sizeof(char) == byte */ if (option_mask32 & (CUT_OPT_CHAR_FLGS | CUT_OPT_BYTE_FLGS)) { /* print the chars specified in each cut list */ for (; cl_pos < nlists; cl_pos++) { spos = cut_lists[cl_pos].startpos; while (spos < strlen(line)) { if (!printed[spos]) { printed[spos] = 'X'; putchar(line[spos]); } spos++; if (spos > cut_lists[cl_pos].endpos || cut_lists[cl_pos].endpos == NON_RANGE) break; } } } else if (delim == '\n') { /* cut by lines */ spos = cut_lists[cl_pos].startpos; /* get out if we have no more lists to process or if the lines * are lower than what we're interested in */ if (linenum < spos || cl_pos >= nlists) goto next_line; /* if the line we're looking for is lower than the one we were * passed, it means we displayed it already, so move on */ while (spos < linenum) { spos++; /* go to the next list if we're at the end of this one */ if (spos > cut_lists[cl_pos].endpos || cut_lists[cl_pos].endpos == NON_RANGE) { cl_pos++; /* get out if there's no more lists to process */ if (cl_pos >= nlists) goto next_line; spos = cut_lists[cl_pos].startpos; /* get out if the current line is lower than the one * we just became interested in */ if (linenum < spos) goto next_line; } } /* If we made it here, it means we've found the line we're * looking for, so print it */ puts(line); goto next_line; } else { /* cut by fields */ int ndelim = -1; /* zero-based / one-based problem */ int nfields_printed = 0; char *field = NULL; const char delimiter[2] = { delim, 0 }; /* does this line contain any delimiters? */ if (strchr(line, delim) == NULL) { if (!(option_mask32 & CUT_OPT_SUPPRESS_FLGS)) puts(line); goto next_line; } /* process each list on this line, for as long as we've got * a line to process */ for (; cl_pos < nlists && line; cl_pos++) { spos = cut_lists[cl_pos].startpos; do { /* find the field we're looking for */ while (line && ndelim < spos) { field = strsep(&line, delimiter); ndelim++; } /* we found it, and it hasn't been printed yet */ if (field && ndelim == spos && !printed[ndelim]) { /* if this isn't our first time through, we need to * print the delimiter after the last field that was * printed */ if (nfields_printed > 0) putchar(delim); fputs(field, stdout); printed[ndelim] = 'X'; nfields_printed++; /* shouldn't overflow.. */ } spos++; /* keep going as long as we have a line to work with, * this is a list, and we're not at the end of that * list */ } while (spos <= cut_lists[cl_pos].endpos && line && cut_lists[cl_pos].endpos != NON_RANGE); } } /* if we printed anything at all, we need to finish it with a * newline cuz we were handed a chomped line */ putchar('\n'); next_line: linenum++; free(printed); free(orig_line); } }
int md5_sha1_sum_main(int argc, char **argv) { int return_value = EXIT_SUCCESS; uint8_t *hash_value; unsigned flags; hash_algo_t hash_algo = ENABLE_MD5SUM ? (ENABLE_SHA1SUM ? (**argv=='m' ? HASH_MD5 : HASH_SHA1) : HASH_MD5) : HASH_SHA1; if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK) flags = getopt32(argc, argv, "scw"); else optind = 1; if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK && !(flags & FLAG_CHECK)) { if (flags & FLAG_SILENT) { bb_error_msg_and_die ("-%c is meaningful only when verifying checksums", 's'); } else if (flags & FLAG_WARN) { bb_error_msg_and_die ("-%c is meaningful only when verifying checksums", 'w'); } } if (argc == optind) { argv[argc++] = "-"; } if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK && (flags & FLAG_CHECK)) { FILE *pre_computed_stream; int count_total = 0; int count_failed = 0; char *file_ptr = argv[optind]; char *line; if (optind + 1 != argc) { bb_error_msg_and_die ("only one argument may be specified when using -c"); } pre_computed_stream = stdin; if (NOT_LONE_DASH(file_ptr)) { pre_computed_stream = xfopen(file_ptr, "r"); } while ((line = xmalloc_getline(pre_computed_stream)) != NULL) { char *filename_ptr; count_total++; filename_ptr = strstr(line, " "); /* handle format for binary checksums */ if (filename_ptr == NULL) { filename_ptr = strstr(line, " *"); } if (filename_ptr == NULL) { if (flags & FLAG_WARN) { bb_error_msg("invalid format"); } count_failed++; return_value = EXIT_FAILURE; free(line); continue; } *filename_ptr = '\0'; filename_ptr += 2; hash_value = hash_file(filename_ptr, hash_algo); if (hash_value && (strcmp((char*)hash_value, line) == 0)) { if (!(flags & FLAG_SILENT)) printf("%s: OK\n", filename_ptr); } else { if (!(flags & FLAG_SILENT)) printf("%s: FAILED\n", filename_ptr); count_failed++; return_value = EXIT_FAILURE; } /* possible free(NULL) */ free(hash_value); free(line); } if (count_failed && !(flags & FLAG_SILENT)) { bb_error_msg("WARNING: %d of %d computed checksums did NOT match", count_failed, count_total); } /* if (fclose_if_not_stdin(pre_computed_stream) == EOF) { bb_perror_msg_and_die("cannot close file %s", file_ptr); } */ } else { while (optind < argc) { char *file_ptr = argv[optind++]; hash_value = hash_file(file_ptr, hash_algo); if (hash_value == NULL) { return_value = EXIT_FAILURE; } else { printf("%s %s\n", hash_value, file_ptr); free(hash_value); } } } return return_value; }
int uniq_main(int argc ATTRIBUTE_UNUSED, char **argv) { FILE *in, *out; unsigned long dups, skip_fields, skip_chars, i; const char *s0, *e0, *s1, *e1, *input_filename; unsigned opt; enum { OPT_c = 0x1, OPT_d = 0x2, OPT_u = 0x4, OPT_f = 0x8, OPT_s = 0x10, }; skip_fields = skip_chars = 0; opt = getopt32(argv, "cduf:s:", &s0, &s1); if (opt & OPT_f) skip_fields = xatoul(s0); if (opt & OPT_s) skip_chars = xatoul(s1); argv += optind; input_filename = *argv; in = xgetoptfile_uniq_s(argv, 0); if (*argv) { ++argv; } out = xgetoptfile_uniq_s(argv, 2); if (*argv && argv[1]) { bb_show_usage(); } s1 = e1 = NULL; /* prime the pump */ do { s0 = s1; e0 = e1; dups = 0; /* gnu uniq ignores newlines */ while ((s1 = xmalloc_getline(in)) != NULL) { e1 = s1; for (i = skip_fields; i; i--) { e1 = skip_whitespace(e1); e1 = skip_non_whitespace(e1); } for (i = skip_chars; *e1 && i; i--) { ++e1; } if (!s0 || strcmp(e0, e1)) { break; } ++dups; /* Note: Testing for overflow seems excessive. */ } if (s0) { if (!(opt & (OPT_d << !!dups))) { /* (if dups, opt & OPT_e) */ fprintf(out, "\0%d " + (opt & 1), dups + 1); fprintf(out, "%s\n", s0); } free((void *)s0); } } while (s1); die_if_ferror(in, input_filename); fflush_stdout_and_exit(EXIT_SUCCESS); }
int patch_main(int argc, char **argv) { int patch_level = -1; char *patch_line; int ret; FILE *patch_file = NULL; { char *p, *i; ret = getopt32(argv, "p:i:", &p, &i); if (ret & 1) patch_level = xatol_range(p, -1, USHRT_MAX); if (ret & 2) { patch_file = xfopen(i, "r"); } else { patch_file = stdin; } ret = 0; } patch_line = xmalloc_getline(patch_file); while (patch_line) { FILE *src_stream; FILE *dst_stream; char *original_filename; char *new_filename; char *backup_filename; unsigned int src_cur_line = 1; unsigned int dest_cur_line = 0; unsigned int dest_beg_line; unsigned int bad_hunk_count = 0; unsigned int hunk_count = 0; char copy_trailing_lines_flag = 0; /* Skip everything upto the "---" marker * No need to parse the lines "Only in <dir>", and "diff <args>" */ while (patch_line && strncmp(patch_line, "--- ", 4) != 0) { free(patch_line); patch_line = xmalloc_getline(patch_file); } /* FIXME: patch_line NULL check?? */ /* Extract the filename used before the patch was generated */ original_filename = extract_filename(patch_line, patch_level); free(patch_line); patch_line = xmalloc_getline(patch_file); /* FIXME: NULL check?? */ if (strncmp(patch_line, "+++ ", 4) != 0) { ret = 2; bb_error_msg("invalid patch"); continue; } new_filename = extract_filename(patch_line, patch_level); free(patch_line); if (file_doesnt_exist(new_filename)) { char *line_ptr; /* Create leading directories */ line_ptr = strrchr(new_filename, '/'); if (line_ptr) { *line_ptr = '\0'; bb_make_directory(new_filename, -1, FILEUTILS_RECUR); *line_ptr = '/'; } dst_stream = xfopen(new_filename, "w+"); backup_filename = NULL; } else { backup_filename = xmalloc(strlen(new_filename) + 6); strcpy(backup_filename, new_filename); strcat(backup_filename, ".orig"); if (rename(new_filename, backup_filename) == -1) { bb_perror_msg_and_die("cannot create file %s", backup_filename); } dst_stream = xfopen(new_filename, "w"); } if ((backup_filename == NULL) || file_doesnt_exist(original_filename)) { src_stream = NULL; } else { if (strcmp(original_filename, new_filename) == 0) { src_stream = xfopen(backup_filename, "r"); } else { src_stream = xfopen(original_filename, "r"); } } printf("patching file %s\n", new_filename); /* Handle each hunk */ patch_line = xmalloc_fgets(patch_file); while (patch_line) { unsigned int count; unsigned int src_beg_line; unsigned int unused; unsigned int hunk_offset_start = 0; int hunk_error = 0; /* This bit should be improved */ if ((sscanf(patch_line, "@@ -%d,%d +%d,%d @@", &src_beg_line, &unused, &dest_beg_line, &unused) != 4) && (sscanf(patch_line, "@@ -%d,%d +%d @@", &src_beg_line, &unused, &dest_beg_line) != 3) && (sscanf(patch_line, "@@ -%d +%d,%d @@", &src_beg_line, &dest_beg_line, &unused) != 3)) { /* No more hunks for this file */ break; } free(patch_line); hunk_count++; if (src_beg_line && dest_beg_line) { /* Copy unmodified lines upto start of hunk */ /* src_beg_line will be 0 if its a new file */ count = src_beg_line - src_cur_line; if (copy_lines(src_stream, dst_stream, count) != count) { bb_error_msg_and_die("bad src file"); } src_cur_line += count; dest_cur_line += count; copy_trailing_lines_flag = 1; } hunk_offset_start = src_cur_line; while ((patch_line = xmalloc_fgets(patch_file)) != NULL) { if ((*patch_line == '-') || (*patch_line == ' ')) { char *src_line = NULL; if (src_stream) { src_line = xmalloc_fgets(src_stream); if (!src_line) { hunk_error++; break; } else { src_cur_line++; } if (strcmp(src_line, patch_line + 1) != 0) { bb_error_msg("hunk #%d FAILED at %d", hunk_count, hunk_offset_start); hunk_error++; free(patch_line); /* Probably need to find next hunk, etc... */ /* but for now we just bail out */ patch_line = NULL; break; } free(src_line); } if (*patch_line == ' ') { fputs(patch_line + 1, dst_stream); dest_cur_line++; } } else if (*patch_line == '+') { fputs(patch_line + 1, dst_stream); dest_cur_line++; } else { break; } free(patch_line); } if (hunk_error) { bad_hunk_count++; } } /* Cleanup last patched file */ if (copy_trailing_lines_flag) { copy_lines(src_stream, dst_stream, -1); } if (src_stream) { fclose(src_stream); } if (dst_stream) { fclose(dst_stream); } if (bad_hunk_count) { if (!ret) { ret = 1; } bb_error_msg("%d out of %d hunk FAILED", bad_hunk_count, hunk_count); } else { /* It worked, we can remove the backup */ if (backup_filename) { unlink(backup_filename); } if ((dest_cur_line == 0) || (dest_beg_line == 0)) { /* The new patched file is empty, remove it */ xunlink(new_filename); if (strcmp(new_filename, original_filename) != 0) xunlink(original_filename); } } } /* 0 = SUCCESS * 1 = Some hunks failed * 2 = More serious problems */ return ret; }
static int grep_file(FILE *file) { char *line; byte_t ret; int linenum = 0; int nmatches = 0; regmatch_t regmatch; #if ENABLE_FEATURE_GREP_CONTEXT int print_n_lines_after = 0; int curpos = 0; /* track where we are in the circular 'before' buffer */ int idx = 0; /* used for iteration through the circular buffer */ #endif /* ENABLE_FEATURE_GREP_CONTEXT */ while ((line = xmalloc_getline(file)) != NULL) { llist_t *pattern_ptr = pattern_head; grep_list_data_t * gl; linenum++; ret = 0; while (pattern_ptr) { gl = (grep_list_data_t *)pattern_ptr->data; if (FGREP_FLAG) { ret = strstr(line, gl->pattern) != NULL; } else { /* * test for a postitive-assertion match (regexec returns success (0) * and the user did not specify invert search), or a negative-assertion * match (regexec returns failure (REG_NOMATCH) and the user specified * invert search) */ if (!(gl->flg_mem_alocated_compiled & COMPILED)) { gl->flg_mem_alocated_compiled |= COMPILED; xregcomp(&(gl->preg), gl->pattern, reflags); } regmatch.rm_so = 0; regmatch.rm_eo = 0; ret |= regexec(&(gl->preg), line, 1, ®match, 0) == 0; } pattern_ptr = pattern_ptr->link; } /* while (pattern_ptr) */ if (ret ^ invert_search) { if (PRINT_FILES_WITH_MATCHES || BE_QUIET) free(line); /* if we found a match but were told to be quiet, stop here */ if (BE_QUIET || PRINT_FILES_WITHOUT_MATCHES) return -1; /* keep track of matches */ nmatches++; /* if we're just printing filenames, we stop after the first match */ if (PRINT_FILES_WITH_MATCHES) break; /* print the matched line */ if (PRINT_MATCH_COUNTS == 0) { #if ENABLE_FEATURE_GREP_CONTEXT int prevpos = (curpos == 0) ? lines_before - 1 : curpos - 1; /* if we were told to print 'before' lines and there is at least * one line in the circular buffer, print them */ if (lines_before && before_buf[prevpos] != NULL) { int first_buf_entry_line_num = linenum - lines_before; /* advance to the first entry in the circular buffer, and * figure out the line number is of the first line in the * buffer */ idx = curpos; while (before_buf[idx] == NULL) { idx = (idx + 1) % lines_before; first_buf_entry_line_num++; } /* now print each line in the buffer, clearing them as we go */ while (before_buf[idx] != NULL) { print_line(before_buf[idx], first_buf_entry_line_num, '-'); free(before_buf[idx]); before_buf[idx] = NULL; idx = (idx + 1) % lines_before; first_buf_entry_line_num++; } } /* make a note that we need to print 'after' lines */ print_n_lines_after = lines_after; #endif if (option_mask32 & GREP_OPT_o) { line[regmatch.rm_eo] = '\0'; print_line(line + regmatch.rm_so, linenum, ':'); } else { print_line(line, linenum, ':'); } } } #if ENABLE_FEATURE_GREP_CONTEXT else { /* no match */ /* Add the line to the circular 'before' buffer */ if (lines_before) { free(before_buf[curpos]); before_buf[curpos] = xstrdup(line); curpos = (curpos + 1) % lines_before; } } /* if we need to print some context lines after the last match, do so */ if (print_n_lines_after && (last_line_printed != linenum)) { print_line(line, linenum, '-'); print_n_lines_after--; } #endif /* ENABLE_FEATURE_GREP_CONTEXT */ free(line); } /* special-case file post-processing for options where we don't print line * matches, just filenames and possibly match counts */ /* grep -c: print [filename:]count, even if count is zero */ if (PRINT_MATCH_COUNTS) { if (print_filename) printf("%s:", cur_file); printf("%d\n", nmatches); } /* grep -l: print just the filename, but only if we grepped the line in the file */ if (PRINT_FILES_WITH_MATCHES && nmatches > 0) { puts(cur_file); } /* grep -L: print just the filename, but only if we didn't grep the line in the file */ if (PRINT_FILES_WITHOUT_MATCHES && nmatches == 0) { puts(cur_file); } return nmatches; }
int hexdump_main(int argc, char **argv) { const char *p; int ch; #if ENABLE_FEATURE_HEXDUMP_REVERSE FILE *fp; smallint rdump = 0; #endif bb_dump_vflag = FIRST; bb_dump_length = -1; if (ENABLE_HD && !applet_name[2]) { /* we are "hd" */ ch = 'C'; goto hd_applet; } /* We cannot use getopt32: in hexdump options are cumulative. * E.g. "hexdump -C -C file" should dump each line twice */ while ((ch = getopt(argc, argv, hexdump_opts)) > 0) { p = strchr(hexdump_opts, ch); if (!p) bb_show_usage(); if ((p - hexdump_opts) < 5) { bb_dump_add(add_first); bb_dump_add(add_strings[(int)(p - hexdump_opts)]); } /* Save a little bit of space below by omitting the 'else's. */ if (ch == 'C') { hd_applet: bb_dump_add("\"%08.8_Ax\n\""); bb_dump_add("\"%08.8_ax \" 8/1 \"%02x \" \" \" 8/1 \"%02x \" "); bb_dump_add("\" |\" 16/1 \"%_p\" \"|\\n\""); } if (ch == 'e') { bb_dump_add(optarg); } /* else */ if (ch == 'f') { bb_dump_addfile(optarg); } /* else */ if (ch == 'n') { bb_dump_length = xatoi_u(optarg); } /* else */ if (ch == 's') { bb_dump_skip = xatoul_range_sfx(optarg, 0, LONG_MAX, suffixes); } /* else */ if (ch == 'v') { bb_dump_vflag = ALL; } #if ENABLE_FEATURE_HEXDUMP_REVERSE if (ch == 'R') { rdump = 1; } #endif } if (!bb_dump_fshead) { bb_dump_add(add_first); bb_dump_add("\"%07.7_ax \" 8/2 \"%04x \" \"\\n\""); } argv += optind; #if !ENABLE_FEATURE_HEXDUMP_REVERSE return bb_dump_dump(argv); #else if (!rdump) { return bb_dump_dump(argv); } /* -R: reverse of 'hexdump -Cv' */ fp = stdin; if (!*argv) { argv--; goto jump_in; } do { char *buf; fp = xfopen(*argv, "r"); jump_in: while ((buf = xmalloc_getline(fp)) != NULL) { p = buf; while (1) { /* skip address or previous byte */ while (isxdigit(*p)) p++; while (*p == ' ') p++; /* '|' char will break the line */ if (!isxdigit(*p) || sscanf(p, "%x ", &ch) != 1) break; putchar(ch); } free(buf); } fclose(fp); } while (*++argv); fflush_stdout_and_exit(EXIT_SUCCESS); #endif }
static void process_files(void) { char *pattern_space, *next_line; int linenum = 0; char last_puts_char = '\n'; char last_gets_char, next_gets_char; sed_cmd_t *sed_cmd; int substituted; /* Prime the pump */ next_line = get_next_line(&next_gets_char); /* go through every line in each file */ again: substituted = 0; /* Advance to next line. Stop if out of lines. */ pattern_space = next_line; if (!pattern_space) return; last_gets_char = next_gets_char; /* Read one line in advance so we can act on the last line, * the '$' address */ next_line = get_next_line(&next_gets_char); linenum++; restart: /* for every line, go through all the commands */ for (sed_cmd = G.sed_cmd_head.next; sed_cmd; sed_cmd = sed_cmd->next) { int old_matched, matched; old_matched = sed_cmd->in_match; /* Determine if this command matches this line: */ /* Are we continuing a previous multi-line match? */ sed_cmd->in_match = sed_cmd->in_match /* Or is no range necessary? */ || (!sed_cmd->beg_line && !sed_cmd->end_line && !sed_cmd->beg_match && !sed_cmd->end_match) /* Or did we match the start of a numerical range? */ || (sed_cmd->beg_line > 0 && (sed_cmd->beg_line == linenum)) /* Or does this line match our begin address regex? */ || (beg_match(sed_cmd, pattern_space)) /* Or did we match last line of input? */ || (sed_cmd->beg_line == -1 && next_line == NULL); /* Snapshot the value */ matched = sed_cmd->in_match; /* Is this line the end of the current match? */ if (matched) { sed_cmd->in_match = !( /* has the ending line come, or is this a single address command? */ (sed_cmd->end_line ? sed_cmd->end_line == -1 ? !next_line : (sed_cmd->end_line <= linenum) : !sed_cmd->end_match ) /* or does this line matches our last address regex */ || (sed_cmd->end_match && old_matched && (regexec(sed_cmd->end_match, pattern_space, 0, NULL, 0) == 0)) ); } /* Skip blocks of commands we didn't match. */ if (sed_cmd->cmd == '{') { if (sed_cmd->invert ? matched : !matched) { while (sed_cmd->cmd != '}') { sed_cmd = sed_cmd->next; if (!sed_cmd) bb_error_msg_and_die("unterminated {"); } } continue; } /* Okay, so did this line match? */ if (sed_cmd->invert ? !matched : matched) { /* Update last used regex in case a blank substitute BRE is found */ if (sed_cmd->beg_match) { G.previous_regex_ptr = sed_cmd->beg_match; } /* actual sedding */ switch (sed_cmd->cmd) { /* Print line number */ case '=': fprintf(G.nonstdout, "%d\n", linenum); break; /* Write the current pattern space up to the first newline */ case 'P': { char *tmp = strchr(pattern_space, '\n'); if (tmp) { *tmp = '\0'; /* TODO: explain why '\n' below */ sed_puts(pattern_space, '\n'); *tmp = '\n'; break; } /* Fall Through */ } /* Write the current pattern space to output */ case 'p': /* NB: we print this _before_ the last line * (of current file) is printed. Even if * that line is nonterminated, we print * '\n' here (gnu sed does the same) */ sed_puts(pattern_space, '\n'); break; /* Delete up through first newline */ case 'D': { char *tmp = strchr(pattern_space, '\n'); if (tmp) { tmp = xstrdup(tmp+1); free(pattern_space); pattern_space = tmp; goto restart; } } /* discard this line. */ case 'd': goto discard_line; /* Substitute with regex */ case 's': if (!do_subst_command(sed_cmd, &pattern_space)) break; substituted |= 1; /* handle p option */ if (sed_cmd->sub_p) sed_puts(pattern_space, last_gets_char); /* handle w option */ if (sed_cmd->sw_file) puts_maybe_newline( pattern_space, sed_cmd->sw_file, &sed_cmd->sw_last_char, last_gets_char); break; /* Append line to linked list to be printed later */ case 'a': append(sed_cmd->string); break; /* Insert text before this line */ case 'i': sed_puts(sed_cmd->string, '\n'); break; /* Cut and paste text (replace) */ case 'c': /* Only triggers on last line of a matching range. */ if (!sed_cmd->in_match) sed_puts(sed_cmd->string, NO_EOL_CHAR); goto discard_line; /* Read file, append contents to output */ case 'r': { FILE *rfile; rfile = fopen(sed_cmd->string, "r"); if (rfile) { char *line; while ((line = xmalloc_getline(rfile)) != NULL) append(line); xprint_and_close_file(rfile); } break; } /* Write pattern space to file. */ case 'w': puts_maybe_newline( pattern_space, sed_cmd->sw_file, &sed_cmd->sw_last_char, last_gets_char); break; /* Read next line from input */ case 'n': if (!G.be_quiet) sed_puts(pattern_space, last_gets_char); if (next_line) { free(pattern_space); pattern_space = next_line; last_gets_char = next_gets_char; next_line = get_next_line(&next_gets_char); linenum++; break; } /* fall through */ /* Quit. End of script, end of input. */ case 'q': /* Exit the outer while loop */ free(next_line); next_line = NULL; goto discard_commands; /* Append the next line to the current line */ case 'N': { int len; /* If no next line, jump to end of script and exit. */ if (next_line == NULL) { /* Jump to end of script and exit */ free(next_line); next_line = NULL; goto discard_line; /* append next_line, read new next_line. */ } len = strlen(pattern_space); pattern_space = realloc(pattern_space, len + strlen(next_line) + 2); pattern_space[len] = '\n'; strcpy(pattern_space + len+1, next_line); last_gets_char = next_gets_char; next_line = get_next_line(&next_gets_char); linenum++; break; } /* Test/branch if substitution occurred */ case 't': if (!substituted) break; substituted = 0; /* Fall through */ /* Test/branch if substitution didn't occur */ case 'T': if (substituted) break; /* Fall through */ /* Branch to label */ case 'b': if (!sed_cmd->string) goto discard_commands; else sed_cmd = branch_to(sed_cmd->string); break; /* Transliterate characters */ case 'y': { int i, j; for (i = 0; pattern_space[i]; i++) { for (j = 0; sed_cmd->string[j]; j += 2) { if (pattern_space[i] == sed_cmd->string[j]) { pattern_space[i] = sed_cmd->string[j + 1]; break; } } } break; } case 'g': /* Replace pattern space with hold space */ free(pattern_space); pattern_space = xstrdup(G.hold_space ? G.hold_space : ""); break; case 'G': /* Append newline and hold space to pattern space */ { int pattern_space_size = 2; int hold_space_size = 0; if (pattern_space) pattern_space_size += strlen(pattern_space); if (G.hold_space) hold_space_size = strlen(G.hold_space); pattern_space = xrealloc(pattern_space, pattern_space_size + hold_space_size); if (pattern_space_size == 2) pattern_space[0] = 0; strcat(pattern_space, "\n"); if (G.hold_space) strcat(pattern_space, G.hold_space); last_gets_char = '\n'; break; } case 'h': /* Replace hold space with pattern space */ free(G.hold_space); G.hold_space = xstrdup(pattern_space); break; case 'H': /* Append newline and pattern space to hold space */ { int hold_space_size = 2; int pattern_space_size = 0; if (G.hold_space) hold_space_size += strlen(G.hold_space); if (pattern_space) pattern_space_size = strlen(pattern_space); G.hold_space = xrealloc(G.hold_space, hold_space_size + pattern_space_size); if (hold_space_size == 2) *G.hold_space = 0; strcat(G.hold_space, "\n"); if (pattern_space) strcat(G.hold_space, pattern_space); break; } case 'x': /* Exchange hold and pattern space */ { char *tmp = pattern_space; pattern_space = G.hold_space ? : xzalloc(1); last_gets_char = '\n'; G.hold_space = tmp; break; } } } } /* * exit point from sedding... */ discard_commands: /* we will print the line unless we were told to be quiet ('-n') or if the line was suppressed (ala 'd'elete) */ if (!G.be_quiet) sed_puts(pattern_space, last_gets_char); /* Delete and such jump here. */ discard_line: flush_append(); free(pattern_space); goto again; }
int sed_main(int argc, char **argv) { enum { OPT_in_place = 1 << 0, }; unsigned opt; llist_t *opt_e, *opt_f; int status = EXIT_SUCCESS; INIT_G(); /* destroy command strings on exit */ if (ENABLE_FEATURE_CLEAN_UP) atexit(sed_free_and_close_stuff); /* Lie to autoconf when it starts asking stupid questions. */ if (argc == 2 && !strcmp(argv[1], "--version")) { puts("This is not GNU sed version 4.0"); return 0; } /* do normal option parsing */ opt_e = opt_f = NULL; opt_complementary = "e::f::" /* can occur multiple times */ "nn"; /* count -n */ opt = getopt32(argv, "irne:f:", &opt_e, &opt_f, &G.be_quiet); /* counter for -n */ argc -= optind; argv += optind; if (opt & OPT_in_place) { // -i atexit(cleanup_outname); } if (opt & 0x2) G.regex_type |= REG_EXTENDED; // -r //if (opt & 0x4) G.be_quiet++; // -n while (opt_e) { // -e add_cmd_block(opt_e->data); opt_e = opt_e->link; /* we leak opt_e here... */ } while (opt_f) { // -f char *line; FILE *cmdfile; cmdfile = xfopen(opt_f->data, "r"); while ((line = xmalloc_getline(cmdfile)) != NULL) { add_cmd(line); free(line); } fclose(cmdfile); opt_f = opt_f->link; /* we leak opt_f here... */ } /* if we didn't get a pattern from -e or -f, use argv[0] */ if (!(opt & 0x18)) { if (!argc) bb_show_usage(); add_cmd_block(*argv++); argc--; } /* Flush any unfinished commands. */ add_cmd(""); /* By default, we write to stdout */ G.nonstdout = stdout; /* argv[0..(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[0] == NULL) { if (opt & OPT_in_place) bb_error_msg_and_die(bb_msg_requires_arg, "-i"); add_input_file(stdin); process_files(); } else { int i; FILE *file; for (i = 0; i < argc; i++) { struct stat statbuf; int nonstdoutfd; if (LONE_DASH(argv[i]) && !(opt & OPT_in_place)) { add_input_file(stdin); process_files(); continue; } file = fopen_or_warn(argv[i], "r"); if (!file) { status = EXIT_FAILURE; continue; } if (!(opt & OPT_in_place)) { add_input_file(file); continue; } G.outname = xasprintf("%sXXXXXX", argv[i]); nonstdoutfd = mkstemp(G.outname); if (-1 == nonstdoutfd) bb_perror_msg_and_die("cannot create temp file %s", G.outname); G.nonstdout = fdopen(nonstdoutfd, "w"); /* Set permissions of output file */ fstat(fileno(file), &statbuf); fchmod(nonstdoutfd, statbuf.st_mode); add_input_file(file); process_files(); fclose(G.nonstdout); G.nonstdout = stdout; /* unlink(argv[i]); */ // FIXME: error check / message? rename(G.outname, argv[i]); free(G.outname); G.outname = NULL; } if (G.input_file_count > G.current_input_file) process_files(); } return status; }