Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / config / util / lndir.c
1 /* $TOG: lndir.c /main/17 1998/02/06 11:23:50 kaleb $ */
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, 1998 The Open Group
7
8 All Rights Reserved.
9
10 The above copyright notice and this permission notice shall be included in
11 all copies or substantial portions of the Software.
12
13 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
16 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
17 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19
20 Except as contained in this notice, the name of The Open Group shall not be
21 used in advertising or otherwise to promote the sale, use or other dealings
22 in this Software without prior written authorization from The Open Group.
23
24 */
25
26 /* From the original /bin/sh script:
27
28   Used to create a copy of the a directory tree that has links for all
29   non-directories (except those named RCS, SCCS or CVS.adm).  If you are
30   building the distribution on more than one machine, you should use
31   this technique.
32
33   If your master sources are located in /usr/local/src/X and you would like
34   your link tree to be in /usr/local/src/new-X, do the following:
35
36         %  mkdir /usr/local/src/new-X
37         %  cd /usr/local/src/new-X
38         %  lndir ../X
39 */
40
41 #include <X11/Xos.h>
42 #include <X11/Xfuncproto.h>
43 #include <stdio.h>
44 #include <sys/stat.h>
45 #include <sys/param.h>
46 #include <errno.h>
47
48 #ifndef X_NOT_POSIX
49 #include <dirent.h>
50 #else
51 #ifdef SYSV
52 #include <dirent.h>
53 #else
54 #ifdef USG
55 #include <dirent.h>
56 #else
57 #include <sys/dir.h>
58 #ifndef dirent
59 #define dirent direct
60 #endif
61 #endif
62 #endif
63 #endif
64 #ifndef MAXPATHLEN
65 #define MAXPATHLEN 2048
66 #endif
67
68 #if NeedVarargsPrototypes
69 #include <stdarg.h>
70 #endif
71
72 #ifdef X_NOT_STDC_ENV
73 extern int errno;
74 #endif
75 int silent = 0;                 /* -silent */
76 int ignore_links = 0;           /* -ignorelinks */
77
78 char *rcurdir;
79 char *curdir;
80
81 void
82 quit (
83 #if NeedVarargsPrototypes
84     int code, char * fmt, ...)
85 #else
86     code, fmt, a1, a2, a3)
87     char *fmt;
88 #endif
89 {
90 #if NeedVarargsPrototypes
91     va_list args;
92     va_start(args, fmt);
93     vfprintf (stderr, fmt, args);
94     va_end(args);
95 #else
96     fprintf (stderr, fmt, a1, a2, a3);
97 #endif
98     putc ('\n', stderr);
99     exit (code);
100 }
101
102 void
103 quiterr (code, s)
104     char *s;
105 {
106     perror (s);
107     exit (code);
108 }
109
110 void
111 msg (
112 #if NeedVarargsPrototypes
113     char * fmt, ...)
114 #else
115     fmt, a1, a2, a3)
116     char *fmt;
117 #endif
118 {
119 #if NeedVarargsPrototypes
120     va_list args;
121 #endif
122     if (curdir) {
123         fprintf (stderr, "%s:\n", curdir);
124         curdir = 0;
125     }
126 #if NeedVarargsPrototypes
127     va_start(args, fmt);
128     vfprintf (stderr, fmt, args);
129     va_end(args);
130 #else
131     fprintf (stderr, fmt, a1, a2, a3);
132 #endif
133     putc ('\n', stderr);
134 }
135
136 void
137 mperror (s)
138     char *s;
139 {
140     if (curdir) {
141         fprintf (stderr, "%s:\n", curdir);
142         curdir = 0;
143     }
144     perror (s);
145 }
146
147
148 int equivalent(lname, rname)
149     char *lname;
150     char *rname;
151 {
152     char *s;
153
154     if (!strcmp(lname, rname))
155         return 1;
156     for (s = lname; *s && (s = strchr(s, '/')); s++) {
157         while (s[1] == '/')
158             strcpy(s+1, s+2);
159     }
160     return !strcmp(lname, rname);
161 }
162
163
164 /* Recursively create symbolic links from the current directory to the "from"
165    directory.  Assumes that files described by fs and ts are directories. */
166
167 dodir (fn, fs, ts, rel)
168 char *fn;                       /* name of "from" directory, either absolute or
169                                    relative to cwd */
170 struct stat *fs, *ts;           /* stats for the "from" directory and cwd */
171 int rel;                        /* if true, prepend "../" to fn before using */
172 {
173     DIR *df;
174     struct dirent *dp;
175     char buf[MAXPATHLEN + 1], *p;
176     char symbuf[MAXPATHLEN + 1];
177     char basesym[MAXPATHLEN + 1];
178     struct stat sb, sc;
179     int n_dirs;
180     int symlen;
181     int basesymlen = -1;
182     char *ocurdir;
183
184     if ((fs->st_dev == ts->st_dev) && (fs->st_ino == ts->st_ino)) {
185         msg ("%s: From and to directories are identical!", fn);
186         return 1;
187     }
188
189     if (rel)
190         strcpy (buf, "../");
191     else
192         buf[0] = '\0';
193     strcat (buf, fn);
194     
195     if (!(df = opendir (buf))) {
196         msg ("%s: Cannot opendir", buf);
197         return 1;
198     }
199
200     p = buf + strlen (buf);
201     *p++ = '/';
202     n_dirs = fs->st_nlink;
203     while (dp = readdir (df)) {
204         if (dp->d_name[strlen(dp->d_name) - 1] == '~')
205             continue;
206         strcpy (p, dp->d_name);
207
208         if (n_dirs > 0) {
209             if (stat (buf, &sb) < 0) {
210                 mperror (buf);
211                 continue;
212             }
213
214 #ifdef S_ISDIR
215             if(S_ISDIR(sb.st_mode))
216 #else
217             if (sb.st_mode & S_IFDIR) 
218 #endif
219             {
220                 /* directory */
221                 n_dirs--;
222                 if (dp->d_name[0] == '.' &&
223                     (dp->d_name[1] == '\0' || (dp->d_name[1] == '.' &&
224                                                dp->d_name[2] == '\0')))
225                     continue;
226                 if (!strcmp (dp->d_name, "RCS"))
227                     continue;
228                 if (!strcmp (dp->d_name, "SCCS"))
229                     continue;
230                 if (!strcmp (dp->d_name, "CVS"))
231                     continue;
232                 if (!strcmp (dp->d_name, "CVS.adm"))
233                     continue;
234                 ocurdir = rcurdir;
235                 rcurdir = buf;
236                 curdir = silent ? buf : (char *)0;
237                 if (!silent)
238                     printf ("%s:\n", buf);
239                 if ((stat (dp->d_name, &sc) < 0) && (errno == ENOENT)) {
240                     if (mkdir (dp->d_name, 0777) < 0 ||
241                         stat (dp->d_name, &sc) < 0) {
242                         mperror (dp->d_name);
243                         curdir = rcurdir = ocurdir;
244                         continue;
245                     }
246                 }
247                 if (readlink (dp->d_name, symbuf, sizeof(symbuf) - 1) >= 0) {
248                     msg ("%s: is a link instead of a directory", dp->d_name);
249                     curdir = rcurdir = ocurdir;
250                     continue;
251                 }
252                 if (chdir (dp->d_name) < 0) {
253                     mperror (dp->d_name);
254                     curdir = rcurdir = ocurdir;
255                     continue;
256                 }
257                 dodir (buf, &sb, &sc, (buf[0] != '/'));
258                 if (chdir ("..") < 0)
259                     quiterr (1, "..");
260                 curdir = rcurdir = ocurdir;
261                 continue;
262             }
263         }
264
265         /* non-directory */
266         symlen = readlink (dp->d_name, symbuf, sizeof(symbuf) - 1);
267         if (symlen >= 0)
268             symbuf[symlen] = '\0';
269
270         /* The option to ignore links exists mostly because
271            checking for them slows us down by 10-20%.
272            But it is off by default because this really is a useful check. */
273         if (!ignore_links) {
274             /* see if the file in the base tree was a symlink */
275             basesymlen = readlink(buf, basesym, sizeof(basesym) - 1);
276             if (basesymlen >= 0)
277                 basesym[basesymlen] = '\0';
278         }
279
280         if (symlen >= 0) {
281             /* Link exists in new tree.  Print message if it doesn't match. */
282             if (!equivalent (basesymlen>=0 ? basesym : buf, symbuf))
283                 msg ("%s: %s", dp->d_name, symbuf);
284         } else {
285             if (symlink (basesymlen>=0 ? basesym : buf, dp->d_name) < 0)
286                 mperror (dp->d_name);
287         }
288     }
289
290     closedir (df);
291     return 0;
292 }
293
294
295 main (ac, av)
296 int ac;
297 char **av;
298 {
299     char *prog_name = av[0];
300     char *fn, *tn;
301     struct stat fs, ts;
302
303     while (++av, --ac) {
304         if (strcmp(*av, "-silent") == 0)
305             silent = 1;
306         else if (strcmp(*av, "-ignorelinks") == 0)
307             ignore_links = 1;
308         else if (strcmp(*av, "--") == 0) {
309             ++av, --ac;
310             break;
311         }
312         else
313             break;
314     }
315
316     if (ac < 1 || ac > 2)
317         quit (1, "usage: %s [-silent] [-ignorelinks] fromdir [todir]",
318               prog_name);
319
320     fn = av[0];
321     if (ac == 2)
322         tn = av[1];
323     else
324         tn = ".";
325
326     /* to directory */
327     if (stat (tn, &ts) < 0)
328         quiterr (1, tn);
329 #ifdef S_ISDIR
330     if (!(S_ISDIR(ts.st_mode)))
331 #else
332     if (!(ts.st_mode & S_IFDIR))
333 #endif
334         quit (2, "%s: Not a directory", tn);
335     if (chdir (tn) < 0)
336         quiterr (1, tn);
337
338     /* from directory */
339     if (stat (fn, &fs) < 0)
340         quiterr (1, fn);
341 #ifdef S_ISDIR
342     if (!(S_ISDIR(fs.st_mode)))
343 #else
344     if (!(fs.st_mode & S_IFDIR))
345 #endif
346         quit (2, "%s: Not a directory", fn);
347
348     exit (dodir (fn, &fs, &ts, 0));
349 }