1 /* $NetBSD: util.c,v 1.21 2005/03/25 23:00:55 skd Exp $ */ 2 3 /* 4 * Copyright (c) 1988, Larry Wall 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following condition 8 * is met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this condition and the following disclaimer. 11 * 12 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 13 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 14 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 15 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 16 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 17 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 18 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 19 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 20 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 21 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 22 * SUCH DAMAGE. 23 */ 24 25 #include <sys/cdefs.h> 26 #ifndef lint 27 __RCSID("$NetBSD: util.c,v 1.21 2005/03/25 23:00:55 skd Exp $"); 28 #endif /* not lint */ 29 30 #include <sys/param.h> 31 32 #include "EXTERN.h" 33 #include "common.h" 34 #include "INTERN.h" 35 #include "util.h" 36 #include "backupfile.h" 37 38 #include <stdarg.h> 39 #include <stdlib.h> 40 #include <unistd.h> 41 #include <fcntl.h> 42 43 /* 44 * Rename a file, copying it if necessary. 45 */ 46 int 47 move_file(char *from, char *to) 48 { 49 char bakname[MAXPATHLEN]; 50 char *s; 51 size_t i; 52 int fromfd; 53 54 if ( check_only == TRUE ) 55 return(0); 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, 0); 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(1, buf, i) != 1) 69 pfatal("write failed"); 70 Close(fromfd); 71 return 0; 72 } 73 74 if (origprae) { 75 strlcpy(bakname, origprae, sizeof(bakname)); 76 strlcat(bakname, to, sizeof(bakname)); 77 } else { 78 #ifndef NODIR 79 char *backupname = find_backup_file_name(to); 80 strlcpy(bakname, backupname, sizeof(bakname)); 81 free(backupname); 82 #else /* NODIR */ 83 strlcpy(bakname, to, sizeof(bakname)); 84 strlcat(bakname, simple_backup_suffix, sizeof(bakname)); 85 #endif /* NODIR */ 86 } 87 88 if (stat(to, &filestat) == 0) { /* output file exists */ 89 dev_t to_device = filestat.st_dev; 90 ino_t to_inode = filestat.st_ino; 91 char *simplename = bakname; 92 93 for (s = bakname; *s; s++) { 94 if (*s == '/') 95 simplename = s + 1; 96 } 97 /* 98 * Find a backup name that is not the same file. 99 * Change the first lowercase char into uppercase; 100 * if that isn't sufficient, chop off the first char 101 * and try again. 102 */ 103 while (stat(bakname, &filestat) == 0 && 104 to_device == filestat.st_dev && 105 to_inode == filestat.st_ino) { 106 /* Skip initial non-lowercase chars. */ 107 for (s = simplename; 108 *s && *s == toupper((unsigned char)*s); 109 s++) 110 ; 111 if (*s) 112 *s = toupper((unsigned char)*s); 113 else 114 strcpy(simplename, simplename + 1); 115 } 116 while (unlink(bakname) >= 0) 117 ; /* while() is for benefit of Eunice */ 118 #ifdef DEBUGGING 119 if (debug & 4) 120 say("Moving %s to %s.\n", to, bakname); 121 #endif 122 if (link(to, bakname) < 0) { 123 /* 124 * Maybe `to' is a symlink into a different file 125 * system. Copying replaces the symlink with a file; 126 * using rename would be better. 127 */ 128 int tofd; 129 int bakfd; 130 131 bakfd = creat(bakname, 0666); 132 if (bakfd < 0) { 133 say("Can't backup %s, output is in %s: %s\n", 134 to, from, strerror(errno)); 135 return -1; 136 } 137 tofd = open(to, 0); 138 if (tofd < 0) 139 pfatal("internal error, can't open %s", to); 140 while ((i = read(tofd, buf, sizeof buf)) > 0) 141 if (write(bakfd, buf, i) != i) 142 pfatal("write failed"); 143 Close(tofd); 144 Close(bakfd); 145 } 146 while (unlink(to) >= 0) ; 147 } 148 #ifdef DEBUGGING 149 if (debug & 4) 150 say("Moving %s to %s.\n", from, to); 151 #endif 152 if (link(from, to) < 0) { /* different file system? */ 153 int tofd; 154 155 tofd = creat(to, 0666); 156 if (tofd < 0) { 157 say("Can't create %s, output is in %s: %s\n", 158 to, from, strerror(errno)); 159 return -1; 160 } 161 fromfd = open(from, 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 failed"); 167 Close(fromfd); 168 Close(tofd); 169 } 170 Unlink(from); 171 return 0; 172 } 173 174 /* 175 * Copy a file. 176 */ 177 void 178 copy_file(char *from, char *to) 179 { 180 int tofd; 181 int fromfd; 182 size_t i; 183 184 tofd = creat(to, 0666); 185 if (tofd < 0) 186 pfatal("can't create %s", to); 187 fromfd = open(from, 0); 188 if (fromfd < 0) 189 pfatal("internal error, can't reopen %s", from); 190 while ((i = read(fromfd, buf, sizeof buf)) > 0) 191 if (write(tofd, buf, i) != i) 192 pfatal("write to %s failed", to); 193 Close(fromfd); 194 Close(tofd); 195 } 196 197 /* 198 * malloc with result test. 199 */ 200 void * 201 xmalloc(size_t size) 202 { 203 void *p; 204 205 if ((p = malloc(size)) == NULL) 206 fatal("out of memory\n"); 207 return p; 208 } 209 210 /* 211 * realloc with result test. 212 */ 213 void * 214 xrealloc(void *ptr, size_t size) 215 { 216 void *p; 217 218 if ((p = realloc(ptr, size)) == NULL) 219 fatal("out of memory\n"); 220 return p; 221 } 222 223 /* 224 * strdup with result test. 225 */ 226 char * 227 xstrdup(const char *s) 228 { 229 char *p; 230 231 if ((p = strdup(s)) == NULL) 232 fatal("out of memory\n"); 233 return p; 234 } 235 236 /* 237 * Vanilla terminal output. 238 */ 239 void 240 say(const char *pat, ...) 241 { 242 va_list ap; 243 va_start(ap, pat); 244 245 vfprintf(stderr, pat, ap); 246 va_end(ap); 247 Fflush(stderr); 248 } 249 250 /* 251 * Terminal output, pun intended. 252 */ 253 void /* very void */ 254 fatal(const char *pat, ...) 255 { 256 va_list ap; 257 va_start(ap, pat); 258 259 fprintf(stderr, "patch: **** "); 260 vfprintf(stderr, pat, ap); 261 va_end(ap); 262 my_exit(1); 263 } 264 265 /* 266 * Say something from patch, something from the system, then silence... 267 */ 268 void /* very void */ 269 pfatal(const char *pat, ...) 270 { 271 va_list ap; 272 int errnum = errno; 273 va_start(ap, pat); 274 275 fprintf(stderr, "patch: **** "); 276 vfprintf(stderr, pat, ap); 277 fprintf(stderr, ": %s\n", strerror(errnum)); 278 va_end(ap); 279 my_exit(1); 280 } 281 282 /* 283 * Get a response from the user, somehow or other. 284 */ 285 void 286 ask(const char *pat, ...) 287 { 288 int ttyfd; 289 int r; 290 bool tty2 = isatty(2); 291 va_list ap; 292 va_start(ap, pat); 293 294 (void)vsprintf(buf, pat, ap); 295 va_end(ap); 296 Fflush(stderr); 297 write(2, buf, strlen(buf)); 298 if (tty2) { /* might be redirected to a file */ 299 r = read(2, buf, sizeof buf); 300 } else if (isatty(1)) { /* this may be new file output */ 301 Fflush(stdout); 302 write(1, buf, strlen(buf)); 303 r = read(1, buf, sizeof buf); 304 } else if ((ttyfd = open("/dev/tty", 2)) >= 0 && isatty(ttyfd)) { 305 /* might be deleted or unwritable */ 306 write(ttyfd, buf, strlen(buf)); 307 r = read(ttyfd, buf, sizeof buf); 308 Close(ttyfd); 309 } else if (isatty(0)) { /* this is probably patch input */ 310 Fflush(stdin); 311 write(0, buf, strlen(buf)); 312 r = read(0, buf, sizeof buf); 313 } else { /* no terminal at all--default it */ 314 buf[0] = '\n'; 315 r = 1; 316 } 317 if (r <= 0) 318 buf[0] = 0; 319 else 320 buf[r] = '\0'; 321 if (!tty2) 322 say("%s", buf); 323 } 324 325 /* 326 * How to handle certain events when not in a critical region. 327 */ 328 void 329 set_signals(int reset) 330 { 331 static void (*hupval)(int); 332 static void (*intval)(int); 333 334 if (!reset) { 335 hupval = signal(SIGHUP, SIG_IGN); 336 if (hupval != SIG_IGN) 337 hupval = my_exit; 338 intval = signal(SIGINT, SIG_IGN); 339 if (intval != SIG_IGN) 340 intval = my_exit; 341 } 342 Signal(SIGHUP, hupval); 343 Signal(SIGINT, intval); 344 } 345 346 /* 347 * How to handle certain events when in a critical region. 348 */ 349 void 350 ignore_signals() 351 { 352 Signal(SIGHUP, SIG_IGN); 353 Signal(SIGINT, SIG_IGN); 354 } 355 356 /* 357 * Make sure we'll have the directories to create a file. 358 * If `striplast' is TRUE, ignore the last element of `filename'. 359 */ 360 void 361 makedirs(char *filename, bool striplast) 362 { 363 char tmpbuf[MAXPATHLEN]; 364 char *s = tmpbuf; 365 char *dirv[MAXPATHLEN]; /* Point to the NULs between elements. */ 366 int i; 367 int dirvp = 0; /* Number of finished entries in dirv. */ 368 369 /* 370 * Copy `filename' into `tmpbuf' with a NUL instead of a slash 371 * between the directories. 372 */ 373 while (*filename) { 374 if (*filename == '/') { 375 filename++; 376 dirv[dirvp++] = s; 377 *s++ = '\0'; 378 } else { 379 *s++ = *filename++; 380 } 381 } 382 *s = '\0'; 383 dirv[dirvp] = s; 384 if (striplast) 385 dirvp--; 386 if (dirvp < 0) 387 return; 388 389 strlcpy(buf, "mkdir", sizeof(buf)); 390 s = buf; 391 for (i = 0; i <= dirvp; i++) { 392 struct stat sbuf; 393 394 if (stat(tmpbuf, &sbuf) && errno == ENOENT) { 395 while (*s) 396 s++; 397 *s++ = ' '; 398 strlcpy(s, tmpbuf, sizeof(buf) - (s - buf)); 399 } 400 *dirv[i] = '/'; 401 } 402 if (s != buf) 403 system(buf); 404 } 405 406 /* 407 * Make filenames more reasonable. 408 */ 409 char * 410 fetchname(char *at, int strip_leading, int assume_exists) 411 { 412 char *fullname; 413 char *name; 414 char *t; 415 char tmpbuf[MAXPATHLEN]; 416 int sleading = strip_leading; 417 418 if (!at) 419 return NULL; 420 while (isspace((unsigned char)*at)) 421 at++; 422 #ifdef DEBUGGING 423 if (debug & 128) 424 say("fetchname %s %d %d\n", at, strip_leading, assume_exists); 425 #endif 426 filename_is_dev_null = FALSE; 427 if (strnEQ(at, "/dev/null", 9)) { 428 /* So files can be created by diffing against /dev/null. */ 429 filename_is_dev_null = TRUE; 430 return NULL; 431 } 432 name = fullname = t = xstrdup(at); 433 434 /* Strip off up to `sleading' leading slashes and null terminate. */ 435 for (; *t && !isspace((unsigned char)*t); t++) 436 if (*t == '/') 437 if (--sleading >= 0) 438 name = t + 1; 439 *t = '\0'; 440 441 /* 442 * If no -p option was given (957 is the default value!), 443 * we were given a relative pathname, 444 * and the leading directories that we just stripped off all exist, 445 * put them back on. 446 */ 447 if (strip_leading == 957 && name != fullname && *fullname != '/') { 448 name[-1] = '\0'; 449 if (stat(fullname, &filestat) == 0 && 450 S_ISDIR(filestat.st_mode)) { 451 name[-1] = '/'; 452 name = fullname; 453 } 454 } 455 456 name = xstrdup(name); 457 free(fullname); 458 459 if (stat(name, &filestat) && !assume_exists) { 460 char *filebase = basename(name); 461 size_t pathlen = filebase - name; 462 463 /* Put any leading path into `tmpbuf'. */ 464 if (pathlen >= sizeof(tmpbuf)) 465 return NULL; 466 strncpy(tmpbuf, name, pathlen); 467 tmpbuf[pathlen] = '\0'; 468 469 #define try(f, a1, a2) \ 470 (snprintf(tmpbuf + pathlen, sizeof(tmpbuf) - pathlen, f, a1, a2), \ 471 stat(tmpbuf, &filestat) == 0) 472 #define try1(f, a1) \ 473 (snprintf(tmpbuf + pathlen, sizeof(tmpbuf) - pathlen, f, a1), \ 474 stat(tmpbuf, &filestat) == 0) 475 if (try("RCS/%s%s", filebase, RCSSUFFIX) || 476 try1("RCS/%s" , filebase) || 477 try( "%s%s", filebase, RCSSUFFIX) || 478 try("SCCS/%s%s", SCCSPREFIX, filebase) || 479 try( "%s%s", SCCSPREFIX, filebase)) 480 return name; 481 free(name); 482 name = NULL; 483 } 484 485 return name; 486 } 487