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