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);

Reply via email to