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