#include <sys/types.h>
#include <stdio.h>
#ifdef BSD
#include <strings.h>
#define strchr index
#else
#include <string.h>
#endif
#include <sys/stat.h>
#if defined(S_IFDIR) && !defined(S_ISDIR)
#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
#endif
#if defined(S_IFLNK) && !defined(S_ISLNK)
#define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK)
#endif
#ifndef S_ISLNK
#define lstat stat
#endif
#ifndef MAXPATHLEN
#define MAXPATHLEN 1024
#endif
#include <errno.h>
#ifndef errno
extern int errno;
#endif
extern char * savedir();
fatal(msg)
char *msg;
{
if (errno) perror(msg ? msg : "");
else if (msg) fprintf(stderr, "mkshadow: %s\n", msg);
exit(-1);
}
#define MAX_DEPTH 20
#define PREPEND_BUFFER_SIZE (MAX_DEPTH*3)
char master_buffer[MAXPATHLEN+PREPEND_BUFFER_SIZE] =
"../../../../../../../../../../../../../../../../../../../../";
#define master_start (master_buffer+PREPEND_BUFFER_SIZE)
char shadow_buffer[MAXPATHLEN];
void bad_args(msg)
{
if (msg) fprintf(stderr, "%s\n", msg);
fprintf (stderr, "usage: mkshadow [-X exclude_file] [-x exclude_pattern]");
fprintf (stderr, " master [shadow]\n");
exit(-1);
}
int exclude_count = 0;
char **exclude_patterns = NULL;
int exclude_limit = 0;
void add_exclude(pattern)
char *pattern;
{
if (exclude_limit == 0) {
exclude_limit = 100;
exclude_patterns = (char**)malloc(exclude_limit * sizeof(char*));
} else if (exclude_count + 1 >= exclude_limit) {
exclude_limit += 100;
exclude_patterns = (char**)realloc(exclude_patterns,
exclude_limit * sizeof(char*));
}
exclude_patterns[exclude_count] = pattern;
exclude_count++;
}
void add_exclude_file(name)
char *name;
{
char buf[MAXPATHLEN];
FILE *file = fopen(name, "r");
if (file == NULL) fatal("failed to find -X (exclude) file");
for (;;) {
int len;
char *str = fgets(buf, MAXPATHLEN, file);
if (str == NULL) break;
len = strlen(str);
if (len && str[len-1] == '\n') str[--len] = 0;
if (!len) continue;
str = (char*)malloc(len+1);
strcpy(str, buf);
add_exclude(str);
}
fclose(file);
}
main(argc, argv)
char **argv;
{
char *master_name = NULL;
char *shadow_name = NULL;
int i;
for (i = 1; i < argc; i++) {
if (argv[i][0] == '-') {
switch(argv[i][1]) {
case 'X':
if (argv[i][2]) add_exclude_file(&argv[i][2]);
else if (++i >= argc) bad_args(NULL);
else add_exclude_file(argv[i]);
break;
case 'x':
if (argv[i][2]) add_exclude(&argv[i][2]);
else if (++i >= argc) bad_args(NULL);
else add_exclude(argv[i]);
break;
default:
bad_args(NULL);
}
} else if (master_name == NULL)
master_name = argv[i];
else if (shadow_name == NULL)
shadow_name = argv[i];
else bad_args (NULL);
}
if (master_name == NULL) bad_args(NULL);
if (shadow_name == NULL)
shadow_name = ".";
else if ((shadow_name[0] != '.' || shadow_name[1])
&& master_name[0] != '/') {
fprintf(stderr, "Shadowing a relative directory pathname to a \n");
fprintf(stderr, "shadow other than '.' is not supported!\n");
exit(-1);
}
strcpy(shadow_buffer, shadow_name);
strcpy(master_start, master_name);
DoCopy(master_start, shadow_buffer, 0);
return 0;
}
int compare_strings(ptr1, ptr2)
char **ptr1, **ptr2;
{
return strcmp(*ptr1, *ptr2);
}
void MakeLink(master, current, depth)
char *master;
char *current;
int depth;
{
if (master[0] != '/') {
if (master != master_start) {
fatal("Internal bug: bad string buffer use");
}
master -= 3 * depth;
}
if (symlink(master, current)) {
fprintf(stderr, "Failed to create symbolic link %s->%s\n",
current, master);
exit (-1);
}
}
char ** get_name_pointers(names)
char *names;
{
int n_names = 0;
int names_buf_size = 64;
char *namep;
char ** pointers = (char**)malloc(names_buf_size * sizeof(char*));
if (!names || !pointers) fatal("virtual memory exhausted");
for (namep = names; *namep; namep += strlen(namep) + 1) {
if (n_names + 1 >= names_buf_size) {
names_buf_size *= 2;
pointers = (char**)realloc(pointers,
names_buf_size * sizeof(char*));
if (!pointers) fatal("virtual memory exhausted");
}
pointers[n_names++] = namep;
}
pointers[n_names] = 0;
qsort(pointers, n_names, sizeof(char*), compare_strings);
return pointers;
}
DoCopy(master, current, depth)
char *master;
char *current;
int depth;
{
struct stat stat_master, stat_current;
char **master_pointer, **current_pointer;
char **master_names, **current_names;
char *master_end, *current_end;
char *master_name_buf, *current_name_buf;
master_end = master + strlen(master);
current_end = current + strlen(current);
if (master_end[-1] == '/' && master != master_end - 1)
*--master_end = 0;
if (current_end[-1] == '/' && current != current_end - 1)
*--current_end = 0;
if (depth >= MAX_DEPTH) {
fprintf(stderr,
"Nesting too deep (depth %d at %s). Probable circularity.\n",
depth, master);
exit(-1);
}
master_name_buf = savedir(master, 500);
if (master_name_buf == NULL) {
fprintf(stderr, "Not enough memory or no such directory: %s\n",
master);
exit(-1);
}
current_name_buf = savedir(current, 500);
if (current_name_buf == NULL) {
fprintf(stderr, "Not enough memory or no such directory: %s\n",
current);
exit(-1);
}
master_names = get_name_pointers(master_name_buf);
current_names = get_name_pointers(current_name_buf);
master_pointer = master_names;
current_pointer = current_names;
for (;;) {
int cmp, ipat;
int in_master, in_current;
char *cur_name;
if (*master_pointer == NULL && *current_pointer == NULL)
break;
if (*master_pointer == NULL) cmp = 1;
else if (*current_pointer == NULL) cmp = -1;
else cmp = strcmp(*master_pointer, *current_pointer);
if (cmp < 0) {
in_master = 1; in_current = 0;
} else if (cmp == 0) {
in_master = 1; in_current = 1;
} else {
in_current = 1; in_master = 0;
}
cur_name = in_master ? *master_pointer : *current_pointer;
sprintf(master_end, "/%s", cur_name);
sprintf(current_end, "/%s", cur_name);
for (ipat = 0; ipat < exclude_count; ipat++) {
char *pat = exclude_patterns[ipat];
char *cur;
if (strchr(pat, '/')) cur = current + 2;
else cur = cur_name;
if (wildmat(cur, pat)) goto skip;
}
if (in_master)
if (lstat(master, &stat_master) != 0) fatal("stat failed");
if (in_current)
if (lstat(current, &stat_current) != 0) fatal("stat failed");
if (in_current && !in_master) {
if (S_ISLNK(stat_current.st_mode))
if (unlink(current)) {
fprintf(stderr, "Failed to remove symbolic link %s.\n",
current);
}
else
fprintf(stderr, "Removed symbolic link %s.\n",
current);
else {
fprintf(stderr,
"The file %s does not exist in the master tree.\n",
current);
}
}
else if (S_ISDIR(stat_master.st_mode)
&& strcmp(cur_name, "RCS") != 0
&& strcmp(cur_name, "SCCS") != 0) {
if (!in_current) {
if (mkdir(current, 0775)) fatal("mkdir failed");
}
else if (stat(current, &stat_current)) fatal("stat failed");
if (!in_current || stat_current.st_dev != stat_master.st_dev
|| stat_current.st_ino != stat_master.st_ino)
DoCopy(master, current, depth+1);
else
fprintf(stderr, "Link %s is the same as directory %s.\n",
current, master);
}
else {
if (!in_current)
MakeLink(master, current, depth);
else if (!S_ISLNK(stat_current.st_mode)) {
fprintf(stderr, "Existing file %s is not a symbolc link.\n",
current);
} else {
if (stat(current, &stat_current) || stat(master, &stat_master))
fatal("stat failed");
if (stat_current.st_dev != stat_master.st_dev
|| stat_current.st_ino != stat_master.st_ino) {
fprintf(stderr, "Fixing incorrect symbolic link %s.\n",
current);
if (unlink(current)) {
fprintf(stderr, "Failed to remove symbolic link %s.\n",
current);
}
else
MakeLink(master, current, depth);
}
}
}
skip:
if (in_master) master_pointer++;
if (in_current) current_pointer++;
}
free(master_names); free(current_names);
free(master_name_buf); free(current_name_buf);
}