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