1246074Sgabor/*- 2246074Sgabor * Copyright 1986, Larry Wall 3246074Sgabor * 4246074Sgabor * Redistribution and use in source and binary forms, with or without 5246074Sgabor * modification, are permitted provided that the following condition is met: 6246074Sgabor * 1. Redistributions of source code must retain the above copyright notice, 7246074Sgabor * this condition and the following disclaimer. 8246074Sgabor * 9246074Sgabor * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY 10246074Sgabor * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 11246074Sgabor * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 12246074Sgabor * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 13246074Sgabor * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 14246074Sgabor * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 15246074Sgabor * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 16246074Sgabor * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 17246074Sgabor * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 18246074Sgabor * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 19246074Sgabor * SUCH DAMAGE. 20246074Sgabor * 21246074Sgabor * patch - a program to apply diffs to original files 22246074Sgabor * 23246074Sgabor * -C option added in 1998, original code by Marc Espie, based on FreeBSD 24246074Sgabor * behaviour 25246074Sgabor * 26246091Sdelphij * $OpenBSD: util.c,v 1.35 2010/07/24 01:10:12 ray Exp $ 27246091Sdelphij * $FreeBSD$ 28246074Sgabor */ 29246074Sgabor 30246074Sgabor#include <sys/param.h> 31246074Sgabor#include <sys/stat.h> 32246074Sgabor 33246074Sgabor#include <ctype.h> 34246074Sgabor#include <errno.h> 35246074Sgabor#include <fcntl.h> 36246074Sgabor#include <libgen.h> 37246074Sgabor#include <paths.h> 38246074Sgabor#include <signal.h> 39246074Sgabor#include <stdarg.h> 40246074Sgabor#include <stdlib.h> 41246074Sgabor#include <stdio.h> 42246074Sgabor#include <string.h> 43246074Sgabor#include <unistd.h> 44246074Sgabor 45246074Sgabor#include "common.h" 46246074Sgabor#include "util.h" 47246074Sgabor#include "backupfile.h" 48246074Sgabor#include "pathnames.h" 49246074Sgabor 50246074Sgabor/* Rename a file, copying it if necessary. */ 51246074Sgabor 52246074Sgaborint 53246074Sgabormove_file(const char *from, const char *to) 54246074Sgabor{ 55246074Sgabor int fromfd; 56246074Sgabor ssize_t i; 57246074Sgabor 58246074Sgabor /* to stdout? */ 59246074Sgabor 60246074Sgabor if (strEQ(to, "-")) { 61246074Sgabor#ifdef DEBUGGING 62246074Sgabor if (debug & 4) 63246074Sgabor say("Moving %s to stdout.\n", from); 64246074Sgabor#endif 65246074Sgabor fromfd = open(from, O_RDONLY); 66246074Sgabor if (fromfd < 0) 67246074Sgabor pfatal("internal error, can't reopen %s", from); 68246074Sgabor while ((i = read(fromfd, buf, buf_size)) > 0) 69246074Sgabor if (write(STDOUT_FILENO, buf, i) != i) 70246074Sgabor pfatal("write failed"); 71246074Sgabor close(fromfd); 72246074Sgabor return 0; 73246074Sgabor } 74246074Sgabor if (backup_file(to) < 0) { 75246074Sgabor say("Can't backup %s, output is in %s: %s\n", to, from, 76246074Sgabor strerror(errno)); 77246074Sgabor return -1; 78246074Sgabor } 79246074Sgabor#ifdef DEBUGGING 80246074Sgabor if (debug & 4) 81246074Sgabor say("Moving %s to %s.\n", from, to); 82246074Sgabor#endif 83246074Sgabor if (rename(from, to) < 0) { 84246074Sgabor if (errno != EXDEV || copy_file(from, to) < 0) { 85246074Sgabor say("Can't create %s, output is in %s: %s\n", 86246074Sgabor to, from, strerror(errno)); 87246074Sgabor return -1; 88246074Sgabor } 89246074Sgabor } 90246074Sgabor return 0; 91246074Sgabor} 92246074Sgabor 93246074Sgabor/* Backup the original file. */ 94246074Sgabor 95246074Sgaborint 96246074Sgaborbackup_file(const char *orig) 97246074Sgabor{ 98246074Sgabor struct stat filestat; 99246074Sgabor char bakname[MAXPATHLEN], *s, *simplename; 100246074Sgabor dev_t orig_device; 101246074Sgabor ino_t orig_inode; 102246074Sgabor 103246074Sgabor if (backup_type == none || stat(orig, &filestat) != 0) 104246074Sgabor return 0; /* nothing to do */ 105246074Sgabor /* 106246074Sgabor * If the user used zero prefixes or suffixes, then 107246074Sgabor * he doesn't want backups. Yet we have to remove 108246074Sgabor * orig to break possible hardlinks. 109246074Sgabor */ 110246074Sgabor if ((origprae && *origprae == 0) || *simple_backup_suffix == 0) { 111246074Sgabor unlink(orig); 112246074Sgabor return 0; 113246074Sgabor } 114246074Sgabor orig_device = filestat.st_dev; 115246074Sgabor orig_inode = filestat.st_ino; 116246074Sgabor 117246074Sgabor if (origprae) { 118246074Sgabor if (strlcpy(bakname, origprae, sizeof(bakname)) >= sizeof(bakname) || 119246074Sgabor strlcat(bakname, orig, sizeof(bakname)) >= sizeof(bakname)) 120246074Sgabor fatal("filename %s too long for buffer\n", origprae); 121246074Sgabor } else { 122246074Sgabor if ((s = find_backup_file_name(orig)) == NULL) 123246074Sgabor fatal("out of memory\n"); 124246074Sgabor if (strlcpy(bakname, s, sizeof(bakname)) >= sizeof(bakname)) 125246074Sgabor fatal("filename %s too long for buffer\n", s); 126246074Sgabor free(s); 127246074Sgabor } 128246074Sgabor 129246074Sgabor if ((simplename = strrchr(bakname, '/')) != NULL) 130246074Sgabor simplename = simplename + 1; 131246074Sgabor else 132246074Sgabor simplename = bakname; 133246074Sgabor 134246074Sgabor /* 135246074Sgabor * Find a backup name that is not the same file. Change the 136246074Sgabor * first lowercase char into uppercase; if that isn't 137246074Sgabor * sufficient, chop off the first char and try again. 138246074Sgabor */ 139246074Sgabor while (stat(bakname, &filestat) == 0 && 140246074Sgabor orig_device == filestat.st_dev && orig_inode == filestat.st_ino) { 141246074Sgabor /* Skip initial non-lowercase chars. */ 142246074Sgabor for (s = simplename; *s && !islower((unsigned char)*s); s++) 143246074Sgabor ; 144246074Sgabor if (*s) 145246074Sgabor *s = toupper((unsigned char)*s); 146246074Sgabor else 147246074Sgabor memmove(simplename, simplename + 1, 148246074Sgabor strlen(simplename + 1) + 1); 149246074Sgabor } 150246074Sgabor#ifdef DEBUGGING 151246074Sgabor if (debug & 4) 152246074Sgabor say("Moving %s to %s.\n", orig, bakname); 153246074Sgabor#endif 154246074Sgabor if (rename(orig, bakname) < 0) { 155246074Sgabor if (errno != EXDEV || copy_file(orig, bakname) < 0) 156246074Sgabor return -1; 157246074Sgabor } 158246074Sgabor return 0; 159246074Sgabor} 160246074Sgabor 161246074Sgabor/* 162246074Sgabor * Copy a file. 163246074Sgabor */ 164246074Sgaborint 165246074Sgaborcopy_file(const char *from, const char *to) 166246074Sgabor{ 167246074Sgabor int tofd, fromfd; 168246074Sgabor ssize_t i; 169246074Sgabor 170246074Sgabor tofd = open(to, O_CREAT|O_TRUNC|O_WRONLY, 0666); 171246074Sgabor if (tofd < 0) 172246074Sgabor return -1; 173246074Sgabor fromfd = open(from, O_RDONLY, 0); 174246074Sgabor if (fromfd < 0) 175246074Sgabor pfatal("internal error, can't reopen %s", from); 176246074Sgabor while ((i = read(fromfd, buf, buf_size)) > 0) 177246074Sgabor if (write(tofd, buf, i) != i) 178246074Sgabor pfatal("write to %s failed", to); 179246074Sgabor close(fromfd); 180246074Sgabor close(tofd); 181246074Sgabor return 0; 182246074Sgabor} 183246074Sgabor 184246074Sgabor/* 185246074Sgabor * Allocate a unique area for a string. 186246074Sgabor */ 187246074Sgaborchar * 188246074Sgaborsavestr(const char *s) 189246074Sgabor{ 190246074Sgabor char *rv; 191246074Sgabor 192246074Sgabor if (!s) 193246074Sgabor s = "Oops"; 194246074Sgabor rv = strdup(s); 195246074Sgabor if (rv == NULL) { 196246074Sgabor if (using_plan_a) 197246074Sgabor out_of_mem = true; 198246074Sgabor else 199246074Sgabor fatal("out of memory\n"); 200246074Sgabor } 201246074Sgabor return rv; 202246074Sgabor} 203246074Sgabor 204246074Sgabor/* 205246074Sgabor * Vanilla terminal output (buffered). 206246074Sgabor */ 207246074Sgaborvoid 208246074Sgaborsay(const char *fmt, ...) 209246074Sgabor{ 210246074Sgabor va_list ap; 211246074Sgabor 212246074Sgabor va_start(ap, fmt); 213246091Sdelphij vfprintf(stdout, fmt, ap); 214246074Sgabor va_end(ap); 215246091Sdelphij fflush(stdout); 216246074Sgabor} 217246074Sgabor 218246074Sgabor/* 219246074Sgabor * Terminal output, pun intended. 220246074Sgabor */ 221246074Sgaborvoid 222246074Sgaborfatal(const char *fmt, ...) 223246074Sgabor{ 224246074Sgabor va_list ap; 225246074Sgabor 226246074Sgabor va_start(ap, fmt); 227246074Sgabor fprintf(stderr, "patch: **** "); 228246074Sgabor vfprintf(stderr, fmt, ap); 229246074Sgabor va_end(ap); 230246074Sgabor my_exit(2); 231246074Sgabor} 232246074Sgabor 233246074Sgabor/* 234246074Sgabor * Say something from patch, something from the system, then silence . . . 235246074Sgabor */ 236246074Sgaborvoid 237246074Sgaborpfatal(const char *fmt, ...) 238246074Sgabor{ 239246074Sgabor va_list ap; 240246074Sgabor int errnum = errno; 241246074Sgabor 242246074Sgabor fprintf(stderr, "patch: **** "); 243246074Sgabor va_start(ap, fmt); 244246074Sgabor vfprintf(stderr, fmt, ap); 245246074Sgabor va_end(ap); 246246074Sgabor fprintf(stderr, ": %s\n", strerror(errnum)); 247246074Sgabor my_exit(2); 248246074Sgabor} 249246074Sgabor 250246074Sgabor/* 251246074Sgabor * Get a response from the user via /dev/tty 252246074Sgabor */ 253246074Sgaborvoid 254246074Sgaborask(const char *fmt, ...) 255246074Sgabor{ 256246074Sgabor va_list ap; 257246074Sgabor ssize_t nr = 0; 258246074Sgabor static int ttyfd = -1; 259246074Sgabor 260246074Sgabor va_start(ap, fmt); 261246074Sgabor vfprintf(stdout, fmt, ap); 262246074Sgabor va_end(ap); 263246074Sgabor fflush(stdout); 264246074Sgabor if (ttyfd < 0) 265246074Sgabor ttyfd = open(_PATH_TTY, O_RDONLY); 266246074Sgabor if (ttyfd >= 0) { 267246074Sgabor if ((nr = read(ttyfd, buf, buf_size)) > 0 && 268246074Sgabor buf[nr - 1] == '\n') 269246074Sgabor buf[nr - 1] = '\0'; 270246074Sgabor } 271246074Sgabor if (ttyfd < 0 || nr <= 0) { 272246074Sgabor /* no tty or error reading, pretend user entered 'return' */ 273246074Sgabor putchar('\n'); 274246074Sgabor buf[0] = '\0'; 275246074Sgabor } 276246074Sgabor} 277246074Sgabor 278246074Sgabor/* 279246074Sgabor * How to handle certain events when not in a critical region. 280246074Sgabor */ 281246074Sgaborvoid 282246074Sgaborset_signals(int reset) 283246074Sgabor{ 284246074Sgabor static sig_t hupval, intval; 285246074Sgabor 286246074Sgabor if (!reset) { 287246074Sgabor hupval = signal(SIGHUP, SIG_IGN); 288246074Sgabor if (hupval != SIG_IGN) 289246074Sgabor hupval = my_exit; 290246074Sgabor intval = signal(SIGINT, SIG_IGN); 291246074Sgabor if (intval != SIG_IGN) 292246074Sgabor intval = my_exit; 293246074Sgabor } 294246074Sgabor signal(SIGHUP, hupval); 295246074Sgabor signal(SIGINT, intval); 296246074Sgabor} 297246074Sgabor 298246074Sgabor/* 299246074Sgabor * How to handle certain events when in a critical region. 300246074Sgabor */ 301246074Sgaborvoid 302246074Sgaborignore_signals(void) 303246074Sgabor{ 304246074Sgabor signal(SIGHUP, SIG_IGN); 305246074Sgabor signal(SIGINT, SIG_IGN); 306246074Sgabor} 307246074Sgabor 308246074Sgabor/* 309246074Sgabor * Make sure we'll have the directories to create a file. If `striplast' is 310246074Sgabor * true, ignore the last element of `filename'. 311246074Sgabor */ 312246074Sgabor 313246074Sgaborvoid 314246074Sgabormakedirs(const char *filename, bool striplast) 315246074Sgabor{ 316246074Sgabor char *tmpbuf; 317246074Sgabor 318246074Sgabor if ((tmpbuf = strdup(filename)) == NULL) 319246074Sgabor fatal("out of memory\n"); 320246074Sgabor 321246074Sgabor if (striplast) { 322246074Sgabor char *s = strrchr(tmpbuf, '/'); 323246074Sgabor if (s == NULL) { 324246074Sgabor free(tmpbuf); 325246074Sgabor return; /* nothing to be done */ 326246074Sgabor } 327246074Sgabor *s = '\0'; 328246074Sgabor } 329246074Sgabor if (mkpath(tmpbuf) != 0) 330246074Sgabor pfatal("creation of %s failed", tmpbuf); 331246074Sgabor free(tmpbuf); 332246074Sgabor} 333246074Sgabor 334246074Sgabor/* 335246074Sgabor * Make filenames more reasonable. 336246074Sgabor */ 337246074Sgaborchar * 338246074Sgaborfetchname(const char *at, bool *exists, int strip_leading) 339246074Sgabor{ 340246074Sgabor char *fullname, *name, *t; 341246074Sgabor int sleading, tab; 342246074Sgabor struct stat filestat; 343246074Sgabor 344246074Sgabor if (at == NULL || *at == '\0') 345246074Sgabor return NULL; 346246074Sgabor while (isspace((unsigned char)*at)) 347246074Sgabor at++; 348246074Sgabor#ifdef DEBUGGING 349246074Sgabor if (debug & 128) 350246074Sgabor say("fetchname %s %d\n", at, strip_leading); 351246074Sgabor#endif 352246074Sgabor /* So files can be created by diffing against /dev/null. */ 353246074Sgabor if (strnEQ(at, _PATH_DEVNULL, sizeof(_PATH_DEVNULL) - 1)) 354246074Sgabor return NULL; 355246074Sgabor name = fullname = t = savestr(at); 356246074Sgabor 357246074Sgabor tab = strchr(t, '\t') != NULL; 358246074Sgabor /* Strip off up to `strip_leading' path components and NUL terminate. */ 359246074Sgabor for (sleading = strip_leading; *t != '\0' && ((tab && *t != '\t') || 360246074Sgabor !isspace((unsigned char)*t)); t++) { 361246074Sgabor if (t[0] == '/' && t[1] != '/' && t[1] != '\0') 362246074Sgabor if (--sleading >= 0) 363246074Sgabor name = t + 1; 364246074Sgabor } 365246074Sgabor *t = '\0'; 366246074Sgabor 367246074Sgabor /* 368246074Sgabor * If no -p option was given (957 is the default value!), we were 369246074Sgabor * given a relative pathname, and the leading directories that we 370246074Sgabor * just stripped off all exist, put them back on. 371246074Sgabor */ 372246074Sgabor if (strip_leading == 957 && name != fullname && *fullname != '/') { 373246074Sgabor name[-1] = '\0'; 374246074Sgabor if (stat(fullname, &filestat) == 0 && S_ISDIR(filestat.st_mode)) { 375246074Sgabor name[-1] = '/'; 376246074Sgabor name = fullname; 377246074Sgabor } 378246074Sgabor } 379246074Sgabor name = savestr(name); 380246074Sgabor free(fullname); 381246074Sgabor 382246074Sgabor *exists = stat(name, &filestat) == 0; 383246074Sgabor return name; 384246074Sgabor} 385246074Sgabor 386246074Sgabor/* 387246074Sgabor * Takes the name returned by fetchname and looks in RCS/SCCS directories 388246074Sgabor * for a checked in version. 389246074Sgabor */ 390246074Sgaborchar * 391246074Sgaborchecked_in(char *file) 392246074Sgabor{ 393246074Sgabor char *filebase, *filedir, tmpbuf[MAXPATHLEN]; 394246074Sgabor struct stat filestat; 395246074Sgabor 396246074Sgabor filebase = basename(file); 397246074Sgabor filedir = dirname(file); 398246074Sgabor 399246074Sgabor#define try(f, a1, a2, a3) \ 400246074Sgabor(snprintf(tmpbuf, sizeof tmpbuf, f, a1, a2, a3), stat(tmpbuf, &filestat) == 0) 401246074Sgabor 402246074Sgabor if (try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) || 403246074Sgabor try("%s/RCS/%s%s", filedir, filebase, "") || 404246074Sgabor try("%s/%s%s", filedir, filebase, RCSSUFFIX) || 405246074Sgabor try("%s/SCCS/%s%s", filedir, SCCSPREFIX, filebase) || 406246074Sgabor try("%s/%s%s", filedir, SCCSPREFIX, filebase)) 407246074Sgabor return file; 408246074Sgabor 409246074Sgabor return NULL; 410246074Sgabor} 411246074Sgabor 412246074Sgaborvoid 413246074Sgaborversion(void) 414246074Sgabor{ 415255894Sdelphij fprintf(stderr, "patch 2.0-12u10 FreeBSD\n"); 416246074Sgabor my_exit(EXIT_SUCCESS); 417246074Sgabor} 418246074Sgabor 419246074Sgabor/* 420246074Sgabor * Exit with cleanup. 421246074Sgabor */ 422246074Sgaborvoid 423246074Sgabormy_exit(int status) 424246074Sgabor{ 425246074Sgabor unlink(TMPINNAME); 426246074Sgabor if (!toutkeep) 427246074Sgabor unlink(TMPOUTNAME); 428246074Sgabor if (!trejkeep) 429246074Sgabor unlink(TMPREJNAME); 430246074Sgabor unlink(TMPPATNAME); 431246074Sgabor exit(status); 432246074Sgabor} 433