Prefer #if defined to #ifdef
[ghc.git] / utils / lndir / lndir.c
1 /* $XConsortium: lndir.c /main/16 1996/09/28 16:16:40 rws $ */
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, X Consortium
7
8 Permission is hereby granted, free of charge, to any person obtaining a copy
9 of this software and associated documentation files (the "Software"), to deal
10 in the Software without restriction, including without limitation the rights
11 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 copies of the Software, and to permit persons to whom the Software is
13 furnished to do so, subject to the following conditions:
14
15 The above copyright notice and this permission notice shall be included in
16 all copies or substantial portions of the Software.
17
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
25 Except as contained in this notice, the name of the X Consortium shall not be
26 used in advertising or otherwise to promote the sale, use or other dealings
27 in this Software without prior written authorization from the X Consortium.
28
29 */
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 those named RCS, SCCS or CVS.adm). If you are
35 building the distribution on more than one machine, you should use
36 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 #define NeedVarargsPrototypes 1
47
48 #include "lndir-Xos.h"
49 #include <stdlib.h>
50 #include <stdio.h>
51 #include <sys/stat.h>
52 #include <sys/param.h>
53 #include <errno.h>
54
55 #if !defined(X_NOT_POSIX)
56 #include <dirent.h>
57 #else
58 #if defined(SYSV)
59 #include <dirent.h>
60 #else
61 #if defined(USG)
62 #include <dirent.h>
63 #else
64 #include <sys/dir.h>
65 #if !defined(dirent)
66 #define dirent direct
67 #endif
68 #endif
69 #endif
70 #endif
71 #if !defined(MAXPATHLEN)
72 #define MAXPATHLEN 2048
73 #endif
74
75 #if defined(__CYGWIN32__)
76 #include <sys/cygwin.h>
77 #endif
78
79 #if NeedVarargsPrototypes
80 #include <stdarg.h>
81 #endif
82
83 #if defined(X_NOT_STDC_ENV)
84 extern int errno;
85 #endif
86 int silent = 0; /* -silent */
87 int copy = 0; /* -copy */
88 int ignore_links = 0; /* -ignorelinks */
89
90 char *rcurdir;
91 char *curdir;
92
93 int force=0;
94
95 #if defined(WIN32)
96 #define mymkdir(X, Y) mkdir(X)
97 #else
98 #define mymkdir(X, Y) mkdir(X, Y)
99 #endif
100
101 #if !defined(WIN32)
102 #define SYMLINKS
103 #define BELIEVE_ST_NLINK
104 #endif
105
106 void
107 quit (
108 #if NeedVarargsPrototypes
109 int code, char * fmt, ...)
110 #else
111 code, fmt, a1, a2, a3)
112 char *fmt;
113 #endif
114 {
115 #if NeedVarargsPrototypes
116 va_list args;
117 va_start(args, fmt);
118 vfprintf (stderr, fmt, args);
119 va_end(args);
120 #else
121 fprintf (stderr, fmt, a1, a2, a3);
122 #endif
123 putc ('\n', stderr);
124 exit (code);
125 }
126
127 void
128 quiterr (code, s)
129 char *s;
130 {
131 perror (s);
132 exit (code);
133 }
134
135 void
136 msg (
137 #if NeedVarargsPrototypes
138 char * fmt, ...)
139 #else
140 fmt, a1, a2, a3)
141 char *fmt;
142 #endif
143 {
144 #if NeedVarargsPrototypes
145 va_list args;
146 #endif
147 if (curdir) {
148 fprintf (stderr, "%s:\n", curdir);
149 curdir = 0;
150 }
151 #if NeedVarargsPrototypes
152 va_start(args, fmt);
153 vfprintf (stderr, fmt, args);
154 va_end(args);
155 #else
156 fprintf (stderr, fmt, a1, a2, a3);
157 #endif
158 putc ('\n', stderr);
159 }
160
161 void
162 mperror (s)
163 char *s;
164 {
165 if (curdir) {
166 fprintf (stderr, "%s:\n", curdir);
167 curdir = 0;
168 }
169 perror (s);
170 }
171
172 #define BUFSIZE 1024
173 int copyfile(const char *oldpath, const char *newpath) {
174 FILE *f_old;
175 FILE *f_new;
176 int e;
177 ssize_t s;
178 char buf[BUFSIZE];
179
180 #if defined(SYMLINKS)
181 if (copy == 0) {
182 return symlink(oldpath, newpath);
183 } else {
184 #endif
185 f_old = fopen(oldpath, "rb");
186 if (f_old == NULL) {
187 return -1;
188 }
189 f_new = fopen(newpath, "wbx");
190 if (f_new == NULL) {
191 e = errno;
192 fclose(f_old);
193 errno = e;
194 return -1;
195 }
196 while ((s = fread(buf, 1, BUFSIZE, f_old)) > 0) {
197 if (fwrite(buf, 1, s, f_new) < s) {
198 e = errno;
199 fclose(f_old);
200 fclose(f_new);
201 errno = e;
202 return -1;
203 }
204 }
205 if (!feof(f_old)) {
206 e = errno;
207 fclose(f_old);
208 fclose(f_new);
209 errno = e;
210 return -1;
211 }
212 if (fclose(f_new) == EOF) {
213 e = errno;
214 fclose(f_old);
215 errno = e;
216 return -1;
217 }
218 fclose(f_old);
219 return 0;
220 #if defined(SYMLINKS)
221 }
222 #endif
223 }
224
225 int equivalent(lname, rname)
226 char *lname;
227 char *rname;
228 {
229 char *s;
230
231 if (!strcmp(lname, rname))
232 return 1;
233 for (s = lname; *s && (s = strchr(s, '/')); s++) {
234 while (s[1] == '/')
235 strcpy(s+1, s+2);
236 }
237 return !strcmp(lname, rname);
238 }
239
240
241 /* Recursively create symbolic links from the current directory to the "from"
242 directory. Assumes that files described by fs and ts are directories. */
243
244 dodir (fn, fs, ts, rel)
245 char *fn; /* name of "from" directory, either absolute or
246 relative to cwd */
247 struct stat *fs, *ts; /* stats for the "from" directory and cwd */
248 int rel; /* if true, prepend "../" to fn before using */
249 {
250 DIR *df;
251 struct dirent *dp;
252 char buf[MAXPATHLEN + 1], *p;
253 char symbuf[MAXPATHLEN + 1];
254 char basesym[MAXPATHLEN + 1];
255 struct stat sb, sc;
256 int n_dirs;
257 int symlen = -1;
258 int basesymlen = -1;
259 char *ocurdir;
260
261 if ((fs->st_dev == ts->st_dev) &&
262 (fs->st_ino == ts->st_ino) &&
263 /* inode is always 0 on Windows; we don't want to fail in that case */
264 (fs->st_ino != 0)
265 ) {
266 msg ("%s: From and to directories are identical!", fn);
267 return 1;
268 }
269
270 if (rel)
271 strcpy (buf, "../");
272 else
273 buf[0] = '\0';
274 strcat (buf, fn);
275
276 if (!(df = opendir (buf))) {
277 msg ("%s: Cannot opendir", buf);
278 return 1;
279 }
280
281 p = buf + strlen (buf);
282 *p++ = '/';
283 n_dirs = fs->st_nlink;
284 while (dp = readdir (df)) {
285 if (dp->d_name[strlen(dp->d_name) - 1] == '~')
286 continue;
287 if (dp->d_name[0] == '.' && dp->d_name[1] == '#') /* 'non-conflict files' left behind by CVS */
288 continue;
289 strcpy (p, dp->d_name);
290
291 if (
292 #if defined(BELIEVE_ST_NLINK)
293 n_dirs > 0
294 #else
295 /* st_nlink is 1 on Windows, so we have to keep looking for
296 * directories forever */
297 1
298 #endif
299 ) {
300 if (stat (buf, &sb) < 0) {
301 mperror (buf);
302 continue;
303 }
304
305 #if defined(S_ISDIR)
306 if(S_ISDIR(sb.st_mode))
307 #else
308 if (sb.st_mode & S_IFDIR)
309 #endif
310 {
311 /* directory */
312 #if !defined(__CYGWIN32__) /* don't trust cygwin's n_dirs count */
313 n_dirs--;
314 #endif
315 if (dp->d_name[0] == '.' &&
316 (dp->d_name[1] == '\0' || (dp->d_name[1] == '.' &&
317 dp->d_name[2] == '\0')))
318 continue;
319 if (!strcmp (dp->d_name, "RCS"))
320 continue;
321 if (!strcmp (dp->d_name, "SCCS"))
322 continue;
323 if (!strcmp (dp->d_name, "CVS"))
324 continue;
325 if (!strcmp (dp->d_name, ".svn"))
326 continue;
327 if (!strcmp (dp->d_name, ".git"))
328 continue;
329 if (!strcmp (dp->d_name, "_darcs"))
330 continue;
331 if (!strcmp (dp->d_name, "CVS.adm"))
332 continue;
333 ocurdir = rcurdir;
334 rcurdir = buf;
335 curdir = silent ? buf : (char *)0;
336 if (!silent)
337 printf ("%s:\n", buf);
338 if ((stat (dp->d_name, &sc) < 0) && (errno == ENOENT)) {
339 if (mymkdir (dp->d_name, 0777) < 0 ||
340 stat (dp->d_name, &sc) < 0) {
341 mperror (dp->d_name);
342 curdir = rcurdir = ocurdir;
343 continue;
344 }
345 }
346 #if defined(SYMLINKS)
347 if (readlink (dp->d_name, symbuf, sizeof(symbuf) - 1) >= 0) {
348 msg ("%s: is a link instead of a directory", dp->d_name);
349 curdir = rcurdir = ocurdir;
350 continue;
351 }
352 #endif
353 if (chdir (dp->d_name) < 0) {
354 mperror (dp->d_name);
355 curdir = rcurdir = ocurdir;
356 continue;
357 }
358 rel = (fn[0] != '/') && ((fn[0] == '\0') || (fn[1] != ':'));
359 dodir (buf, &sb, &sc, rel);
360 if (chdir ("..") < 0)
361 quiterr (1, "..");
362 curdir = rcurdir = ocurdir;
363 continue;
364 }
365 }
366
367 /* non-directory */
368 #if defined(SYMLINKS)
369 symlen = readlink (dp->d_name, symbuf, sizeof(symbuf) - 1);
370 if (symlen >= 0)
371 symbuf[symlen] = '\0';
372
373 /* The option to ignore links exists mostly because
374 checking for them slows us down by 10-20%.
375 But it is off by default because this really is a useful check. */
376 if (!ignore_links) {
377 /* see if the file in the base tree was a symlink */
378 basesymlen = readlink(buf, basesym, sizeof(basesym) - 1);
379 if (basesymlen >= 0)
380 basesym[basesymlen] = '\0';
381 }
382 #endif
383
384 if (symlen >= 0) {
385 if (!equivalent (basesymlen>=0 ? basesym : buf, symbuf)) {
386 if (force) {
387 unlink(dp->d_name);
388 if (copyfile (basesymlen>=0 ? basesym : buf, dp->d_name) < 0)
389 mperror (dp->d_name);
390 } else {
391 /* Link exists in new tree. Print message if it doesn't match. */
392 msg ("%s: %s", dp->d_name, symbuf);
393 }
394 }
395 } else {
396 if (copyfile (basesymlen>=0 ? basesym : buf, dp->d_name) < 0)
397 mperror (dp->d_name);
398 }
399 }
400
401 closedir (df);
402 return 0;
403 }
404
405 main (ac, av)
406 int ac;
407 char **av;
408 {
409 char *prog_name = av[0];
410 char* tn;
411 struct stat fs, ts;
412 #if defined(__CYGWIN32__)
413 /*
414 The lndir code assumes unix-style paths to work. cygwin
415 lets you get away with using dos'ish paths (e.g., "f:/oo")
416 in most contexts. Using them with 'lndir' will seriously
417 confuse the user though, so under-the-hood, we convert the
418 path into something POSIX-like.
419 */
420 static char fn[MAXPATHLEN+1];
421 #else
422 char *fn;
423 #endif
424
425 while (++av, --ac) {
426 if (strcmp(*av, "-silent") == 0)
427 silent = 1;
428 else if (strcmp(*av, "-f") == 0)
429 force = 1;
430 else if (strcmp(*av, "-ignorelinks") == 0)
431 ignore_links = 1;
432 else if (strcmp(*av, "-copy") == 0)
433 copy = 1;
434 else if (strcmp(*av, "--") == 0) {
435 ++av, --ac;
436 break;
437 } else
438 break;
439 }
440
441 if (ac < 1 || ac > 2)
442 quit (1, "usage: %s [-f] [-silent] [-ignorelinks] fromdir [todir]",
443 prog_name);
444
445 #if defined(__CYGWIN32__)
446 cygwin_conv_to_full_posix_path(av[0], fn);
447 #else
448 fn = av[0];
449 #endif
450
451 if (ac == 2)
452 tn = av[1];
453 else
454 tn = ".";
455
456 /* to directory */
457 if (stat (tn, &ts) < 0) {
458 if (force && (tn[0] != '.' || tn[1] != '\0') ) {
459 mymkdir(tn, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH );
460 }
461 else {
462 quiterr (1, tn);
463 #if defined(S_ISDIR)
464 if (!(S_ISDIR(ts.st_mode)))
465 #else
466 if (!(ts.st_mode & S_IFDIR))
467 #endif
468 quit (2, "%s: Not a directory", tn);
469 }
470 }
471 if (chdir (tn) < 0)
472 quiterr (1, tn);
473
474 /* from directory */
475 if (stat (fn, &fs) < 0)
476 quiterr (1, fn);
477 #if defined(S_ISDIR)
478 if (!(S_ISDIR(fs.st_mode)))
479 #else
480 if (!(fs.st_mode & S_IFDIR))
481 #endif
482 quit (2, "%s: Not a directory", fn);
483
484 exit (dodir (fn, &fs, &ts, 0));
485 }