On Tue, Oct 13, 2015 at 06:43:31PM +0200, Tobias Stoeckmann wrote: > - Check for our new /.// substitution expression > - Be more restrictive about command parsing, so we do not > allow commands like "10insert" instead of "10i".
And now, fix regression which I introduced with that diff, of course it takes strncmp() now instead of strcmp() for s-subsitution. The latest regress test, t17, covers this. This version passes just fine. Thought about changing the fatal-calls to return -1 in get_command, so we would be more relaxed towards garbage at the end of file that looks like ed commands, e.g. "1,1a" (illegal, "a" takes only one address, not a range). The problem would be though that patch gets straight back into the ed routine because it says "looks like an ed command", which basically means an endless loop. Let's see if there is an ed-user who'll complain about fatal() usage here. Q: What does patch currently do with these illegal ed commands? A: It passes it to ed. And ed continues after printing "?"... Index: Makefile =================================================================== RCS file: /cvs/src/usr.bin/patch/Makefile,v retrieving revision 1.4 diff -u -p -r1.4 Makefile --- Makefile 16 May 2005 15:22:46 -0000 1.4 +++ Makefile 13 Oct 2015 21:31:52 -0000 @@ -1,6 +1,6 @@ # $OpenBSD: Makefile,v 1.4 2005/05/16 15:22:46 espie 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 <bsd.prog.mk> Index: ed.c =================================================================== RCS file: ed.c diff -N ed.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ ed.c 13 Oct 2015 21:31:52 -0000 @@ -0,0 +1,342 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2015 Tobias Stoeckmann <tob...@openbsd.org> + * + * 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 <sys/queue.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#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; +} Index: ed.h =================================================================== RCS file: ed.h diff -N ed.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ ed.h 13 Oct 2015 21:31:52 -0000 @@ -0,0 +1,19 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2015 Tobias Stoeckmann <tob...@openbsd.org> + * + * 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); Index: patch.c =================================================================== RCS file: /cvs/src/usr.bin/patch/patch.c,v retrieving revision 1.59 diff -u -p -r1.59 patch.c --- patch.c 9 Oct 2015 01:37:08 -0000 1.59 +++ patch.c 13 Oct 2015 21:31:52 -0000 @@ -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); @@ -233,6 +229,12 @@ main(int argc, char *argv[]) /* find out where all the lines are */ 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 */ Index: pch.c =================================================================== RCS file: /cvs/src/usr.bin/patch/pch.c,v retrieving revision 1.53 diff -u -p -r1.53 pch.c --- pch.c 31 Jul 2015 00:24:14 -0000 1.53 +++ pch.c 13 Oct 2015 21:31:53 -0000 @@ -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 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 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); @@ -1370,82 +1368,6 @@ pch_hunk_beg(void) } /* - * 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 * if the user specified --posix or set POSIXLY_CORRECT. @@ -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; Index: pch.h =================================================================== RCS file: /cvs/src/usr.bin/patch/pch.h,v retrieving revision 1.9 diff -u -p -r1.9 pch.h --- pch.h 31 Oct 2003 20:20:45 -0000 1.9 +++ pch.h 13 Oct 2015 21:31:53 -0000 @@ -1,3 +1,9 @@ +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.9 2003/10/31 20:20:45 millert Exp $ */ /* @@ -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);