1 /* $OpenBSD: util.c,v 1.41 2018/04/07 14:55:13 anton 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 47 /* Rename a file, copying it if necessary. */ 48 49 int 50 move_file(const char *from, const char *to) 51 { 52 int fromfd; 53 ssize_t i; 54 55 /* to stdout? */ 56 57 if (strEQ(to, "-")) { 58 #ifdef DEBUGGING 59 if (debug & 4) 60 say("Moving %s to stdout.\n", from); 61 #endif 62 fromfd = open(from, O_RDONLY); 63 if (fromfd < 0) 64 pfatal("internal error, can't reopen %s", from); 65 while ((i = read(fromfd, buf, sizeof buf)) > 0) 66 if (write(STDOUT_FILENO, buf, i) != i) 67 pfatal("write failed"); 68 close(fromfd); 69 return 0; 70 } 71 if (backup_file(to) < 0) { 72 say("Can't backup %s, output is in %s: %s\n", to, from, 73 strerror(errno)); 74 return -1; 75 } 76 #ifdef DEBUGGING 77 if (debug & 4) 78 say("Moving %s to %s.\n", from, to); 79 #endif 80 if (rename(from, to) < 0) { 81 if (errno != EXDEV || copy_file(from, to) < 0) { 82 say("Can't create %s, output is in %s: %s\n", 83 to, from, strerror(errno)); 84 return -1; 85 } 86 } 87 return 0; 88 } 89 90 /* Backup the original file. */ 91 92 int 93 backup_file(const char *orig) 94 { 95 struct stat filestat; 96 char bakname[PATH_MAX], *s, *simplename; 97 dev_t orig_device; 98 ino_t orig_inode; 99 100 if (backup_type == none || stat(orig, &filestat) != 0) 101 return 0; /* nothing to do */ 102 orig_device = filestat.st_dev; 103 orig_inode = filestat.st_ino; 104 105 if (origprae) { 106 if (strlcpy(bakname, origprae, sizeof(bakname)) >= sizeof(bakname) || 107 strlcat(bakname, orig, sizeof(bakname)) >= sizeof(bakname)) 108 fatal("filename %s too long for buffer\n", origprae); 109 } else { 110 if ((s = find_backup_file_name(orig)) == NULL) 111 fatal("out of memory\n"); 112 if (strlcpy(bakname, s, sizeof(bakname)) >= sizeof(bakname)) 113 fatal("filename %s too long for buffer\n", s); 114 free(s); 115 } 116 117 if ((simplename = strrchr(bakname, '/')) != NULL) 118 simplename = simplename + 1; 119 else 120 simplename = bakname; 121 122 /* 123 * Find a backup name that is not the same file. Change the 124 * first lowercase char into uppercase; if that isn't 125 * sufficient, chop off the first char and try again. 126 */ 127 while (stat(bakname, &filestat) == 0 && 128 orig_device == filestat.st_dev && orig_inode == filestat.st_ino) { 129 /* Skip initial non-lowercase chars. */ 130 for (s = simplename; *s && !islower((unsigned char)*s); s++) 131 ; 132 if (*s) 133 *s = toupper((unsigned char)*s); 134 else 135 memmove(simplename, simplename + 1, 136 strlen(simplename + 1) + 1); 137 } 138 #ifdef DEBUGGING 139 if (debug & 4) 140 say("Moving %s to %s.\n", orig, bakname); 141 #endif 142 if (rename(orig, bakname) < 0) { 143 if (errno != EXDEV || copy_file(orig, bakname) < 0) 144 return -1; 145 } 146 return 0; 147 } 148 149 /* 150 * Copy a file. 151 */ 152 int 153 copy_file(const char *from, const char *to) 154 { 155 int tofd, fromfd; 156 ssize_t i; 157 158 tofd = open(to, O_CREAT|O_TRUNC|O_WRONLY, 0666); 159 if (tofd < 0) 160 return -1; 161 fromfd = open(from, O_RDONLY, 0); 162 if (fromfd < 0) 163 pfatal("internal error, can't reopen %s", from); 164 while ((i = read(fromfd, buf, sizeof buf)) > 0) 165 if (write(tofd, buf, i) != i) 166 pfatal("write to %s failed", to); 167 close(fromfd); 168 close(tofd); 169 return 0; 170 } 171 172 /* 173 * Allocate a unique area for a string. 174 */ 175 char * 176 savestr(const char *s) 177 { 178 char *rv; 179 180 if (!s) 181 s = "Oops"; 182 rv = strdup(s); 183 if (rv == NULL) { 184 if (using_plan_a) 185 out_of_mem = true; 186 else 187 fatal("out of memory\n"); 188 } 189 return rv; 190 } 191 192 /* 193 * Allocate a unique area for a string. Call fatal if out of memory. 194 */ 195 char * 196 xstrdup(const char *s) 197 { 198 char *rv; 199 200 if (!s) 201 s = "Oops"; 202 rv = strdup(s); 203 if (rv == NULL) 204 fatal("out of memory\n"); 205 return rv; 206 } 207 208 /* 209 * Vanilla terminal output (buffered). 210 */ 211 void 212 say(const char *fmt, ...) 213 { 214 va_list ap; 215 216 va_start(ap, fmt); 217 vfprintf(stdout, fmt, ap); 218 va_end(ap); 219 fflush(stdout); 220 } 221 222 /* 223 * Terminal output, pun intended. 224 */ 225 void 226 fatal(const char *fmt, ...) 227 { 228 va_list ap; 229 230 va_start(ap, fmt); 231 fprintf(stderr, "patch: **** "); 232 vfprintf(stderr, fmt, ap); 233 va_end(ap); 234 my_exit(2); 235 } 236 237 /* 238 * Say something from patch, something from the system, then silence . . . 239 */ 240 void 241 pfatal(const char *fmt, ...) 242 { 243 va_list ap; 244 int errnum = errno; 245 246 fprintf(stderr, "patch: **** "); 247 va_start(ap, fmt); 248 vfprintf(stderr, fmt, ap); 249 va_end(ap); 250 fprintf(stderr, ": %s\n", strerror(errnum)); 251 my_exit(2); 252 } 253 254 /* 255 * Get a response from the user via /dev/tty 256 */ 257 void 258 ask(const char *fmt, ...) 259 { 260 va_list ap; 261 ssize_t nr; 262 static int ttyfd = -1; 263 264 va_start(ap, fmt); 265 vfprintf(stdout, fmt, ap); 266 va_end(ap); 267 fflush(stdout); 268 if (ttyfd < 0) 269 ttyfd = open(_PATH_TTY, O_RDONLY); 270 if (ttyfd >= 0) { 271 if ((nr = read(ttyfd, buf, sizeof(buf))) > 0 && 272 buf[nr - 1] == '\n') 273 buf[nr - 1] = '\0'; 274 } 275 if (ttyfd < 0 || nr <= 0) { 276 /* no tty or error reading, pretend user entered 'return' */ 277 putchar('\n'); 278 buf[0] = '\0'; 279 } 280 } 281 282 /* 283 * How to handle certain events when not in a critical region. 284 */ 285 void 286 set_signals(int reset) 287 { 288 static sig_t hupval, intval; 289 290 if (!reset) { 291 hupval = signal(SIGHUP, SIG_IGN); 292 if (hupval != SIG_IGN) 293 hupval = (sig_t) my_exit; 294 intval = signal(SIGINT, SIG_IGN); 295 if (intval != SIG_IGN) 296 intval = (sig_t) my_exit; 297 } 298 signal(SIGHUP, hupval); 299 signal(SIGINT, intval); 300 } 301 302 /* 303 * How to handle certain events when in a critical region. 304 */ 305 void 306 ignore_signals(void) 307 { 308 signal(SIGHUP, SIG_IGN); 309 signal(SIGINT, SIG_IGN); 310 } 311 312 /* 313 * Make sure we'll have the directories to create a file. If `striplast' is 314 * true, ignore the last element of `filename'. 315 */ 316 317 void 318 makedirs(const char *filename, bool striplast) 319 { 320 char *tmpbuf; 321 322 if ((tmpbuf = strdup(filename)) == NULL) 323 fatal("out of memory\n"); 324 325 if (striplast) { 326 char *s = strrchr(tmpbuf, '/'); 327 if (s == NULL) { 328 free(tmpbuf); 329 return; /* nothing to be done */ 330 } 331 *s = '\0'; 332 } 333 if (mkpath(tmpbuf) != 0) 334 pfatal("creation of %s failed", tmpbuf); 335 free(tmpbuf); 336 } 337 338 /* 339 * Make filenames more reasonable. 340 */ 341 char * 342 fetchname(const char *at, bool *exists, int strip_leading) 343 { 344 char *fullname, *name, *t; 345 int sleading, tab; 346 struct stat filestat; 347 348 if (at == NULL || *at == '\0') 349 return NULL; 350 while (isspace((unsigned char)*at)) 351 at++; 352 #ifdef DEBUGGING 353 if (debug & 128) 354 say("fetchname %s %d\n", at, strip_leading); 355 #endif 356 /* So files can be created by diffing against /dev/null. */ 357 if (strnEQ(at, _PATH_DEVNULL, sizeof(_PATH_DEVNULL) - 1)) 358 return NULL; 359 name = fullname = t = savestr(at); 360 361 tab = strchr(t, '\t') != NULL; 362 /* Strip off up to `strip_leading' path components and NUL terminate. */ 363 for (sleading = strip_leading; *t != '\0' && ((tab && *t != '\t') || 364 !isspace((unsigned char)*t)); t++) { 365 if (t[0] == '/' && t[1] != '/' && t[1] != '\0') 366 if (--sleading >= 0) 367 name = t + 1; 368 } 369 *t = '\0'; 370 371 /* 372 * If no -p option was given (957 is the default value!), we were 373 * given a relative pathname, and the leading directories that we 374 * just stripped off all exist, put them back on. 375 */ 376 if (strip_leading == 957 && name != fullname && *fullname != '/') { 377 name[-1] = '\0'; 378 if (stat(fullname, &filestat) == 0 && S_ISDIR(filestat.st_mode)) { 379 name[-1] = '/'; 380 name = fullname; 381 } 382 } 383 name = savestr(name); 384 free(fullname); 385 386 *exists = stat(name, &filestat) == 0; 387 return name; 388 } 389 390 void 391 version(void) 392 { 393 fprintf(stderr, "Patch version 2.0-12u8-OpenBSD\n"); 394 my_exit(EXIT_SUCCESS); 395 } 396 397 /* 398 * Exit with cleanup. 399 */ 400 void 401 my_exit(int status) 402 { 403 unlink(TMPINNAME); 404 if (!toutkeep) 405 unlink(TMPOUTNAME); 406 if (!trejkeep) 407 unlink(TMPREJNAME); 408 unlink(TMPPATNAME); 409 exit(status); 410 } 411