Home | History | Annotate | Line # | Download | only in patch
util.c revision 1.7.2.1
      1 /*	$NetBSD: util.c,v 1.7.2.1 2000/10/19 16:32:19 he Exp $	*/
      2 #include <sys/cdefs.h>
      3 #ifndef lint
      4 __RCSID("$NetBSD: util.c,v 1.7.2.1 2000/10/19 16:32:19 he 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 	say2("%s",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