version 1.12, 2003/07/18 02:00:09 |
version 1.13, 2003/07/21 14:00:41 |
|
|
/* $OpenBSD$ */ |
/* $OpenBSD$ */ |
|
|
#ifndef lint |
#ifndef lint |
static char rcsid[] = "$OpenBSD$"; |
static char rcsid[] = "$OpenBSD$"; |
#endif /* not lint */ |
#endif /* not lint */ |
|
|
#include "EXTERN.h" |
#include "EXTERN.h" |
#include "common.h" |
#include "common.h" |
|
|
#include "util.h" |
#include "util.h" |
#include "backupfile.h" |
#include "backupfile.h" |
|
|
void my_exit(int) __attribute__((noreturn)); |
void my_exit(int) __attribute__((noreturn)); |
|
|
/* Rename a file, copying it if necessary. */ |
/* Rename a file, copying it if necessary. */ |
|
|
int |
int |
move_file(from,to) |
move_file(char *from, char *to) |
char *from, *to; |
|
{ |
{ |
char bakname[MAXPATHLEN]; |
char bakname[MAXPATHLEN], *s; |
char *s; |
int i, fromfd; |
int i; |
|
int fromfd; |
|
|
|
/* to stdout? */ |
/* to stdout? */ |
|
|
if (strEQ(to, "-")) { |
if (strEQ(to, "-")) { |
#ifdef DEBUGGING |
#ifdef DEBUGGING |
if (debug & 4) |
if (debug & 4) |
say("Moving %s to stdout.\n", from); |
say("Moving %s to stdout.\n", from); |
#endif |
#endif |
fromfd = open(from, O_RDONLY); |
fromfd = open(from, O_RDONLY); |
if (fromfd < 0) |
if (fromfd < 0) |
pfatal("internal error, can't reopen %s", from); |
pfatal("internal error, can't reopen %s", from); |
while ((i=read(fromfd, buf, sizeof buf)) > 0) |
while ((i = read(fromfd, buf, sizeof buf)) > 0) |
if (write(1, buf, i) != 1) |
if (write(1, buf, i) != 1) |
pfatal("write failed"); |
pfatal("write failed"); |
close(fromfd); |
close(fromfd); |
return 0; |
return 0; |
} |
} |
|
if (origprae) { |
|
if (strlcpy(bakname, origprae, sizeof(bakname)) >= sizeof(bakname) || |
|
strlcat(bakname, to, sizeof(bakname)) >= sizeof(bakname)) |
|
fatal("filename %s too long for buffer\n", origprae); |
|
} else { |
|
char *backupname = find_backup_file_name(to); |
|
if (backupname == (char *) 0) |
|
fatal("out of memory\n"); |
|
if (strlcpy(bakname, backupname, sizeof(bakname)) >= sizeof(bakname)) |
|
fatal("filename %s too long for buffer\n", backupname); |
|
free(backupname); |
|
} |
|
|
if (origprae) { |
if (stat(to, &filestat) == 0) { /* output file exists */ |
if (strlcpy(bakname, origprae, sizeof(bakname)) >= sizeof(bakname) || |
dev_t to_device = filestat.st_dev; |
strlcat(bakname, to, sizeof(bakname)) >= sizeof(bakname)) |
ino_t to_inode = filestat.st_ino; |
fatal("filename %s too long for buffer\n", origprae); |
char *simplename = bakname; |
} else { |
|
#ifndef NODIR |
|
char *backupname = find_backup_file_name(to); |
|
if (backupname == (char *) 0) |
|
fatal("out of memory\n"); |
|
if (strlcpy(bakname, backupname, sizeof(bakname)) >= sizeof(bakname)) |
|
fatal("filename %s too long for buffer\n", backupname); |
|
free(backupname); |
|
#else /* NODIR */ |
|
if (strlcpy(bakname, to, sizeof(bakname)) >= sizeof(bakname) || |
|
strlcat(bakname, simple_backup_suffix, sizeof(bakname)) >= sizeof(bakname)) |
|
fatal("filename %s too long for buffer\n", to); |
|
#endif /* NODIR */ |
|
} |
|
|
|
if (stat(to, &filestat) == 0) { /* output file exists */ |
for (s = bakname; *s; s++) { |
dev_t to_device = filestat.st_dev; |
if (*s == '/') |
ino_t to_inode = filestat.st_ino; |
simplename = s + 1; |
char *simplename = bakname; |
} |
|
|
for (s=bakname; *s; s++) { |
/* |
if (*s == '/') |
* Find a backup name that is not the same file. Change the |
simplename = s+1; |
* first lowercase char into uppercase; if that isn't |
} |
* sufficient, chop off the first char and try again. |
/* Find a backup name that is not the same file. |
*/ |
Change the first lowercase char into uppercase; |
while (stat(bakname, &filestat) == 0 && |
if that isn't sufficient, chop off the first char and try again. */ |
to_device == filestat.st_dev && to_inode == filestat.st_ino) { |
while (stat(bakname, &filestat) == 0 && |
/* Skip initial non-lowercase chars. */ |
to_device == filestat.st_dev && to_inode == filestat.st_ino) { |
for (s = simplename; *s && !islower(*s); s++) |
/* Skip initial non-lowercase chars. */ |
; |
for (s=simplename; *s && !islower(*s); s++) ; |
if (*s) |
if (*s) |
*s = toupper(*s); |
*s = toupper(*s); |
else |
else |
memmove(simplename, simplename + 1, |
memmove(simplename, simplename+1, strlen(simplename+1)+1); |
strlen(simplename + 1) + 1); |
} |
} |
while (unlink(bakname) >= 0) ; /* while() is for benefit of Eunice */ |
unlink(bakname); |
|
|
#ifdef DEBUGGING |
#ifdef DEBUGGING |
if (debug & 4) |
if (debug & 4) |
say("Moving %s to %s.\n", to, bakname); |
say("Moving %s to %s.\n", to, bakname); |
#endif |
#endif |
if (link(to, bakname) < 0) { |
if (link(to, bakname) < 0) { |
/* Maybe `to' is a symlink into a different file system. |
/* |
Copying replaces the symlink with a file; using rename |
* Maybe `to' is a symlink into a different file |
would be better. */ |
* system. Copying replaces the symlink with a file; |
int tofd; |
* using rename would be better. |
int bakfd; |
*/ |
|
int tofd, bakfd; |
|
|
bakfd = creat(bakname, 0666); |
bakfd = creat(bakname, 0666); |
if (bakfd < 0) { |
if (bakfd < 0) { |
say("Can't backup %s, output is in %s: %s\n", to, from, |
say("Can't backup %s, output is in %s: %s\n", |
strerror(errno)); |
to, from, strerror(errno)); |
return -1; |
return -1; |
} |
} |
tofd = open(to, O_RDONLY); |
tofd = open(to, O_RDONLY); |
if (tofd < 0) |
if (tofd < 0) |
pfatal("internal error, can't open %s", to); |
pfatal("internal error, can't open %s", to); |
while ((i=read(tofd, buf, sizeof buf)) > 0) |
while ((i = read(tofd, buf, sizeof buf)) > 0) |
if (write(bakfd, buf, i) != i) |
if (write(bakfd, buf, i) != i) |
pfatal("write failed"); |
pfatal("write failed"); |
close(tofd); |
close(tofd); |
close(bakfd); |
close(bakfd); |
|
} |
|
unlink(to); |
} |
} |
while (unlink(to) >= 0) ; |
|
} |
|
#ifdef DEBUGGING |
#ifdef DEBUGGING |
if (debug & 4) |
if (debug & 4) |
say("Moving %s to %s.\n", from, to); |
say("Moving %s to %s.\n", from, to); |
#endif |
#endif |
if (link(from, to) < 0) { /* different file system? */ |
if (link(from, to) < 0) { /* different file system? */ |
int tofd; |
int tofd; |
|
|
tofd = creat(to, 0666); |
tofd = creat(to, 0666); |
if (tofd < 0) { |
if (tofd < 0) { |
say("Can't create %s, output is in %s: %s\n", |
say("Can't create %s, output is in %s: %s\n", |
to, from, strerror(errno)); |
to, from, strerror(errno)); |
return -1; |
return -1; |
|
} |
|
fromfd = open(from, O_RDONLY); |
|
if (fromfd < 0) |
|
pfatal("internal error, can't reopen %s", from); |
|
while ((i = read(fromfd, buf, sizeof buf)) > 0) |
|
if (write(tofd, buf, i) != i) |
|
pfatal("write failed"); |
|
close(fromfd); |
|
close(tofd); |
} |
} |
fromfd = open(from, O_RDONLY); |
unlink(from); |
if (fromfd < 0) |
return 0; |
pfatal("internal error, can't reopen %s", from); |
|
while ((i=read(fromfd, buf, sizeof buf)) > 0) |
|
if (write(tofd, buf, i) != i) |
|
pfatal("write failed"); |
|
close(fromfd); |
|
close(tofd); |
|
} |
|
unlink(from); |
|
return 0; |
|
} |
} |
|
|
/* Copy a file. */ |
/* |
|
* Copy a file. |
|
*/ |
void |
void |
copy_file(from,to) |
copy_file(char *from, char *to) |
char *from, *to; |
|
{ |
{ |
int tofd; |
int tofd, fromfd, i; |
int fromfd; |
|
int i; |
|
|
|
tofd = creat(to, 0666); |
tofd = creat(to, 0666); |
if (tofd < 0) |
if (tofd < 0) |
pfatal("can't create %s", to); |
pfatal("can't create %s", to); |
fromfd = open(from, O_RDONLY); |
fromfd = open(from, O_RDONLY); |
if (fromfd < 0) |
if (fromfd < 0) |
pfatal("internal error, can't reopen %s", from); |
pfatal("internal error, can't reopen %s", from); |
while ((i=read(fromfd, buf, sizeof buf)) > 0) |
while ((i = read(fromfd, buf, sizeof buf)) > 0) |
if (write(tofd, buf, i) != i) |
if (write(tofd, buf, i) != i) |
pfatal("write to %s failed", to); |
pfatal("write to %s failed", to); |
close(fromfd); |
close(fromfd); |
close(tofd); |
close(tofd); |
} |
} |
|
|
/* Allocate a unique area for a string. */ |
/* |
|
* Allocate a unique area for a string. |
|
*/ |
char * |
char * |
savestr(s) |
savestr(char *s) |
char *s; |
|
{ |
{ |
char *rv; |
char *rv, *t; |
char *t; |
|
|
|
if (!s) |
if (!s) |
s = "Oops"; |
s = "Oops"; |
t = s; |
t = s; |
while (*t++); |
while (*t++) |
rv = malloc((MEM) (t - s)); |
; |
if (rv == Nullch) { |
rv = malloc((MEM) (t - s)); |
if (using_plan_a) |
if (rv == Nullch) { |
out_of_mem = TRUE; |
if (using_plan_a) |
else |
out_of_mem = TRUE; |
fatal("out of memory\n"); |
else |
} |
fatal("out of memory\n"); |
else { |
} else { |
t = rv; |
t = rv; |
while ((*t++ = *s++)) |
while ((*t++ = *s++)) |
; |
; |
} |
} |
return rv; |
return rv; |
} |
} |
|
|
/* Vanilla terminal output (buffered). */ |
/* |
|
* Vanilla terminal output (buffered). |
|
*/ |
void |
void |
say(char *fmt, ...) |
say(char *fmt, ...) |
{ |
{ |
va_list ap; |
va_list ap; |
|
|
va_start(ap, fmt); |
va_start(ap, fmt); |
vfprintf(stderr, fmt, ap); |
vfprintf(stderr, fmt, ap); |
va_end(ap); |
va_end(ap); |
fflush(stderr); |
fflush(stderr); |
} |
} |
|
|
/* Terminal output, pun intended. */ |
/* |
|
* Terminal output, pun intended. |
|
*/ |
void |
void |
fatal(char *fmt, ...) |
fatal(char *fmt, ...) |
{ |
{ |
va_list ap; |
va_list ap; |
|
|
va_start(ap, fmt); |
va_start(ap, fmt); |
fprintf(stderr, "patch: **** "); |
fprintf(stderr, "patch: **** "); |
vfprintf(stderr, fmt, ap); |
vfprintf(stderr, fmt, ap); |
va_end(ap); |
va_end(ap); |
my_exit(1); |
my_exit(1); |
} |
} |
|
|
/* Say something from patch, something from the system, then silence . . . */ |
/* |
|
* Say something from patch, something from the system, then silence . . . |
|
*/ |
void |
void |
pfatal(char *fmt, ...) |
pfatal(char *fmt, ...) |
{ |
{ |
va_list ap; |
va_list ap; |
int errnum = errno; |
int errnum = errno; |
|
|
fprintf(stderr, "patch: **** "); |
fprintf(stderr, "patch: **** "); |
va_start(ap, fmt); |
va_start(ap, fmt); |
vfprintf(stderr, fmt, ap); |
vfprintf(stderr, fmt, ap); |
va_end(ap); |
va_end(ap); |
fprintf(stderr, ": %s\n", strerror(errnum)); |
fprintf(stderr, ": %s\n", strerror(errnum)); |
my_exit(1); |
my_exit(1); |
} |
} |
|
|
/* Get a response from the user, somehow or other. */ |
/* |
|
* Get a response from the user, somehow or other. |
|
*/ |
void |
void |
ask(char *fmt, ...) |
ask(char *fmt, ...) |
{ |
{ |
va_list ap; |
va_list ap; |
int ttyfd; |
int ttyfd, r; |
int r; |
bool tty2 = isatty(2); |
bool tty2 = isatty(2); |
|
|
|
va_start(ap, fmt); |
va_start(ap, fmt); |
vsnprintf(buf, sizeof buf, fmt, ap); |
vsnprintf(buf, sizeof buf, fmt, ap); |
va_end(ap); |
va_end(ap); |
fflush(stderr); |
fflush(stderr); |
write(2, buf, strlen(buf)); |
write(2, buf, strlen(buf)); |
if (tty2) { /* might be redirected to a file */ |
if (tty2) { |
r = read(2, buf, sizeof buf); |
/* might be redirected to a file */ |
} else if (isatty(1)) { /* this may be new file output */ |
r = read(2, buf, sizeof buf); |
fflush(stdout); |
} else if (isatty(1)) { /* this may be new file output */ |
write(1, buf, strlen(buf)); |
fflush(stdout); |
r = read(1, buf, sizeof buf); |
write(1, buf, strlen(buf)); |
} else if ((ttyfd = open(_PATH_TTY, O_RDWR)) >= 0 && isatty(ttyfd)) { |
r = read(1, buf, sizeof buf); |
/* might be deleted or unwriteable */ |
} else if ((ttyfd = open(_PATH_TTY, O_RDWR)) >= 0 && isatty(ttyfd)) { |
write(ttyfd, buf, strlen(buf)); |
/* might be deleted or unwriteable */ |
r = read(ttyfd, buf, sizeof buf); |
write(ttyfd, buf, strlen(buf)); |
close(ttyfd); |
r = read(ttyfd, buf, sizeof buf); |
} else if (isatty(0)) { /* this is probably patch input */ |
close(ttyfd); |
fflush(stdin); |
} else if (isatty(0)) { /* this is probably patch input */ |
write(0, buf, strlen(buf)); |
fflush(stdin); |
r = read(0, buf, sizeof buf); |
write(0, buf, strlen(buf)); |
} else { /* no terminal at all--default it */ |
r = read(0, buf, sizeof buf); |
buf[0] = '\n'; |
} else { /* no terminal at all--default it */ |
r = 1; |
buf[0] = '\n'; |
} |
r = 1; |
if (r <= 0) |
} |
buf[0] = 0; |
if (r <= 0) |
else |
buf[0] = 0; |
buf[r] = '\0'; |
else |
if (!tty2) |
buf[r] = '\0'; |
say(buf); |
if (!tty2) |
|
say(buf); |
} |
} |
|
|
/* How to handle certain events when not in a critical region. */ |
/* |
|
* How to handle certain events when not in a critical region. |
|
*/ |
void |
void |
set_signals(reset) |
set_signals(int reset) |
int reset; |
|
{ |
{ |
#ifndef lint |
#ifndef lint |
static sig_t hupval, intval; |
static sig_t hupval, intval; |
|
|
if (!reset) { |
if (!reset) { |
hupval = signal(SIGHUP, SIG_IGN); |
hupval = signal(SIGHUP, SIG_IGN); |
if (hupval != SIG_IGN) |
if (hupval != SIG_IGN) |
hupval = (sig_t)my_exit; |
hupval = (sig_t) my_exit; |
intval = signal(SIGINT, SIG_IGN); |
intval = signal(SIGINT, SIG_IGN); |
if (intval != SIG_IGN) |
if (intval != SIG_IGN) |
intval = (sig_t)my_exit; |
intval = (sig_t) my_exit; |
} |
} |
signal(SIGHUP, hupval); |
signal(SIGHUP, hupval); |
signal(SIGINT, intval); |
signal(SIGINT, intval); |
#endif |
#endif |
} |
} |
|
|
/* How to handle certain events when in a critical region. */ |
/* |
|
* How to handle certain events when in a critical region. |
|
*/ |
void |
void |
ignore_signals() |
ignore_signals(void) |
{ |
{ |
#ifndef lint |
#ifndef lint |
signal(SIGHUP, SIG_IGN); |
signal(SIGHUP, SIG_IGN); |
signal(SIGINT, SIG_IGN); |
signal(SIGINT, SIG_IGN); |
#endif |
#endif |
} |
} |
|
|
/* Make sure we'll have the directories to create a file. |
/* |
If `striplast' is TRUE, ignore the last element of `filename'. */ |
* Make sure we'll have the directories to create a file. If `striplast' is |
|
* TRUE, ignore the last element of `filename'. |
|
*/ |
|
|
void |
void |
makedirs(filename,striplast) |
makedirs(char *filename, bool striplast) |
char *filename; |
|
bool striplast; |
|
{ |
{ |
char *tmpbuf; |
char *tmpbuf; |
|
|
if ((tmpbuf = strdup(filename)) == NULL) |
if ((tmpbuf = strdup(filename)) == NULL) |
fatal("out of memory\n"); |
fatal("out of memory\n"); |
|
|
if (striplast) { |
if (striplast) { |
char *s = strrchr(tmpbuf, '/'); |
char *s = strrchr(tmpbuf, '/'); |
if (s == NULL) |
if (s == NULL) |
return; /* nothing to be done */ |
return; /* nothing to be done */ |
*s = '\0'; |
*s = '\0'; |
} |
} |
|
strlcpy(buf, "/bin/mkdir -p ", sizeof buf); |
|
if (strlcat(buf, tmpbuf, sizeof(buf)) >= sizeof(buf)) |
|
fatal("buffer too small to hold %.20s...\n", tmpbuf); |
|
|
strlcpy(buf, "/bin/mkdir -p ", sizeof buf); |
if (system(buf)) |
if (strlcat(buf, tmpbuf, sizeof(buf)) >= sizeof(buf)) |
pfatal("%.40s failed", buf); |
fatal("buffer too small to hold %.20s...\n", tmpbuf); |
|
|
|
if (system(buf)) |
|
pfatal("%.40s failed", buf); |
|
} |
} |
|
|
/* Make filenames more reasonable. */ |
/* |
|
* Make filenames more reasonable. |
|
*/ |
char * |
char * |
fetchname(at,strip_leading,assume_exists) |
fetchname(char *at, int strip_leading, int assume_exists) |
char *at; |
|
int strip_leading; |
|
int assume_exists; |
|
{ |
{ |
char *fullname; |
char *fullname, *name, *t, tmpbuf[200]; |
char *name; |
int sleading = strip_leading; |
char *t; |
|
char tmpbuf[200]; |
|
int sleading = strip_leading; |
|
|
|
if (!at || *at == '\0') |
if (!at || *at == '\0') |
return Nullch; |
return Nullch; |
while (isspace(*at)) |
while (isspace(*at)) |
at++; |
at++; |
#ifdef DEBUGGING |
#ifdef DEBUGGING |
if (debug & 128) |
if (debug & 128) |
say("fetchname %s %d %d\n",at,strip_leading,assume_exists); |
say("fetchname %s %d %d\n", at, strip_leading, assume_exists); |
#endif |
#endif |
if (strnEQ(at, "/dev/null", 9)) /* so files can be created by diffing */ |
if (strnEQ(at, "/dev/null", 9)) /* so files can be created by diffing */ |
return Nullch; /* against /dev/null. */ |
return Nullch; /* against /dev/null. */ |
name = fullname = t = savestr(at); |
name = fullname = t = savestr(at); |
|
|
/* Strip off up to `sleading' leading slashes and null terminate. */ |
/* Strip off up to `sleading' leading slashes and null terminate. */ |
for (; *t && !isspace(*t); t++) |
for (; *t && !isspace(*t); t++) |
if (*t == '/') |
if (*t == '/') |
if (--sleading >= 0) |
if (--sleading >= 0) |
name = t+1; |
name = t + 1; |
*t = '\0'; |
*t = '\0'; |
|
|
/* If no -p option was given (957 is the default value!), |
/* |
we were given a relative pathname, |
* If no -p option was given (957 is the default value!), we were |
and the leading directories that we just stripped off all exist, |
* given a relative pathname, and the leading directories that we |
put them back on. */ |
* just stripped off all exist, put them back on. |
if (strip_leading == 957 && name != fullname && *fullname != '/') { |
*/ |
name[-1] = '\0'; |
if (strip_leading == 957 && name != fullname && *fullname != '/') { |
if (stat(fullname, &filestat) == 0 && S_ISDIR (filestat.st_mode)) { |
name[-1] = '\0'; |
name[-1] = '/'; |
if (stat(fullname, &filestat) == 0 && S_ISDIR(filestat.st_mode)) { |
name=fullname; |
name[-1] = '/'; |
|
name = fullname; |
|
} |
} |
} |
} |
name = savestr(name); |
|
free(fullname); |
|
|
name = savestr(name); |
if (stat(name, &filestat) && !assume_exists) { |
free(fullname); |
char *filebase = basename(name); |
|
char *filedir = dirname(name); |
|
|
if (stat(name, &filestat) && !assume_exists) { |
#define try(f, a1, a2, a3) \ |
char *filebase = basename(name); |
(snprintf(tmpbuf, sizeof tmpbuf, f, a1, a2, a3), stat(tmpbuf, &filestat) == 0) |
char *filedir = dirname(name); |
|
|
|
#define try(f, a1, a2, a3) (snprintf(tmpbuf, sizeof tmpbuf, f, a1, a2, a3), stat(tmpbuf, &filestat) == 0) |
if (try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) || |
if ( try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) |
try("%s/RCS/%s%s", filedir, filebase, "") || |
|| try("%s/RCS/%s%s", filedir, filebase, "") |
try("%s/%s%s", filedir, filebase, RCSSUFFIX) || |
|| try( "%s/%s%s", filedir, filebase, RCSSUFFIX) |
try("%s/SCCS/%s%s", filedir, SCCSPREFIX, filebase) || |
|| try("%s/SCCS/%s%s", filedir, SCCSPREFIX, filebase) |
try("%s/%s%s", filedir, SCCSPREFIX, filebase)) |
|| try( "%s/%s%s", filedir, SCCSPREFIX, filebase)) |
return name; |
return name; |
free(name); |
free(name); |
name = Nullch; |
name = Nullch; |
} |
} |
return name; |
|
|
return name; |
|
} |
} |
|
|
void |
void |
version() |
version(void) |
{ |
{ |
fprintf(stderr, "Patch version 2.0-12u8-OpenBSD\n"); |
fprintf(stderr, "Patch version 2.0-12u8-OpenBSD\n"); |
my_exit(0); |
my_exit(0); |
} |
} |