1 /* $OpenBSD: util.c,v 1.9 1999/12/04 21:00:03 provos Exp $ */ 2 3 #ifndef lint 4 static char rcsid[] = "$OpenBSD: util.c,v 1.9 1999/12/04 21:00:03 provos 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 if (strlcpy(bakname, origprae, sizeof(bakname)) >= sizeof(bakname) || 49 strlcat(bakname, to, sizeof(bakname)) >= sizeof(bakname)) 50 fatal2("filename %s too long for buffer\n", origprae); 51 } else { 52 #ifndef NODIR 53 char *backupname = find_backup_file_name(to); 54 if (backupname == (char *) 0) 55 fatal1("out of memory\n"); 56 if (strlcpy(bakname, backupname, sizeof(bakname)) >= sizeof(bakname)) 57 fatal2("filename %s too long for buffer\n", backupname); 58 free(backupname); 59 #else /* NODIR */ 60 if (strlcpy(bakname, to, sizeof(bakname)) >= sizeof(bakname) || 61 strlcat(bakname, simple_backup_suffix, sizeof(bakname)) >= sizeof(bakname)) 62 fatal2("filename %s too long for buffer\n", to); 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(*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, O_RDONLY); 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, O_RDONLY); 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, O_RDONLY); 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++)) 188 ; 189 } 190 return rv; 191 } 192 193 #if defined(lint) && defined(CANVARARG) 194 195 /*VARARGS ARGSUSED*/ 196 say(pat) char *pat; { ; } 197 /*VARARGS ARGSUSED*/ 198 fatal(pat) char *pat; { ; } 199 /*VARARGS ARGSUSED*/ 200 pfatal(pat) char *pat; { ; } 201 /*VARARGS ARGSUSED*/ 202 ask(pat) char *pat; { ; } 203 204 #else 205 206 /* Vanilla terminal output (buffered). */ 207 208 void 209 say(pat,arg1,arg2,arg3) 210 char *pat; 211 long arg1,arg2,arg3; 212 { 213 fprintf(stderr, pat, arg1, arg2, arg3); 214 Fflush(stderr); 215 } 216 217 /* Terminal output, pun intended. */ 218 219 void /* very void */ 220 fatal(pat,arg1,arg2,arg3) 221 char *pat; 222 long arg1,arg2,arg3; 223 { 224 fprintf(stderr, "patch: **** "); 225 fprintf(stderr, pat, arg1, arg2, arg3); 226 my_exit(1); 227 } 228 229 /* Say something from patch, something from the system, then silence . . . */ 230 231 void /* very void */ 232 pfatal(pat,arg1,arg2,arg3) 233 char *pat; 234 long arg1,arg2,arg3; 235 { 236 int errnum = errno; 237 238 fprintf(stderr, "patch: **** "); 239 fprintf(stderr, pat, arg1, arg2, arg3); 240 fprintf(stderr, ": %s\n", strerror(errnum)); 241 my_exit(1); 242 } 243 244 /* Get a response from the user, somehow or other. */ 245 246 void 247 ask(pat,arg1,arg2,arg3) 248 char *pat; 249 long arg1,arg2,arg3; 250 { 251 int ttyfd; 252 int r; 253 bool tty2 = isatty(2); 254 255 Snprintf(buf, sizeof buf, pat, arg1, arg2, arg3); 256 Fflush(stderr); 257 write(2, buf, strlen(buf)); 258 if (tty2) { /* might be redirected to a file */ 259 r = read(2, buf, sizeof buf); 260 } 261 else if (isatty(1)) { /* this may be new file output */ 262 Fflush(stdout); 263 write(1, buf, strlen(buf)); 264 r = read(1, buf, sizeof buf); 265 } 266 else if ((ttyfd = open(_PATH_TTY, O_RDWR)) >= 0 && isatty(ttyfd)) { 267 /* might be deleted or unwriteable */ 268 write(ttyfd, buf, strlen(buf)); 269 r = read(ttyfd, buf, sizeof buf); 270 Close(ttyfd); 271 } 272 else if (isatty(0)) { /* this is probably patch input */ 273 Fflush(stdin); 274 write(0, buf, strlen(buf)); 275 r = read(0, buf, sizeof buf); 276 } 277 else { /* no terminal at all--default it */ 278 buf[0] = '\n'; 279 r = 1; 280 } 281 if (r <= 0) 282 buf[0] = 0; 283 else 284 buf[r] = '\0'; 285 if (!tty2) 286 say1(buf); 287 } 288 #endif /* lint */ 289 290 /* How to handle certain events when not in a critical region. */ 291 292 void 293 set_signals(reset) 294 int reset; 295 { 296 #ifndef lint 297 static sig_t hupval, intval; 298 299 if (!reset) { 300 hupval = signal(SIGHUP, SIG_IGN); 301 if (hupval != SIG_IGN) 302 hupval = (sig_t)my_exit; 303 intval = signal(SIGINT, SIG_IGN); 304 if (intval != SIG_IGN) 305 intval = (sig_t)my_exit; 306 } 307 Signal(SIGHUP, hupval); 308 Signal(SIGINT, intval); 309 #endif 310 } 311 312 /* How to handle certain events when in a critical region. */ 313 314 void 315 ignore_signals() 316 { 317 #ifndef lint 318 Signal(SIGHUP, SIG_IGN); 319 Signal(SIGINT, SIG_IGN); 320 #endif 321 } 322 323 /* Make sure we'll have the directories to create a file. 324 If `striplast' is TRUE, ignore the last element of `filename'. */ 325 326 void 327 makedirs(filename,striplast) 328 Reg1 char *filename; 329 bool striplast; 330 { 331 char *tmpbuf; 332 333 if ((tmpbuf = strdup(filename)) == NULL) 334 fatal1("out of memory\n"); 335 336 if (striplast) { 337 char *s = strrchr(tmpbuf, '/'); 338 if (s == NULL) 339 return; /* nothing to be done */ 340 *s = '\0'; 341 } 342 343 strcpy(buf, "/bin/mkdir -p "); 344 if (strlcat(buf, tmpbuf, sizeof(buf)) >= sizeof(buf)) 345 fatal2("buffer too small to hold %.20s...\n", tmpbuf); 346 347 if (system(buf)) 348 pfatal2("%.40s failed", buf); 349 } 350 351 /* Make filenames more reasonable. */ 352 353 char * 354 fetchname(at,strip_leading,assume_exists) 355 char *at; 356 int strip_leading; 357 int assume_exists; 358 { 359 char *fullname; 360 char *name; 361 Reg1 char *t; 362 char tmpbuf[200]; 363 int sleading = strip_leading; 364 365 if (!at || *at == '\0') 366 return Nullch; 367 while (isspace(*at)) 368 at++; 369 #ifdef DEBUGGING 370 if (debug & 128) 371 say4("fetchname %s %d %d\n",at,strip_leading,assume_exists); 372 #endif 373 if (strnEQ(at, "/dev/null", 9)) /* so files can be created by diffing */ 374 return Nullch; /* against /dev/null. */ 375 name = fullname = t = savestr(at); 376 377 /* Strip off up to `sleading' leading slashes and null terminate. */ 378 for (; *t && !isspace(*t); t++) 379 if (*t == '/') 380 if (--sleading >= 0) 381 name = t+1; 382 *t = '\0'; 383 384 /* If no -p option was given (957 is the default value!), 385 we were given a relative pathname, 386 and the leading directories that we just stripped off all exist, 387 put them back on. */ 388 if (strip_leading == 957 && name != fullname && *fullname != '/') { 389 name[-1] = '\0'; 390 if (stat(fullname, &filestat) == 0 && S_ISDIR (filestat.st_mode)) { 391 name[-1] = '/'; 392 name=fullname; 393 } 394 } 395 396 name = savestr(name); 397 free(fullname); 398 399 if (stat(name, &filestat) && !assume_exists) { 400 char *filebase = basename(name); 401 char *filedir = dirname(name); 402 403 #define try(f, a1, a2, a3) (Snprintf(tmpbuf, sizeof tmpbuf, f, a1, a2, a3), stat(tmpbuf, &filestat) == 0) 404 if ( try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) 405 || try("%s/RCS/%s%s", filedir, filebase, "") 406 || try( "%s/%s%s", filedir, filebase, RCSSUFFIX) 407 || try("%s/SCCS/%s%s", filedir, SCCSPREFIX, filebase) 408 || try( "%s/%s%s", filedir, SCCSPREFIX, filebase)) 409 return name; 410 free(name); 411 name = Nullch; 412 } 413 414 return name; 415 } 416