-# $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 <bsd.prog.mk>
--- /dev/null
+/* $OpenBSD: ed.c,v 1.1 2015/10/16 07:33:47 tobias Exp $ */
+
+/*
+ * Copyright (c) 2015 Tobias Stoeckmann <tobias@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;
+}
--- /dev/null
+/* $OpenBSD: ed.h,v 1.1 2015/10/16 07:33:47 tobias Exp $ */
+
+/*
+ * Copyright (c) 2015 Tobias Stoeckmann <tobias@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);
-/* $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
#include "inp.h"
#include "backupfile.h"
#include "pathnames.h"
+#include "ed.h"
mode_t filemode = 0644;
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);
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);
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 */
-/* $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
/* 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 */
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 ! */
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.
/*
* 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;
/*
* 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);
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
* 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;
-/* $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
LINENUM pch_hunk_beg(void);
char pch_char(LINENUM);
char *pfetch(LINENUM);
-void do_ed_script(void);