]> git.sesse.net Git - rdpsrv/blob - Xserver/config/util/mkshadow/mkshadow.c
Import X server from vnc-3.3.7.
[rdpsrv] / Xserver / config / util / mkshadow / mkshadow.c
1 /* $XConsortium: mkshadow.c /main/2 1996/12/04 10:11:51 swick $ */
2 /* mkshadow.c - make a "shadow copy" of a directory tree with symlinks.
3    Copyright 1990, 1993 Free Software Foundation, Inc.
4
5    Permission to use, copy, modify, and distribute this program for
6    any purpose and without fee is hereby granted, provided that this
7    copyright and permission notice appear on all copies, and that
8    notice be given that copying and distribution is by permission of
9    the Free Software Foundation.  The Free Software Foundation makes
10    no representations about the suitability of this software for any
11    purpose.  It is provided "as is" without expressed or implied
12    warranty.
13
14    (The FSF has modified its usual distribution terms, for this file,
15    as a courtesy to the X project.)  */
16
17 /*
18  * Usage: mkshadow [-X exclude_file] [-x exclude_pattern] ... MASTER [SHADOW]
19  * Makes SHADOW be a "shadow copy" of MASTER.  SHADOW defaults to the current
20  * directory. Sort of like a recursive copy of MASTER to SHADOW.
21  * However, symbolic links are used instead of actually
22  * copying (non-directory) files.
23  * Also, directories named RCS or SCCS are shared (with a symbolic link).
24  * Warning messages are printed for files (and directories) in .
25  * that don't match a corresponding file in MASTER (though
26  * symbolic links are silently removed).
27  * Also, a warning message is printed for non-directory files
28  * under SHADOW that are not symbolic links.
29  *
30  * Files and directories can be excluded from the sharing
31  * with the -X and -x flags. The flag `-x pattern' (or `-xpattern')
32  * means that mkshadow should ignore any file whose name matches
33  * the pattern. The pattern is a "globbing" pattern, i.e. the
34  * characters *?[^-] are interpreted as by the shell.
35  * If the pattern contains a '/' is is matched against the complete
36  * current path (relative to '.'); otherwise, it is matched
37  * against the last component of the path.
38  * A `-X filename' flag means to read a set of exclusion patterns
39  * from the named file, one pattern to a line.
40  *
41  * Originally written by Per Bothner at University of Wisconsin-Madison,
42  * inspired by the lndir script distributed with X11.
43  * Modified by Per Bothner <bothner@cygnus.com> November 1993
44  * to more-or-less follow Posix.
45  */
46
47 #include <sys/types.h>
48 #include <stdio.h>
49 #ifdef BSD
50 #include <strings.h>
51 #define strchr index
52 #else
53 #include <string.h>
54 #endif
55 #include <sys/stat.h>
56 #if defined(S_IFDIR) && !defined(S_ISDIR)
57 #define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
58 #endif
59 #if defined(S_IFLNK) && !defined(S_ISLNK)
60 #define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK)
61 #endif
62 #ifndef S_ISLNK
63 #define lstat stat
64 #endif
65 #ifndef MAXPATHLEN
66 #define MAXPATHLEN 1024
67 #endif
68 #include <errno.h>
69 #ifndef errno
70 extern int errno;
71 #endif
72
73 extern char * savedir();
74
75 fatal(msg)
76      char *msg;
77 {
78     if (errno) perror(msg ? msg : "");
79     else if (msg) fprintf(stderr, "mkshadow: %s\n", msg);
80     exit(-1);
81 }
82
83 /* When handling symbolic links to relative directories,
84  * we need to prepend "../" to the "source".
85  * We preallocate MAX_DEPTH repetations of "../" using a simple trick.
86  */
87 #define MAX_DEPTH 20
88 #define PREPEND_BUFFER_SIZE (MAX_DEPTH*3)
89 char master_buffer[MAXPATHLEN+PREPEND_BUFFER_SIZE] =
90     "../../../../../../../../../../../../../../../../../../../../";
91 /* The logical start of the master_buffer is defined by
92  * master_start, which skips the fixed prepend area.
93  */
94 #define master_start (master_buffer+PREPEND_BUFFER_SIZE)
95 char shadow_buffer[MAXPATHLEN];
96
97 void bad_args(msg)
98 {
99     if (msg) fprintf(stderr, "%s\n", msg);
100     fprintf (stderr, "usage: mkshadow [-X exclude_file] [-x exclude_pattern]");
101     fprintf (stderr, " master [shadow]\n");
102     exit(-1);
103 }
104
105 int exclude_count = 0;
106 char **exclude_patterns = NULL;
107 int exclude_limit = 0;
108
109 void add_exclude(pattern)
110     char *pattern;
111 {
112     if (exclude_limit == 0) {
113         exclude_limit = 100;
114         exclude_patterns = (char**)malloc(exclude_limit * sizeof(char*));
115     } else if (exclude_count + 1 >= exclude_limit) {
116         exclude_limit += 100;
117         exclude_patterns = (char**)realloc(exclude_patterns, 
118                                            exclude_limit * sizeof(char*));
119     }
120     exclude_patterns[exclude_count] = pattern;
121     exclude_count++;
122 }
123
124 void add_exclude_file(name)
125      char *name;
126 {
127     char buf[MAXPATHLEN];
128     FILE *file = fopen(name, "r");
129     if (file == NULL) fatal("failed to find -X (exclude) file");
130     for (;;) {
131         int len;
132         char *str = fgets(buf, MAXPATHLEN, file);
133         if (str == NULL) break;
134         len = strlen(str);
135         if (len && str[len-1] == '\n') str[--len] = 0;
136         if (!len) continue;
137         str = (char*)malloc(len+1);
138         strcpy(str, buf);
139         add_exclude(str);
140     }
141     fclose(file);
142 }
143
144 main(argc, argv)
145      char **argv;
146 {
147     char *master_name = NULL;
148     char *shadow_name = NULL;
149     int i;
150     for (i = 1; i < argc; i++) {
151         if (argv[i][0] == '-') {
152             switch(argv[i][1]) {
153               case 'X':
154                 if (argv[i][2]) add_exclude_file(&argv[i][2]);
155                 else if (++i >= argc) bad_args(NULL);
156                 else add_exclude_file(argv[i]);
157                 break;
158               case 'x':
159                 if (argv[i][2]) add_exclude(&argv[i][2]);
160                 else if (++i >= argc) bad_args(NULL);
161                 else add_exclude(argv[i]);
162                 break;
163               default:
164                 bad_args(NULL);
165             }
166         } else if (master_name == NULL)
167             master_name = argv[i];
168         else if (shadow_name == NULL)
169             shadow_name = argv[i];
170         else bad_args (NULL);
171     }
172
173     if (master_name == NULL) bad_args(NULL);
174     if (shadow_name == NULL)
175         shadow_name = ".";
176     else if ((shadow_name[0] != '.' || shadow_name[1])
177              && master_name[0] != '/') {
178         fprintf(stderr, "Shadowing a relative directory pathname to a \n");
179         fprintf(stderr, "shadow other than '.' is not supported!\n");
180         exit(-1);
181     }
182     strcpy(shadow_buffer, shadow_name);
183     strcpy(master_start, master_name);
184     DoCopy(master_start, shadow_buffer, 0);
185     return 0;
186 }
187
188 int compare_strings(ptr1, ptr2)
189      char **ptr1, **ptr2;
190 {
191     return strcmp(*ptr1, *ptr2);
192 }
193
194 void MakeLink(master, current, depth)
195      char *master;
196      char *current;
197      int depth;
198 {
199     if (master[0] != '/') {
200         /* Source directory was specified with a relative pathname. */
201         if (master != master_start) {
202             fatal("Internal bug: bad string buffer use");
203         }
204         /* Pre-pend "../" depth times. This compensates for
205          * the directories we've entered. */
206         master -= 3 * depth;
207     }
208     if (symlink(master, current)) {
209         fprintf(stderr, "Failed to create symbolic link %s->%s\n",
210                 current, master);
211         exit (-1);
212     }
213 }
214
215
216 /* Get a sorted NULL_terminator array of (char*) using 'names'
217  * (created by save_dir) as data.
218  */
219 char ** get_name_pointers(names)
220      char *names;
221 {
222     int n_names = 0;
223     int names_buf_size = 64;
224     char *namep;
225     char ** pointers = (char**)malloc(names_buf_size * sizeof(char*));
226     if (!names || !pointers) fatal("virtual memory exhausted");
227
228     for (namep = names; *namep; namep += strlen(namep) + 1) {
229         if (n_names + 1 >= names_buf_size) {
230             names_buf_size *= 2;
231             pointers = (char**)realloc(pointers,
232                                        names_buf_size * sizeof(char*));
233             if (!pointers) fatal("virtual memory exhausted");
234         }
235         pointers[n_names++] = namep;
236     }
237     pointers[n_names] = 0;
238     qsort(pointers, n_names, sizeof(char*), compare_strings);
239     return pointers;
240 }
241
242 /* Recursively shadow the directory whose name is in MASTER
243  * (which is == MASTER_START) into the destination directory named CURRENT.
244  */
245
246 DoCopy(master, current, depth)
247      char *master; /* The source directory. */
248      char *current; /* The destination directory. */
249      int depth;
250 {
251     struct stat stat_master, stat_current;
252     char **master_pointer, **current_pointer;
253     char **master_names, **current_names;
254     char *master_end, *current_end;
255     char *master_name_buf, *current_name_buf;
256     master_end = master + strlen(master);
257     current_end = current + strlen(current);
258
259     /* Get rid of terminal '/' */
260     if (master_end[-1] == '/' && master != master_end - 1)
261         *--master_end = 0;
262     if (current_end[-1] == '/' && current != current_end - 1)
263         *--current_end = 0;
264
265     if (depth >= MAX_DEPTH) {
266         fprintf(stderr,
267                 "Nesting too deep (depth %d at %s). Probable circularity.\n",
268                 depth, master);
269         exit(-1);
270     }
271
272     master_name_buf = savedir(master, 500);
273     if (master_name_buf == NULL) {
274         fprintf(stderr, "Not enough memory or no such directory: %s\n",
275                 master);
276         exit(-1);
277     }
278     current_name_buf = savedir(current, 500);
279     if (current_name_buf == NULL) {
280         fprintf(stderr, "Not enough memory or no such directory: %s\n",
281                 current);
282         exit(-1);
283     }
284
285     master_names = get_name_pointers(master_name_buf);
286     current_names = get_name_pointers(current_name_buf);
287
288     master_pointer = master_names;
289     current_pointer = current_names;
290     for (;;) {
291         int cmp, ipat;
292         int in_master, in_current;
293         char *cur_name;
294         if (*master_pointer == NULL && *current_pointer == NULL)
295             break;
296         if (*master_pointer == NULL) cmp = 1;
297         else if (*current_pointer == NULL) cmp = -1;
298         else cmp = strcmp(*master_pointer, *current_pointer);
299         if (cmp < 0) { /* file only exists in master directory */
300             in_master = 1; in_current = 0;
301         } else if (cmp == 0) { /* file exists in both directories */
302             in_master = 1; in_current = 1;
303         } else { /* file only exists in current directory */
304             in_current = 1; in_master = 0;
305         }
306         cur_name = in_master ? *master_pointer : *current_pointer;
307         sprintf(master_end, "/%s", cur_name);
308         sprintf(current_end, "/%s", cur_name);
309         for (ipat = 0; ipat < exclude_count; ipat++) {
310             char *pat = exclude_patterns[ipat];
311             char *cur;
312             if (strchr(pat, '/')) cur = current + 2; /* Skip initial "./" */
313             else cur = cur_name;
314             if (wildmat(cur, pat)) goto skip;
315         }
316         if (in_master)
317             if (lstat(master, &stat_master) != 0) fatal("stat failed");
318         if (in_current)
319             if (lstat(current, &stat_current) != 0) fatal("stat failed");
320         if (in_current && !in_master) {
321             if (S_ISLNK(stat_current.st_mode))
322                 if (unlink(current)) {
323                     fprintf(stderr, "Failed to remove symbolic link %s.\n",
324                             current);
325                 }
326                 else
327                     fprintf(stderr, "Removed symbolic link %s.\n",
328                             current);
329             else {
330                 fprintf(stderr,
331                         "The file %s does not exist in the master tree.\n",
332                         current);
333             }
334         }
335         else if (S_ISDIR(stat_master.st_mode)
336                  && strcmp(cur_name, "RCS") != 0
337                  && strcmp(cur_name, "SCCS") != 0) {
338             if (!in_current) {
339                 if (mkdir(current, 0775)) fatal("mkdir failed");
340             }
341             else if (stat(current, &stat_current)) fatal("stat failed");
342             if (!in_current || stat_current.st_dev != stat_master.st_dev
343                 || stat_current.st_ino != stat_master.st_ino)
344                 DoCopy(master, current, depth+1);
345             else
346                 fprintf(stderr, "Link %s is the same as directory %s.\n",
347                         current, master);
348         }
349         else {
350             if (!in_current)
351                 MakeLink(master, current, depth);
352             else if (!S_ISLNK(stat_current.st_mode)) {
353                 fprintf(stderr, "Existing file %s is not a symbolc link.\n",
354                         current);
355             } else {
356                 if (stat(current, &stat_current) || stat(master, &stat_master))
357                     fatal("stat failed");
358                 if (stat_current.st_dev != stat_master.st_dev
359                     || stat_current.st_ino != stat_master.st_ino) {
360                     fprintf(stderr, "Fixing incorrect symbolic link %s.\n",
361                             current);
362                     if (unlink(current)) {
363                         fprintf(stderr, "Failed to remove symbolic link %s.\n",
364                                 current);
365                     }
366                     else
367                         MakeLink(master, current, depth);
368                 }
369             }
370         }
371       skip:
372         if (in_master) master_pointer++;
373         if (in_current) current_pointer++;
374     }
375
376     free(master_names); free(current_names);
377     free(master_name_buf); free(current_name_buf);
378 }