[project @ 2003-06-27 18:28:31 by sof]
[ghc-hetmet.git] / glafp-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 #include "lndir-Xos.h"
47 #include <stdio.h>
48 #include <sys/stat.h>
49 #include <sys/param.h>
50 #include <errno.h>
51
52 #ifndef X_NOT_POSIX
53 #include <dirent.h>
54 #else
55 #ifdef SYSV
56 #include <dirent.h>
57 #else
58 #ifdef USG
59 #include <dirent.h>
60 #else
61 #include <sys/dir.h>
62 #ifndef dirent
63 #define dirent direct
64 #endif
65 #endif
66 #endif
67 #endif
68 #ifndef MAXPATHLEN
69 #define MAXPATHLEN 2048
70 #endif
71
72 #ifdef __CYGWIN32__
73 #include <sys/cygwin.h>
74 #endif
75
76 #if NeedVarargsPrototypes
77 #include <stdarg.h>
78 #endif
79
80 #ifdef X_NOT_STDC_ENV
81 extern int errno;
82 #endif
83 int silent = 0;                 /* -silent */
84 int ignore_links = 0;           /* -ignorelinks */
85
86 char *rcurdir;
87 char *curdir;
88
89 int force=0;
90
91 void
92 quit (
93 #if NeedVarargsPrototypes
94     int code, char * fmt, ...)
95 #else
96     code, fmt, a1, a2, a3)
97     char *fmt;
98 #endif
99 {
100 #if NeedVarargsPrototypes
101     va_list args;
102     va_start(args, fmt);
103     vfprintf (stderr, fmt, args);
104     va_end(args);
105 #else
106     fprintf (stderr, fmt, a1, a2, a3);
107 #endif
108     putc ('\n', stderr);
109     exit (code);
110 }
111
112 void
113 quiterr (code, s)
114     char *s;
115 {
116     perror (s);
117     exit (code);
118 }
119
120 void
121 msg (
122 #if NeedVarargsPrototypes
123     char * fmt, ...)
124 #else
125     fmt, a1, a2, a3)
126     char *fmt;
127 #endif
128 {
129 #if NeedVarargsPrototypes
130     va_list args;
131 #endif
132     if (curdir) {
133         fprintf (stderr, "%s:\n", curdir);
134         curdir = 0;
135     }
136 #if NeedVarargsPrototypes
137     va_start(args, fmt);
138     vfprintf (stderr, fmt, args);
139     va_end(args);
140 #else
141     fprintf (stderr, fmt, a1, a2, a3);
142 #endif
143     putc ('\n', stderr);
144 }
145
146 void
147 mperror (s)
148     char *s;
149 {
150     if (curdir) {
151         fprintf (stderr, "%s:\n", curdir);
152         curdir = 0;
153     }
154     perror (s);
155 }
156
157
158 int equivalent(lname, rname)
159     char *lname;
160     char *rname;
161 {
162     char *s;
163
164     if (!strcmp(lname, rname))
165         return 1;
166     for (s = lname; *s && (s = strchr(s, '/')); s++) {
167         while (s[1] == '/')
168             strcpy(s+1, s+2);
169     }
170     return !strcmp(lname, rname);
171 }
172
173
174 /* Recursively create symbolic links from the current directory to the "from"
175    directory.  Assumes that files described by fs and ts are directories. */
176
177 dodir (fn, fs, ts, rel)
178 char *fn;                       /* name of "from" directory, either absolute or
179                                    relative to cwd */
180 struct stat *fs, *ts;           /* stats for the "from" directory and cwd */
181 int rel;                        /* if true, prepend "../" to fn before using */
182 {
183     DIR *df;
184     struct dirent *dp;
185     char buf[MAXPATHLEN + 1], *p;
186     char symbuf[MAXPATHLEN + 1];
187     char basesym[MAXPATHLEN + 1];
188     struct stat sb, sc;
189     int n_dirs;
190     int symlen;
191     int basesymlen = -1;
192     char *ocurdir;
193
194     if ((fs->st_dev == ts->st_dev) && (fs->st_ino == ts->st_ino)) {
195         msg ("%s: From and to directories are identical!", fn);
196         return 1;
197     }
198
199     if (rel)
200         strcpy (buf, "../");
201     else
202         buf[0] = '\0';
203     strcat (buf, fn);
204     
205     if (!(df = opendir (buf))) {
206         msg ("%s: Cannot opendir", buf);
207         return 1;
208     }
209
210     p = buf + strlen (buf);
211     *p++ = '/';
212     n_dirs = fs->st_nlink;
213     while (dp = readdir (df)) {
214         if (dp->d_name[strlen(dp->d_name) - 1] == '~')
215             continue;
216         if (dp->d_name[0] == '.' && dp->d_name[1] == '#') /* 'non-conflict files' left behind by CVS */
217             continue;
218         strcpy (p, dp->d_name);
219
220         if (n_dirs > 0) {
221             if (stat (buf, &sb) < 0) {
222                 mperror (buf);
223                 continue;
224             }
225
226 #ifdef S_ISDIR
227             if(S_ISDIR(sb.st_mode))
228 #else
229             if (sb.st_mode & S_IFDIR) 
230 #endif
231             {
232                 /* directory */
233 #ifndef __CYGWIN32__   /* don't trust cygwin's n_dirs count */
234                 n_dirs--;
235 #endif
236                 if (dp->d_name[0] == '.' &&
237                     (dp->d_name[1] == '\0' || (dp->d_name[1] == '.' &&
238                                                dp->d_name[2] == '\0')))
239                     continue;
240                 if (!strcmp (dp->d_name, "RCS"))
241                     continue;
242                 if (!strcmp (dp->d_name, "SCCS"))
243                     continue;
244                 if (!strcmp (dp->d_name, "CVS"))
245                     continue;
246                 if (!strcmp (dp->d_name, "CVS.adm"))
247                     continue;
248                 ocurdir = rcurdir;
249                 rcurdir = buf;
250                 curdir = silent ? buf : (char *)0;
251                 if (!silent)
252                     printf ("%s:\n", buf);
253                 if ((stat (dp->d_name, &sc) < 0) && (errno == ENOENT)) {
254                     if (mkdir (dp->d_name, 0777) < 0 ||
255                         stat (dp->d_name, &sc) < 0) {
256                         mperror (dp->d_name);
257                         curdir = rcurdir = ocurdir;
258                         continue;
259                     }
260                 }
261                 if (readlink (dp->d_name, symbuf, sizeof(symbuf) - 1) >= 0) {
262                     msg ("%s: is a link instead of a directory", dp->d_name);
263                     curdir = rcurdir = ocurdir;
264                     continue;
265                 }
266                 if (chdir (dp->d_name) < 0) {
267                     mperror (dp->d_name);
268                     curdir = rcurdir = ocurdir;
269                     continue;
270                 }
271                 dodir (buf, &sb, &sc, (buf[0] != '/'));
272                 if (chdir ("..") < 0)
273                     quiterr (1, "..");
274                 curdir = rcurdir = ocurdir;
275                 continue;
276             }
277         }
278
279         /* non-directory */
280         symlen = readlink (dp->d_name, symbuf, sizeof(symbuf) - 1);
281         if (symlen >= 0)
282             symbuf[symlen] = '\0';
283
284         /* The option to ignore links exists mostly because
285            checking for them slows us down by 10-20%.
286            But it is off by default because this really is a useful check. */
287         if (!ignore_links) {
288             /* see if the file in the base tree was a symlink */
289             basesymlen = readlink(buf, basesym, sizeof(basesym) - 1);
290             if (basesymlen >= 0)
291                 basesym[basesymlen] = '\0';
292         }
293
294         if (symlen >= 0) {
295           if (!equivalent (basesymlen>=0 ? basesym : buf, symbuf)) {
296             if (force) {
297               unlink(dp->d_name);
298               if (symlink (basesymlen>=0 ? basesym : buf, dp->d_name) < 0)
299                 mperror (dp->d_name);
300             } else {
301               /* Link exists in new tree.  Print message if it doesn't match. */
302               msg ("%s: %s", dp->d_name, symbuf);
303             }
304           }
305         } else {
306           if (symlink (basesymlen>=0 ? basesym : buf, dp->d_name) < 0)
307             mperror (dp->d_name);
308         }
309     }
310     
311     closedir (df);
312     return 0;
313 }
314
315
316 main (ac, av)
317 int ac;
318 char **av;
319 {
320     char *prog_name = av[0];
321     char* tn;
322     struct stat fs, ts;
323 #ifdef __CYGWIN32__
324     /*   
325     The lndir code assumes unix-style paths to work. cygwin
326     lets you get away with using dos'ish paths (e.g., "f:/oo")
327     in most contexts. Using them with 'lndir' will seriously
328     confuse the user though, so under-the-hood, we convert the
329     path into something POSIX-like.
330     */
331     static char fn[MAXPATHLEN+1];
332 #else
333     char *fn;
334 #endif
335
336     while (++av, --ac) {
337         if (strcmp(*av, "-silent") == 0)
338             silent = 1;
339         if (strcmp(*av, "-f") == 0)
340             force = 1;
341         else if (strcmp(*av, "-ignorelinks") == 0)
342             ignore_links = 1;
343         else if (strcmp(*av, "--") == 0) {
344             ++av, --ac;
345             break;
346         }
347         else
348             break;
349     }
350
351     if (ac < 1 || ac > 2)
352         quit (1, "usage: %s [-f] [-silent] [-ignorelinks] fromdir [todir]",
353               prog_name);
354
355 #ifdef __CYGWIN32__
356     cygwin_conv_to_full_posix_path(av[0], fn);
357 #else
358     fn = av[0];
359 #endif
360
361     if (ac == 2)
362         tn = av[1];
363     else
364         tn = ".";
365
366     /* to directory */
367     if (stat (tn, &ts) < 0) {
368       if (force && (tn[0] != '.' || tn[1] != '\0') ) {
369          mkdir(tn, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH );
370       } 
371       else {
372         quiterr (1, tn);
373 #ifdef S_ISDIR
374         if (!(S_ISDIR(ts.st_mode)))
375 #else
376         if (!(ts.st_mode & S_IFDIR))
377 #endif
378            quit (2, "%s: Not a directory", tn);
379       }
380     }
381     if (chdir (tn) < 0)
382         quiterr (1, tn);
383
384     /* from directory */
385     if (stat (fn, &fs) < 0)
386         quiterr (1, fn);
387 #ifdef S_ISDIR
388     if (!(S_ISDIR(fs.st_mode)))
389 #else
390     if (!(fs.st_mode & S_IFDIR))
391 #endif
392         quit (2, "%s: Not a directory", fn);
393
394     exit (dodir (fn, &fs, &ts, 0));
395 }