util.c revision 287223
1139749Simp/*- 272016Scg * Copyright 1986, Larry Wall 372016Scg * 472016Scg * Redistribution and use in source and binary forms, with or without 572016Scg * modification, are permitted provided that the following condition is met: 672016Scg * 1. Redistributions of source code must retain the above copyright notice, 772016Scg * this condition and the following disclaimer. 872016Scg * 972016Scg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY 1072016Scg * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 1172016Scg * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 1272016Scg * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 1372016Scg * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1472016Scg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 1572016Scg * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 1672016Scg * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 1772016Scg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 1872016Scg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 1972016Scg * SUCH DAMAGE. 2072016Scg * 2172016Scg * patch - a program to apply diffs to original files 2272016Scg * 2372016Scg * -C option added in 1998, original code by Marc Espie, based on FreeBSD 2472016Scg * behaviour 2572016Scg * 2672016Scg * $OpenBSD: util.c,v 1.35 2010/07/24 01:10:12 ray Exp $ 2772016Scg * $FreeBSD: stable/10/usr.bin/patch/util.c 287223 2015-08-27 21:52:09Z delphij $ 2872016Scg */ 2972016Scg 3072016Scg#include <sys/stat.h> 3172016Scg 3272016Scg#include <ctype.h> 3372016Scg#include <errno.h> 3472016Scg#include <fcntl.h> 3572016Scg#include <libgen.h> 3672016Scg#include <limits.h> 3772016Scg#include <paths.h> 3872016Scg#include <signal.h> 3972016Scg#include <stdarg.h> 4072016Scg#include <stdlib.h> 4172016Scg#include <stdio.h> 4272016Scg#include <string.h> 4372016Scg#include <unistd.h> 4472016Scg 4572016Scg#include "common.h" 4672016Scg#include "util.h" 4772016Scg#include "backupfile.h" 4872016Scg#include "pathnames.h" 4972016Scg 5072016Scg/* Rename a file, copying it if necessary. */ 5172016Scg 5272016Scgint 5372016Scgmove_file(const char *from, const char *to) 5472016Scg{ 5572016Scg int fromfd; 5672016Scg ssize_t i; 5772016Scg 5872016Scg /* to stdout? */ 5972016Scg 6072016Scg if (strEQ(to, "-")) { 6172016Scg#ifdef DEBUGGING 6272016Scg if (debug & 4) 6372016Scg say("Moving %s to stdout.\n", from); 6472016Scg#endif 6572016Scg fromfd = open(from, O_RDONLY); 6672016Scg if (fromfd < 0) 6772016Scg pfatal("internal error, can't reopen %s", from); 6872016Scg while ((i = read(fromfd, buf, buf_size)) > 0) 6972016Scg if (write(STDOUT_FILENO, buf, i) != i) 7072016Scg pfatal("write failed"); 7172016Scg close(fromfd); 7272016Scg return 0; 7372016Scg } 7472016Scg if (backup_file(to) < 0) { 7572016Scg say("Can't backup %s, output is in %s: %s\n", to, from, 7672016Scg strerror(errno)); 7772016Scg return -1; 7872016Scg } 7972016Scg#ifdef DEBUGGING 8072016Scg if (debug & 4) 8172016Scg say("Moving %s to %s.\n", from, to); 8272016Scg#endif 8372016Scg if (rename(from, to) < 0) { 8472016Scg if (errno != EXDEV || copy_file(from, to) < 0) { 8572016Scg say("Can't create %s, output is in %s: %s\n", 8672016Scg to, from, strerror(errno)); 8772016Scg return -1; 8872016Scg } 8972016Scg } 9072016Scg return 0; 9172016Scg} 9272016Scg 9372016Scg/* Backup the original file. */ 9472016Scg 9572016Scgint 9672016Scgbackup_file(const char *orig) 9772016Scg{ 9872016Scg struct stat filestat; 9972016Scg char bakname[PATH_MAX], *s, *simplename; 10072016Scg dev_t orig_device; 10172016Scg ino_t orig_inode; 10272016Scg 10372016Scg if (backup_type == none || stat(orig, &filestat) != 0) 10472016Scg return 0; /* nothing to do */ 10572016Scg /* 10672016Scg * If the user used zero prefixes or suffixes, then 10772016Scg * he doesn't want backups. Yet we have to remove 10872016Scg * orig to break possible hardlinks. 10972016Scg */ 11072016Scg if ((origprae && *origprae == 0) || *simple_backup_suffix == 0) { 11172016Scg unlink(orig); 11272016Scg return 0; 11372016Scg } 11472016Scg orig_device = filestat.st_dev; 11572016Scg orig_inode = filestat.st_ino; 11672016Scg 11774994Sorion if (origprae) { 11872016Scg if (strlcpy(bakname, origprae, sizeof(bakname)) >= sizeof(bakname) || 11974994Sorion strlcat(bakname, orig, sizeof(bakname)) >= sizeof(bakname)) 12074994Sorion fatal("filename %s too long for buffer\n", origprae); 12172016Scg } else { 12272016Scg if ((s = find_backup_file_name(orig)) == NULL) 12372016Scg fatal("out of memory\n"); 12472016Scg if (strlcpy(bakname, s, sizeof(bakname)) >= sizeof(bakname)) 12572016Scg fatal("filename %s too long for buffer\n", s); 12672016Scg free(s); 12772016Scg } 12872016Scg 12972016Scg if ((simplename = strrchr(bakname, '/')) != NULL) 13072016Scg simplename = simplename + 1; 13172016Scg else 13272016Scg simplename = bakname; 13372016Scg 13472016Scg /* 13572016Scg * Find a backup name that is not the same file. Change the 13672016Scg * first lowercase char into uppercase; if that isn't 13772016Scg * sufficient, chop off the first char and try again. 13872016Scg */ 13972016Scg while (stat(bakname, &filestat) == 0 && 14072016Scg orig_device == filestat.st_dev && orig_inode == filestat.st_ino) { 14172016Scg /* Skip initial non-lowercase chars. */ 14272016Scg for (s = simplename; *s && !islower((unsigned char)*s); s++) 14372016Scg ; 14472016Scg if (*s) 14572016Scg *s = toupper((unsigned char)*s); 14672016Scg else 14772016Scg memmove(simplename, simplename + 1, 14872016Scg strlen(simplename + 1) + 1); 14972016Scg } 15072016Scg#ifdef DEBUGGING 15172016Scg if (debug & 4) 15272016Scg say("Moving %s to %s.\n", orig, bakname); 15372016Scg#endif 15472016Scg if (rename(orig, bakname) < 0) { 15572016Scg if (errno != EXDEV || copy_file(orig, bakname) < 0) 15672016Scg return -1; 15772016Scg } 15872016Scg return 0; 15972016Scg} 16072016Scg 16172016Scg/* 16272016Scg * Copy a file. 16372016Scg */ 16472016Scgint 16572016Scgcopy_file(const char *from, const char *to) 16672016Scg{ 16772016Scg int tofd, fromfd; 16872016Scg ssize_t i; 16972016Scg 17072016Scg tofd = open(to, O_CREAT|O_TRUNC|O_WRONLY, 0666); 17172016Scg if (tofd < 0) 17274994Sorion return -1; 17374994Sorion fromfd = open(from, O_RDONLY, 0); 17474994Sorion if (fromfd < 0) 17574994Sorion pfatal("internal error, can't reopen %s", from); 17674994Sorion while ((i = read(fromfd, buf, buf_size)) > 0) 17772016Scg if (write(tofd, buf, i) != i) 17872016Scg pfatal("write to %s failed", to); 17972016Scg close(fromfd); 18072016Scg close(tofd); 18172016Scg return 0; 18272016Scg} 18372016Scg 18472016Scg/* 18572016Scg * Allocate a unique area for a string. 18672016Scg */ 18772016Scgchar * 18872016Scgsavestr(const char *s) 18972016Scg{ 19072016Scg char *rv; 19172016Scg 19272016Scg if (!s) 19372016Scg s = "Oops"; 19472016Scg rv = strdup(s); 19572016Scg if (rv == NULL) { 19672016Scg if (using_plan_a) 19772016Scg out_of_mem = true; 19872016Scg else 19972016Scg fatal("out of memory\n"); 20072016Scg } 20172016Scg return rv; 20272016Scg} 20372016Scg 20472016Scg/* 20572016Scg * Allocate a unique area for a string. Call fatal if out of memory. 20672016Scg */ 20772016Scgchar * 20872016Scgxstrdup(const char *s) 20972016Scg{ 21072016Scg char *rv; 211 212 if (!s) 213 s = "Oops"; 214 rv = strdup(s); 215 if (rv == NULL) 216 fatal("out of memory\n"); 217 return rv; 218} 219 220/* 221 * Vanilla terminal output (buffered). 222 */ 223void 224say(const char *fmt, ...) 225{ 226 va_list ap; 227 228 va_start(ap, fmt); 229 vfprintf(stdout, fmt, ap); 230 va_end(ap); 231 fflush(stdout); 232} 233 234/* 235 * Terminal output, pun intended. 236 */ 237void 238fatal(const char *fmt, ...) 239{ 240 va_list ap; 241 242 va_start(ap, fmt); 243 fprintf(stderr, "patch: **** "); 244 vfprintf(stderr, fmt, ap); 245 va_end(ap); 246 my_exit(2); 247} 248 249/* 250 * Say something from patch, something from the system, then silence . . . 251 */ 252void 253pfatal(const char *fmt, ...) 254{ 255 va_list ap; 256 int errnum = errno; 257 258 fprintf(stderr, "patch: **** "); 259 va_start(ap, fmt); 260 vfprintf(stderr, fmt, ap); 261 va_end(ap); 262 fprintf(stderr, ": %s\n", strerror(errnum)); 263 my_exit(2); 264} 265 266/* 267 * Get a response from the user via /dev/tty 268 */ 269void 270ask(const char *fmt, ...) 271{ 272 va_list ap; 273 ssize_t nr = 0; 274 static int ttyfd = -1; 275 276 va_start(ap, fmt); 277 vfprintf(stdout, fmt, ap); 278 va_end(ap); 279 fflush(stdout); 280 if (ttyfd < 0) 281 ttyfd = open(_PATH_TTY, O_RDONLY); 282 if (ttyfd >= 0) { 283 if ((nr = read(ttyfd, buf, buf_size)) > 0 && 284 buf[nr - 1] == '\n') 285 buf[nr - 1] = '\0'; 286 } 287 if (ttyfd < 0 || nr <= 0) { 288 /* no tty or error reading, pretend user entered 'return' */ 289 putchar('\n'); 290 buf[0] = '\0'; 291 } 292} 293 294/* 295 * How to handle certain events when not in a critical region. 296 */ 297void 298set_signals(int reset) 299{ 300 static sig_t hupval, intval; 301 302 if (!reset) { 303 hupval = signal(SIGHUP, SIG_IGN); 304 if (hupval != SIG_IGN) 305 hupval = my_exit; 306 intval = signal(SIGINT, SIG_IGN); 307 if (intval != SIG_IGN) 308 intval = my_exit; 309 } 310 signal(SIGHUP, hupval); 311 signal(SIGINT, intval); 312} 313 314/* 315 * How to handle certain events when in a critical region. 316 */ 317void 318ignore_signals(void) 319{ 320 signal(SIGHUP, SIG_IGN); 321 signal(SIGINT, SIG_IGN); 322} 323 324/* 325 * Make sure we'll have the directories to create a file. If `striplast' is 326 * true, ignore the last element of `filename'. 327 */ 328 329void 330makedirs(const char *filename, bool striplast) 331{ 332 char *tmpbuf; 333 334 if ((tmpbuf = strdup(filename)) == NULL) 335 fatal("out of memory\n"); 336 337 if (striplast) { 338 char *s = strrchr(tmpbuf, '/'); 339 if (s == NULL) { 340 free(tmpbuf); 341 return; /* nothing to be done */ 342 } 343 *s = '\0'; 344 } 345 if (mkpath(tmpbuf) != 0) 346 pfatal("creation of %s failed", tmpbuf); 347 free(tmpbuf); 348} 349 350/* 351 * Make filenames more reasonable. 352 */ 353char * 354fetchname(const char *at, bool *exists, int strip_leading) 355{ 356 char *fullname, *name, *t; 357 int sleading, tab; 358 struct stat filestat; 359 360 if (at == NULL || *at == '\0') 361 return NULL; 362 while (isspace((unsigned char)*at)) 363 at++; 364#ifdef DEBUGGING 365 if (debug & 128) 366 say("fetchname %s %d\n", at, strip_leading); 367#endif 368 /* So files can be created by diffing against /dev/null. */ 369 if (strnEQ(at, _PATH_DEVNULL, sizeof(_PATH_DEVNULL) - 1)) 370 return NULL; 371 name = fullname = t = savestr(at); 372 373 tab = strchr(t, '\t') != NULL; 374 /* Strip off up to `strip_leading' path components and NUL terminate. */ 375 for (sleading = strip_leading; *t != '\0' && ((tab && *t != '\t') || 376 !isspace((unsigned char)*t)); t++) { 377 if (t[0] == '/' && t[1] != '/' && t[1] != '\0') 378 if (--sleading >= 0) 379 name = t + 1; 380 } 381 *t = '\0'; 382 383 /* 384 * If no -p option was given (957 is the default value!), we were 385 * given a relative pathname, and the leading directories that we 386 * just stripped off all exist, put them back on. 387 */ 388 if (strip_leading == 957 && name != fullname && *fullname != '/') { 389 name[-1] = '\0'; 390 if (stat(fullname, &filestat) == 0 && S_ISDIR(filestat.st_mode)) { 391 name[-1] = '/'; 392 name = fullname; 393 } 394 } 395 name = savestr(name); 396 free(fullname); 397 398 *exists = stat(name, &filestat) == 0; 399 return name; 400} 401 402/* 403 * Takes the name returned by fetchname and looks in RCS/SCCS directories 404 * for a checked in version. 405 */ 406char * 407checked_in(char *file) 408{ 409 char *filebase, *filedir, tmpbuf[PATH_MAX]; 410 struct stat filestat; 411 412 filebase = basename(file); 413 filedir = dirname(file); 414 415#define try(f, a1, a2, a3) \ 416(snprintf(tmpbuf, sizeof tmpbuf, f, a1, a2, a3), stat(tmpbuf, &filestat) == 0) 417 418 if (try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) || 419 try("%s/RCS/%s%s", filedir, filebase, "") || 420 try("%s/%s%s", filedir, filebase, RCSSUFFIX) || 421 try("%s/SCCS/%s%s", filedir, SCCSPREFIX, filebase) || 422 try("%s/%s%s", filedir, SCCSPREFIX, filebase)) 423 return file; 424 425 return NULL; 426} 427 428void 429version(void) 430{ 431 printf("patch 2.0-12u10 FreeBSD\n"); 432 my_exit(EXIT_SUCCESS); 433} 434 435/* 436 * Exit with cleanup. 437 */ 438void 439my_exit(int status) 440{ 441 unlink(TMPINNAME); 442 if (!toutkeep) 443 unlink(TMPOUTNAME); 444 if (!trejkeep) 445 unlink(TMPREJNAME); 446 unlink(TMPPATNAME); 447 exit(status); 448} 449