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