1 #include "EXTERN.h" 2 #include "common.h" 3 #include "INTERN.h" 4 #include "util.h" 5 #include "backupfile.h" 6 7 void my_exit(); 8 9 static char * 10 private_strerror (errnum) 11 int errnum; 12 { 13 extern char *sys_errlist[]; 14 extern int sys_nerr; 15 16 if (errnum > 0 && errnum <= sys_nerr) 17 return sys_errlist[errnum]; 18 return "Unknown system error"; 19 } 20 #define strerror private_strerror 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(*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++); 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 say(pat,arg1,arg2,arg3) 209 char *pat; 210 long arg1,arg2,arg3; 211 { 212 fprintf(stderr, pat, arg1, arg2, arg3); 213 Fflush(stderr); 214 } 215 216 /* Terminal output, pun intended. */ 217 218 void /* very void */ 219 fatal(pat,arg1,arg2,arg3) 220 char *pat; 221 long arg1,arg2,arg3; 222 { 223 fprintf(stderr, "patch: **** "); 224 fprintf(stderr, pat, arg1, arg2, arg3); 225 my_exit(1); 226 } 227 228 /* Say something from patch, something from the system, then silence . . . */ 229 230 void /* very void */ 231 pfatal(pat,arg1,arg2,arg3) 232 char *pat; 233 long arg1,arg2,arg3; 234 { 235 int errnum = errno; 236 237 fprintf(stderr, "patch: **** "); 238 fprintf(stderr, pat, arg1, arg2, arg3); 239 fprintf(stderr, ": %s\n", strerror(errnum)); 240 my_exit(1); 241 } 242 243 /* Get a response from the user, somehow or other. */ 244 245 void 246 ask(pat,arg1,arg2,arg3) 247 char *pat; 248 long arg1,arg2,arg3; 249 { 250 int ttyfd; 251 int r; 252 bool tty2 = isatty(2); 253 254 Sprintf(buf, pat, arg1, arg2, arg3); 255 Fflush(stderr); 256 write(2, buf, strlen(buf)); 257 if (tty2) { /* might be redirected to a file */ 258 r = read(2, buf, sizeof buf); 259 } 260 else if (isatty(1)) { /* this may be new file output */ 261 Fflush(stdout); 262 write(1, buf, strlen(buf)); 263 r = read(1, buf, sizeof buf); 264 } 265 else if ((ttyfd = open("/dev/tty", 2)) >= 0 && isatty(ttyfd)) { 266 /* might be deleted or unwriteable */ 267 write(ttyfd, buf, strlen(buf)); 268 r = read(ttyfd, buf, sizeof buf); 269 Close(ttyfd); 270 } 271 else if (isatty(0)) { /* this is probably patch input */ 272 Fflush(stdin); 273 write(0, buf, strlen(buf)); 274 r = read(0, buf, sizeof buf); 275 } 276 else { /* no terminal at all--default it */ 277 buf[0] = '\n'; 278 r = 1; 279 } 280 if (r <= 0) 281 buf[0] = 0; 282 else 283 buf[r] = '\0'; 284 if (!tty2) 285 say1(buf); 286 } 287 #endif /* lint */ 288 289 /* How to handle certain events when not in a critical region. */ 290 291 void 292 set_signals(reset) 293 int reset; 294 { 295 #ifndef lint 296 #ifdef VOIDSIG 297 static void (*hupval)(),(*intval)(); 298 #else 299 static int (*hupval)(),(*intval)(); 300 #endif 301 302 if (!reset) { 303 hupval = signal(SIGHUP, SIG_IGN); 304 if (hupval != SIG_IGN) 305 #ifdef VOIDSIG 306 hupval = my_exit; 307 #else 308 hupval = (int(*)())my_exit; 309 #endif 310 intval = signal(SIGINT, SIG_IGN); 311 if (intval != SIG_IGN) 312 #ifdef VOIDSIG 313 intval = my_exit; 314 #else 315 intval = (int(*)())my_exit; 316 #endif 317 } 318 Signal(SIGHUP, hupval); 319 Signal(SIGINT, intval); 320 #endif 321 } 322 323 /* How to handle certain events when in a critical region. */ 324 325 void 326 ignore_signals() 327 { 328 #ifndef lint 329 Signal(SIGHUP, SIG_IGN); 330 Signal(SIGINT, SIG_IGN); 331 #endif 332 } 333 334 /* Make sure we'll have the directories to create a file. 335 If `striplast' is TRUE, ignore the last element of `filename'. */ 336 337 void 338 makedirs(filename,striplast) 339 Reg1 char *filename; 340 bool striplast; 341 { 342 char tmpbuf[256]; 343 Reg2 char *s = tmpbuf; 344 char *dirv[20]; /* Point to the NULs between elements. */ 345 Reg3 int i; 346 Reg4 int dirvp = 0; /* Number of finished entries in dirv. */ 347 348 /* Copy `filename' into `tmpbuf' with a NUL instead of a slash 349 between the directories. */ 350 while (*filename) { 351 if (*filename == '/') { 352 filename++; 353 dirv[dirvp++] = s; 354 *s++ = '\0'; 355 } 356 else { 357 *s++ = *filename++; 358 } 359 } 360 *s = '\0'; 361 dirv[dirvp] = s; 362 if (striplast) 363 dirvp--; 364 if (dirvp < 0) 365 return; 366 367 strcpy(buf, "mkdir"); 368 s = buf; 369 for (i=0; i<=dirvp; i++) { 370 struct stat sbuf; 371 372 if (stat(tmpbuf, &sbuf) && errno == ENOENT) { 373 while (*s) s++; 374 *s++ = ' '; 375 strcpy(s, tmpbuf); 376 } 377 *dirv[i] = '/'; 378 } 379 if (s != buf) 380 system(buf); 381 } 382 383 /* Make filenames more reasonable. */ 384 385 char * 386 fetchname(at,strip_leading,assume_exists) 387 char *at; 388 int strip_leading; 389 int assume_exists; 390 { 391 char *fullname; 392 char *name; 393 Reg1 char *t; 394 char tmpbuf[200]; 395 int sleading = strip_leading; 396 397 if (!at) 398 return Nullch; 399 while (isspace(*at)) 400 at++; 401 #ifdef DEBUGGING 402 if (debug & 128) 403 say4("fetchname %s %d %d\n",at,strip_leading,assume_exists); 404 #endif 405 if (strnEQ(at, "/dev/null", 9)) /* so files can be created by diffing */ 406 return Nullch; /* against /dev/null. */ 407 name = fullname = t = savestr(at); 408 409 /* Strip off up to `sleading' leading slashes and null terminate. */ 410 for (; *t && !isspace(*t); t++) 411 if (*t == '/') 412 if (--sleading >= 0) 413 name = t+1; 414 *t = '\0'; 415 416 /* If no -p option was given (957 is the default value!), 417 we were given a relative pathname, 418 and the leading directories that we just stripped off all exist, 419 put them back on. */ 420 if (strip_leading == 957 && name != fullname && *fullname != '/') { 421 name[-1] = '\0'; 422 if (stat(fullname, &filestat) == 0 && S_ISDIR (filestat.st_mode)) { 423 name[-1] = '/'; 424 name=fullname; 425 } 426 } 427 428 name = savestr(name); 429 free(fullname); 430 431 if (stat(name, &filestat) && !assume_exists) { 432 char *filebase = basename(name); 433 int pathlen = filebase - name; 434 435 /* Put any leading path into `tmpbuf'. */ 436 strncpy(tmpbuf, name, pathlen); 437 438 #define try(f, a1, a2) (Sprintf(tmpbuf + pathlen, f, a1, a2), stat(tmpbuf, &filestat) == 0) 439 if ( try("RCS/%s%s", filebase, RCSSUFFIX) 440 || try("RCS/%s" , filebase, 0) 441 || try( "%s%s", filebase, RCSSUFFIX) 442 || try("SCCS/%s%s", SCCSPREFIX, filebase) 443 || try( "%s%s", SCCSPREFIX, filebase)) 444 return name; 445 free(name); 446 name = Nullch; 447 } 448 449 return name; 450 } 451