File: [local] / src / usr.bin / patch / ed.c (download)
Revision 1.4, Mon Dec 2 22:17:32 2019 UTC (4 years, 6 months ago) by jca
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, OPENBSD_7_4_BASE, OPENBSD_7_4, OPENBSD_7_3_BASE, OPENBSD_7_3, OPENBSD_7_2_BASE, OPENBSD_7_2, OPENBSD_7_1_BASE, OPENBSD_7_1, OPENBSD_7_0_BASE, OPENBSD_7_0, OPENBSD_6_9_BASE, OPENBSD_6_9, OPENBSD_6_8_BASE, OPENBSD_6_8, OPENBSD_6_7_BASE, OPENBSD_6_7, HEAD Changes since 1.3: +3 -3 lines
Use getline(3) to handle lines longer than 8192 bytes in patch files
Spotted by jsg@ when working on mesa. Diff tested by sthen@ in
a partial i386 bulk. Input from and ok jsg@ millert@
|
/* $OpenBSD: ed.c,v 1.4 2019/12/02 22:17:32 jca 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 (pgetline(&buf, &bufsz, pfp) == -1)
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--;
}
cline = get_line(first_addr - 1);
fsm = (fsm == FSM_C) ? FSM_A : FSM_CMD;
break;
case FSM_S:
cline->subst++;
fsm = FSM_CMD;
break;
default:
break;
}
continue;
}
if (strcmp(buf, ".\n") == 0) {
fsm = FSM_CMD;
continue;
}
nline = create_line(linepos);
if (cline == NULL)
LIST_INSERT_HEAD(&head, nline, entries);
else if (fsm == FSM_A)
LIST_INSERT_AFTER(cline, nline, entries);
else
LIST_INSERT_BEFORE(cline, nline, entries);
cline = nline;
line_count++;
fsm = FSM_A;
}
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 (pgetline(&buf, &bufsz, pfp) == -1)
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;
}