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