1 /* $Xorg: lndir.c,v 1.5 2001/02/09 02:03:17 xorgcvs Exp $ */
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 Permission to use, copy, modify, distribute, and sell this software and its
9 documentation for any purpose is hereby granted without fee, provided that
10 the above copyright notice appear in all copies and that both that
11 copyright notice and this permission notice appear in supporting
12 documentation.
13 
14 The above copyright notice and this permission notice shall be included in
15 all copies or substantial portions of the Software.
16 
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
20 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
21 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 
24 Except as contained in this notice, the name of The Open Group shall not be
25 used in advertising or otherwise to promote the sale, use or other dealings
26 in this Software without prior written authorization from The Open Group.
27 
28 */
29 /* $XFree86: xc/config/util/lndir.c,v 3.18 2003/06/24 15:44:45 eich Exp $ */
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, by default, those named BitKeeper, RCS, SCCS
35   or CVS.adm).  If you are building the distribution on more than one
36   machine, you should use 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 #ifdef	XORG_VERSION
47 
48 #include <X11/Xos.h>
49 #include <X11/Xfuncproto.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <sys/stat.h>
53 #if !defined(MINIX) && !defined(Lynx)
54 #include <sys/param.h>
55 #endif
56 #include <errno.h>
57 
58 #ifndef X_NOT_POSIX
59 #include <dirent.h>
60 #else
61 #ifdef SYSV
62 #include <dirent.h>
63 #else
64 #ifdef USG
65 #include <dirent.h>
66 #else
67 #include <sys/dir.h>
68 #ifndef dirent
69 #define dirent direct
70 #endif
71 #endif
72 #endif
73 #endif
74 #ifndef MAXPATHLEN
75 #define MAXPATHLEN 2048
76 #endif
77 
78 #include <stdarg.h>
79 
80 #else	/* XORG_VERSION */
81 
82 #include <schily/mconfig.h>
83 #include <schily/stdio.h>
84 #include <schily/stdlib.h>
85 #include <schily/unistd.h>
86 #include <schily/string.h>
87 #include <schily/stat.h>
88 #include <schily/dirent.h>
89 #include <schily/maxpath.h>
90 #include <schily/errno.h>
91 
92 #ifndef MAXPATHLEN
93 #define MAXPATHLEN MAXPATHNAME
94 #endif
95 #include <schily/varargs.h>
96 #include <schily/libport.h>	/* Define missing prototypes */
97 
98 #ifndef	HAVE_LSTAT
99 #define	lstat	stat
100 #endif
101 
102 static void quit __PR((int code, char * fmt, ...));
103 static void quiterr __PR((int code, char *s));
104 static void msg  __PR((char * fmt, ...));
105 static void mperror __PR((char *s));
106 static void ovstrcpy __PR((char *p2, char *p1));
107 static int  equivalent __PR((char *lname, char *rname, char **p));
108 static int dodir __PR((char *fn, struct stat *fs, struct stat *ts, int rel));
109 	int main __PR((int ac, char *av[]));
110 
111 #endif	/* !XORG_VERSION */
112 
113 int silent = 0;			/* -silent */
114 int ignore_links = 0;		/* -ignorelinks */
115 int with_revinfo = 0;		/* -withrevinfo */
116 
117 char *rcurdir;
118 char *curdir;
119 
120 #ifdef	PROTOTYPES
121 static void
quit(int code,char * fmt,...)122 quit (int code, char * fmt, ...)
123 #else
124 static void
125 quit (code, fmt, va_alist)
126 	int	code;
127 	char	*fmt;
128 	va_dcl
129 #endif
130 {
131     va_list args;
132 #ifdef	PROTOTYPES
133     va_start(args, fmt);
134 #else
135     va_start(args);
136 #endif
137     vfprintf (stderr, fmt, args);
138     va_end(args);
139     putc ('\n', stderr);
140     exit (code);
141 }
142 
143 static void
quiterr(code,s)144 quiterr (code, s)
145 	int	code;
146 	char	*s;
147 {
148     perror (s);
149     exit (code);
150 }
151 
152 #ifdef	PROTOTYPES
153 static void
msg(char * fmt,...)154 msg (char * fmt, ...)
155 #else
156 static void
157 msg(fmt, va_alist)
158 	char	*fmt;
159 	va_dcl
160 #endif
161 {
162     va_list args;
163     if (curdir) {
164 	fprintf (stderr, "%s:\n", curdir);
165 	curdir = 0;
166     }
167 #ifdef	PROTOTYPES
168     va_start(args, fmt);
169 #else
170     va_start(args);
171 #endif
172     vfprintf (stderr, fmt, args);
173     va_end(args);
174     putc ('\n', stderr);
175 }
176 
177 static void
mperror(s)178 mperror(s)
179 	char	*s;
180 {
181     if (curdir) {
182 	fprintf (stderr, "%s:\n", curdir);
183 	curdir = 0;
184     }
185     perror (s);
186 }
187 
188 /*
189  * A strcpy() that works with overlapping buffers
190  */
191 static void
ovstrcpy(p2,p1)192 ovstrcpy(p2, p1)
193 	register char	*p2;
194 	register char	*p1;
195 {
196 	while ((*p2++ = *p1++) != '\0')
197 		;
198 }
199 
200 
201 static int
equivalent(lname,rname,p)202 equivalent(lname, rname, p)
203 	char	*lname;
204 	char	*rname;
205 	char	**p;
206 {
207     char *s;
208 
209     if (!strcmp(lname, rname))
210 	return 1;
211     for (s = lname; *s && (s = strchr(s, '/')); s++) {
212 	while (s[1] == '/') {
213 	    ovstrcpy(s+1, s+2);
214 	    if (*p) (*p)--;
215 	}
216     }
217     return !strcmp(lname, rname);
218 }
219 
220 
221 /* Recursively create symbolic links from the current directory to the "from"
222    directory.  Assumes that files described by fs and ts are directories. */
223 static int
dodir(fn,fs,ts,rel)224 dodir (fn, fs, ts, rel)
225 	char	*fn;		/* name of "from" directory, either absolute or
226 				   relative to cwd */
227 	struct stat *fs;
228 	struct stat *ts;	/* stats for the "from" directory and cwd */
229 	int	rel;		/* if true, prepend "../" to fn before using */
230 {
231     DIR *df;
232     struct dirent *dp;
233     char buf[MAXPATHLEN + 1], *p;
234     char symbuf[MAXPATHLEN + 1];
235     char basesym[MAXPATHLEN + 1];
236     struct stat sb, sc;
237     int n_dirs;
238     int symlen = -1;
239     int basesymlen = -1;
240     char *ocurdir;
241 
242     if ((fs->st_dev == ts->st_dev) && (fs->st_ino == ts->st_ino)) {
243 	msg ("%s: From and to directories are identical!", fn);
244 	return 1;
245     }
246 
247     if (rel)
248 	strcpy (buf, "../");
249     else
250 	buf[0] = '\0';
251     strcat (buf, fn);
252 
253     if (!(df = opendir (buf))) {
254 	msg ("%s: Cannot opendir", buf);
255 	return 1;
256     }
257 
258     p = buf + strlen (buf);
259     if (*(p - 1) != '/')
260 	*p++ = '/';
261     n_dirs = fs->st_nlink;
262     while ((dp = readdir (df))) {
263 	if (dp->d_name[strlen(dp->d_name) - 1] == '~')
264 	    continue;
265 #ifdef __DARWIN__
266 	/* Ignore these Mac OS X Finder data files */
267 	if (!strcmp(dp->d_name, ".DS_Store") ||
268 	    !strcmp(dp->d_name, "._.DS_Store"))
269 	    continue;
270 #endif
271 	strcpy (p, dp->d_name);
272 
273 	if (n_dirs > 0) {
274 	    if (lstat (buf, &sb) < 0) {
275 		mperror (buf);
276 		continue;
277 	    }
278 
279 #ifdef S_ISDIR
280 	    if(S_ISDIR(sb.st_mode))
281 #else
282 	    if (sb.st_mode & S_IFDIR)
283 #endif
284 	    {
285 		/* directory */
286 		n_dirs--;
287 		if (dp->d_name[0] == '.' &&
288 		    (dp->d_name[1] == '\0' || (dp->d_name[1] == '.' &&
289 					       dp->d_name[2] == '\0')))
290 		    continue;
291 		if (!with_revinfo) {
292 		    if (!strcmp (dp->d_name, "BitKeeper"))
293 			continue;
294 		    if (!strcmp (dp->d_name, "RCS"))
295 			continue;
296 		    if (!strcmp (dp->d_name, "SCCS"))
297 			continue;
298 		    if (!strcmp (dp->d_name, "CVS"))
299 			continue;
300 		    if (!strcmp (dp->d_name, "CVS.adm"))
301 			continue;
302 		    if (!strcmp (dp->d_name, ".svn"))
303 			continue;
304 		}
305 		ocurdir = rcurdir;
306 		rcurdir = buf;
307 		curdir = silent ? buf : (char *)0;
308 		if (!silent)
309 		    printf ("%s:\n", buf);
310 		if ((stat (dp->d_name, &sc) < 0) && (errno == ENOENT)) {
311 		    if (mkdir (dp->d_name, 0777) < 0 ||
312 			stat (dp->d_name, &sc) < 0) {
313 			mperror (dp->d_name);
314 			curdir = rcurdir = ocurdir;
315 			continue;
316 		    }
317 		}
318 #ifdef	HAVE_READLINK
319 		if (readlink (dp->d_name, symbuf, sizeof(symbuf) - 1) >= 0) {
320 		    msg ("%s: is a link instead of a directory", dp->d_name);
321 		    curdir = rcurdir = ocurdir;
322 		    continue;
323 		}
324 #endif
325 		if (chdir (dp->d_name) < 0) {
326 		    mperror (dp->d_name);
327 		    curdir = rcurdir = ocurdir;
328 		    continue;
329 		}
330 		dodir (buf, &sb, &sc, (buf[0] != '/'));
331 		if (chdir ("..") < 0)
332 		    quiterr (1, "..");
333 		curdir = rcurdir = ocurdir;
334 		continue;
335 	    }
336 	}
337 
338 #ifdef	HAVE_READLINK
339 	/* non-directory */
340 	symlen = readlink (dp->d_name, symbuf, sizeof(symbuf) - 1);
341 	if (symlen >= 0)
342 	    symbuf[symlen] = '\0';
343 
344 	/* The option to ignore links exists mostly because
345 	   checking for them slows us down by 10-20%.
346 	   But it is off by default because this really is a useful check. */
347 	if (!ignore_links) {
348 	    /* see if the file in the base tree was a symlink */
349 	    basesymlen = readlink(buf, basesym, sizeof(basesym) - 1);
350 	    if (basesymlen >= 0)
351 		basesym[basesymlen] = '\0';
352 	}
353 #endif
354 
355 	if (symlen >= 0) {
356 	    /* Link exists in new tree.  Print message if it doesn't match. */
357 	    if (!equivalent (basesymlen>=0 ? basesym : buf, symbuf,
358 			     basesymlen>=0 ? (char **) 0 : &p))
359 		msg ("%s: %s", dp->d_name, symbuf);
360 	} else {
361 	    char *sympath;
362 
363 	    if (basesymlen>=0) {
364 		if ((buf[0] == '.') && (buf[1] == '.') && (buf[2] == '/') &&
365 		    (basesym[0] == '.') && (basesym[1] == '.') &&
366 		    (basesym[2] == '/')) {
367 		    /* It becomes very tricky here. We have
368 		       ../../bar/foo symlinked to ../xxx/yyy. We
369 		       can't just use ../xxx/yyy. We have to use
370 		       ../../bar/foo/../xxx/yyy.  */
371 
372 		    int i;
373 		    char *start, *end;
374 
375 		    strcpy (symbuf, buf);
376 		    /* Find the first char after "../" in symbuf.  */
377 		    start = symbuf;
378 		    do {
379 			start += 3;
380 		    } while ((start[0] == '.') && (start[1] == '.') &&
381 			     (start[2] == '/'));
382 
383 		    /* Then try to eliminate "../"s in basesym.  */
384 		    i = 0;
385 		    end = strrchr (symbuf, '/');
386 		    if (start < end) {
387 			do {
388 			    i += 3;
389 			    end--;
390 			    while ((*end != '/') && (end != start))
391 				end--;
392 			    if (end == start)
393 				break;
394 			} while ((basesym[i] == '.') &&
395 				 (basesym[i + 1] == '.') &&
396 				 (basesym[i + 2] == '/'));
397 		    }
398 		    if (*end == '/')
399 			end++;
400 		    strcpy (end, &basesym[i]);
401 		    sympath = symbuf;
402 		}
403 		else
404 		    sympath = basesym;
405 	    }
406 	    else
407 		sympath = buf;
408 #ifdef	HAVE_SYMLINK
409 	    if (symlink (sympath, dp->d_name) < 0)
410 		mperror (dp->d_name);
411 #endif
412 	}
413     }
414 
415     closedir (df);
416     return 0;
417 }
418 
419 int
main(ac,av)420 main(ac, av)
421 	int	ac;
422 	char	*av[];
423 {
424     char *prog_name = av[0];
425     char *fn, *tn;
426     struct stat fs, ts;
427 
428     while (++av, --ac) {
429 	if (strcmp(*av, "-silent") == 0)
430 	    silent = 1;
431 	else if (strcmp(*av, "-ignorelinks") == 0)
432 	    ignore_links = 1;
433 	else if (strcmp(*av, "-withrevinfo") == 0)
434 	    with_revinfo = 1;
435 	else if (strcmp(*av, "--") == 0) {
436 	    ++av, --ac;
437 	    break;
438 	}
439 	else
440 	    break;
441     }
442 
443     if (ac < 1 || ac > 2)
444 	quit (1, "usage: %s [-silent] [-ignorelinks] fromdir [todir]",
445 	      prog_name);
446 
447     fn = av[0];
448     if (ac == 2)
449 	tn = av[1];
450     else
451 	tn = ".";
452 
453     /* to directory */
454     if (stat (tn, &ts) < 0)
455 	quiterr (1, tn);
456 #ifdef S_ISDIR
457     if (!(S_ISDIR(ts.st_mode)))
458 #else
459     if (!(ts.st_mode & S_IFDIR))
460 #endif
461 	quit (2, "%s: Not a directory", tn);
462     if (chdir (tn) < 0)
463 	quiterr (1, tn);
464 
465     /* from directory */
466     if (stat (fn, &fs) < 0)
467 	quiterr (1, fn);
468 #ifdef S_ISDIR
469     if (!(S_ISDIR(fs.st_mode)))
470 #else
471     if (!(fs.st_mode & S_IFDIR))
472 #endif
473 	quit (2, "%s: Not a directory", fn);
474 
475     exit (dodir (fn, &fs, &ts, 0));
476     return (-1);
477 }
478