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