* Preserve the timestamp of the source file if necesary.
* Preserve the timestamp of the source file if necessary.
install(from_name, to_name, fset, flags)	char *from_name, *to_name;	u_long fset;	u_int flags;{	struct stat from_sb, to_sb;	struct utimbuf utb;	int devnull, from_fd, to_fd, serrno, files_match = 0;	char *p;	(void)memset((void *)&from_sb, 0, sizeof(from_sb));	(void)memset((void *)&to_sb, 0, sizeof(to_sb));	/* If try to install NULL file to a directory, fails. */	if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL)) {		if (stat(from_name, &from_sb))			err(EX_OSERR, "%s", from_name);		if (!S_ISREG(from_sb.st_mode))			errx(EX_OSERR, "%s: %s", from_name, strerror(EFTYPE));		/* Build the target path. */		if (flags & DIRECTORY) {			(void)snprintf(pathbuf, sizeof(pathbuf), "%s/%s",			    to_name,			    (p = strrchr(from_name, '/')) ? ++p : from_name);			to_name = pathbuf;		}		devnull = 0;	} else {		devnull = 1;	}	if (stat(to_name, &to_sb) == 0) {		/* Only compare against regular files. */		if (docompare && !S_ISREG(to_sb.st_mode)) {			docompare = 0;			warnx("%s: %s", to_name, strerror(EFTYPE));		}	} else if (docompare) {		/* File does not exist so silently ignore compare flag. */		docompare = 0;	}	if (safecopy) {		to_fd = create_tempfile(to_name, tempfile, sizeof(tempfile));		if (to_fd < 0)			err(EX_OSERR, "%s", tempfile);	} else if (docompare && !dostrip) {		if ((to_fd = open(to_name, O_RDONLY, 0)) < 0)			err(EX_OSERR, "%s", to_name);	} else {		if ((to_fd = create_newfile(to_name, &to_sb)) < 0)			err(EX_OSERR, "%s", to_name);	}	if (!devnull) {		if ((from_fd = open(from_name, O_RDONLY, 0)) < 0) {			serrno = errno;			(void)unlink(safecopy ? tempfile : to_name);			errx(EX_OSERR, "%s: %s", from_name, strerror(serrno));		}		if (docompare && !safecopy) {			files_match = !(compare(from_fd, from_name,					(size_t)from_sb.st_size, to_fd,					to_name, (size_t)to_sb.st_size));			/* Truncate "to" file for copy unless we match */			if (!files_match) {				(void)close(to_fd);				if ((to_fd = create_newfile(to_name, &to_sb)) < 0)					err(EX_OSERR, "%s", to_name);			}		}		if (!files_match)			copy(from_fd, from_name, to_fd,			     safecopy ? tempfile : to_name, from_sb.st_size,			     ((off_t)from_sb.st_blocks * S_BLKSIZE < from_sb.st_size));	}	if (dostrip) {		strip(safecopy ? tempfile : to_name);		/*		 * Re-open our fd on the target, in case we used a strip		 *  that does not work in-place -- like gnu binutils strip.		 */		close(to_fd);		if ((to_fd = open(safecopy ? tempfile : to_name, O_RDONLY,		     0)) < 0)			err(EX_OSERR, "stripping %s", to_name);	}	/*	 * Compare the (possibly stripped) temp file to the target.	 */	if (safecopy && docompare) {		int temp_fd = to_fd;		struct stat temp_sb;		/* Re-open to_fd using the real target name. */		if ((to_fd = open(to_name, O_RDONLY, 0)) < 0)			err(EX_OSERR, "%s", to_name);		if (fstat(temp_fd, &temp_sb)) {			serrno = errno;			(void)unlink(tempfile);			errx(EX_OSERR, "%s: %s", tempfile, strerror(serrno));		}		if (compare(temp_fd, tempfile, (size_t)temp_sb.st_size, to_fd,			    to_name, (size_t)to_sb.st_size) == 0) {			/*			 * If target has more than one link we need to			 * replace it in order to snap the extra links.			 * Need to preserve target file times, though.			 */			if (to_sb.st_nlink != 1) {				utb.actime = to_sb.st_atime;				utb.modtime = to_sb.st_mtime;				(void)utime(tempfile, &utb);			} else {				files_match = 1;				(void)unlink(tempfile);			}			(void) close(temp_fd);		}	}	/*	 * Move the new file into place if doing a safe copy	 * and the files are different (or just not compared).	 */	if (safecopy && !files_match) {		/* Try to turn off the immutable bits. */		if (to_sb.st_flags & (NOCHANGEBITS))			(void)chflags(to_name, to_sb.st_flags & ~(NOCHANGEBITS));		if (dobackup) {			char backup[MAXPATHLEN];			(void)snprintf(backup, MAXPATHLEN, "%s%s", to_name,			    suffix);			/* It is ok for the target file not to exist. */			if (rename(to_name, backup) < 0 && errno != ENOENT) {				serrno = errno;				unlink(tempfile);				errx(EX_OSERR, "rename: %s to %s: %s", to_name,				     backup, strerror(serrno));			}		}		if (rename(tempfile, to_name) < 0 ) {			serrno = errno;			unlink(tempfile);			errx(EX_OSERR, "rename: %s to %s: %s", tempfile,			     to_name, strerror(serrno));		}		/* Re-open to_fd so we aren't hosed by the rename(2). */		(void) close(to_fd);		if ((to_fd = open(to_name, O_RDONLY, 0)) < 0)			err(EX_OSERR, "%s", to_name);	}	/*	 * Preserve the timestamp of the source file if necesary.	 */	if (dopreserve && !files_match) {		utb.actime = from_sb.st_atime;		utb.modtime = from_sb.st_mtime;		(void)utime(to_name, &utb);	}	/*	 * Set owner, group, mode for target; do the chown first,	 * chown may lose the setuid bits.	 */	if ((gid != (gid_t)-1 || uid != (uid_t)-1) && fchown(to_fd, uid, gid)) {		serrno = errno;		(void)unlink(to_name);		errx(EX_OSERR, "%s: chown/chgrp: %s", to_name, strerror(serrno));	}	if (fchmod(to_fd, mode)) {		serrno = errno;		(void)unlink(to_name);		errx(EX_OSERR, "%s: chmod: %s", to_name, strerror(serrno));	}	/*	 * If provided a set of flags, set them, otherwise, preserve the	 * flags, except for the dump flag.	 */	if (fchflags(to_fd,	    flags & SETFLAGS ? fset : from_sb.st_flags & ~UF_NODUMP)) {		if (errno != EOPNOTSUPP || (from_sb.st_flags & ~UF_NODUMP) != 0)			warnx("%s: chflags: %s", to_name, strerror(errno));	}	(void)close(to_fd);	if (!devnull)		(void)close(from_fd);}