lndir.c revision baaedd75
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#include <string.h> 62 63#ifndef MAXPATHLEN 64#define MAXPATHLEN 2048 65#endif 66 67#include <stdarg.h> 68 69static int silent = 0; /* -silent */ 70static int ignore_links = 0; /* -ignorelinks */ 71static int with_revinfo = 0; /* -withrevinfo */ 72 73static char *rcurdir; 74static char *curdir; 75 76static void _X_ATTRIBUTE_PRINTF(2,3) _X_NORETURN 77quit (int code, const char * fmt, ...) 78{ 79 va_list args; 80 va_start(args, fmt); 81 vfprintf (stderr, fmt, args); 82 va_end(args); 83 putc ('\n', stderr); 84 exit (code); 85} 86 87static void _X_NORETURN 88quiterr (int code, const char *s) 89{ 90 perror (s); 91 exit (code); 92} 93 94static void _X_ATTRIBUTE_PRINTF(1,2) 95msg (const char * fmt, ...) 96{ 97 va_list args; 98 if (curdir) { 99 fprintf (stderr, "%s:\n", curdir); 100 curdir = 0; 101 } 102 va_start(args, fmt); 103 vfprintf (stderr, fmt, args); 104 va_end(args); 105 putc ('\n', stderr); 106} 107 108static void 109mperror (const char *s) 110{ 111 if (curdir) { 112 fprintf (stderr, "%s:\n", curdir); 113 curdir = 0; 114 } 115 perror (s); 116} 117 118 119static int 120equivalent(char *lname, const char *rname, char **p) 121{ 122 char *s; 123 124 if (!strcmp(lname, rname)) 125 return 1; 126 for (s = lname; *s && (s = strchr(s, '/')); s++) { 127 while (s[1] == '/') { 128 size_t len = strlen(s+2); 129 memmove(s+1, s+2, len+1); 130 if (p && *p) (*p)--; 131 } 132 } 133 return !strcmp(lname, rname); 134} 135 136 137/* Recursively create symbolic links from the current directory to the "from" 138 directory. Assumes that files described by fs and ts are directories. */ 139static int 140dodir (const char *fn, /* name of "from" directory, either absolute or 141 relative to cwd */ 142 struct stat *fs, 143 struct stat *ts, /* stats for the "from" directory and cwd */ 144 int rel) /* if true, prepend "../" to fn before using */ 145{ 146 DIR *df; 147 struct dirent *dp; 148 char buf[MAXPATHLEN + 1], *p; 149 char symbuf[MAXPATHLEN + 1]; 150 char basesym[MAXPATHLEN + 1]; 151 struct stat sb, sc; 152 unsigned int n_dirs; 153 ssize_t symlen; 154 ssize_t basesymlen = -1; 155 char *ocurdir; 156 157 if ((fs->st_dev == ts->st_dev) && (fs->st_ino == ts->st_ino)) { 158 msg ("%s: From and to directories are identical!", fn); 159 return 1; 160 } 161 162 if (rel) 163#ifdef HAVE_STRLCPY 164 strlcpy (buf, "../", sizeof(buf)); 165#else 166 strcpy (buf, "../"); 167#endif 168 else 169 buf[0] = '\0'; 170#ifdef HAVE_STRLCAT 171 if (strlcat (buf, fn, sizeof(buf)) >= sizeof(buf)) 172 quit(1, "Pathname too long: %s", fn); 173#else 174 strcat (buf, fn); 175#endif 176 177 if (!(df = opendir (buf))) { 178 msg ("%s: Cannot opendir", buf); 179 return 1; 180 } 181 182 p = buf + strlen (buf); 183 if (*(p - 1) != '/') 184 *p++ = '/'; 185 n_dirs = fs->st_nlink; 186 if (n_dirs == 1) 187 n_dirs = INT_MAX; 188 while ((dp = readdir (df))) { 189 if (dp->d_name[strlen(dp->d_name) - 1] == '~') 190 continue; 191#ifdef __APPLE__ 192 /* Ignore these Mac OS X Finder data files */ 193 if (!strcmp(dp->d_name, ".DS_Store") || 194 !strcmp(dp->d_name, "._.DS_Store")) 195 continue; 196#endif 197 198#ifdef HAVE_STRLCAT 199 *p = '\0'; 200 if (strlcat (buf, dp->d_name, sizeof(buf)) >= sizeof(buf)) { 201 *p = '\0'; 202 quit(1, "Pathname too long: %s%s", buf, dp->d_name); 203 } 204#else 205 strcpy (p, dp->d_name); 206#endif 207 208 if (n_dirs > 0) { 209 if (lstat (buf, &sb) < 0) { 210 mperror (buf); 211 continue; 212 } 213 214 if (S_ISDIR(sb.st_mode)) 215 { 216 /* directory */ 217 n_dirs--; 218 if (dp->d_name[0] == '.' && 219 (dp->d_name[1] == '\0' || (dp->d_name[1] == '.' && 220 dp->d_name[2] == '\0'))) 221 continue; 222 if (!with_revinfo) { 223 if (dp->d_name[0] == '.') { 224 if (!strcmp (dp->d_name, ".git")) 225 continue; 226 if (!strcmp (dp->d_name, ".hg")) 227 continue; 228 if (!strcmp (dp->d_name, ".svn")) 229 continue; 230 } else { 231 if (!strcmp (dp->d_name, "BitKeeper")) 232 continue; 233 if (!strcmp (dp->d_name, "RCS")) 234 continue; 235 if (!strcmp (dp->d_name, "SCCS")) 236 continue; 237 if (!strcmp (dp->d_name, "CVS")) 238 continue; 239 if (!strcmp (dp->d_name, "CVS.adm")) 240 continue; 241 } 242 } 243 ocurdir = rcurdir; 244 rcurdir = buf; 245 curdir = silent ? buf : (char *)0; 246 if (!silent) 247 printf ("%s:\n", buf); 248 if ((stat (dp->d_name, &sc) < 0) && (errno == ENOENT)) { 249 if (mkdir (dp->d_name, 0777) < 0 || 250 stat (dp->d_name, &sc) < 0) { 251 mperror (dp->d_name); 252 curdir = rcurdir = ocurdir; 253 continue; 254 } 255 } 256 if (readlink (dp->d_name, symbuf, sizeof(symbuf) - 1) >= 0) { 257 msg ("%s: is a link instead of a directory", dp->d_name); 258 curdir = rcurdir = ocurdir; 259 continue; 260 } 261 if (chdir (dp->d_name) < 0) { 262 mperror (dp->d_name); 263 curdir = rcurdir = ocurdir; 264 continue; 265 } 266 dodir (buf, &sb, &sc, (buf[0] != '/')); 267 if (chdir ("..") < 0) 268 quiterr (1, ".."); 269 curdir = rcurdir = ocurdir; 270 continue; 271 } 272 } 273 274 /* non-directory */ 275 symlen = readlink (dp->d_name, symbuf, sizeof(symbuf) - 1); 276 if (symlen >= 0) 277 symbuf[symlen] = '\0'; 278 279 /* The option to ignore links exists mostly because 280 checking for them slows us down by 10-20%. 281 But it is off by default because this really is a useful check. */ 282 if (!ignore_links) { 283 /* see if the file in the base tree was a symlink */ 284 basesymlen = readlink(buf, basesym, sizeof(basesym) - 1); 285 if (basesymlen >= 0) 286 basesym[basesymlen] = '\0'; 287 } 288 289 if (symlen >= 0) { 290 /* Link exists in new tree. Print message if it doesn't match. */ 291 if (!equivalent (basesymlen>=0 ? basesym : buf, symbuf, 292 basesymlen>=0 ? (char **) 0 : &p)) 293 msg ("%s: Keeping existing link to %s", dp->d_name, symbuf); 294 } else { 295 char *sympath; 296 297 if (basesymlen>=0) { 298 if ((buf[0] == '.') && (buf[1] == '.') && (buf[2] == '/') && 299 (basesym[0] == '.') && (basesym[1] == '.') && 300 (basesym[2] == '/')) { 301 /* It becomes very tricky here. We have 302 ../../bar/foo symlinked to ../xxx/yyy. We 303 can't just use ../xxx/yyy. We have to use 304 ../../bar/foo/../xxx/yyy. */ 305 306 int i; 307 char *start, *end; 308 309#ifdef HAVE_STRLCPY 310 if (strlcpy (symbuf, buf, sizeof(symbuf)) >= sizeof(symbuf)) 311 quit(1, "Pathname too long: %s", buf); 312#else 313 strcpy (symbuf, buf); 314#endif 315 /* Find the first char after "../" in symbuf. */ 316 start = symbuf; 317 do { 318 start += 3; 319 } while ((start[0] == '.') && (start[1] == '.') && 320 (start[2] == '/')); 321 322 /* Then try to eliminate "../"s in basesym. */ 323 i = 0; 324 end = strrchr (symbuf, '/'); 325 if (start < end) { 326 do { 327 i += 3; 328 end--; 329 while ((*end != '/') && (end != start)) 330 end--; 331 if (end == start) 332 break; 333 } while ((basesym[i] == '.') && 334 (basesym[i + 1] == '.') && 335 (basesym[i + 2] == '/')); 336 } 337 if (*end == '/') 338 end++; 339#ifdef HAVE_STRLCPY 340 *end = '\0'; 341 if (strlcat (symbuf, &basesym[i], sizeof(symbuf)) >= 342 sizeof(symbuf)) { 343 *end = '\0'; 344 quit(1, "Pathname too long: %s%s", symbuf, &basesym[i]); 345 } 346#else 347 strcpy (end, &basesym[i]); 348#endif 349 sympath = symbuf; 350 } 351 else 352 sympath = basesym; 353 } 354 else 355 sympath = buf; 356 if (symlink (sympath, dp->d_name) < 0) 357 mperror (dp->d_name); 358 } 359 } 360 361 closedir (df); 362 return 0; 363} 364 365int 366main (int argc, char *argv[]) 367{ 368 char *prog_name = argv[0]; 369 const char *fn, *tn; 370 struct stat fs, ts; 371 372 while (++argv, --argc) { 373 if (strcmp(*argv, "-silent") == 0) 374 silent = 1; 375 else if (strcmp(*argv, "-ignorelinks") == 0) 376 ignore_links = 1; 377 else if (strcmp(*argv, "-withrevinfo") == 0) 378 with_revinfo = 1; 379 else if (strcmp(*argv, "--") == 0) { 380 ++argv, --argc; 381 break; 382 } 383 else 384 break; 385 } 386 387 if (argc < 1 || argc > 2) 388 quit (1, 389 "usage: %s [-silent] [-ignorelinks] [-withrevinfo] fromdir [todir]", 390 prog_name); 391 392 fn = argv[0]; 393 if (argc == 2) 394 tn = argv[1]; 395 else 396 tn = "."; 397 398 /* to directory */ 399 if (stat (tn, &ts) < 0) 400 quiterr (1, tn); 401 if (!(S_ISDIR(ts.st_mode))) 402 quit (2, "%s: Not a directory", tn); 403 if (chdir (tn) < 0) 404 quiterr (1, tn); 405 406 /* from directory */ 407 if (stat (fn, &fs) < 0) 408 quiterr (1, fn); 409 if (!(S_ISDIR(fs.st_mode))) 410 quit (2, "%s: Not a directory", fn); 411 412 exit (dodir (fn, &fs, &ts, 0)); 413} 414