1 
2 /* lndir.c; part of GNU CSSC.
3  *
4  * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007,
5  * 2008, 2009, 2010, 2011, 2014, 2019 Free Software Foundation, Inc.
6  *
7  * This file is derived from xc/config/util/lndir.c in the X11R6 distribution.
8  * It's been changed to make use of GNU Autoconf rather than xmkmf (imake)
9  * for portability.
10  *   -- James Youngman <jay@gnu.org>
11  */
12 
13 
14 /* $XConsortium: lndir.c,v 1.13 94/04/17 20:10:42 rws Exp $ */
15 /* Create shadow link tree (after X11R4 script of the same name)
16    Mark Reinhold (mbr@lcs.mit.edu)/3 January 1990 */
17 
18 /*
19 Copyright (c) 1990,  X Consortium
20 
21 Permission is hereby granted, free of charge, to any person obtaining a copy
22 of this software and associated documentation files (the "Software"), to deal
23 in the Software without restriction, including without limitation the rights
24 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
25 copies of the Software, and to permit persons to whom the Software is
26 furnished to do so, subject to the following conditions:
27 
28 The above copyright notice and this permission notice shall be included in
29 all copies or substantial portions of the Software.
30 
31 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
32 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
33 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
34 X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
35 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
36 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
37 
38 Except as contained in this notice, the name of the X Consortium shall not be
39 used in advertising or otherwise to promote the sale, use or other dealings
40 in this Software without prior written authorization from the X Consortium.
41 
42 */
43 
44 /* From the original /bin/sh script:
45 
46   Used to create a copy of the a directory tree that has links for all
47   non-directories (except those named RCS, SCCS or CVS.adm).  If you are
48   building the distribution on more than one machine, you should use
49   this technique.
50 
51   If your master sources are located in /usr/local/src/X and you would like
52   your link tree to be in /usr/local/src/new-X, do the following:
53 
54         %  mkdir /usr/local/src/new-X
55         %  cd /usr/local/src/new-X
56         %  lndir ../X
57 */
58 #include <config.h>
59 
60 #ifndef HAVE_SYMLINK
61 #error I need to be patched to support either hard links or copying.
62 #endif
63 
64 #ifndef HAVE_READLINK
65 #error I need to be patched to support either hard links or copying.
66 #endif
67 
68 
69 #include <stdlib.h>
70 #include <assert.h>
71 #include <stdarg.h>
72 #include <string.h>
73 #include <stdio.h>
74 #include <errno.h>
75 #include <unistd.h>
76 #include <sys/stat.h>
77 #include <sys/param.h>
78 #include <sys/types.h>
79 
80 #include "dirent-safer.h"
81 
82 #ifndef MAXPATHLEN
83 #define MAXPATHLEN 2048
84 #endif
85 
86 int silent;
87 
88 char *rcurdir;
89 char *curdir;
90 
91 static void vmsg (const char *, int, va_list);
92 
93 void
quit(int code,int err_num,char * fmt,...)94 quit (int code, int err_num, char * fmt, ...)
95 {
96     va_list args;
97     va_start(args, fmt);
98     vmsg (fmt, err_num, args);
99     va_end(args);
100     exit (code);
101 }
102 
103 static void
vmsg(const char * fmt,int errnum,va_list ap)104 vmsg (const char *fmt, int errnum, va_list ap)
105 {
106   assert (fmt && fmt[0]);
107   if (curdir) {
108     fprintf (stderr, "%s:\n", curdir);
109     curdir = 0;
110   }
111   vfprintf (stderr, fmt, ap);
112   if (errnum)
113     {
114       fprintf (stderr, ": %s", strerror (errnum));
115     }
116   putc ('\n', stderr);
117 }
118 
119 void
mperror(int err_num,const char * fmt,...)120 mperror (int err_num, const char *fmt, ...)
121 {
122   va_list args;
123   va_start(args, fmt);
124   vmsg (fmt, err_num, args);
125   va_end(args);
126 }
127 
equivalent(char * lname,const char * rname)128 int equivalent(char *lname, const char *rname)
129 {
130     char *s;
131 
132     if (!strcmp(lname, rname))
133         return 1;
134     for (s = lname; *s && (s = strchr(s, '/')); s++) {
135         while (s[1] == '/')
136             strcpy(s+1, s+2);
137     }
138     return !strcmp(lname, rname);
139 }
140 
is_dir(const struct stat * p)141 static int is_dir(const struct stat *p)
142 {
143   return S_ISDIR(p->st_mode);
144 }
145 
ignored(const char * n)146 static int ignored(const char *n)
147 {
148   const char *histdirs[] = {"SCCS","RCS","CVS.adm","CVS", (const char*)0};
149   const char **p;
150   for (p=histdirs; *p; ++p)
151     {
152       if (0 == strcmp(*p, n))
153         return 1;
154     }
155   return 0;
156 }
157 
158 static int
is_dot_or_dotdot(const char * p)159 is_dot_or_dotdot (const char *p)
160 {
161   if (p[0] == '.') {
162     if (p[1] == 0) {
163       return 1;			/* . */
164     } else if (p[1] == '.' && p[2] == 0) {
165       return 1;			/* .. */
166     }
167   }
168   return 0;
169 }
170 
171 
172 /* Recursively create symbolic links from the current directory to the "from"
173    directory.  Assumes that files described by fs and ts are directories. */
174 
175 int
dodir(const char * fn,const struct stat * fs,const struct stat * ts,int rel)176 dodir (const char *fn, /* name of "from" directory, either absolute or relative to cwd */
177        const struct stat *fs, /* stats for the "from" directory */
178        const struct stat *ts, /* stats for the cwd */
179        int rel)
180 {
181     DIR *df;
182     struct dirent *dp;
183     char buf[MAXPATHLEN + 1], *p;
184     char symbuf[MAXPATHLEN + 1];
185     struct stat sb, sc;
186     int symlen;
187     char *ocurdir;
188 
189     if ((fs->st_dev == ts->st_dev) && (fs->st_ino == ts->st_ino))
190       {
191         mperror (errno,
192 		 "%s: From and to directories are identical, "
193 		 "hence no work to do!", fn);
194         return 0;               /* nothing to do! */
195       }
196 
197     if (rel)
198         strcpy (buf, "../");
199     else
200         buf[0] = '\0';
201     strcat (buf, fn);
202 
203     if (!(df = opendir (buf))) {
204         mperror (errno, "%s: Cannot opendir", buf);
205         return 1;
206     }
207 
208     p = buf + strlen (buf);
209     *p++ = '/';
210 
211     while ( (struct dirent*)0 != (dp = readdir (df)) ) {
212         const size_t namlen = strlen(dp->d_name);
213 	if (namlen) {
214 	  if (dp->d_name[namlen - 1] == '~')
215             continue;
216 	}
217 	if (is_dot_or_dotdot (dp->d_name)) {
218 	  continue;		/* ignore. */
219 	}
220 
221 
222         strcpy (p, dp->d_name);
223 
224         if (1) {
225             if (stat (buf, &sb) < 0) {
226 	        mperror (errno, "failed to stat %s", buf);
227                 continue;
228             }
229 
230 
231             if (is_dir(&sb))
232             {
233                 /* directory */
234                 if (is_dot_or_dotdot (dp->d_name) || ignored (dp->d_name))
235                     continue;
236 
237                 ocurdir = rcurdir;
238                 rcurdir = buf;
239                 curdir = silent ? buf : (char *)0;
240                 if (!silent)
241                     printf ("making links in %s:\n", buf);
242                 if ((stat (dp->d_name, &sc) < 0) && (errno == ENOENT)) {
243                     if (mkdir (dp->d_name, 0777) < 0 ||
244                         stat (dp->d_name, &sc) < 0) {
245 		        mperror (errno, "failed to stat %s", dp->d_name);
246                         curdir = rcurdir = ocurdir;
247                         continue;
248                     }
249                 }
250                 if (readlink (dp->d_name, symbuf, sizeof(symbuf) - 1) >= 0) {
251 		    mperror (0, "%s: is a link instead of a directory",
252 			     dp->d_name);
253                     curdir = rcurdir = ocurdir;
254                     continue;
255                 }
256                 if (chdir (dp->d_name) < 0) {
257 		    mperror (errno,
258 			     "failed to change directory into %s", dp->d_name);
259                     curdir = rcurdir = ocurdir;
260                     continue;
261                 }
262                 dodir (buf, &sb, &sc, (buf[0] != '/'));
263                 if (chdir ("..") < 0)
264 		  quit (1, errno, "..");
265                 curdir = rcurdir = ocurdir;
266                 continue;
267             }
268         }
269 
270         /* non-directory */
271         symlen = readlink (dp->d_name, symbuf, sizeof(symbuf) - 1);
272         if (symlen >= 0) {
273             symbuf[symlen] = '\0';
274             if (!equivalent (symbuf, buf))
275                 mperror (0, "%s: %s", dp->d_name, symbuf);
276         } else if (symlink (buf, dp->d_name) < 0)
277             mperror (errno,
278 		     "failed to create a symbolic link %s pointing to %s",
279 		     dp->d_name, buf);
280     }
281 
282     closedir (df);
283     return 0;
284 }
285 
286 int
main(int ac,char * av[])287 main (int ac, char *av[])
288 {
289     char *fn, *tn;
290     struct stat fs, ts;
291 
292     silent = 0;
293     if (ac > 1)
294       {
295         if (!strcmp(av[1], "--silent")) /* GNU-style long options. */
296           silent = 1;
297         else if (!strcmp(av[1], "-silent")) /* X11R4 compatibility. */
298           silent = 1;
299       }
300 
301     if (ac < silent + 2 || ac > silent + 3)
302         quit (1, 0, "usage: %s [-silent] fromdir [todir]", av[0]);
303 
304     fn = av[silent + 1];
305     if (ac == silent + 3)
306         tn = av[silent + 2];
307     else
308         tn = ".";
309 
310     /* to directory */
311     if (stat (tn, &ts) < 0)
312         quit (1, errno, "%s", tn);
313 
314     if (!is_dir(&ts))
315         quit (2, 0, "%s: Not a directory", tn);
316 
317     if (chdir (tn) < 0)
318         quit (1, errno, "%s", tn);
319 
320     /* from directory */
321     if (stat (fn, &fs) < 0)
322         quit (1, errno, "%s", fn);
323 
324     if (!is_dir(&fs))
325         quit (2, 0, "%s: Not a directory", fn);
326 
327     return dodir (fn, &fs, &ts, 0);
328 }
329