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/stat.h> 31246074Sgabor 32246074Sgabor#include <ctype.h> 33246074Sgabor#include <errno.h> 34246074Sgabor#include <fcntl.h> 35246074Sgabor#include <libgen.h> 36287223Sdelphij#include <limits.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; 99287223Sdelphij char bakname[PATH_MAX], *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/* 205276807Spfg * Allocate a unique area for a string. Call fatal if out of memory. 206276807Spfg */ 207276807Spfgchar * 208276807Spfgxstrdup(const char *s) 209276807Spfg{ 210276807Spfg char *rv; 211276807Spfg 212276807Spfg if (!s) 213276807Spfg s = "Oops"; 214276807Spfg rv = strdup(s); 215276807Spfg if (rv == NULL) 216276807Spfg fatal("out of memory\n"); 217276807Spfg return rv; 218276807Spfg} 219276807Spfg 220276807Spfg/* 221246074Sgabor * Vanilla terminal output (buffered). 222246074Sgabor */ 223246074Sgaborvoid 224246074Sgaborsay(const char *fmt, ...) 225246074Sgabor{ 226246074Sgabor va_list ap; 227246074Sgabor 228246074Sgabor va_start(ap, fmt); 229246091Sdelphij vfprintf(stdout, fmt, ap); 230246074Sgabor va_end(ap); 231246091Sdelphij fflush(stdout); 232246074Sgabor} 233246074Sgabor 234246074Sgabor/* 235246074Sgabor * Terminal output, pun intended. 236246074Sgabor */ 237246074Sgaborvoid 238246074Sgaborfatal(const char *fmt, ...) 239246074Sgabor{ 240246074Sgabor va_list ap; 241246074Sgabor 242246074Sgabor va_start(ap, fmt); 243246074Sgabor fprintf(stderr, "patch: **** "); 244246074Sgabor vfprintf(stderr, fmt, ap); 245246074Sgabor va_end(ap); 246246074Sgabor my_exit(2); 247246074Sgabor} 248246074Sgabor 249246074Sgabor/* 250246074Sgabor * Say something from patch, something from the system, then silence . . . 251246074Sgabor */ 252246074Sgaborvoid 253246074Sgaborpfatal(const char *fmt, ...) 254246074Sgabor{ 255246074Sgabor va_list ap; 256246074Sgabor int errnum = errno; 257246074Sgabor 258246074Sgabor fprintf(stderr, "patch: **** "); 259246074Sgabor va_start(ap, fmt); 260246074Sgabor vfprintf(stderr, fmt, ap); 261246074Sgabor va_end(ap); 262246074Sgabor fprintf(stderr, ": %s\n", strerror(errnum)); 263246074Sgabor my_exit(2); 264246074Sgabor} 265246074Sgabor 266246074Sgabor/* 267246074Sgabor * Get a response from the user via /dev/tty 268246074Sgabor */ 269246074Sgaborvoid 270246074Sgaborask(const char *fmt, ...) 271246074Sgabor{ 272246074Sgabor va_list ap; 273246074Sgabor ssize_t nr = 0; 274246074Sgabor static int ttyfd = -1; 275246074Sgabor 276246074Sgabor va_start(ap, fmt); 277246074Sgabor vfprintf(stdout, fmt, ap); 278246074Sgabor va_end(ap); 279246074Sgabor fflush(stdout); 280246074Sgabor if (ttyfd < 0) 281246074Sgabor ttyfd = open(_PATH_TTY, O_RDONLY); 282246074Sgabor if (ttyfd >= 0) { 283246074Sgabor if ((nr = read(ttyfd, buf, buf_size)) > 0 && 284246074Sgabor buf[nr - 1] == '\n') 285246074Sgabor buf[nr - 1] = '\0'; 286246074Sgabor } 287246074Sgabor if (ttyfd < 0 || nr <= 0) { 288246074Sgabor /* no tty or error reading, pretend user entered 'return' */ 289246074Sgabor putchar('\n'); 290246074Sgabor buf[0] = '\0'; 291246074Sgabor } 292246074Sgabor} 293246074Sgabor 294246074Sgabor/* 295246074Sgabor * How to handle certain events when not in a critical region. 296246074Sgabor */ 297246074Sgaborvoid 298246074Sgaborset_signals(int reset) 299246074Sgabor{ 300246074Sgabor static sig_t hupval, intval; 301246074Sgabor 302246074Sgabor if (!reset) { 303246074Sgabor hupval = signal(SIGHUP, SIG_IGN); 304246074Sgabor if (hupval != SIG_IGN) 305246074Sgabor hupval = my_exit; 306246074Sgabor intval = signal(SIGINT, SIG_IGN); 307246074Sgabor if (intval != SIG_IGN) 308246074Sgabor intval = my_exit; 309246074Sgabor } 310246074Sgabor signal(SIGHUP, hupval); 311246074Sgabor signal(SIGINT, intval); 312246074Sgabor} 313246074Sgabor 314246074Sgabor/* 315246074Sgabor * How to handle certain events when in a critical region. 316246074Sgabor */ 317246074Sgaborvoid 318246074Sgaborignore_signals(void) 319246074Sgabor{ 320246074Sgabor signal(SIGHUP, SIG_IGN); 321246074Sgabor signal(SIGINT, SIG_IGN); 322246074Sgabor} 323246074Sgabor 324246074Sgabor/* 325246074Sgabor * Make sure we'll have the directories to create a file. If `striplast' is 326246074Sgabor * true, ignore the last element of `filename'. 327246074Sgabor */ 328246074Sgabor 329246074Sgaborvoid 330246074Sgabormakedirs(const char *filename, bool striplast) 331246074Sgabor{ 332246074Sgabor char *tmpbuf; 333246074Sgabor 334246074Sgabor if ((tmpbuf = strdup(filename)) == NULL) 335246074Sgabor fatal("out of memory\n"); 336246074Sgabor 337246074Sgabor if (striplast) { 338246074Sgabor char *s = strrchr(tmpbuf, '/'); 339246074Sgabor if (s == NULL) { 340246074Sgabor free(tmpbuf); 341246074Sgabor return; /* nothing to be done */ 342246074Sgabor } 343246074Sgabor *s = '\0'; 344246074Sgabor } 345246074Sgabor if (mkpath(tmpbuf) != 0) 346246074Sgabor pfatal("creation of %s failed", tmpbuf); 347246074Sgabor free(tmpbuf); 348246074Sgabor} 349246074Sgabor 350246074Sgabor/* 351246074Sgabor * Make filenames more reasonable. 352246074Sgabor */ 353246074Sgaborchar * 354246074Sgaborfetchname(const char *at, bool *exists, int strip_leading) 355246074Sgabor{ 356246074Sgabor char *fullname, *name, *t; 357246074Sgabor int sleading, tab; 358246074Sgabor struct stat filestat; 359246074Sgabor 360246074Sgabor if (at == NULL || *at == '\0') 361246074Sgabor return NULL; 362246074Sgabor while (isspace((unsigned char)*at)) 363246074Sgabor at++; 364246074Sgabor#ifdef DEBUGGING 365246074Sgabor if (debug & 128) 366246074Sgabor say("fetchname %s %d\n", at, strip_leading); 367246074Sgabor#endif 368246074Sgabor /* So files can be created by diffing against /dev/null. */ 369246074Sgabor if (strnEQ(at, _PATH_DEVNULL, sizeof(_PATH_DEVNULL) - 1)) 370246074Sgabor return NULL; 371246074Sgabor name = fullname = t = savestr(at); 372246074Sgabor 373246074Sgabor tab = strchr(t, '\t') != NULL; 374246074Sgabor /* Strip off up to `strip_leading' path components and NUL terminate. */ 375246074Sgabor for (sleading = strip_leading; *t != '\0' && ((tab && *t != '\t') || 376246074Sgabor !isspace((unsigned char)*t)); t++) { 377246074Sgabor if (t[0] == '/' && t[1] != '/' && t[1] != '\0') 378246074Sgabor if (--sleading >= 0) 379246074Sgabor name = t + 1; 380246074Sgabor } 381246074Sgabor *t = '\0'; 382246074Sgabor 383246074Sgabor /* 384246074Sgabor * If no -p option was given (957 is the default value!), we were 385246074Sgabor * given a relative pathname, and the leading directories that we 386246074Sgabor * just stripped off all exist, put them back on. 387246074Sgabor */ 388246074Sgabor if (strip_leading == 957 && name != fullname && *fullname != '/') { 389246074Sgabor name[-1] = '\0'; 390246074Sgabor if (stat(fullname, &filestat) == 0 && S_ISDIR(filestat.st_mode)) { 391246074Sgabor name[-1] = '/'; 392246074Sgabor name = fullname; 393246074Sgabor } 394246074Sgabor } 395246074Sgabor name = savestr(name); 396246074Sgabor free(fullname); 397246074Sgabor 398246074Sgabor *exists = stat(name, &filestat) == 0; 399246074Sgabor return name; 400246074Sgabor} 401246074Sgabor 402246074Sgabor/* 403246074Sgabor * Takes the name returned by fetchname and looks in RCS/SCCS directories 404246074Sgabor * for a checked in version. 405246074Sgabor */ 406246074Sgaborchar * 407246074Sgaborchecked_in(char *file) 408246074Sgabor{ 409287223Sdelphij char *filebase, *filedir, tmpbuf[PATH_MAX]; 410246074Sgabor struct stat filestat; 411246074Sgabor 412246074Sgabor filebase = basename(file); 413246074Sgabor filedir = dirname(file); 414246074Sgabor 415246074Sgabor#define try(f, a1, a2, a3) \ 416246074Sgabor(snprintf(tmpbuf, sizeof tmpbuf, f, a1, a2, a3), stat(tmpbuf, &filestat) == 0) 417246074Sgabor 418246074Sgabor if (try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) || 419246074Sgabor try("%s/RCS/%s%s", filedir, filebase, "") || 420246074Sgabor try("%s/%s%s", filedir, filebase, RCSSUFFIX) || 421246074Sgabor try("%s/SCCS/%s%s", filedir, SCCSPREFIX, filebase) || 422246074Sgabor try("%s/%s%s", filedir, SCCSPREFIX, filebase)) 423246074Sgabor return file; 424246074Sgabor 425246074Sgabor return NULL; 426246074Sgabor} 427246074Sgabor 428246074Sgaborvoid 429246074Sgaborversion(void) 430246074Sgabor{ 431276706Spfg printf("patch 2.0-12u10 FreeBSD\n"); 432246074Sgabor my_exit(EXIT_SUCCESS); 433246074Sgabor} 434246074Sgabor 435246074Sgabor/* 436246074Sgabor * Exit with cleanup. 437246074Sgabor */ 438246074Sgaborvoid 439246074Sgabormy_exit(int status) 440246074Sgabor{ 441246074Sgabor unlink(TMPINNAME); 442246074Sgabor if (!toutkeep) 443246074Sgabor unlink(TMPOUTNAME); 444246074Sgabor if (!trejkeep) 445246074Sgabor unlink(TMPREJNAME); 446246074Sgabor unlink(TMPPATNAME); 447246074Sgabor exit(status); 448246074Sgabor} 449