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