From 6d4b3e9e0bc5d740ca39ae31930a7cec26a8ac69 Mon Sep 17 00:00:00 2001 From: tobias Date: Fri, 16 Oct 2015 07:33:47 +0000 Subject: [PATCH] Add native support for ed-style diffs. No need to pledge "proc exec" anymore. ok deraadt --- usr.bin/patch/Makefile | 4 +- usr.bin/patch/ed.c | 342 +++++++++++++++++++++++++++++++++++++++++ usr.bin/patch/ed.h | 19 +++ usr.bin/patch/patch.c | 16 +- usr.bin/patch/pch.c | 92 +---------- usr.bin/patch/pch.h | 9 +- 6 files changed, 386 insertions(+), 96 deletions(-) create mode 100644 usr.bin/patch/ed.c create mode 100644 usr.bin/patch/ed.h diff --git a/usr.bin/patch/Makefile b/usr.bin/patch/Makefile index d7071a4a068..7ff9ec2c61b 100644 --- a/usr.bin/patch/Makefile +++ b/usr.bin/patch/Makefile @@ -1,6 +1,6 @@ -# $OpenBSD: Makefile,v 1.4 2005/05/16 15:22:46 espie Exp $ +# $OpenBSD: Makefile,v 1.5 2015/10/16 07:33:47 tobias Exp $ PROG= patch -SRCS= patch.c pch.c inp.c util.c backupfile.c mkpath.c +SRCS= patch.c pch.c inp.c util.c backupfile.c mkpath.c ed.c .include diff --git a/usr.bin/patch/ed.c b/usr.bin/patch/ed.c new file mode 100644 index 00000000000..0196ff61369 --- /dev/null +++ b/usr.bin/patch/ed.c @@ -0,0 +1,342 @@ +/* $OpenBSD: ed.c,v 1.1 2015/10/16 07:33:47 tobias Exp $ */ + +/* + * Copyright (c) 2015 Tobias Stoeckmann + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include +#include +#include +#include + +#include "common.h" +#include "util.h" +#include "pch.h" +#include "inp.h" + +/* states of finite state machine */ +#define FSM_CMD 1 +#define FSM_A 2 +#define FSM_C 3 +#define FSM_D 4 +#define FSM_I 5 +#define FSM_S 6 + +#define SRC_INP 1 /* line's origin is input file */ +#define SRC_PCH 2 /* line's origin is patch file */ + +#define S_PATTERN "/.//" + +static void init_lines(void); +static void free_lines(void); +static struct ed_line *get_line(LINENUM); +static struct ed_line *create_line(off_t); +static int valid_addr(LINENUM, LINENUM); +static int get_command(void); +static void write_lines(char *); + +LIST_HEAD(ed_head, ed_line) head; +struct ed_line { + LIST_ENTRY(ed_line) entries; + int src; + unsigned long subst; + union { + LINENUM lineno; + off_t seek; + } pos; +}; + +static LINENUM first_addr; +static LINENUM second_addr; +static LINENUM line_count; +static struct ed_line *cline; /* current line */ + +void +do_ed_script(void) +{ + off_t linepos; + struct ed_line *nline; + LINENUM i, range; + int fsm; + + init_lines(); + cline = NULL; + fsm = FSM_CMD; + + for (;;) { + linepos = ftello(pfp); + if (pgets(buf, sizeof buf, pfp) == NULL) + break; + p_input_line++; + + if (fsm == FSM_CMD) { + if ((fsm = get_command()) == -1) + break; + + switch (fsm) { + case FSM_C: + case FSM_D: + /* delete lines in specified range */ + if (second_addr == -1) + range = 1; + else + range = second_addr - first_addr + 1; + for (i = 0; i < range; i++) { + nline = LIST_NEXT(cline, entries); + LIST_REMOVE(cline, entries); + free(cline); + cline = nline; + line_count--; + } + fsm = (fsm == FSM_C) ? FSM_I : FSM_CMD; + break; + case FSM_S: + cline->subst++; + fsm = FSM_CMD; + break; + default: + break; + } + + continue; + } + + if (strcmp(buf, ".\n") == 0) { + fsm = FSM_CMD; + continue; + } + + if (fsm == FSM_A) { + nline = create_line(linepos); + if (cline == NULL) + LIST_INSERT_HEAD(&head, nline, entries); + else + LIST_INSERT_AFTER(cline, nline, entries); + cline = nline; + line_count++; + } else if (fsm == FSM_I) { + nline = create_line(linepos); + if (cline == NULL) { + LIST_INSERT_HEAD(&head, nline, entries); + cline = nline; + } else + LIST_INSERT_BEFORE(cline, nline, entries); + line_count++; + } + } + + next_intuit_at(linepos, p_input_line); + + if (skip_rest_of_patch) { + free_lines(); + return; + } + + write_lines(TMPOUTNAME); + free_lines(); + + ignore_signals(); + if (!check_only) { + if (move_file(TMPOUTNAME, outname) < 0) { + toutkeep = true; + chmod(TMPOUTNAME, filemode); + } else + chmod(outname, filemode); + } + set_signals(1); +} + +static int +get_command(void) +{ + char *p; + LINENUM min_addr; + int fsm; + + min_addr = 0; + fsm = -1; + p = buf; + + /* maybe garbage encountered at end of patch */ + if (!isdigit((unsigned char)*p)) + return -1; + + first_addr = strtolinenum(buf, &p); + second_addr = (*p == ',') ? strtolinenum(p + 1, &p) : -1; + + switch (*p++) { + case 'a': + if (second_addr != -1) + fatal("invalid address at line %ld: %s", + p_input_line, buf); + fsm = FSM_A; + break; + case 'c': + fsm = FSM_C; + min_addr = 1; + break; + case 'd': + fsm = FSM_D; + min_addr = 1; + break; + case 'i': + if (second_addr != -1) + fatal("invalid address at line %ld: %s", + p_input_line, buf); + fsm = FSM_I; + break; + case 's': + if (second_addr != -1) + fatal("unsupported address range at line %ld: %s", + p_input_line, buf); + if (strncmp(p, S_PATTERN, sizeof(S_PATTERN) - 1) != 0) + fatal("unsupported substitution at " + "line %ld: %s", p_input_line, buf); + p += sizeof(S_PATTERN) - 1; + fsm = FSM_S; + min_addr = 1; + break; + default: + return -1; + /* NOTREACHED */ + } + + if (*p != '\n') + return -1; + + if (!valid_addr(first_addr, min_addr) || + (second_addr != -1 && !valid_addr(second_addr, first_addr))) + fatal("invalid address at line %ld: %s", p_input_line, buf); + + cline = get_line(first_addr); + + return fsm; +} + +static void +write_lines(char *filename) +{ + FILE *ofp; + char *p; + struct ed_line *line; + off_t linepos; + + linepos = ftello(pfp); + ofp = fopen(filename, "w"); + if (ofp == NULL) + pfatal("can't create %s", filename); + + LIST_FOREACH(line, &head, entries) { + if (line->src == SRC_INP) { + p = ifetch(line->pos.lineno, 0); + /* Note: string is not NUL terminated. */ + for (; *p != '\n'; p++) + if (line->subst != 0) + line->subst--; + else + putc(*p, ofp); + putc('\n', ofp); + } else if (line->src == SRC_PCH) { + fseeko(pfp, line->pos.seek, SEEK_SET); + if (pgets(buf, sizeof buf, pfp) == NULL) + fatal("unexpected end of file"); + p = buf; + if (line->subst != 0) + for (; *p != '\0' && *p != '\n'; p++) + if (line->subst-- == 0) + break; + fputs(p, ofp); + if (strchr(p, '\n') == NULL) + putc('\n', ofp); + } + } + fclose(ofp); + + /* restore patch file position to match p_input_line */ + fseeko(pfp, linepos, SEEK_SET); +} + +/* initialize list with input file */ +static void +init_lines(void) +{ + struct ed_line *line; + LINENUM i; + + LIST_INIT(&head); + for (i = input_lines; i > 0; i--) { + line = malloc(sizeof(*line)); + if (line == NULL) + fatal("cannot allocate memory"); + line->src = SRC_INP; + line->subst = 0; + line->pos.lineno = i; + LIST_INSERT_HEAD(&head, line, entries); + } + line_count = input_lines; +} + +static void +free_lines(void) +{ + struct ed_line *line; + + while (!LIST_EMPTY(&head)) { + line = LIST_FIRST(&head); + LIST_REMOVE(line, entries); + free(line); + } +} + +static struct ed_line * +get_line(LINENUM lineno) +{ + struct ed_line *line; + LINENUM i; + + if (lineno == 0) + return NULL; + + i = 0; + LIST_FOREACH(line, &head, entries) + if (++i == lineno) + return line; + + return NULL; +} + +static struct ed_line * +create_line(off_t seek) +{ + struct ed_line *line; + + line = malloc(sizeof(*line)); + if (line == NULL) + fatal("cannot allocate memory"); + line->src = SRC_PCH; + line->subst = 0; + line->pos.seek = seek; + + return line; +} + +static int +valid_addr(LINENUM lineno, LINENUM min) +{ + return lineno >= min && lineno <= line_count; +} diff --git a/usr.bin/patch/ed.h b/usr.bin/patch/ed.h new file mode 100644 index 00000000000..86bf9b9afde --- /dev/null +++ b/usr.bin/patch/ed.h @@ -0,0 +1,19 @@ +/* $OpenBSD: ed.h,v 1.1 2015/10/16 07:33:47 tobias Exp $ */ + +/* + * Copyright (c) 2015 Tobias Stoeckmann + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +void do_ed_script(void); diff --git a/usr.bin/patch/patch.c b/usr.bin/patch/patch.c index 0d0c5058484..259524f514c 100644 --- a/usr.bin/patch/patch.c +++ b/usr.bin/patch/patch.c @@ -1,4 +1,4 @@ -/* $OpenBSD: patch.c,v 1.59 2015/10/09 01:37:08 deraadt Exp $ */ +/* $OpenBSD: patch.c,v 1.60 2015/10/16 07:33:47 tobias Exp $ */ /* * patch - a program to apply diffs to original files @@ -43,6 +43,7 @@ #include "inp.h" #include "backupfile.h" #include "pathnames.h" +#include "ed.h" mode_t filemode = 0644; @@ -147,7 +148,7 @@ main(int argc, char *argv[]) const char *tmpdir; char *v; - if (pledge("stdio rpath wpath cpath tmppath fattr proc exec", NULL) == -1) + if (pledge("stdio rpath wpath cpath tmppath fattr", NULL) == -1) perror("pledge"); setvbuf(stdout, NULL, _IOLBF, 0); @@ -218,11 +219,6 @@ main(int argc, char *argv[]) if (outname == NULL) outname = xstrdup(filearg[0]); - /* for ed script just up and do it and exit */ - if (diff_type == ED_DIFF) { - do_ed_script(); - continue; - } /* initialize the patched file */ if (!skip_rest_of_patch) init_output(TMPOUTNAME); @@ -234,6 +230,12 @@ main(int argc, char *argv[]) if (!skip_rest_of_patch) scan_input(filearg[0]); + /* for ed script just up and do it and exit */ + if (diff_type == ED_DIFF) { + do_ed_script(); + continue; + } + /* from here on, open no standard i/o files, because malloc */ /* might misfire and we can't catch it easily */ diff --git a/usr.bin/patch/pch.c b/usr.bin/patch/pch.c index bd434f40bbc..46e5414fc6e 100644 --- a/usr.bin/patch/pch.c +++ b/usr.bin/patch/pch.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pch.c,v 1.53 2015/07/31 00:24:14 millert Exp $ */ +/* $OpenBSD: pch.c,v 1.54 2015/10/16 07:33:47 tobias Exp $ */ /* * patch - a program to apply diffs to original files @@ -45,6 +45,9 @@ /* Patch (diff listing) abstract type. */ +FILE *pfp = NULL; /* patch file pointer */ +LINENUM p_input_line = 0; /* current line # from patch file */ + static off_t p_filesize; /* size of the patch file */ static LINENUM p_first; /* 1st line number */ static LINENUM p_newfirst; /* 1st line number of replacement */ @@ -53,7 +56,6 @@ static LINENUM p_repl_lines; /* # lines in replacement text */ static LINENUM p_end = -1; /* last line in hunk */ static LINENUM p_max; /* max allowed value of p_end */ static LINENUM p_context = 3; /* # of context lines */ -static LINENUM p_input_line = 0; /* current line # from patch file */ static char **p_line = NULL;/* the text of the hunk */ static short *p_len = NULL; /* length of each line */ static char *p_char = NULL; /* +, -, and ! */ @@ -66,18 +68,14 @@ static LINENUM p_sline; /* and the line number for it */ static LINENUM p_hunk_beg; /* line number of current hunk */ static LINENUM p_efake = -1; /* end of faked up lines--don't free */ static LINENUM p_bfake = -1; /* beg of faked up lines */ -static FILE *pfp = NULL; /* patch file pointer */ static char *bestguess = NULL; /* guess at correct filename */ static void grow_hunkmax(void); static int intuit_diff_type(void); -static void next_intuit_at(off_t, LINENUM); static void skip_to(off_t, LINENUM); -static char *pgets(char *, int, FILE *); static char *best_name(const struct file_name *, bool); static char *posix_name(const struct file_name *, bool); static size_t num_components(const char *); -static LINENUM strtolinenum(char *, char **); /* * Prepare to look for the next patch in the patch file. @@ -411,7 +409,7 @@ scan_exit: /* * Remember where this patch ends so we know where to start up again. */ -static void +void next_intuit_at(off_t file_pos, LINENUM file_line) { p_base = file_pos; @@ -1151,7 +1149,7 @@ hunk_done: /* * Input a line from the patch file, worrying about indentation. */ -static char * +char * pgets(char *bf, int sz, FILE *fp) { char *s, *ret = fgets(bf, sz, fp); @@ -1369,82 +1367,6 @@ pch_hunk_beg(void) return p_hunk_beg; } -/* - * Apply an ed script by feeding ed itself. - */ -void -do_ed_script(void) -{ - char *t; - off_t beginning_of_this_line; - FILE *pipefp = NULL; - - if (!skip_rest_of_patch) { - if (copy_file(filearg[0], TMPOUTNAME) < 0) { - unlink(TMPOUTNAME); - fatal("can't create temp file %s", TMPOUTNAME); - } - snprintf(buf, sizeof buf, "%s%s%s", _PATH_ED, - verbose ? " " : " -s ", TMPOUTNAME); - pipefp = popen(buf, "w"); - } - for (;;) { - beginning_of_this_line = ftello(pfp); - if (pgets(buf, sizeof buf, pfp) == NULL) { - next_intuit_at(beginning_of_this_line, p_input_line); - break; - } - p_input_line++; - for (t = buf; isdigit((unsigned char)*t) || *t == ','; t++) - ; - /* POSIX defines allowed commands as {a,c,d,i,s} */ - if (isdigit((unsigned char)*buf) && - *t != '\0' && strchr("acdis", *t) != NULL) { - if (pipefp != NULL) - fputs(buf, pipefp); - if (*t == 's') { - for (;;) { - bool continued = false; - t = buf + strlen(buf) - 1; - while (--t >= buf && *t == '\\') - continued = !continued; - if (!continued || - pgets(buf, sizeof buf, pfp) == NULL) - break; - if (pipefp != NULL) - fputs(buf, pipefp); - } - } else if (*t != 'd') { - while (pgets(buf, sizeof buf, pfp) != NULL) { - p_input_line++; - if (pipefp != NULL) - fputs(buf, pipefp); - if (strEQ(buf, ".\n")) - break; - } - } - } else { - next_intuit_at(beginning_of_this_line, p_input_line); - break; - } - } - if (pipefp == NULL) - return; - fprintf(pipefp, "w\n"); - fprintf(pipefp, "q\n"); - fflush(pipefp); - pclose(pipefp); - ignore_signals(); - if (!check_only) { - if (move_file(TMPOUTNAME, outname) < 0) { - toutkeep = true; - chmod(TMPOUTNAME, filemode); - } else - chmod(outname, filemode); - } - set_signals(1); -} - /* * Choose the name of the file to be patched based on POSIX rules. * NOTE: the POSIX rules are amazingly stupid and we only follow them @@ -1556,7 +1478,7 @@ num_components(const char *path) * character that is not a digit in ENDPTR. If conversion is not * possible, call fatal. */ -static LINENUM +LINENUM strtolinenum(char *nptr, char **endptr) { LINENUM rv; diff --git a/usr.bin/patch/pch.h b/usr.bin/patch/pch.h index b7bf8e8a224..86dde3db79f 100644 --- a/usr.bin/patch/pch.h +++ b/usr.bin/patch/pch.h @@ -1,4 +1,10 @@ -/* $OpenBSD: pch.h,v 1.9 2003/10/31 20:20:45 millert Exp $ */ +void next_intuit_at(off_t, LINENUM); +LINENUM strtolinenum(char *, char **); + +extern FILE *pfp; +extern LINENUM p_input_line; +char *pgets(char *, int, FILE *); +/* $OpenBSD: pch.h,v 1.10 2015/10/16 07:33:47 tobias Exp $ */ /* * patch - a program to apply diffs to original files @@ -53,4 +59,3 @@ LINENUM pch_context(void); LINENUM pch_hunk_beg(void); char pch_char(LINENUM); char *pfetch(LINENUM); -void do_ed_script(void); -- 2.20.1