#include <sys/stat.h>
#include <dirent.h>
#include <err.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
extern char *__progname;
int silent;
int ignore_links;
char *rcurdir;
char *curdir;
int equivalent(char *, char *);
void addexcept(char *);
int dodir(char *, struct stat *, struct stat *, int);
void usage(void);
struct except {
char *name;
struct except *next;
};
struct except *exceptions;
int
main(int argc, char *argv[])
{
struct stat fs, ts;
char *fn, *tn;
if (pledge("stdio rpath cpath", NULL) == -1)
err(1, "pledge");
while (++argv, --argc) {
if ((strcmp(*argv, "-silent") == 0) ||
(strcmp(*argv, "-s") == 0))
silent = 1;
else if ((strcmp(*argv, "-ignorelinks") == 0) ||
(strcmp(*argv, "-i") == 0))
ignore_links = 1;
else if (strcmp(*argv, "-e") == 0) {
++argv, --argc;
if (argc < 2)
usage();
addexcept(*argv);
} else if (strcmp(*argv, "--") == 0) {
++argv, --argc;
break;
} else
break;
}
if (argc < 1 || argc > 2)
usage();
fn = argv[0];
if (argc == 2)
tn = argv[1];
else
tn = ".";
if (stat(tn, &ts) == -1)
err(1, "%s", tn);
if (!(S_ISDIR(ts.st_mode)))
errc(2, ENOTDIR, "%s", tn);
if (chdir(tn) == -1)
err(1, "%s", tn);
if (stat(fn, &fs) == -1)
err(1, "%s", fn);
if (!(S_ISDIR(fs.st_mode)))
errc(2, ENOTDIR, "%s", fn);
exit(dodir(fn, &fs, &ts, 0));
}
int
equivalent(char *lname, char *rname)
{
char *s, *ns;
if (strcmp(lname, rname) == 0)
return(1);
for (s = lname; *s && (s = strchr(s, '/')); s++) {
if (s[1] == '/') {
for (ns = s + 1; *ns == '/'; ns++)
;
memmove(s + 1, ns, strlen(ns) + 1);
}
}
return (strcmp(lname, rname) == 0);
}
void
addexcept(char *name)
{
struct except *new;
new = malloc(sizeof(struct except));
if (new == NULL)
err(1, NULL);
new->name = strdup(name);
if (new->name == NULL)
err(1, NULL);
new->next = exceptions;
exceptions = new;
}
#if 0
char *fn;
struct stat *fs, *ts;
int rel;
#endif
int
dodir(char *fn, struct stat *fs, struct stat *ts, int rel)
{
char buf[PATH_MAX + 1], symbuf[PATH_MAX + 1];
char basesym[PATH_MAX + 1];
int n_dirs, symlen, basesymlen = -1;
struct stat sb, sc;
struct except *cur;
struct dirent *dp;
char *ocurdir, *p;
DIR *df;
if (fs->st_dev == ts->st_dev && fs->st_ino == ts->st_ino) {
warnx("%s: From and to directories are identical!", fn);
return(1);
}
if (rel)
strlcpy(buf, "../", sizeof(buf));
else
buf[0] = '\0';
strlcat(buf, fn, sizeof(buf));
if (!(df = opendir(buf))) {
warn("%s: Cannot opendir", buf);
return(1);
}
p = buf + strlen(buf);
*p++ = '/';
n_dirs = fs->st_nlink;
while ((dp = readdir(df))) {
if (dp->d_namlen == 0 || dp->d_name[dp->d_namlen - 1] == '~' ||
strncmp(dp->d_name, ".#", 2) == 0)
continue;
for (cur = exceptions; cur != NULL; cur = cur->next) {
if (!strcmp(dp->d_name, cur->name))
goto next;
}
strlcpy(p, dp->d_name, buf + sizeof(buf) - p);
if (n_dirs > 0) {
if (stat(buf, &sb) == -1) {
warn("%s", buf);
continue;
}
if (S_ISDIR(sb.st_mode)) {
n_dirs--;
if (dp->d_name[0] == '.' &&
(dp->d_name[1] == '\0' ||
(dp->d_name[1] == '.' &&
dp->d_name[2] == '\0')))
continue;
if (!strcmp(dp->d_name, "RCS"))
continue;
if (!strcmp(dp->d_name, "SCCS"))
continue;
if (!strcmp(dp->d_name, "CVS"))
continue;
if (!strcmp(dp->d_name, "CVS.adm"))
continue;
ocurdir = rcurdir;
rcurdir = buf;
curdir = silent ? buf : NULL;
if (!silent)
printf("%s:\n", buf);
if (stat(dp->d_name, &sc) == -1 &&
errno == ENOENT) {
if (mkdir(dp->d_name, 0777) == -1 ||
stat(dp->d_name, &sc) == -1) {
warn("%s", dp->d_name);
curdir = rcurdir = ocurdir;
continue;
}
}
if (readlink(dp->d_name, symbuf,
sizeof(symbuf) - 1) >= 0) {
fprintf(stderr,
"%s: is a link instead of a "
"directory\n",
dp->d_name);
curdir = rcurdir = ocurdir;
continue;
}
if (chdir(dp->d_name) == -1) {
warn("%s", dp->d_name);
curdir = rcurdir = ocurdir;
continue;
}
dodir(buf, &sb, &sc, (buf[0] != '/'));
if (chdir("..") == -1)
err(1, "..");
curdir = rcurdir = ocurdir;
continue;
}
}
symlen = readlink(dp->d_name, symbuf, sizeof(symbuf) - 1);
if (symlen >= 0)
symbuf[symlen] = '\0';
if (!ignore_links) {
basesymlen = readlink(buf, basesym,
sizeof(basesym) - 1);
if (basesymlen >= 0)
basesym[basesymlen] = '\0';
}
if (symlen >= 0) {
if (!equivalent(basesymlen >= 0 ? basesym : buf,
symbuf))
fprintf(stderr,"%s: %s\n", dp->d_name, symbuf);
} else {
if (symlink(basesymlen >= 0 ? basesym : buf,
dp->d_name) == -1)
warn("%s", dp->d_name);
}
next:
;
}
closedir(df);
return (0);
}
void
usage(void)
{
fprintf(stderr, "usage: %s [-is] [-e exceptfile] fromdir [todir]\n",
__progname);
exit(1);
}