xref: /openbsd/usr.bin/lndir/lndir.c (revision 78b63d65)
1 /*	$OpenBSD: lndir.c,v 1.6 2001/07/04 04:56:15 jasoni Exp $	*/
2 /* $XConsortium: lndir.c /main/15 1995/08/30 10:56:18 gildea $ */
3 
4 /*
5  * Create shadow link tree (after X11R4 script of the same name)
6  * Mark Reinhold (mbr@lcs.mit.edu)/3 January 1990
7  */
8 
9 /*
10 Copyright (c) 1990,  X Consortium
11 
12 Permission is hereby granted, free of charge, to any person obtaining a copy
13 of this software and associated documentation files (the "Software"), to deal
14 in the Software without restriction, including without limitation the rights
15 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
18 
19 The above copyright notice and this permission notice shall be included in
20 all copies or substantial portions of the Software.
21 
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
25 X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
26 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
27 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 
29 Except as contained in this notice, the name of the X Consortium shall not be
30 used in advertising or otherwise to promote the sale, use or other dealings
31 in this Software without prior written authorization from the X Consortium.
32 
33 */
34 
35 /* From the original /bin/sh script:
36 
37   Used to create a copy of the a directory tree that has links for all
38   non-directories (except those named RCS, SCCS or CVS.adm).  If you are
39   building the distribution on more than one machine, you should use
40   this technique.
41 
42   If your master sources are located in /usr/local/src/X and you would like
43   your link tree to be in /usr/local/src/new-X, do the following:
44 
45    	%  mkdir /usr/local/src/new-X
46 	%  cd /usr/local/src/new-X
47    	%  lndir ../X
48 */
49 
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <sys/types.h>
53 #include <sys/stat.h>
54 #include <sys/param.h>
55 #include <err.h>
56 #include <errno.h>
57 #include <dirent.h>
58 #include <stdarg.h>
59 #include <string.h>
60 #include <unistd.h>
61 
62 extern	char *__progname;
63 
64 int silent = 0;			/* -silent */
65 int ignore_links = 0;		/* -ignorelinks */
66 
67 char *rcurdir;
68 char *curdir;
69 
70 int equivalent __P((char *, char *));
71 void addexcept __P((char *));
72 int dodir __P((char *, struct stat *, struct stat *, int));
73 void usage __P((void));
74 
75 struct except {
76 	char *name;
77 	struct except *next;
78 };
79 
80 struct except *exceptions = (struct except *)NULL;
81 
82 int
83 main (argc, argv)
84 	int	argc;
85 	char 	**argv;
86 {
87 	char *fn, *tn;
88 	struct stat fs, ts;
89 
90         while (++argv, --argc) {
91                 if ((strcmp(*argv, "-silent") == 0)
92                     || (strcmp(*argv, "-s") == 0))
93                         silent = 1;
94                 else if ((strcmp(*argv, "-ignorelinks") == 0)
95                         || (strcmp(*argv, "-i") == 0))
96                         ignore_links = 1;
97                 else if (strcmp(*argv, "-e") == 0) {
98                         ++argv, --argc;
99 
100                         if (argc < 2)
101                                 usage();
102                         addexcept(*argv);
103                 } else if (strcmp(*argv, "--") == 0) {
104                         ++argv, --argc;
105                         break;
106                 } else
107                         break;
108         }
109 
110 
111     if (argc < 1 || argc > 2)
112 	usage();
113 
114     fn = argv[0];
115     if (argc == 2)
116 	tn = argv[1];
117     else
118 	tn = ".";
119 
120     /* to directory */
121     if (stat(tn, &ts) < 0)
122 	errx(1, "%s", tn);
123     if (!(S_ISDIR(ts.st_mode)))
124 	errx(2, "%s: Not a directory", tn);
125     if (chdir(tn) < 0)
126 	errx(1, "%s", tn);
127 
128     /* from directory */
129     if (stat(fn, &fs) < 0)
130 	errx(1, "%s", fn);
131     if (!(S_ISDIR(fs.st_mode)))
132 	errx(2, "%s: Not a directory", fn);
133 
134     exit(dodir (fn, &fs, &ts, 0));
135 }
136 
137 int
138 equivalent(lname, rname)
139     char *lname;
140     char *rname;
141 {
142     char *s;
143 
144     if (!strcmp(lname, rname))
145 	return(1);
146     for (s = lname; *s && (s = strchr(s, '/')); s++) {
147 	while (s[1] == '/')
148 	    strcpy(s+1, s+2);
149     }
150     return(!strcmp(lname, rname));
151 }
152 
153 void
154 addexcept(name)
155     char *name;
156 {
157     struct except *new;
158 
159     new = (struct except *)malloc(sizeof(struct except));
160     if (new == (struct except *)NULL)
161         errx(1, "addexcept");
162     new->name = strdup(name);
163     if (new->name == (char *)NULL)
164         errx(1, "addexcept");
165 
166     new->next = exceptions;
167     exceptions = new;
168 }
169 
170 
171 /*
172  * Recursively create symbolic links from the current directory to the "from"
173  * directory.  Assumes that files described by fs and ts are directories.
174  */
175 int
176 dodir(fn, fs, ts, rel)
177 	char *fn;		/* name of "from" directory, either absolute or
178 				   relative to cwd */
179 	struct stat *fs, *ts;	/* stats for the "from" directory and cwd */
180 	int rel;		/* if true, prepend "../" to fn before using */
181 {
182     DIR *df;
183     struct dirent *dp;
184     char buf[MAXPATHLEN + 1], *p;
185     char symbuf[MAXPATHLEN + 1];
186     char basesym[MAXPATHLEN + 1];
187     struct stat sb, sc;
188     int n_dirs;
189     int symlen;
190     int basesymlen = -1;
191     char *ocurdir;
192     struct except *cur;
193 
194     if ((fs->st_dev == ts->st_dev) && (fs->st_ino == ts->st_ino)) {
195 	warn("%s: From and to directories are identical!", fn);
196 	return(1);
197     }
198 
199     if (rel)
200 	strcpy(buf, "../");
201     else
202 	buf[0] = '\0';
203     strcat(buf, fn);
204 
205     if (!(df = opendir(buf))) {
206 	warn("%s: Cannot opendir", buf);
207 	return(1);
208     }
209 
210     p = buf + strlen(buf);
211     *p++ = '/';
212     n_dirs = fs->st_nlink;
213     while ((dp = readdir(df))) {
214 	if (dp->d_name[strlen(dp->d_name) - 1] == '~')
215 	    continue;
216 	for (cur = exceptions; cur != (struct except *)NULL;
217 	     cur = cur->next) {
218 	    if (!strcmp (dp->d_name, cur->name))
219 	    	goto next;	/* can't continue */
220 	}
221 	strcpy(p, dp->d_name);
222 
223 	if (n_dirs > 0) {
224 	    if (stat(buf, &sb) < 0) {
225 		warn("%s", buf);
226 		continue;
227 	    }
228 
229 	    if (S_ISDIR(sb.st_mode)) {
230 		/* directory */
231 		n_dirs--;
232 		if (dp->d_name[0] == '.' &&
233 		    (dp->d_name[1] == '\0' || (dp->d_name[1] == '.' &&
234 					       dp->d_name[2] == '\0')))
235 		    continue;
236 		if (!strcmp(dp->d_name, "RCS"))
237 		    continue;
238 		if (!strcmp(dp->d_name, "SCCS"))
239 		    continue;
240 		if (!strcmp(dp->d_name, "CVS"))
241 		    continue;
242 		if (!strcmp(dp->d_name, "CVS.adm"))
243 		    continue;
244 		ocurdir = rcurdir;
245 		rcurdir = buf;
246 		curdir = silent ? buf : NULL;
247 		if (!silent)
248 		    printf("%s:\n", buf);
249 		if ((stat(dp->d_name, &sc) < 0) && (errno == ENOENT)) {
250 		    if (mkdir(dp->d_name, 0777) < 0 ||
251 			stat(dp->d_name, &sc) < 0) {
252 			warn("%s", dp->d_name);
253 			curdir = rcurdir = ocurdir;
254 			continue;
255 		    }
256 		}
257 		if (readlink(dp->d_name, symbuf, sizeof(symbuf) - 1) >= 0) {
258 		    (void)fprintf(stderr, "%s: is a link instead of a directory\n",
259 				dp->d_name);
260 		    curdir = rcurdir = ocurdir;
261 		    continue;
262 		}
263 		if (chdir(dp->d_name) < 0) {
264 		    warn("%s", dp->d_name);
265 		    curdir = rcurdir = ocurdir;
266 		    continue;
267 		}
268 		dodir(buf, &sb, &sc, (buf[0] != '/'));
269 		if (chdir ("..") < 0)
270 		    errx (1, "..");
271 		curdir = rcurdir = ocurdir;
272 		continue;
273 	    }
274 	}
275 
276 	/* non-directory */
277 	symlen = readlink(dp->d_name, symbuf, sizeof(symbuf) - 1);
278 	if (symlen >= 0)
279 	    symbuf[symlen] = '\0';
280 
281 	/*
282 	 * The option to ignore links exists mostly because
283 	 * checking for them slows us down by 10-20%.
284 	 * But it is off by default because this really is a useful check.
285 	 */
286 	if (!ignore_links) {
287 	    /* see if the file in the base tree was a symlink */
288 	    basesymlen = readlink(buf, basesym, sizeof(basesym) - 1);
289 	    if (basesymlen >= 0)
290 		basesym[basesymlen] = '\0';
291 	}
292 
293 	if (symlen >= 0) {
294 	    /* Link exists in new tree.  Print message if it doesn't match. */
295 	    if (!equivalent(basesymlen>=0 ? basesym : buf, symbuf))
296 		(void)fprintf(stderr,"%s: %s\n", dp->d_name, symbuf);
297 	} else {
298 	    if (symlink(basesymlen>=0 ? basesym : buf, dp->d_name) < 0)
299 		warn("%s", dp->d_name);
300 	}
301 next:
302     }
303 
304     closedir(df);
305     return(0);
306 }
307 
308 void
309 usage()
310 {
311 	(void)fprintf(stderr, "usage: %s [-e except] [-si] fromdir todir\n",
312 		__progname);
313 	exit(1);
314 }
315