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