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