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