1 /* $NetBSD: util.c,v 1.16 2003/07/12 13:47:44 itojun 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.16 2003/07/12 13:47:44 itojun 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 /* to stdout? */ 55 56 if (strEQ(to, "-")) { 57 #ifdef DEBUGGING 58 if (debug & 4) 59 say("Moving %s to stdout.\n", from); 60 #endif 61 fromfd = open(from, 0); 62 if (fromfd < 0) 63 pfatal("internal error, can't reopen %s", from); 64 while ((i = read(fromfd, buf, sizeof buf)) > 0) 65 if (write(1, buf, i) != 1) 66 pfatal("write failed"); 67 Close(fromfd); 68 return 0; 69 } 70 71 if (origprae) { 72 Strcpy(bakname, origprae); 73 Strcat(bakname, to); 74 } else { 75 #ifndef NODIR 76 char *backupname = find_backup_file_name(to); 77 Strcpy(bakname, backupname); 78 free(backupname); 79 #else /* NODIR */ 80 Strcpy(bakname, to); 81 Strcat(bakname, simple_backup_suffix); 82 #endif /* NODIR */ 83 } 84 85 if (stat(to, &filestat) == 0) { /* output file exists */ 86 dev_t to_device = filestat.st_dev; 87 ino_t to_inode = filestat.st_ino; 88 char *simplename = bakname; 89 90 for (s = bakname; *s; s++) { 91 if (*s == '/') 92 simplename = s + 1; 93 } 94 /* 95 * Find a backup name that is not the same file. 96 * Change the first lowercase char into uppercase; 97 * if that isn't sufficient, chop off the first char 98 * and try again. 99 */ 100 while (stat(bakname, &filestat) == 0 && 101 to_device == filestat.st_dev && 102 to_inode == filestat.st_ino) { 103 /* Skip initial non-lowercase chars. */ 104 for (s = simplename; 105 *s && !islower((unsigned char)*s); 106 s++) 107 ; 108 if (*s) 109 *s = toupper(*s); 110 else 111 Strcpy(simplename, simplename + 1); 112 } 113 while (unlink(bakname) >= 0) 114 ; /* while() is for benefit of Eunice */ 115 #ifdef DEBUGGING 116 if (debug & 4) 117 say("Moving %s to %s.\n", to, bakname); 118 #endif 119 if (link(to, bakname) < 0) { 120 /* 121 * Maybe `to' is a symlink into a different file 122 * system. Copying replaces the symlink with a file; 123 * using rename would be better. 124 */ 125 int tofd; 126 int bakfd; 127 128 bakfd = creat(bakname, 0666); 129 if (bakfd < 0) { 130 say("Can't backup %s, output is in %s: %s\n", 131 to, from, strerror(errno)); 132 return -1; 133 } 134 tofd = open(to, 0); 135 if (tofd < 0) 136 pfatal("internal error, can't open %s", to); 137 while ((i = read(tofd, buf, sizeof buf)) > 0) 138 if (write(bakfd, buf, i) != i) 139 pfatal("write failed"); 140 Close(tofd); 141 Close(bakfd); 142 } 143 while (unlink(to) >= 0) ; 144 } 145 #ifdef DEBUGGING 146 if (debug & 4) 147 say("Moving %s to %s.\n", from, to); 148 #endif 149 if (link(from, to) < 0) { /* different file system? */ 150 int tofd; 151 152 tofd = creat(to, 0666); 153 if (tofd < 0) { 154 say("Can't create %s, output is in %s: %s\n", 155 to, from, strerror(errno)); 156 return -1; 157 } 158 fromfd = open(from, 0); 159 if (fromfd < 0) 160 pfatal("internal error, can't reopen %s", from); 161 while ((i = read(fromfd, buf, sizeof buf)) > 0) 162 if (write(tofd, buf, i) != i) 163 pfatal("write failed"); 164 Close(fromfd); 165 Close(tofd); 166 } 167 Unlink(from); 168 return 0; 169 } 170 171 /* 172 * Copy a file. 173 */ 174 void 175 copy_file(char *from, char *to) 176 { 177 int tofd; 178 int fromfd; 179 size_t i; 180 181 tofd = creat(to, 0666); 182 if (tofd < 0) 183 pfatal("can't create %s", to); 184 fromfd = open(from, 0); 185 if (fromfd < 0) 186 pfatal("internal error, can't reopen %s", from); 187 while ((i = read(fromfd, buf, sizeof buf)) > 0) 188 if (write(tofd, buf, i) != i) 189 pfatal("write to %s failed", to); 190 Close(fromfd); 191 Close(tofd); 192 } 193 194 /* 195 * malloc with result test. 196 */ 197 void * 198 xmalloc(size_t size) 199 { 200 void *p; 201 202 if ((p = malloc(size)) == NULL) 203 fatal("out of memory\n"); 204 return p; 205 } 206 207 /* 208 * realloc with result test. 209 */ 210 void * 211 xrealloc(void *ptr, size_t size) 212 { 213 void *p; 214 215 if ((p = realloc(ptr, size)) == NULL) 216 fatal("out of memory\n"); 217 return p; 218 } 219 220 /* 221 * strdup with result test. 222 */ 223 char * 224 xstrdup(const char *s) 225 { 226 char *p; 227 228 if ((p = strdup(s)) == NULL) 229 fatal("out of memory\n"); 230 return p; 231 } 232 233 /* 234 * Vanilla terminal output. 235 */ 236 void 237 say(const char *pat, ...) 238 { 239 va_list ap; 240 va_start(ap, pat); 241 242 vfprintf(stderr, pat, ap); 243 va_end(ap); 244 Fflush(stderr); 245 } 246 247 /* 248 * Terminal output, pun intended. 249 */ 250 void /* very void */ 251 fatal(const char *pat, ...) 252 { 253 va_list ap; 254 va_start(ap, pat); 255 256 fprintf(stderr, "patch: **** "); 257 vfprintf(stderr, pat, ap); 258 va_end(ap); 259 my_exit(1); 260 } 261 262 /* 263 * Say something from patch, something from the system, then silence... 264 */ 265 void /* very void */ 266 pfatal(const char *pat, ...) 267 { 268 va_list ap; 269 int errnum = errno; 270 va_start(ap, pat); 271 272 fprintf(stderr, "patch: **** "); 273 vfprintf(stderr, pat, ap); 274 fprintf(stderr, ": %s\n", strerror(errnum)); 275 va_end(ap); 276 my_exit(1); 277 } 278 279 /* 280 * Get a response from the user, somehow or other. 281 */ 282 void 283 ask(const char *pat, ...) 284 { 285 int ttyfd; 286 int r; 287 bool tty2 = isatty(2); 288 va_list ap; 289 va_start(ap, pat); 290 291 (void)vsprintf(buf, pat, ap); 292 va_end(ap); 293 Fflush(stderr); 294 write(2, buf, strlen(buf)); 295 if (tty2) { /* might be redirected to a file */ 296 r = read(2, buf, sizeof buf); 297 } else if (isatty(1)) { /* this may be new file output */ 298 Fflush(stdout); 299 write(1, buf, strlen(buf)); 300 r = read(1, buf, sizeof buf); 301 } else if ((ttyfd = open("/dev/tty", 2)) >= 0 && isatty(ttyfd)) { 302 /* might be deleted or unwritable */ 303 write(ttyfd, buf, strlen(buf)); 304 r = read(ttyfd, buf, sizeof buf); 305 Close(ttyfd); 306 } else if (isatty(0)) { /* this is probably patch input */ 307 Fflush(stdin); 308 write(0, buf, strlen(buf)); 309 r = read(0, buf, sizeof buf); 310 } else { /* no terminal at all--default it */ 311 buf[0] = '\n'; 312 r = 1; 313 } 314 if (r <= 0) 315 buf[0] = 0; 316 else 317 buf[r] = '\0'; 318 if (!tty2) 319 say("%s", buf); 320 } 321 322 /* 323 * How to handle certain events when not in a critical region. 324 */ 325 void 326 set_signals(int reset) 327 { 328 static void (*hupval)(int); 329 static void (*intval)(int); 330 331 if (!reset) { 332 hupval = signal(SIGHUP, SIG_IGN); 333 if (hupval != SIG_IGN) 334 hupval = my_exit; 335 intval = signal(SIGINT, SIG_IGN); 336 if (intval != SIG_IGN) 337 intval = my_exit; 338 } 339 Signal(SIGHUP, hupval); 340 Signal(SIGINT, intval); 341 } 342 343 /* 344 * How to handle certain events when in a critical region. 345 */ 346 void 347 ignore_signals() 348 { 349 Signal(SIGHUP, SIG_IGN); 350 Signal(SIGINT, SIG_IGN); 351 } 352 353 /* 354 * Make sure we'll have the directories to create a file. 355 * If `striplast' is TRUE, ignore the last element of `filename'. 356 */ 357 void 358 makedirs(char *filename, bool striplast) 359 { 360 char tmpbuf[MAXPATHLEN]; 361 char *s = tmpbuf; 362 char *dirv[MAXPATHLEN]; /* Point to the NULs between elements. */ 363 int i; 364 int dirvp = 0; /* Number of finished entries in dirv. */ 365 366 /* 367 * Copy `filename' into `tmpbuf' with a NUL instead of a slash 368 * between the directories. 369 */ 370 while (*filename) { 371 if (*filename == '/') { 372 filename++; 373 dirv[dirvp++] = s; 374 *s++ = '\0'; 375 } else { 376 *s++ = *filename++; 377 } 378 } 379 *s = '\0'; 380 dirv[dirvp] = s; 381 if (striplast) 382 dirvp--; 383 if (dirvp < 0) 384 return; 385 386 strcpy(buf, "mkdir"); 387 s = buf; 388 for (i = 0; i <= dirvp; i++) { 389 struct stat sbuf; 390 391 if (stat(tmpbuf, &sbuf) && errno == ENOENT) { 392 while (*s) 393 s++; 394 *s++ = ' '; 395 strcpy(s, tmpbuf); 396 } 397 *dirv[i] = '/'; 398 } 399 if (s != buf) 400 system(buf); 401 } 402 403 /* 404 * Make filenames more reasonable. 405 */ 406 char * 407 fetchname(char *at, int strip_leading, int assume_exists) 408 { 409 char *fullname; 410 char *name; 411 char *t; 412 char tmpbuf[MAXPATHLEN]; 413 int sleading = strip_leading; 414 415 if (!at) 416 return NULL; 417 while (isspace((unsigned char)*at)) 418 at++; 419 #ifdef DEBUGGING 420 if (debug & 128) 421 say("fetchname %s %d %d\n", at, strip_leading, assume_exists); 422 #endif 423 filename_is_dev_null = FALSE; 424 if (strnEQ(at, "/dev/null", 9)) { 425 /* So files can be created by diffing against /dev/null. */ 426 filename_is_dev_null = TRUE; 427 return NULL; 428 } 429 name = fullname = t = xstrdup(at); 430 431 /* Strip off up to `sleading' leading slashes and null terminate. */ 432 for (; *t && !isspace((unsigned char)*t); t++) 433 if (*t == '/') 434 if (--sleading >= 0) 435 name = t + 1; 436 *t = '\0'; 437 438 /* 439 * If no -p option was given (957 is the default value!), 440 * we were given a relative pathname, 441 * and the leading directories that we just stripped off all exist, 442 * put them back on. 443 */ 444 if (strip_leading == 957 && name != fullname && *fullname != '/') { 445 name[-1] = '\0'; 446 if (stat(fullname, &filestat) == 0 && 447 S_ISDIR(filestat.st_mode)) { 448 name[-1] = '/'; 449 name = fullname; 450 } 451 } 452 453 name = xstrdup(name); 454 free(fullname); 455 456 if (stat(name, &filestat) && !assume_exists) { 457 char *filebase = basename(name); 458 size_t pathlen = filebase - name; 459 460 /* Put any leading path into `tmpbuf'. */ 461 strncpy(tmpbuf, name, pathlen); 462 463 #define try(f, a1, a2) \ 464 (Sprintf(tmpbuf + pathlen, f, a1, a2), stat(tmpbuf, &filestat) == 0) 465 #define try1(f, a1) \ 466 (Sprintf(tmpbuf + pathlen, f, a1), stat(tmpbuf, &filestat) == 0) 467 if (try("RCS/%s%s", filebase, RCSSUFFIX) || 468 try1("RCS/%s" , filebase) || 469 try( "%s%s", filebase, RCSSUFFIX) || 470 try("SCCS/%s%s", SCCSPREFIX, filebase) || 471 try( "%s%s", SCCSPREFIX, filebase)) 472 return name; 473 free(name); 474 name = NULL; 475 } 476 477 return name; 478 } 479