1 /* $OpenBSD: util.c,v 1.39 2015/01/16 06:40:10 deraadt Exp $ */ 2 3 /* 4 * patch - a program to apply diffs to original files 5 * 6 * Copyright 1986, Larry Wall 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following condition is met: 10 * 1. Redistributions of source code must retain the above copyright notice, 11 * this condition and the following disclaimer. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 17 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 * 25 * -C option added in 1998, original code by Marc Espie, based on FreeBSD 26 * behaviour 27 */ 28 29 #include <sys/stat.h> 30 31 #include <ctype.h> 32 #include <errno.h> 33 #include <fcntl.h> 34 #include <libgen.h> 35 #include <paths.h> 36 #include <signal.h> 37 #include <stdarg.h> 38 #include <stdlib.h> 39 #include <stdio.h> 40 #include <string.h> 41 #include <unistd.h> 42 43 #include "common.h" 44 #include "util.h" 45 #include "backupfile.h" 46 #include "pathnames.h" 47 48 /* Rename a file, copying it if necessary. */ 49 50 int 51 move_file(const char *from, const char *to) 52 { 53 int fromfd; 54 ssize_t i; 55 56 /* to stdout? */ 57 58 if (strEQ(to, "-")) { 59 #ifdef DEBUGGING 60 if (debug & 4) 61 say("Moving %s to stdout.\n", from); 62 #endif 63 fromfd = open(from, O_RDONLY); 64 if (fromfd < 0) 65 pfatal("internal error, can't reopen %s", from); 66 while ((i = read(fromfd, buf, sizeof buf)) > 0) 67 if (write(STDOUT_FILENO, buf, i) != i) 68 pfatal("write failed"); 69 close(fromfd); 70 return 0; 71 } 72 if (backup_file(to) < 0) { 73 say("Can't backup %s, output is in %s: %s\n", to, from, 74 strerror(errno)); 75 return -1; 76 } 77 #ifdef DEBUGGING 78 if (debug & 4) 79 say("Moving %s to %s.\n", from, to); 80 #endif 81 if (rename(from, to) < 0) { 82 if (errno != EXDEV || copy_file(from, to) < 0) { 83 say("Can't create %s, output is in %s: %s\n", 84 to, from, strerror(errno)); 85 return -1; 86 } 87 } 88 return 0; 89 } 90 91 /* Backup the original file. */ 92 93 int 94 backup_file(const char *orig) 95 { 96 struct stat filestat; 97 char bakname[PATH_MAX], *s, *simplename; 98 dev_t orig_device; 99 ino_t orig_inode; 100 101 if (backup_type == none || stat(orig, &filestat) != 0) 102 return 0; /* nothing to do */ 103 orig_device = filestat.st_dev; 104 orig_inode = filestat.st_ino; 105 106 if (origprae) { 107 if (strlcpy(bakname, origprae, sizeof(bakname)) >= sizeof(bakname) || 108 strlcat(bakname, orig, sizeof(bakname)) >= sizeof(bakname)) 109 fatal("filename %s too long for buffer\n", origprae); 110 } else { 111 if ((s = find_backup_file_name(orig)) == NULL) 112 fatal("out of memory\n"); 113 if (strlcpy(bakname, s, sizeof(bakname)) >= sizeof(bakname)) 114 fatal("filename %s too long for buffer\n", s); 115 free(s); 116 } 117 118 if ((simplename = strrchr(bakname, '/')) != NULL) 119 simplename = simplename + 1; 120 else 121 simplename = bakname; 122 123 /* 124 * Find a backup name that is not the same file. Change the 125 * first lowercase char into uppercase; if that isn't 126 * sufficient, chop off the first char and try again. 127 */ 128 while (stat(bakname, &filestat) == 0 && 129 orig_device == filestat.st_dev && orig_inode == filestat.st_ino) { 130 /* Skip initial non-lowercase chars. */ 131 for (s = simplename; *s && !islower((unsigned char)*s); s++) 132 ; 133 if (*s) 134 *s = toupper((unsigned char)*s); 135 else 136 memmove(simplename, simplename + 1, 137 strlen(simplename + 1) + 1); 138 } 139 #ifdef DEBUGGING 140 if (debug & 4) 141 say("Moving %s to %s.\n", orig, bakname); 142 #endif 143 if (rename(orig, bakname) < 0) { 144 if (errno != EXDEV || copy_file(orig, bakname) < 0) 145 return -1; 146 } 147 return 0; 148 } 149 150 /* 151 * Copy a file. 152 */ 153 int 154 copy_file(const char *from, const char *to) 155 { 156 int tofd, fromfd; 157 ssize_t i; 158 159 tofd = open(to, O_CREAT|O_TRUNC|O_WRONLY, 0666); 160 if (tofd < 0) 161 return -1; 162 fromfd = open(from, O_RDONLY, 0); 163 if (fromfd < 0) 164 pfatal("internal error, can't reopen %s", from); 165 while ((i = read(fromfd, buf, sizeof buf)) > 0) 166 if (write(tofd, buf, i) != i) 167 pfatal("write to %s failed", to); 168 close(fromfd); 169 close(tofd); 170 return 0; 171 } 172 173 /* 174 * Allocate a unique area for a string. 175 */ 176 char * 177 savestr(const char *s) 178 { 179 char *rv; 180 181 if (!s) 182 s = "Oops"; 183 rv = strdup(s); 184 if (rv == NULL) { 185 if (using_plan_a) 186 out_of_mem = true; 187 else 188 fatal("out of memory\n"); 189 } 190 return rv; 191 } 192 193 /* 194 * Allocate a unique area for a string. Call fatal if out of memory. 195 */ 196 char * 197 xstrdup(const char *s) 198 { 199 char *rv; 200 201 if (!s) 202 s = "Oops"; 203 rv = strdup(s); 204 if (rv == NULL) 205 fatal("out of memory\n"); 206 return rv; 207 } 208 209 /* 210 * Vanilla terminal output (buffered). 211 */ 212 void 213 say(const char *fmt, ...) 214 { 215 va_list ap; 216 217 va_start(ap, fmt); 218 vfprintf(stdout, fmt, ap); 219 va_end(ap); 220 fflush(stdout); 221 } 222 223 /* 224 * Terminal output, pun intended. 225 */ 226 void 227 fatal(const char *fmt, ...) 228 { 229 va_list ap; 230 231 va_start(ap, fmt); 232 fprintf(stderr, "patch: **** "); 233 vfprintf(stderr, fmt, ap); 234 va_end(ap); 235 my_exit(2); 236 } 237 238 /* 239 * Say something from patch, something from the system, then silence . . . 240 */ 241 void 242 pfatal(const char *fmt, ...) 243 { 244 va_list ap; 245 int errnum = errno; 246 247 fprintf(stderr, "patch: **** "); 248 va_start(ap, fmt); 249 vfprintf(stderr, fmt, ap); 250 va_end(ap); 251 fprintf(stderr, ": %s\n", strerror(errnum)); 252 my_exit(2); 253 } 254 255 /* 256 * Get a response from the user via /dev/tty 257 */ 258 void 259 ask(const char *fmt, ...) 260 { 261 va_list ap; 262 ssize_t nr; 263 static int ttyfd = -1; 264 265 va_start(ap, fmt); 266 vfprintf(stdout, fmt, ap); 267 va_end(ap); 268 fflush(stdout); 269 if (ttyfd < 0) 270 ttyfd = open(_PATH_TTY, O_RDONLY); 271 if (ttyfd >= 0) { 272 if ((nr = read(ttyfd, buf, sizeof(buf))) > 0 && 273 buf[nr - 1] == '\n') 274 buf[nr - 1] = '\0'; 275 } 276 if (ttyfd < 0 || nr <= 0) { 277 /* no tty or error reading, pretend user entered 'return' */ 278 putchar('\n'); 279 buf[0] = '\0'; 280 } 281 } 282 283 /* 284 * How to handle certain events when not in a critical region. 285 */ 286 void 287 set_signals(int reset) 288 { 289 static sig_t hupval, intval; 290 291 if (!reset) { 292 hupval = signal(SIGHUP, SIG_IGN); 293 if (hupval != SIG_IGN) 294 hupval = (sig_t) my_exit; 295 intval = signal(SIGINT, SIG_IGN); 296 if (intval != SIG_IGN) 297 intval = (sig_t) my_exit; 298 } 299 signal(SIGHUP, hupval); 300 signal(SIGINT, intval); 301 } 302 303 /* 304 * How to handle certain events when in a critical region. 305 */ 306 void 307 ignore_signals(void) 308 { 309 signal(SIGHUP, SIG_IGN); 310 signal(SIGINT, SIG_IGN); 311 } 312 313 /* 314 * Make sure we'll have the directories to create a file. If `striplast' is 315 * true, ignore the last element of `filename'. 316 */ 317 318 void 319 makedirs(const char *filename, bool striplast) 320 { 321 char *tmpbuf; 322 323 if ((tmpbuf = strdup(filename)) == NULL) 324 fatal("out of memory\n"); 325 326 if (striplast) { 327 char *s = strrchr(tmpbuf, '/'); 328 if (s == NULL) { 329 free(tmpbuf); 330 return; /* nothing to be done */ 331 } 332 *s = '\0'; 333 } 334 if (mkpath(tmpbuf) != 0) 335 pfatal("creation of %s failed", tmpbuf); 336 free(tmpbuf); 337 } 338 339 /* 340 * Make filenames more reasonable. 341 */ 342 char * 343 fetchname(const char *at, bool *exists, int strip_leading) 344 { 345 char *fullname, *name, *t; 346 int sleading, tab; 347 struct stat filestat; 348 349 if (at == NULL || *at == '\0') 350 return NULL; 351 while (isspace((unsigned char)*at)) 352 at++; 353 #ifdef DEBUGGING 354 if (debug & 128) 355 say("fetchname %s %d\n", at, strip_leading); 356 #endif 357 /* So files can be created by diffing against /dev/null. */ 358 if (strnEQ(at, _PATH_DEVNULL, sizeof(_PATH_DEVNULL) - 1)) 359 return NULL; 360 name = fullname = t = savestr(at); 361 362 tab = strchr(t, '\t') != NULL; 363 /* Strip off up to `strip_leading' path components and NUL terminate. */ 364 for (sleading = strip_leading; *t != '\0' && ((tab && *t != '\t') || 365 !isspace((unsigned char)*t)); t++) { 366 if (t[0] == '/' && t[1] != '/' && t[1] != '\0') 367 if (--sleading >= 0) 368 name = t + 1; 369 } 370 *t = '\0'; 371 372 /* 373 * If no -p option was given (957 is the default value!), we were 374 * given a relative pathname, and the leading directories that we 375 * just stripped off all exist, put them back on. 376 */ 377 if (strip_leading == 957 && name != fullname && *fullname != '/') { 378 name[-1] = '\0'; 379 if (stat(fullname, &filestat) == 0 && S_ISDIR(filestat.st_mode)) { 380 name[-1] = '/'; 381 name = fullname; 382 } 383 } 384 name = savestr(name); 385 free(fullname); 386 387 *exists = stat(name, &filestat) == 0; 388 return name; 389 } 390 391 /* 392 * Takes the name returned by fetchname and looks in RCS directory 393 * for a checked in version. 394 */ 395 char * 396 checked_in(char *file) 397 { 398 char *filebase, *filedir, tmpbuf[PATH_MAX]; 399 struct stat filestat; 400 401 filebase = basename(file); 402 filedir = dirname(file); 403 404 #define try(f, a1, a2, a3) \ 405 (snprintf(tmpbuf, sizeof tmpbuf, f, a1, a2, a3), stat(tmpbuf, &filestat) == 0) 406 407 if (try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) || 408 try("%s/RCS/%s%s", filedir, filebase, "") || 409 try("%s/%s%s", filedir, filebase, RCSSUFFIX)) 410 return file; 411 412 return NULL; 413 } 414 415 void 416 version(void) 417 { 418 fprintf(stderr, "Patch version 2.0-12u8-OpenBSD\n"); 419 my_exit(EXIT_SUCCESS); 420 } 421 422 /* 423 * Exit with cleanup. 424 */ 425 void 426 my_exit(int status) 427 { 428 unlink(TMPINNAME); 429 if (!toutkeep) 430 unlink(TMPOUTNAME); 431 if (!trejkeep) 432 unlink(TMPREJNAME); 433 unlink(TMPPATNAME); 434 exit(status); 435 } 436