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 /*
6 Copyright (c) 1990, 1998 The Open Group
7
8 Permission to use, copy, modify, distribute, and sell this software and its
9 documentation for any purpose is hereby granted without fee, provided that
10 the above copyright notice appear in all copies and that both that
11 copyright notice and this permission notice appear in supporting
12 documentation.
13
14 The above copyright notice and this permission notice shall be included in
15 all copies or substantial portions of the Software.
16
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
21 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
24 Except as contained in this notice, the name of The Open Group shall not be
25 used in advertising or otherwise to promote the sale, use or other dealings
26 in 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, RCS, SCCS
35 or CVS.adm). If you are building the distribution on more than one
36 machine, you should use this technique.
37
38 If your master sources are located in /usr/local/src/X and you would like
39 your link tree to be in /usr/local/src/new-X, do the following:
40
41 % mkdir /usr/local/src/new-X
42 % cd /usr/local/src/new-X
43 % lndir ../X
44 */
45
46 #ifdef XORG_VERSION
47
48 #include <X11/Xos.h>
49 #include <X11/Xfuncproto.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <sys/stat.h>
53 #if !defined(MINIX) && !defined(Lynx)
54 #include <sys/param.h>
55 #endif
56 #include <errno.h>
57
58 #ifndef X_NOT_POSIX
59 #include <dirent.h>
60 #else
61 #ifdef SYSV
62 #include <dirent.h>
63 #else
64 #ifdef USG
65 #include <dirent.h>
66 #else
67 #include <sys/dir.h>
68 #ifndef dirent
69 #define dirent direct
70 #endif
71 #endif
72 #endif
73 #endif
74 #ifndef MAXPATHLEN
75 #define MAXPATHLEN 2048
76 #endif
77
78 #include <stdarg.h>
79
80 #else /* XORG_VERSION */
81
82 #include <schily/mconfig.h>
83 #include <schily/stdio.h>
84 #include <schily/stdlib.h>
85 #include <schily/unistd.h>
86 #include <schily/string.h>
87 #include <schily/stat.h>
88 #include <schily/dirent.h>
89 #include <schily/maxpath.h>
90 #include <schily/errno.h>
91
92 #ifndef MAXPATHLEN
93 #define MAXPATHLEN MAXPATHNAME
94 #endif
95 #include <schily/varargs.h>
96 #include <schily/libport.h> /* Define missing prototypes */
97
98 #ifndef HAVE_LSTAT
99 #define lstat stat
100 #endif
101
102 static void quit __PR((int code, char * fmt, ...));
103 static void quiterr __PR((int code, char *s));
104 static void msg __PR((char * fmt, ...));
105 static void mperror __PR((char *s));
106 static void ovstrcpy __PR((char *p2, char *p1));
107 static int equivalent __PR((char *lname, char *rname, char **p));
108 static int dodir __PR((char *fn, struct stat *fs, struct stat *ts, int rel));
109 int main __PR((int ac, char *av[]));
110
111 #endif /* !XORG_VERSION */
112
113 int silent = 0; /* -silent */
114 int ignore_links = 0; /* -ignorelinks */
115 int with_revinfo = 0; /* -withrevinfo */
116
117 char *rcurdir;
118 char *curdir;
119
120 #ifdef PROTOTYPES
121 static void
quit(int code,char * fmt,...)122 quit (int code, char * fmt, ...)
123 #else
124 static void
125 quit (code, fmt, va_alist)
126 int code;
127 char *fmt;
128 va_dcl
129 #endif
130 {
131 va_list args;
132 #ifdef PROTOTYPES
133 va_start(args, fmt);
134 #else
135 va_start(args);
136 #endif
137 vfprintf (stderr, fmt, args);
138 va_end(args);
139 putc ('\n', stderr);
140 exit (code);
141 }
142
143 static void
quiterr(code,s)144 quiterr (code, s)
145 int code;
146 char *s;
147 {
148 perror (s);
149 exit (code);
150 }
151
152 #ifdef PROTOTYPES
153 static void
msg(char * fmt,...)154 msg (char * fmt, ...)
155 #else
156 static void
157 msg(fmt, va_alist)
158 char *fmt;
159 va_dcl
160 #endif
161 {
162 va_list args;
163 if (curdir) {
164 fprintf (stderr, "%s:\n", curdir);
165 curdir = 0;
166 }
167 #ifdef PROTOTYPES
168 va_start(args, fmt);
169 #else
170 va_start(args);
171 #endif
172 vfprintf (stderr, fmt, args);
173 va_end(args);
174 putc ('\n', stderr);
175 }
176
177 static void
mperror(s)178 mperror(s)
179 char *s;
180 {
181 if (curdir) {
182 fprintf (stderr, "%s:\n", curdir);
183 curdir = 0;
184 }
185 perror (s);
186 }
187
188 /*
189 * A strcpy() that works with overlapping buffers
190 */
191 static void
ovstrcpy(p2,p1)192 ovstrcpy(p2, p1)
193 register char *p2;
194 register char *p1;
195 {
196 while ((*p2++ = *p1++) != '\0')
197 ;
198 }
199
200
201 static int
equivalent(lname,rname,p)202 equivalent(lname, rname, p)
203 char *lname;
204 char *rname;
205 char **p;
206 {
207 char *s;
208
209 if (!strcmp(lname, rname))
210 return 1;
211 for (s = lname; *s && (s = strchr(s, '/')); s++) {
212 while (s[1] == '/') {
213 ovstrcpy(s+1, s+2);
214 if (*p) (*p)--;
215 }
216 }
217 return !strcmp(lname, rname);
218 }
219
220
221 /* Recursively create symbolic links from the current directory to the "from"
222 directory. Assumes that files described by fs and ts are directories. */
223 static int
dodir(fn,fs,ts,rel)224 dodir (fn, fs, ts, rel)
225 char *fn; /* name of "from" directory, either absolute or
226 relative to cwd */
227 struct stat *fs;
228 struct stat *ts; /* stats for the "from" directory and cwd */
229 int rel; /* if true, prepend "../" to fn before using */
230 {
231 DIR *df;
232 struct dirent *dp;
233 char buf[MAXPATHLEN + 1], *p;
234 char symbuf[MAXPATHLEN + 1];
235 char basesym[MAXPATHLEN + 1];
236 struct stat sb, sc;
237 int n_dirs;
238 int symlen = -1;
239 int basesymlen = -1;
240 char *ocurdir;
241
242 if ((fs->st_dev == ts->st_dev) && (fs->st_ino == ts->st_ino)) {
243 msg ("%s: From and to directories are identical!", fn);
244 return 1;
245 }
246
247 if (rel)
248 strcpy (buf, "../");
249 else
250 buf[0] = '\0';
251 strcat (buf, fn);
252
253 if (!(df = opendir (buf))) {
254 msg ("%s: Cannot opendir", buf);
255 return 1;
256 }
257
258 p = buf + strlen (buf);
259 if (*(p - 1) != '/')
260 *p++ = '/';
261 n_dirs = fs->st_nlink;
262 while ((dp = readdir (df))) {
263 if (dp->d_name[strlen(dp->d_name) - 1] == '~')
264 continue;
265 #ifdef __DARWIN__
266 /* Ignore these Mac OS X Finder data files */
267 if (!strcmp(dp->d_name, ".DS_Store") ||
268 !strcmp(dp->d_name, "._.DS_Store"))
269 continue;
270 #endif
271 strcpy (p, dp->d_name);
272
273 if (n_dirs > 0) {
274 if (lstat (buf, &sb) < 0) {
275 mperror (buf);
276 continue;
277 }
278
279 #ifdef S_ISDIR
280 if(S_ISDIR(sb.st_mode))
281 #else
282 if (sb.st_mode & S_IFDIR)
283 #endif
284 {
285 /* directory */
286 n_dirs--;
287 if (dp->d_name[0] == '.' &&
288 (dp->d_name[1] == '\0' || (dp->d_name[1] == '.' &&
289 dp->d_name[2] == '\0')))
290 continue;
291 if (!with_revinfo) {
292 if (!strcmp (dp->d_name, "BitKeeper"))
293 continue;
294 if (!strcmp (dp->d_name, "RCS"))
295 continue;
296 if (!strcmp (dp->d_name, "SCCS"))
297 continue;
298 if (!strcmp (dp->d_name, "CVS"))
299 continue;
300 if (!strcmp (dp->d_name, "CVS.adm"))
301 continue;
302 if (!strcmp (dp->d_name, ".svn"))
303 continue;
304 }
305 ocurdir = rcurdir;
306 rcurdir = buf;
307 curdir = silent ? buf : (char *)0;
308 if (!silent)
309 printf ("%s:\n", buf);
310 if ((stat (dp->d_name, &sc) < 0) && (errno == ENOENT)) {
311 if (mkdir (dp->d_name, 0777) < 0 ||
312 stat (dp->d_name, &sc) < 0) {
313 mperror (dp->d_name);
314 curdir = rcurdir = ocurdir;
315 continue;
316 }
317 }
318 #ifdef HAVE_READLINK
319 if (readlink (dp->d_name, symbuf, sizeof(symbuf) - 1) >= 0) {
320 msg ("%s: is a link instead of a directory", dp->d_name);
321 curdir = rcurdir = ocurdir;
322 continue;
323 }
324 #endif
325 if (chdir (dp->d_name) < 0) {
326 mperror (dp->d_name);
327 curdir = rcurdir = ocurdir;
328 continue;
329 }
330 dodir (buf, &sb, &sc, (buf[0] != '/'));
331 if (chdir ("..") < 0)
332 quiterr (1, "..");
333 curdir = rcurdir = ocurdir;
334 continue;
335 }
336 }
337
338 #ifdef HAVE_READLINK
339 /* non-directory */
340 symlen = readlink (dp->d_name, symbuf, sizeof(symbuf) - 1);
341 if (symlen >= 0)
342 symbuf[symlen] = '\0';
343
344 /* The option to ignore links exists mostly because
345 checking for them slows us down by 10-20%.
346 But it is off by default because this really is a useful check. */
347 if (!ignore_links) {
348 /* see if the file in the base tree was a symlink */
349 basesymlen = readlink(buf, basesym, sizeof(basesym) - 1);
350 if (basesymlen >= 0)
351 basesym[basesymlen] = '\0';
352 }
353 #endif
354
355 if (symlen >= 0) {
356 /* Link exists in new tree. Print message if it doesn't match. */
357 if (!equivalent (basesymlen>=0 ? basesym : buf, symbuf,
358 basesymlen>=0 ? (char **) 0 : &p))
359 msg ("%s: %s", dp->d_name, symbuf);
360 } else {
361 char *sympath;
362
363 if (basesymlen>=0) {
364 if ((buf[0] == '.') && (buf[1] == '.') && (buf[2] == '/') &&
365 (basesym[0] == '.') && (basesym[1] == '.') &&
366 (basesym[2] == '/')) {
367 /* It becomes very tricky here. We have
368 ../../bar/foo symlinked to ../xxx/yyy. We
369 can't just use ../xxx/yyy. We have to use
370 ../../bar/foo/../xxx/yyy. */
371
372 int i;
373 char *start, *end;
374
375 strcpy (symbuf, buf);
376 /* Find the first char after "../" in symbuf. */
377 start = symbuf;
378 do {
379 start += 3;
380 } while ((start[0] == '.') && (start[1] == '.') &&
381 (start[2] == '/'));
382
383 /* Then try to eliminate "../"s in basesym. */
384 i = 0;
385 end = strrchr (symbuf, '/');
386 if (start < end) {
387 do {
388 i += 3;
389 end--;
390 while ((*end != '/') && (end != start))
391 end--;
392 if (end == start)
393 break;
394 } while ((basesym[i] == '.') &&
395 (basesym[i + 1] == '.') &&
396 (basesym[i + 2] == '/'));
397 }
398 if (*end == '/')
399 end++;
400 strcpy (end, &basesym[i]);
401 sympath = symbuf;
402 }
403 else
404 sympath = basesym;
405 }
406 else
407 sympath = buf;
408 #ifdef HAVE_SYMLINK
409 if (symlink (sympath, dp->d_name) < 0)
410 mperror (dp->d_name);
411 #endif
412 }
413 }
414
415 closedir (df);
416 return 0;
417 }
418
419 int
main(ac,av)420 main(ac, av)
421 int ac;
422 char *av[];
423 {
424 char *prog_name = av[0];
425 char *fn, *tn;
426 struct stat fs, ts;
427
428 while (++av, --ac) {
429 if (strcmp(*av, "-silent") == 0)
430 silent = 1;
431 else if (strcmp(*av, "-ignorelinks") == 0)
432 ignore_links = 1;
433 else if (strcmp(*av, "-withrevinfo") == 0)
434 with_revinfo = 1;
435 else if (strcmp(*av, "--") == 0) {
436 ++av, --ac;
437 break;
438 }
439 else
440 break;
441 }
442
443 if (ac < 1 || ac > 2)
444 quit (1, "usage: %s [-silent] [-ignorelinks] fromdir [todir]",
445 prog_name);
446
447 fn = av[0];
448 if (ac == 2)
449 tn = av[1];
450 else
451 tn = ".";
452
453 /* to directory */
454 if (stat (tn, &ts) < 0)
455 quiterr (1, tn);
456 #ifdef S_ISDIR
457 if (!(S_ISDIR(ts.st_mode)))
458 #else
459 if (!(ts.st_mode & S_IFDIR))
460 #endif
461 quit (2, "%s: Not a directory", tn);
462 if (chdir (tn) < 0)
463 quiterr (1, tn);
464
465 /* from directory */
466 if (stat (fn, &fs) < 0)
467 quiterr (1, fn);
468 #ifdef S_ISDIR
469 if (!(S_ISDIR(fs.st_mode)))
470 #else
471 if (!(fs.st_mode & S_IFDIR))
472 #endif
473 quit (2, "%s: Not a directory", fn);
474
475 exit (dodir (fn, &fs, &ts, 0));
476 return (-1);
477 }
478