155de1df9Smrg/* $Xorg: lndir.c,v 1.5 2001/02/09 02:03:17 xorgcvs Exp $ */
255de1df9Smrg/* Create shadow link tree (after X11R4 script of the same name)
355de1df9Smrg   Mark Reinhold (mbr@lcs.mit.edu)/3 January 1990 */
455de1df9Smrg
555de1df9Smrg/*
655de1df9SmrgCopyright (c) 1990, 1998 The Open Group
755de1df9Smrg
855de1df9SmrgPermission to use, copy, modify, distribute, and sell this software and its
955de1df9Smrgdocumentation for any purpose is hereby granted without fee, provided that
1055de1df9Smrgthe above copyright notice appear in all copies and that both that
1155de1df9Smrgcopyright notice and this permission notice appear in supporting
1255de1df9Smrgdocumentation.
1355de1df9Smrg
1455de1df9SmrgThe above copyright notice and this permission notice shall be included in
1555de1df9Smrgall copies or substantial portions of the Software.
1655de1df9Smrg
1755de1df9SmrgTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1855de1df9SmrgIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1955de1df9SmrgFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
2055de1df9SmrgOPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
2155de1df9SmrgAN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
2255de1df9SmrgCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2355de1df9Smrg
2455de1df9SmrgExcept as contained in this notice, the name of The Open Group shall not be
2555de1df9Smrgused in advertising or otherwise to promote the sale, use or other dealings
2655de1df9Smrgin this Software without prior written authorization from The Open Group.
2755de1df9Smrg
2855de1df9Smrg*/
2955de1df9Smrg/* $XFree86: xc/config/util/lndir.c,v 3.18 2003/06/24 15:44:45 eich Exp $ */
3055de1df9Smrg
3155de1df9Smrg/* From the original /bin/sh script:
3255de1df9Smrg
3355de1df9Smrg  Used to create a copy of the a directory tree that has links for all
3455de1df9Smrg  non-directories (except, by default, those named BitKeeper, .git, .hg,
3555de1df9Smrg  RCS, SCCS, .svn, CVS or CVS.adm).
3655de1df9Smrg
3755de1df9Smrg  If you are building the distribution on more than one machine,
3855de1df9Smrg  you should use this technique.
3955de1df9Smrg
4055de1df9Smrg  If your master sources are located in /usr/local/src/X and you would like
4155de1df9Smrg  your link tree to be in /usr/local/src/new-X, do the following:
4255de1df9Smrg
4355de1df9Smrg   	%  mkdir /usr/local/src/new-X
4455de1df9Smrg	%  cd /usr/local/src/new-X
4555de1df9Smrg   	%  lndir ../X
4655de1df9Smrg*/
4755de1df9Smrg
4855de1df9Smrg#ifdef HAVE_CONFIG_H
4955de1df9Smrg#include "config.h"
5055de1df9Smrg#endif
5155de1df9Smrg
5255de1df9Smrg#include <X11/Xos.h>
5355de1df9Smrg#include <X11/Xfuncproto.h>
5455de1df9Smrg#include <stdio.h>
5555de1df9Smrg#include <stdlib.h>
5655de1df9Smrg#include <sys/stat.h>
5755de1df9Smrg#include <limits.h>
5855de1df9Smrg#include <sys/param.h>
5955de1df9Smrg#include <errno.h>
6055de1df9Smrg#include <dirent.h>
6116e5c272Smrg#include <string.h>
6255de1df9Smrg
6355de1df9Smrg#ifndef MAXPATHLEN
6455de1df9Smrg#define MAXPATHLEN 2048
6555de1df9Smrg#endif
6655de1df9Smrg
6755de1df9Smrg#include <stdarg.h>
6855de1df9Smrg
6916e5c272Smrgstatic int silent = 0;			/* -silent */
7016e5c272Smrgstatic int ignore_links = 0;		/* -ignorelinks */
7116e5c272Smrgstatic int with_revinfo = 0;		/* -withrevinfo */
7255de1df9Smrg
7316e5c272Smrgstatic char *rcurdir;
7416e5c272Smrgstatic char *curdir;
7555de1df9Smrg
7639ae6282Smrgstatic void _X_ATTRIBUTE_PRINTF(2,3) _X_NORETURN
7739ae6282Smrgquit (int code, const char * fmt, ...)
7855de1df9Smrg{
7955de1df9Smrg    va_list args;
8055de1df9Smrg    va_start(args, fmt);
8155de1df9Smrg    vfprintf (stderr, fmt, args);
8255de1df9Smrg    va_end(args);
8355de1df9Smrg    putc ('\n', stderr);
8455de1df9Smrg    exit (code);
8555de1df9Smrg}
8655de1df9Smrg
8739ae6282Smrgstatic void _X_NORETURN
8839ae6282Smrgquiterr (int code, const char *s)
8955de1df9Smrg{
9055de1df9Smrg    perror (s);
9155de1df9Smrg    exit (code);
9255de1df9Smrg}
9355de1df9Smrg
9439ae6282Smrgstatic void _X_ATTRIBUTE_PRINTF(1,2)
9539ae6282Smrgmsg (const char * fmt, ...)
9655de1df9Smrg{
9755de1df9Smrg    va_list args;
9855de1df9Smrg    if (curdir) {
9955de1df9Smrg	fprintf (stderr, "%s:\n", curdir);
10055de1df9Smrg	curdir = 0;
10155de1df9Smrg    }
10255de1df9Smrg    va_start(args, fmt);
10355de1df9Smrg    vfprintf (stderr, fmt, args);
10455de1df9Smrg    va_end(args);
10555de1df9Smrg    putc ('\n', stderr);
10655de1df9Smrg}
10755de1df9Smrg
10855de1df9Smrgstatic void
10916e5c272Smrgmperror (const char *s)
11055de1df9Smrg{
11155de1df9Smrg    if (curdir) {
11255de1df9Smrg	fprintf (stderr, "%s:\n", curdir);
11355de1df9Smrg	curdir = 0;
11455de1df9Smrg    }
11555de1df9Smrg    perror (s);
11655de1df9Smrg}
11755de1df9Smrg
11855de1df9Smrg
11955de1df9Smrgstatic int
12016e5c272Smrgequivalent(char *lname, const char *rname, char **p)
12155de1df9Smrg{
12255de1df9Smrg    char *s;
12355de1df9Smrg
12455de1df9Smrg    if (!strcmp(lname, rname))
12555de1df9Smrg	return 1;
12655de1df9Smrg    for (s = lname; *s && (s = strchr(s, '/')); s++) {
12755de1df9Smrg	while (s[1] == '/') {
12816e5c272Smrg	    size_t len = strlen(s+2);
12916e5c272Smrg	    memmove(s+1, s+2, len+1);
130baaedd75Smrg	    if (p && *p) (*p)--;
13155de1df9Smrg	}
13255de1df9Smrg    }
13355de1df9Smrg    return !strcmp(lname, rname);
13455de1df9Smrg}
13555de1df9Smrg
13655de1df9Smrg
13755de1df9Smrg/* Recursively create symbolic links from the current directory to the "from"
13855de1df9Smrg   directory.  Assumes that files described by fs and ts are directories. */
13955de1df9Smrgstatic int
14039ae6282Smrgdodir (const char *fn,		/* name of "from" directory, either absolute or
14155de1df9Smrg				   relative to cwd */
14255de1df9Smrg       struct stat *fs,
14355de1df9Smrg       struct stat *ts,		/* stats for the "from" directory and cwd */
14455de1df9Smrg       int rel)			/* if true, prepend "../" to fn before using */
14555de1df9Smrg{
14655de1df9Smrg    DIR *df;
14755de1df9Smrg    struct dirent *dp;
14855de1df9Smrg    char buf[MAXPATHLEN + 1], *p;
14955de1df9Smrg    char symbuf[MAXPATHLEN + 1];
15055de1df9Smrg    char basesym[MAXPATHLEN + 1];
15155de1df9Smrg    struct stat sb, sc;
15216e5c272Smrg    unsigned int n_dirs;
15316e5c272Smrg    ssize_t symlen;
15416e5c272Smrg    ssize_t basesymlen = -1;
15555de1df9Smrg    char *ocurdir;
15655de1df9Smrg
15755de1df9Smrg    if ((fs->st_dev == ts->st_dev) && (fs->st_ino == ts->st_ino)) {
15855de1df9Smrg	msg ("%s: From and to directories are identical!", fn);
15955de1df9Smrg	return 1;
16055de1df9Smrg    }
16155de1df9Smrg
16255de1df9Smrg    if (rel)
16316e5c272Smrg#ifdef HAVE_STRLCPY
16416e5c272Smrg	strlcpy (buf, "../", sizeof(buf));
16516e5c272Smrg#else
16655de1df9Smrg	strcpy (buf, "../");
16716e5c272Smrg#endif
16855de1df9Smrg    else
16955de1df9Smrg	buf[0] = '\0';
17016e5c272Smrg#ifdef HAVE_STRLCAT
17116e5c272Smrg    if (strlcat (buf, fn, sizeof(buf)) >= sizeof(buf))
17216e5c272Smrg        quit(1, "Pathname too long: %s", fn);
17316e5c272Smrg#else
17455de1df9Smrg    strcat (buf, fn);
17516e5c272Smrg#endif
17655de1df9Smrg
17755de1df9Smrg    if (!(df = opendir (buf))) {
17855de1df9Smrg	msg ("%s: Cannot opendir", buf);
17955de1df9Smrg	return 1;
18055de1df9Smrg    }
18155de1df9Smrg
18255de1df9Smrg    p = buf + strlen (buf);
18355de1df9Smrg    if (*(p - 1) != '/')
18455de1df9Smrg	*p++ = '/';
18555de1df9Smrg    n_dirs = fs->st_nlink;
18655de1df9Smrg    if (n_dirs == 1)
18755de1df9Smrg	n_dirs = INT_MAX;
18855de1df9Smrg    while ((dp = readdir (df))) {
18955de1df9Smrg	if (dp->d_name[strlen(dp->d_name) - 1] == '~')
19055de1df9Smrg	    continue;
19155de1df9Smrg#ifdef __APPLE__
19255de1df9Smrg	/* Ignore these Mac OS X Finder data files */
19355de1df9Smrg	if (!strcmp(dp->d_name, ".DS_Store") ||
19455de1df9Smrg	    !strcmp(dp->d_name, "._.DS_Store"))
19555de1df9Smrg	    continue;
19655de1df9Smrg#endif
19716e5c272Smrg
19816e5c272Smrg#ifdef HAVE_STRLCAT
19916e5c272Smrg	*p = '\0';
20016e5c272Smrg	if (strlcat (buf, dp->d_name, sizeof(buf)) >= sizeof(buf)) {
20116e5c272Smrg	    *p = '\0';
20216e5c272Smrg	    quit(1, "Pathname too long: %s%s", buf, dp->d_name);
20316e5c272Smrg	}
20416e5c272Smrg#else
20555de1df9Smrg	strcpy (p, dp->d_name);
20616e5c272Smrg#endif
20755de1df9Smrg
20855de1df9Smrg	if (n_dirs > 0) {
20955de1df9Smrg	    if (lstat (buf, &sb) < 0) {
21055de1df9Smrg		mperror (buf);
21155de1df9Smrg		continue;
21255de1df9Smrg	    }
21355de1df9Smrg
21455de1df9Smrg	    if (S_ISDIR(sb.st_mode))
21555de1df9Smrg	    {
21655de1df9Smrg		/* directory */
21755de1df9Smrg		n_dirs--;
21855de1df9Smrg		if (dp->d_name[0] == '.' &&
21955de1df9Smrg		    (dp->d_name[1] == '\0' || (dp->d_name[1] == '.' &&
22055de1df9Smrg					       dp->d_name[2] == '\0')))
22155de1df9Smrg		    continue;
22255de1df9Smrg		if (!with_revinfo) {
22316e5c272Smrg                    if (dp->d_name[0] == '.') {
22416e5c272Smrg                        if (!strcmp (dp->d_name, ".git"))
22516e5c272Smrg                            continue;
22616e5c272Smrg                        if (!strcmp (dp->d_name, ".hg"))
22716e5c272Smrg                            continue;
22816e5c272Smrg                        if (!strcmp (dp->d_name, ".svn"))
22916e5c272Smrg                            continue;
23016e5c272Smrg                    } else {
23116e5c272Smrg                        if (!strcmp (dp->d_name, "BitKeeper"))
23216e5c272Smrg                            continue;
23316e5c272Smrg                        if (!strcmp (dp->d_name, "RCS"))
23416e5c272Smrg                            continue;
23516e5c272Smrg                        if (!strcmp (dp->d_name, "SCCS"))
23616e5c272Smrg                            continue;
23716e5c272Smrg                        if (!strcmp (dp->d_name, "CVS"))
23816e5c272Smrg                            continue;
23916e5c272Smrg                        if (!strcmp (dp->d_name, "CVS.adm"))
24016e5c272Smrg                            continue;
24116e5c272Smrg                    }
24216e5c272Smrg                }
24355de1df9Smrg		ocurdir = rcurdir;
24455de1df9Smrg		rcurdir = buf;
24555de1df9Smrg		curdir = silent ? buf : (char *)0;
24655de1df9Smrg		if (!silent)
24755de1df9Smrg		    printf ("%s:\n", buf);
24855de1df9Smrg		if ((stat (dp->d_name, &sc) < 0) && (errno == ENOENT)) {
24955de1df9Smrg		    if (mkdir (dp->d_name, 0777) < 0 ||
25055de1df9Smrg			stat (dp->d_name, &sc) < 0) {
25155de1df9Smrg			mperror (dp->d_name);
25255de1df9Smrg			curdir = rcurdir = ocurdir;
25355de1df9Smrg			continue;
25455de1df9Smrg		    }
25555de1df9Smrg		}
25655de1df9Smrg		if (readlink (dp->d_name, symbuf, sizeof(symbuf) - 1) >= 0) {
25755de1df9Smrg		    msg ("%s: is a link instead of a directory", dp->d_name);
25855de1df9Smrg		    curdir = rcurdir = ocurdir;
25955de1df9Smrg		    continue;
26055de1df9Smrg		}
26155de1df9Smrg		if (chdir (dp->d_name) < 0) {
26255de1df9Smrg		    mperror (dp->d_name);
26355de1df9Smrg		    curdir = rcurdir = ocurdir;
26455de1df9Smrg		    continue;
26555de1df9Smrg		}
26655de1df9Smrg		dodir (buf, &sb, &sc, (buf[0] != '/'));
26755de1df9Smrg		if (chdir ("..") < 0)
26855de1df9Smrg		    quiterr (1, "..");
26955de1df9Smrg		curdir = rcurdir = ocurdir;
27055de1df9Smrg		continue;
27155de1df9Smrg	    }
27255de1df9Smrg	}
27355de1df9Smrg
27455de1df9Smrg	/* non-directory */
27555de1df9Smrg	symlen = readlink (dp->d_name, symbuf, sizeof(symbuf) - 1);
27655de1df9Smrg	if (symlen >= 0)
27755de1df9Smrg	    symbuf[symlen] = '\0';
27855de1df9Smrg
27955de1df9Smrg	/* The option to ignore links exists mostly because
28055de1df9Smrg	   checking for them slows us down by 10-20%.
28155de1df9Smrg	   But it is off by default because this really is a useful check. */
28255de1df9Smrg	if (!ignore_links) {
28355de1df9Smrg	    /* see if the file in the base tree was a symlink */
28455de1df9Smrg	    basesymlen = readlink(buf, basesym, sizeof(basesym) - 1);
28555de1df9Smrg	    if (basesymlen >= 0)
28655de1df9Smrg		basesym[basesymlen] = '\0';
28755de1df9Smrg	}
28855de1df9Smrg
28955de1df9Smrg	if (symlen >= 0) {
29055de1df9Smrg	    /* Link exists in new tree.  Print message if it doesn't match. */
29155de1df9Smrg	    if (!equivalent (basesymlen>=0 ? basesym : buf, symbuf,
29255de1df9Smrg			     basesymlen>=0 ? (char **) 0 : &p))
293baaedd75Smrg		msg ("%s: Keeping existing link to %s", dp->d_name, symbuf);
29455de1df9Smrg	} else {
29555de1df9Smrg	    char *sympath;
29655de1df9Smrg
29755de1df9Smrg	    if (basesymlen>=0) {
29855de1df9Smrg		if ((buf[0] == '.') && (buf[1] == '.') && (buf[2] == '/') &&
29955de1df9Smrg		    (basesym[0] == '.') && (basesym[1] == '.') &&
30055de1df9Smrg		    (basesym[2] == '/')) {
30155de1df9Smrg		    /* It becomes very tricky here. We have
30255de1df9Smrg		       ../../bar/foo symlinked to ../xxx/yyy. We
30355de1df9Smrg		       can't just use ../xxx/yyy. We have to use
30455de1df9Smrg		       ../../bar/foo/../xxx/yyy.  */
30555de1df9Smrg
30655de1df9Smrg		    int i;
30755de1df9Smrg		    char *start, *end;
30855de1df9Smrg
30916e5c272Smrg#ifdef HAVE_STRLCPY
31016e5c272Smrg		    if (strlcpy (symbuf, buf, sizeof(symbuf)) >= sizeof(symbuf))
31116e5c272Smrg			quit(1, "Pathname too long: %s", buf);
31216e5c272Smrg#else
31355de1df9Smrg		    strcpy (symbuf, buf);
31416e5c272Smrg#endif
31555de1df9Smrg		    /* Find the first char after "../" in symbuf.  */
31655de1df9Smrg		    start = symbuf;
31755de1df9Smrg		    do {
31855de1df9Smrg			start += 3;
31955de1df9Smrg		    } while ((start[0] == '.') && (start[1] == '.') &&
32055de1df9Smrg			     (start[2] == '/'));
32155de1df9Smrg
32255de1df9Smrg		    /* Then try to eliminate "../"s in basesym.  */
32355de1df9Smrg		    i = 0;
32455de1df9Smrg		    end = strrchr (symbuf, '/');
32555de1df9Smrg		    if (start < end) {
32655de1df9Smrg			do {
32755de1df9Smrg			    i += 3;
32855de1df9Smrg			    end--;
32955de1df9Smrg			    while ((*end != '/') && (end != start))
33055de1df9Smrg				end--;
33155de1df9Smrg			    if (end == start)
33255de1df9Smrg				break;
33355de1df9Smrg			} while ((basesym[i] == '.') &&
33455de1df9Smrg				 (basesym[i + 1] == '.') &&
33555de1df9Smrg				 (basesym[i + 2] == '/'));
33655de1df9Smrg		    }
33755de1df9Smrg		    if (*end == '/')
33855de1df9Smrg			end++;
33916e5c272Smrg#ifdef HAVE_STRLCPY
34016e5c272Smrg		    *end = '\0';
34116e5c272Smrg		    if (strlcat (symbuf, &basesym[i], sizeof(symbuf)) >=
34216e5c272Smrg			sizeof(symbuf)) {
34316e5c272Smrg			*end = '\0';
34416e5c272Smrg			quit(1, "Pathname too long: %s%s", symbuf, &basesym[i]);
34516e5c272Smrg		    }
34616e5c272Smrg#else
34755de1df9Smrg		    strcpy (end, &basesym[i]);
34816e5c272Smrg#endif
34955de1df9Smrg		    sympath = symbuf;
35055de1df9Smrg		}
35155de1df9Smrg		else
35255de1df9Smrg		    sympath = basesym;
35355de1df9Smrg	    }
35455de1df9Smrg	    else
35555de1df9Smrg		sympath = buf;
35655de1df9Smrg	    if (symlink (sympath, dp->d_name) < 0)
35755de1df9Smrg		mperror (dp->d_name);
35855de1df9Smrg	}
35955de1df9Smrg    }
36055de1df9Smrg
36155de1df9Smrg    closedir (df);
36255de1df9Smrg    return 0;
36355de1df9Smrg}
36455de1df9Smrg
36555de1df9Smrgint
36639ae6282Smrgmain (int argc, char *argv[])
36755de1df9Smrg{
36839ae6282Smrg    char *prog_name = argv[0];
36939ae6282Smrg    const char *fn, *tn;
37055de1df9Smrg    struct stat fs, ts;
37155de1df9Smrg
37239ae6282Smrg    while (++argv, --argc) {
37339ae6282Smrg	if (strcmp(*argv, "-silent") == 0)
37455de1df9Smrg	    silent = 1;
37539ae6282Smrg	else if (strcmp(*argv, "-ignorelinks") == 0)
37655de1df9Smrg	    ignore_links = 1;
37739ae6282Smrg	else if (strcmp(*argv, "-withrevinfo") == 0)
37855de1df9Smrg	    with_revinfo = 1;
37939ae6282Smrg	else if (strcmp(*argv, "--") == 0) {
38039ae6282Smrg	    ++argv, --argc;
38155de1df9Smrg	    break;
38255de1df9Smrg	}
38355de1df9Smrg	else
38455de1df9Smrg	    break;
38555de1df9Smrg    }
38655de1df9Smrg
38739ae6282Smrg    if (argc < 1 || argc > 2)
38839ae6282Smrg	quit (1,
38939ae6282Smrg	      "usage: %s [-silent] [-ignorelinks] [-withrevinfo] fromdir [todir]",
39055de1df9Smrg	      prog_name);
39155de1df9Smrg
39239ae6282Smrg    fn = argv[0];
39339ae6282Smrg    if (argc == 2)
39439ae6282Smrg	tn = argv[1];
39555de1df9Smrg    else
39655de1df9Smrg	tn = ".";
39755de1df9Smrg
39855de1df9Smrg    /* to directory */
39955de1df9Smrg    if (stat (tn, &ts) < 0)
40055de1df9Smrg	quiterr (1, tn);
40155de1df9Smrg    if (!(S_ISDIR(ts.st_mode)))
40255de1df9Smrg	quit (2, "%s: Not a directory", tn);
40355de1df9Smrg    if (chdir (tn) < 0)
40455de1df9Smrg	quiterr (1, tn);
40555de1df9Smrg
40655de1df9Smrg    /* from directory */
40755de1df9Smrg    if (stat (fn, &fs) < 0)
40855de1df9Smrg	quiterr (1, fn);
40955de1df9Smrg    if (!(S_ISDIR(fs.st_mode)))
41055de1df9Smrg	quit (2, "%s: Not a directory", fn);
41155de1df9Smrg
41255de1df9Smrg    exit (dodir (fn, &fs, &ts, 0));
41355de1df9Smrg}
414