update submodules for GHC.HetMet.GArrow -> Control.GArrow renaming
[ghc-hetmet.git] / utils / lndir / lndir.c
1 /* $XConsortium: lndir.c /main/16 1996/09/28 16:16:40 rws $ */
2 /* Create shadow link tree (after X11R4 script of the same name)
3    Mark Reinhold (mbr@lcs.mit.edu)/3 January 1990 */
4
5 /* 
6 Copyright (c) 1990,  X Consortium
7
8 Permission is hereby granted, free of charge, to any person obtaining a copy
9 of this software and associated documentation files (the "Software"), to deal
10 in the Software without restriction, including without limitation the rights
11 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 copies of the Software, and to permit persons to whom the Software is
13 furnished to do so, subject to the following conditions:
14
15 The above copyright notice and this permission notice shall be included in
16 all copies or substantial portions of the Software.
17
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
21 X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
25 Except as contained in this notice, the name of the X Consortium shall not be
26 used in advertising or otherwise to promote the sale, use or other dealings
27 in this Software without prior written authorization from the X Consortium.
28
29 */
30
31 /* From the original /bin/sh script:
32
33   Used to create a copy of the a directory tree that has links for all
34   non-directories (except those named RCS, SCCS or CVS.adm).  If you are
35   building the distribution on more than one machine, you should use
36   this technique.
37
38   If your master sources are located in /usr/local/src/X and you would like
39   your link tree to be in /usr/local/src/new-X, do the following:
40
41         %  mkdir /usr/local/src/new-X
42         %  cd /usr/local/src/new-X
43         %  lndir ../X
44 */
45
46 #define NeedVarargsPrototypes 1
47
48 #include "lndir-Xos.h"
49 #include <stdlib.h>
50 #include <stdio.h>
51 #include <sys/stat.h>
52 #include <sys/param.h>
53 #include <errno.h>
54
55 #ifndef X_NOT_POSIX
56 #include <dirent.h>
57 #else
58 #ifdef SYSV
59 #include <dirent.h>
60 #else
61 #ifdef USG
62 #include <dirent.h>
63 #else
64 #include <sys/dir.h>
65 #ifndef dirent
66 #define dirent direct
67 #endif
68 #endif
69 #endif
70 #endif
71 #ifndef MAXPATHLEN
72 #define MAXPATHLEN 2048
73 #endif
74
75 #ifdef __CYGWIN32__
76 #include <sys/cygwin.h>
77 #endif
78
79 #if NeedVarargsPrototypes
80 #include <stdarg.h>
81 #endif
82
83 #ifdef X_NOT_STDC_ENV
84 extern int errno;
85 #endif
86 int silent = 0;                 /* -silent */
87 int copy = 0;                   /* -copy */
88 int ignore_links = 0;           /* -ignorelinks */
89
90 char *rcurdir;
91 char *curdir;
92
93 int force=0;
94
95 #ifdef WIN32
96 #define mymkdir(X, Y) mkdir(X)
97 #else
98 #define mymkdir(X, Y) mkdir(X, Y)
99 #endif
100
101 #ifndef WIN32
102 #define SYMLINKS
103 #define BELIEVE_ST_NLINK
104 #endif
105
106 void
107 quit (
108 #if NeedVarargsPrototypes
109     int code, char * fmt, ...)
110 #else
111     code, fmt, a1, a2, a3)
112     char *fmt;
113 #endif
114 {
115 #if NeedVarargsPrototypes
116     va_list args;
117     va_start(args, fmt);
118     vfprintf (stderr, fmt, args);
119     va_end(args);
120 #else
121     fprintf (stderr, fmt, a1, a2, a3);
122 #endif
123     putc ('\n', stderr);
124     exit (code);
125 }
126
127 void
128 quiterr (code, s)
129     char *s;
130 {
131     perror (s);
132     exit (code);
133 }
134
135 void
136 msg (
137 #if NeedVarargsPrototypes
138     char * fmt, ...)
139 #else
140     fmt, a1, a2, a3)
141     char *fmt;
142 #endif
143 {
144 #if NeedVarargsPrototypes
145     va_list args;
146 #endif
147     if (curdir) {
148         fprintf (stderr, "%s:\n", curdir);
149         curdir = 0;
150     }
151 #if NeedVarargsPrototypes
152     va_start(args, fmt);
153     vfprintf (stderr, fmt, args);
154     va_end(args);
155 #else
156     fprintf (stderr, fmt, a1, a2, a3);
157 #endif
158     putc ('\n', stderr);
159 }
160
161 void
162 mperror (s)
163     char *s;
164 {
165     if (curdir) {
166         fprintf (stderr, "%s:\n", curdir);
167         curdir = 0;
168     }
169     perror (s);
170 }
171
172 #define BUFSIZE 1024
173 int copyfile(const char *oldpath, const char *newpath) {
174     FILE *f_old;
175     FILE *f_new;
176     int e;
177     ssize_t s;
178     char buf[BUFSIZE];
179
180 #ifdef SYMLINKS
181     if (copy == 0) {
182         return symlink(oldpath, newpath);
183     } else {
184 #endif
185         f_old = fopen(oldpath, "rb");
186         if (f_old == NULL) {
187             return -1;
188         }
189         f_new = fopen(newpath, "wbx");
190         if (f_new == NULL) {
191             e = errno;
192             fclose(f_old);
193             errno = e;
194             return -1;
195         }
196         while ((s = fread(buf, 1, BUFSIZE, f_old)) > 0) {
197             if (fwrite(buf, 1, s, f_new) < s) {
198                 e = errno;
199                 fclose(f_old);
200                 fclose(f_new);
201                 errno = e;
202                 return -1;
203             }
204         }
205         if (!feof(f_old)) {
206             e = errno;
207             fclose(f_old);
208             fclose(f_new);
209             errno = e;
210             return -1;
211         }
212         if (fclose(f_new) == EOF) {
213             e = errno;
214             fclose(f_old);
215             errno = e;
216             return -1;
217         }
218         fclose(f_old);
219         return 0;
220 #ifdef SYMLINKS
221     }
222 #endif
223 }
224
225 int equivalent(lname, rname)
226     char *lname;
227     char *rname;
228 {
229     char *s;
230
231     if (!strcmp(lname, rname))
232         return 1;
233     for (s = lname; *s && (s = strchr(s, '/')); s++) {
234         while (s[1] == '/')
235             strcpy(s+1, s+2);
236     }
237     return !strcmp(lname, rname);
238 }
239
240
241 /* Recursively create symbolic links from the current directory to the "from"
242    directory.  Assumes that files described by fs and ts are directories. */
243
244 dodir (fn, fs, ts, rel)
245 char *fn;                       /* name of "from" directory, either absolute or
246                                    relative to cwd */
247 struct stat *fs, *ts;           /* stats for the "from" directory and cwd */
248 int rel;                        /* if true, prepend "../" to fn before using */
249 {
250     DIR *df;
251     struct dirent *dp;
252     char buf[MAXPATHLEN + 1], *p;
253     char symbuf[MAXPATHLEN + 1];
254     char basesym[MAXPATHLEN + 1];
255     struct stat sb, sc;
256     int n_dirs;
257     int symlen = -1;
258     int basesymlen = -1;
259     char *ocurdir;
260
261     if ((fs->st_dev == ts->st_dev) &&
262         (fs->st_ino == ts->st_ino) &&
263         /* inode is always 0 on Windows; we don't want to fail in that case */
264         (fs->st_ino != 0)
265        ) {
266         msg ("%s: From and to directories are identical!", fn);
267         return 1;
268     }
269
270     if (rel)
271         strcpy (buf, "../");
272     else
273         buf[0] = '\0';
274     strcat (buf, fn);
275     
276     if (!(df = opendir (buf))) {
277         msg ("%s: Cannot opendir", buf);
278         return 1;
279     }
280
281     p = buf + strlen (buf);
282     *p++ = '/';
283     n_dirs = fs->st_nlink;
284     while (dp = readdir (df)) {
285         if (dp->d_name[strlen(dp->d_name) - 1] == '~')
286             continue;
287         if (dp->d_name[0] == '.' && dp->d_name[1] == '#') /* 'non-conflict files' left behind by CVS */
288             continue;
289         strcpy (p, dp->d_name);
290
291         if (
292 #ifdef BELIEVE_ST_NLINK
293             n_dirs > 0
294 #else
295             /* st_nlink is 1 on Windows, so we have to keep looking for
296              * directories forever */
297             1
298 #endif
299            ) {
300             if (stat (buf, &sb) < 0) {
301                 mperror (buf);
302                 continue;
303             }
304
305 #ifdef S_ISDIR
306             if(S_ISDIR(sb.st_mode))
307 #else
308             if (sb.st_mode & S_IFDIR) 
309 #endif
310             {
311                 /* directory */
312 #ifndef __CYGWIN32__   /* don't trust cygwin's n_dirs count */
313                 n_dirs--;
314 #endif
315                 if (dp->d_name[0] == '.' &&
316                     (dp->d_name[1] == '\0' || (dp->d_name[1] == '.' &&
317                                                dp->d_name[2] == '\0')))
318                     continue;
319                 if (!strcmp (dp->d_name, "RCS"))
320                     continue;
321                 if (!strcmp (dp->d_name, "SCCS"))
322                     continue;
323                 if (!strcmp (dp->d_name, "CVS"))
324                     continue;
325                 if (!strcmp (dp->d_name, ".svn"))
326                     continue;
327                 if (!strcmp (dp->d_name, "_darcs"))
328                     continue;
329                 if (!strcmp (dp->d_name, "CVS.adm"))
330                     continue;
331                 ocurdir = rcurdir;
332                 rcurdir = buf;
333                 curdir = silent ? buf : (char *)0;
334                 if (!silent)
335                     printf ("%s:\n", buf);
336                 if ((stat (dp->d_name, &sc) < 0) && (errno == ENOENT)) {
337                     if (mymkdir (dp->d_name, 0777) < 0 ||
338                         stat (dp->d_name, &sc) < 0) {
339                         mperror (dp->d_name);
340                         curdir = rcurdir = ocurdir;
341                         continue;
342                     }
343                 }
344 #ifdef SYMLINKS
345                 if (readlink (dp->d_name, symbuf, sizeof(symbuf) - 1) >= 0) {
346                     msg ("%s: is a link instead of a directory", dp->d_name);
347                     curdir = rcurdir = ocurdir;
348                     continue;
349                 }
350 #endif
351                 if (chdir (dp->d_name) < 0) {
352                     mperror (dp->d_name);
353                     curdir = rcurdir = ocurdir;
354                     continue;
355                 }
356                 rel = (fn[0] != '/') && ((fn[0] == '\0') || (fn[1] != ':'));
357                 dodir (buf, &sb, &sc, rel);
358                 if (chdir ("..") < 0)
359                     quiterr (1, "..");
360                 curdir = rcurdir = ocurdir;
361                 continue;
362             }
363         }
364
365         /* non-directory */
366 #ifdef SYMLINKS
367         symlen = readlink (dp->d_name, symbuf, sizeof(symbuf) - 1);
368         if (symlen >= 0)
369             symbuf[symlen] = '\0';
370
371         /* The option to ignore links exists mostly because
372            checking for them slows us down by 10-20%.
373            But it is off by default because this really is a useful check. */
374         if (!ignore_links) {
375             /* see if the file in the base tree was a symlink */
376             basesymlen = readlink(buf, basesym, sizeof(basesym) - 1);
377             if (basesymlen >= 0)
378                 basesym[basesymlen] = '\0';
379         }
380 #endif
381
382         if (symlen >= 0) {
383           if (!equivalent (basesymlen>=0 ? basesym : buf, symbuf)) {
384             if (force) {
385               unlink(dp->d_name);
386               if (copyfile (basesymlen>=0 ? basesym : buf, dp->d_name) < 0)
387                 mperror (dp->d_name);
388             } else {
389               /* Link exists in new tree.  Print message if it doesn't match. */
390               msg ("%s: %s", dp->d_name, symbuf);
391             }
392           }
393         } else {
394           if (copyfile (basesymlen>=0 ? basesym : buf, dp->d_name) < 0)
395             mperror (dp->d_name);
396         }
397     }
398     
399     closedir (df);
400     return 0;
401 }
402
403 main (ac, av)
404 int ac;
405 char **av;
406 {
407     char *prog_name = av[0];
408     char* tn;
409     struct stat fs, ts;
410 #ifdef __CYGWIN32__
411     /*   
412     The lndir code assumes unix-style paths to work. cygwin
413     lets you get away with using dos'ish paths (e.g., "f:/oo")
414     in most contexts. Using them with 'lndir' will seriously
415     confuse the user though, so under-the-hood, we convert the
416     path into something POSIX-like.
417     */
418     static char fn[MAXPATHLEN+1];
419 #else
420     char *fn;
421 #endif
422
423     while (++av, --ac) {
424       if (strcmp(*av, "-silent") == 0)
425           silent = 1;
426       else if (strcmp(*av, "-f") == 0)
427           force = 1;
428       else if (strcmp(*av, "-ignorelinks") == 0)
429           ignore_links = 1;
430       else if (strcmp(*av, "-copy") == 0)
431           copy = 1;
432       else if (strcmp(*av, "--") == 0) {
433           ++av, --ac;
434           break;
435       } else
436           break;
437     }
438
439     if (ac < 1 || ac > 2)
440         quit (1, "usage: %s [-f] [-silent] [-ignorelinks] fromdir [todir]",
441               prog_name);
442
443 #ifdef __CYGWIN32__
444     cygwin_conv_to_full_posix_path(av[0], fn);
445 #else
446     fn = av[0];
447 #endif
448
449     if (ac == 2)
450         tn = av[1];
451     else
452         tn = ".";
453
454     /* to directory */
455     if (stat (tn, &ts) < 0) {
456       if (force && (tn[0] != '.' || tn[1] != '\0') ) {
457          mymkdir(tn, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH );
458       } 
459       else {
460         quiterr (1, tn);
461 #ifdef S_ISDIR
462         if (!(S_ISDIR(ts.st_mode)))
463 #else
464         if (!(ts.st_mode & S_IFDIR))
465 #endif
466            quit (2, "%s: Not a directory", tn);
467       }
468     }
469     if (chdir (tn) < 0)
470         quiterr (1, tn);
471
472     /* from directory */
473     if (stat (fn, &fs) < 0)
474         quiterr (1, fn);
475 #ifdef S_ISDIR
476     if (!(S_ISDIR(fs.st_mode)))
477 #else
478     if (!(fs.st_mode & S_IFDIR))
479 #endif
480         quit (2, "%s: Not a directory", fn);
481
482     exit (dodir (fn, &fs, &ts, 0));
483 }