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