lndir.c revision 55de1df9
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>
6155de1df9Smrg
6255de1df9Smrg#ifndef MAXPATHLEN
6355de1df9Smrg#define MAXPATHLEN 2048
6455de1df9Smrg#endif
6555de1df9Smrg
6655de1df9Smrg#include <stdarg.h>
6755de1df9Smrg
6855de1df9Smrgint silent = 0;			/* -silent */
6955de1df9Smrgint ignore_links = 0;		/* -ignorelinks */
7055de1df9Smrgint with_revinfo = 0;		/* -withrevinfo */
7155de1df9Smrg
7255de1df9Smrgchar *rcurdir;
7355de1df9Smrgchar *curdir;
7455de1df9Smrg
7555de1df9Smrgstatic void
7655de1df9Smrgquit (int code, char * fmt, ...)
7755de1df9Smrg{
7855de1df9Smrg    va_list args;
7955de1df9Smrg    va_start(args, fmt);
8055de1df9Smrg    vfprintf (stderr, fmt, args);
8155de1df9Smrg    va_end(args);
8255de1df9Smrg    putc ('\n', stderr);
8355de1df9Smrg    exit (code);
8455de1df9Smrg}
8555de1df9Smrg
8655de1df9Smrgstatic void
8755de1df9Smrgquiterr (int code, char *s)
8855de1df9Smrg{
8955de1df9Smrg    perror (s);
9055de1df9Smrg    exit (code);
9155de1df9Smrg}
9255de1df9Smrg
9355de1df9Smrgstatic void
9455de1df9Smrgmsg (char * fmt, ...)
9555de1df9Smrg{
9655de1df9Smrg    va_list args;
9755de1df9Smrg    if (curdir) {
9855de1df9Smrg	fprintf (stderr, "%s:\n", curdir);
9955de1df9Smrg	curdir = 0;
10055de1df9Smrg    }
10155de1df9Smrg    va_start(args, fmt);
10255de1df9Smrg    vfprintf (stderr, fmt, args);
10355de1df9Smrg    va_end(args);
10455de1df9Smrg    putc ('\n', stderr);
10555de1df9Smrg}
10655de1df9Smrg
10755de1df9Smrgstatic void
10855de1df9Smrgmperror (char *s)
10955de1df9Smrg{
11055de1df9Smrg    if (curdir) {
11155de1df9Smrg	fprintf (stderr, "%s:\n", curdir);
11255de1df9Smrg	curdir = 0;
11355de1df9Smrg    }
11455de1df9Smrg    perror (s);
11555de1df9Smrg}
11655de1df9Smrg
11755de1df9Smrg
11855de1df9Smrgstatic int
11955de1df9Smrgequivalent(char *lname, char *rname, char **p)
12055de1df9Smrg{
12155de1df9Smrg    char *s;
12255de1df9Smrg
12355de1df9Smrg    if (!strcmp(lname, rname))
12455de1df9Smrg	return 1;
12555de1df9Smrg    for (s = lname; *s && (s = strchr(s, '/')); s++) {
12655de1df9Smrg	while (s[1] == '/') {
12755de1df9Smrg	    strcpy(s+1, s+2);
12855de1df9Smrg	    if (*p) (*p)--;
12955de1df9Smrg	}
13055de1df9Smrg    }
13155de1df9Smrg    return !strcmp(lname, rname);
13255de1df9Smrg}
13355de1df9Smrg
13455de1df9Smrg
13555de1df9Smrg/* Recursively create symbolic links from the current directory to the "from"
13655de1df9Smrg   directory.  Assumes that files described by fs and ts are directories. */
13755de1df9Smrgstatic int
13855de1df9Smrgdodir (char *fn,		/* name of "from" directory, either absolute or
13955de1df9Smrg				   relative to cwd */
14055de1df9Smrg       struct stat *fs,
14155de1df9Smrg       struct stat *ts,		/* stats for the "from" directory and cwd */
14255de1df9Smrg       int rel)			/* if true, prepend "../" to fn before using */
14355de1df9Smrg{
14455de1df9Smrg    DIR *df;
14555de1df9Smrg    struct dirent *dp;
14655de1df9Smrg    char buf[MAXPATHLEN + 1], *p;
14755de1df9Smrg    char symbuf[MAXPATHLEN + 1];
14855de1df9Smrg    char basesym[MAXPATHLEN + 1];
14955de1df9Smrg    struct stat sb, sc;
15055de1df9Smrg    int n_dirs;
15155de1df9Smrg    int symlen;
15255de1df9Smrg    int basesymlen = -1;
15355de1df9Smrg    char *ocurdir;
15455de1df9Smrg
15555de1df9Smrg    if ((fs->st_dev == ts->st_dev) && (fs->st_ino == ts->st_ino)) {
15655de1df9Smrg	msg ("%s: From and to directories are identical!", fn);
15755de1df9Smrg	return 1;
15855de1df9Smrg    }
15955de1df9Smrg
16055de1df9Smrg    if (rel)
16155de1df9Smrg	strcpy (buf, "../");
16255de1df9Smrg    else
16355de1df9Smrg	buf[0] = '\0';
16455de1df9Smrg    strcat (buf, fn);
16555de1df9Smrg
16655de1df9Smrg    if (!(df = opendir (buf))) {
16755de1df9Smrg	msg ("%s: Cannot opendir", buf);
16855de1df9Smrg	return 1;
16955de1df9Smrg    }
17055de1df9Smrg
17155de1df9Smrg    p = buf + strlen (buf);
17255de1df9Smrg    if (*(p - 1) != '/')
17355de1df9Smrg	*p++ = '/';
17455de1df9Smrg    n_dirs = fs->st_nlink;
17555de1df9Smrg    if (n_dirs == 1)
17655de1df9Smrg	n_dirs = INT_MAX;
17755de1df9Smrg    while ((dp = readdir (df))) {
17855de1df9Smrg	if (dp->d_name[strlen(dp->d_name) - 1] == '~')
17955de1df9Smrg	    continue;
18055de1df9Smrg#ifdef __APPLE__
18155de1df9Smrg	/* Ignore these Mac OS X Finder data files */
18255de1df9Smrg	if (!strcmp(dp->d_name, ".DS_Store") ||
18355de1df9Smrg	    !strcmp(dp->d_name, "._.DS_Store"))
18455de1df9Smrg	    continue;
18555de1df9Smrg#endif
18655de1df9Smrg	strcpy (p, dp->d_name);
18755de1df9Smrg
18855de1df9Smrg	if (n_dirs > 0) {
18955de1df9Smrg	    if (lstat (buf, &sb) < 0) {
19055de1df9Smrg		mperror (buf);
19155de1df9Smrg		continue;
19255de1df9Smrg	    }
19355de1df9Smrg
19455de1df9Smrg	    if (S_ISDIR(sb.st_mode))
19555de1df9Smrg	    {
19655de1df9Smrg		/* directory */
19755de1df9Smrg		n_dirs--;
19855de1df9Smrg		if (dp->d_name[0] == '.' &&
19955de1df9Smrg		    (dp->d_name[1] == '\0' || (dp->d_name[1] == '.' &&
20055de1df9Smrg					       dp->d_name[2] == '\0')))
20155de1df9Smrg		    continue;
20255de1df9Smrg		if (!with_revinfo) {
20355de1df9Smrg		    if (!strcmp (dp->d_name, ".git"))
20455de1df9Smrg			continue;
20555de1df9Smrg		    if (!strcmp (dp->d_name, ".hg"))
20655de1df9Smrg			continue;
20755de1df9Smrg		    if (!strcmp (dp->d_name, "BitKeeper"))
20855de1df9Smrg			continue;
20955de1df9Smrg		    if (!strcmp (dp->d_name, "RCS"))
21055de1df9Smrg			continue;
21155de1df9Smrg		    if (!strcmp (dp->d_name, "SCCS"))
21255de1df9Smrg			continue;
21355de1df9Smrg		    if (!strcmp (dp->d_name, "CVS"))
21455de1df9Smrg			continue;
21555de1df9Smrg		    if (!strcmp (dp->d_name, "CVS.adm"))
21655de1df9Smrg			continue;
21755de1df9Smrg		    if (!strcmp (dp->d_name, ".svn"))
21855de1df9Smrg			continue;
21955de1df9Smrg		}
22055de1df9Smrg		ocurdir = rcurdir;
22155de1df9Smrg		rcurdir = buf;
22255de1df9Smrg		curdir = silent ? buf : (char *)0;
22355de1df9Smrg		if (!silent)
22455de1df9Smrg		    printf ("%s:\n", buf);
22555de1df9Smrg		if ((stat (dp->d_name, &sc) < 0) && (errno == ENOENT)) {
22655de1df9Smrg		    if (mkdir (dp->d_name, 0777) < 0 ||
22755de1df9Smrg			stat (dp->d_name, &sc) < 0) {
22855de1df9Smrg			mperror (dp->d_name);
22955de1df9Smrg			curdir = rcurdir = ocurdir;
23055de1df9Smrg			continue;
23155de1df9Smrg		    }
23255de1df9Smrg		}
23355de1df9Smrg		if (readlink (dp->d_name, symbuf, sizeof(symbuf) - 1) >= 0) {
23455de1df9Smrg		    msg ("%s: is a link instead of a directory", dp->d_name);
23555de1df9Smrg		    curdir = rcurdir = ocurdir;
23655de1df9Smrg		    continue;
23755de1df9Smrg		}
23855de1df9Smrg		if (chdir (dp->d_name) < 0) {
23955de1df9Smrg		    mperror (dp->d_name);
24055de1df9Smrg		    curdir = rcurdir = ocurdir;
24155de1df9Smrg		    continue;
24255de1df9Smrg		}
24355de1df9Smrg		dodir (buf, &sb, &sc, (buf[0] != '/'));
24455de1df9Smrg		if (chdir ("..") < 0)
24555de1df9Smrg		    quiterr (1, "..");
24655de1df9Smrg		curdir = rcurdir = ocurdir;
24755de1df9Smrg		continue;
24855de1df9Smrg	    }
24955de1df9Smrg	}
25055de1df9Smrg
25155de1df9Smrg	/* non-directory */
25255de1df9Smrg	symlen = readlink (dp->d_name, symbuf, sizeof(symbuf) - 1);
25355de1df9Smrg	if (symlen >= 0)
25455de1df9Smrg	    symbuf[symlen] = '\0';
25555de1df9Smrg
25655de1df9Smrg	/* The option to ignore links exists mostly because
25755de1df9Smrg	   checking for them slows us down by 10-20%.
25855de1df9Smrg	   But it is off by default because this really is a useful check. */
25955de1df9Smrg	if (!ignore_links) {
26055de1df9Smrg	    /* see if the file in the base tree was a symlink */
26155de1df9Smrg	    basesymlen = readlink(buf, basesym, sizeof(basesym) - 1);
26255de1df9Smrg	    if (basesymlen >= 0)
26355de1df9Smrg		basesym[basesymlen] = '\0';
26455de1df9Smrg	}
26555de1df9Smrg
26655de1df9Smrg	if (symlen >= 0) {
26755de1df9Smrg	    /* Link exists in new tree.  Print message if it doesn't match. */
26855de1df9Smrg	    if (!equivalent (basesymlen>=0 ? basesym : buf, symbuf,
26955de1df9Smrg			     basesymlen>=0 ? (char **) 0 : &p))
27055de1df9Smrg		msg ("%s: %s", dp->d_name, symbuf);
27155de1df9Smrg	} else {
27255de1df9Smrg	    char *sympath;
27355de1df9Smrg
27455de1df9Smrg	    if (basesymlen>=0) {
27555de1df9Smrg		if ((buf[0] == '.') && (buf[1] == '.') && (buf[2] == '/') &&
27655de1df9Smrg		    (basesym[0] == '.') && (basesym[1] == '.') &&
27755de1df9Smrg		    (basesym[2] == '/')) {
27855de1df9Smrg		    /* It becomes very tricky here. We have
27955de1df9Smrg		       ../../bar/foo symlinked to ../xxx/yyy. We
28055de1df9Smrg		       can't just use ../xxx/yyy. We have to use
28155de1df9Smrg		       ../../bar/foo/../xxx/yyy.  */
28255de1df9Smrg
28355de1df9Smrg		    int i;
28455de1df9Smrg		    char *start, *end;
28555de1df9Smrg
28655de1df9Smrg		    strcpy (symbuf, buf);
28755de1df9Smrg		    /* Find the first char after "../" in symbuf.  */
28855de1df9Smrg		    start = symbuf;
28955de1df9Smrg		    do {
29055de1df9Smrg			start += 3;
29155de1df9Smrg		    } while ((start[0] == '.') && (start[1] == '.') &&
29255de1df9Smrg			     (start[2] == '/'));
29355de1df9Smrg
29455de1df9Smrg		    /* Then try to eliminate "../"s in basesym.  */
29555de1df9Smrg		    i = 0;
29655de1df9Smrg		    end = strrchr (symbuf, '/');
29755de1df9Smrg		    if (start < end) {
29855de1df9Smrg			do {
29955de1df9Smrg			    i += 3;
30055de1df9Smrg			    end--;
30155de1df9Smrg			    while ((*end != '/') && (end != start))
30255de1df9Smrg				end--;
30355de1df9Smrg			    if (end == start)
30455de1df9Smrg				break;
30555de1df9Smrg			} while ((basesym[i] == '.') &&
30655de1df9Smrg				 (basesym[i + 1] == '.') &&
30755de1df9Smrg				 (basesym[i + 2] == '/'));
30855de1df9Smrg		    }
30955de1df9Smrg		    if (*end == '/')
31055de1df9Smrg			end++;
31155de1df9Smrg		    strcpy (end, &basesym[i]);
31255de1df9Smrg		    sympath = symbuf;
31355de1df9Smrg		}
31455de1df9Smrg		else
31555de1df9Smrg		    sympath = basesym;
31655de1df9Smrg	    }
31755de1df9Smrg	    else
31855de1df9Smrg		sympath = buf;
31955de1df9Smrg	    if (symlink (sympath, dp->d_name) < 0)
32055de1df9Smrg		mperror (dp->d_name);
32155de1df9Smrg	}
32255de1df9Smrg    }
32355de1df9Smrg
32455de1df9Smrg    closedir (df);
32555de1df9Smrg    return 0;
32655de1df9Smrg}
32755de1df9Smrg
32855de1df9Smrgint
32955de1df9Smrgmain (int ac, char *av[])
33055de1df9Smrg{
33155de1df9Smrg    char *prog_name = av[0];
33255de1df9Smrg    char *fn, *tn;
33355de1df9Smrg    struct stat fs, ts;
33455de1df9Smrg
33555de1df9Smrg    while (++av, --ac) {
33655de1df9Smrg	if (strcmp(*av, "-silent") == 0)
33755de1df9Smrg	    silent = 1;
33855de1df9Smrg	else if (strcmp(*av, "-ignorelinks") == 0)
33955de1df9Smrg	    ignore_links = 1;
34055de1df9Smrg	else if (strcmp(*av, "-withrevinfo") == 0)
34155de1df9Smrg	    with_revinfo = 1;
34255de1df9Smrg	else if (strcmp(*av, "--") == 0) {
34355de1df9Smrg	    ++av, --ac;
34455de1df9Smrg	    break;
34555de1df9Smrg	}
34655de1df9Smrg	else
34755de1df9Smrg	    break;
34855de1df9Smrg    }
34955de1df9Smrg
35055de1df9Smrg    if (ac < 1 || ac > 2)
35155de1df9Smrg	quit (1, "usage: %s [-silent] [-ignorelinks] fromdir [todir]",
35255de1df9Smrg	      prog_name);
35355de1df9Smrg
35455de1df9Smrg    fn = av[0];
35555de1df9Smrg    if (ac == 2)
35655de1df9Smrg	tn = av[1];
35755de1df9Smrg    else
35855de1df9Smrg	tn = ".";
35955de1df9Smrg
36055de1df9Smrg    /* to directory */
36155de1df9Smrg    if (stat (tn, &ts) < 0)
36255de1df9Smrg	quiterr (1, tn);
36355de1df9Smrg    if (!(S_ISDIR(ts.st_mode)))
36455de1df9Smrg	quit (2, "%s: Not a directory", tn);
36555de1df9Smrg    if (chdir (tn) < 0)
36655de1df9Smrg	quiterr (1, tn);
36755de1df9Smrg
36855de1df9Smrg    /* from directory */
36955de1df9Smrg    if (stat (fn, &fs) < 0)
37055de1df9Smrg	quiterr (1, fn);
37155de1df9Smrg    if (!(S_ISDIR(fs.st_mode)))
37255de1df9Smrg	quit (2, "%s: Not a directory", fn);
37355de1df9Smrg
37455de1df9Smrg    exit (dodir (fn, &fs, &ts, 0));
37555de1df9Smrg}
376