1 /*	$OpenBSD: lndir.c,v 1.15 2004/06/29 17:02:18 mickey 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 <sys/param.h>
51 #include <sys/stat.h>
52 
53 #include <dirent.h>
54 #include <err.h>
55 #include <errno.h>
56 #include <stdarg.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <unistd.h>
61 
62 __RCSID("$MirOS: src/usr.bin/lndir/lndir.c,v 1.2 2008/04/06 23:57:37 tg Exp $");
63 
64 extern const char *__progname;
65 
66 int silent = 0;			/* -silent */
67 int ignore_links = 0;		/* -ignorelinks */
68 
69 char *rcurdir;
70 char *curdir;
71 
72 int equivalent(char *, char *);
73 void addexcept(char *);
74 int dodir(char *, struct stat *, struct stat *, int);
75 void usage(void);
76 
77 struct except {
78 	char *name;
79 	struct except *next;
80 };
81 
82 struct except *exceptions = (struct except *)NULL;
83 
84 int
main(int argc,char * argv[])85 main(int argc, char *argv[])
86 {
87 	struct stat fs, ts;
88 	char *fn, *tn;
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 	if (argc < 1 || argc > 2)
111 		usage();
112 
113 	fn = argv[0];
114 	if (argc == 2)
115 		tn = argv[1];
116 	else
117 		tn = ".";
118 
119 	/* to directory */
120 	if (stat(tn, &ts) < 0)
121 		err(1, "%s", tn);
122 	if (!(S_ISDIR(ts.st_mode)))
123 		errx(2, "%s: %s", tn, strerror(ENOTDIR));
124 	if (chdir(tn) < 0)
125 		err(1, "%s", tn);
126 
127 	/* from directory */
128 	if (stat(fn, &fs) < 0)
129 		err(1, "%s", fn);
130 	if (!(S_ISDIR(fs.st_mode)))
131 		err(2, "%s: %s", fn, strerror(ENOTDIR));
132 
133 	exit(dodir(fn, &fs, &ts, 0));
134 }
135 
136 int
equivalent(char * lname,char * rname)137 equivalent(char *lname, char *rname)
138 {
139 	char *s, *ns;
140 
141 	if (strcmp(lname, rname) == 0)
142 		return(1);
143 	for (s = lname; *s && (s = strchr(s, '/')); s++) {
144 		if (s[1] == '/') {
145 			/* collapse multiple slashes in lname */
146 			for (ns = s + 1; *ns == '/'; ns++)
147 				;
148 			memmove(s + 1, ns, strlen(ns) + 1);
149 		}
150 	}
151 	return(strcmp(lname, rname) == 0);
152 }
153 
154 void
addexcept(char * name)155 addexcept(char *name)
156 {
157 	struct except *new;
158 
159 	new = (struct except *)malloc(sizeof(struct except));
160 	if (new == (struct except *)NULL)
161 		err(1, NULL);
162 	new->name = strdup(name);
163 	if (new->name == (char *)NULL)
164 		err(1, NULL);
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 #if 0
176 	char *fn;		/* name of "from" directory, either absolute or
177 				   relative to cwd */
178 	struct stat *fs, *ts;	/* stats for the "from" directory and cwd */
179 	int rel;		/* if true, prepend "../" to fn before using */
180 #endif
181 int
dodir(char * fn,struct stat * fs,struct stat * ts,int rel)182 dodir(char *fn, struct stat *fs, struct stat *ts, int rel)
183 {
184 	char buf[MAXPATHLEN + 1], symbuf[MAXPATHLEN + 1], basesym[MAXPATHLEN + 1];
185 	int n_dirs, symlen, basesymlen = -1;
186 	struct stat sb, sc;
187 	struct except *cur;
188 	struct dirent *dp;
189 	char *ocurdir, *p;
190 	DIR *df;
191 
192 	if (fs->st_dev == ts->st_dev && fs->st_ino == ts->st_ino) {
193 		warnx("%s: From and to directories are identical!", fn);
194 		return(1);
195 	}
196 
197 	if (rel)
198 		strlcpy(buf, "../", sizeof(buf));
199 	else
200 		buf[0] = '\0';
201 	strlcat(buf, fn, sizeof(buf));
202 
203 	if (!(df = opendir(buf))) {
204 		warn("%s: Cannot opendir", buf);
205 		return(1);
206 	}
207 
208 	p = buf + strlen(buf);
209 	*p++ = '/';
210 	n_dirs = fs->st_nlink;
211 	while ((dp = readdir(df))) {
212 #if defined(__GLIBC__) && !defined(_DIRENT_HAVE_D_NAMLEN)
213 		size_t d_namlen = strlen(dp->d_name);
214 
215 		if (d_namlen == 0 || dp->d_name[d_namlen - 1] == '~')
216 #else
217 		if (dp->d_namlen == 0 || dp->d_name[dp->d_namlen - 1] == '~')
218 #endif
219 			continue;
220 		for (cur = exceptions; cur != NULL; cur = cur->next) {
221 			if (!strcmp(dp->d_name, cur->name))
222 				goto next;	/* can't continue */
223 		}
224 		strlcpy(p, dp->d_name, buf + sizeof(buf) - p);
225 
226 		if (n_dirs > 0) {
227 			if (stat(buf, &sb) < 0) {
228 				warn("%s", buf);
229 				continue;
230 			}
231 
232 			if (S_ISDIR(sb.st_mode)) {
233 				/* directory */
234 				n_dirs--;
235 				if (dp->d_name[0] == '.' &&
236 				    (dp->d_name[1] == '\0' || (dp->d_name[1] == '.' &&
237 				    dp->d_name[2] == '\0')))
238 					continue;
239 				if (!strcmp(dp->d_name, "RCS"))
240 					continue;
241 				if (!strcmp(dp->d_name, "SCCS"))
242 					continue;
243 				if (!strcmp(dp->d_name, "CVS"))
244 					continue;
245 				if (!strcmp(dp->d_name, "CVS.adm"))
246 					continue;
247 				ocurdir = rcurdir;
248 				rcurdir = buf;
249 				curdir = silent ? buf : NULL;
250 				if (!silent)
251 					printf("%s:\n", buf);
252 				if (stat(dp->d_name, &sc) < 0 && errno == ENOENT) {
253 					if (mkdir(dp->d_name, 0777) < 0 ||
254 					    stat(dp->d_name, &sc) < 0) {
255 						warn("%s", dp->d_name);
256 						curdir = rcurdir = ocurdir;
257 						continue;
258 					}
259 				}
260 				if (readlink(dp->d_name, symbuf,
261 				    sizeof(symbuf) - 1) >= 0) {
262 					fprintf(stderr,
263 					    "%s: is a link instead of a directory\n",
264 					    dp->d_name);
265 					curdir = rcurdir = ocurdir;
266 					continue;
267 				}
268 				if (chdir(dp->d_name) < 0) {
269 					warn("%s", dp->d_name);
270 					curdir = rcurdir = ocurdir;
271 					continue;
272 				}
273 				dodir(buf, &sb, &sc, (buf[0] != '/'));
274 				if (chdir("..") < 0)
275 					err(1, "..");
276 				curdir = rcurdir = ocurdir;
277 				continue;
278 			}
279 		}
280 
281 		/* non-directory */
282 		symlen = readlink(dp->d_name, symbuf, sizeof(symbuf) - 1);
283 		if (symlen >= 0)
284 			symbuf[symlen] = '\0';
285 
286 		/*
287 		 * The option to ignore links exists mostly because
288 		 * checking for them slows us down by 10-20%.
289 		 * But it is off by default because this really is a useful check.
290 		 */
291 		if (!ignore_links) {
292 			/* see if the file in the base tree was a symlink */
293 			basesymlen = readlink(buf, basesym, sizeof(basesym) - 1);
294 			if (basesymlen >= 0)
295 				basesym[basesymlen] = '\0';
296 		}
297 
298 		if (symlen >= 0) {
299 			/*
300 			 * Link exists in new tree.  Print message if
301 			 * it doesn't match.
302 			 */
303 			if (!equivalent(basesymlen>=0 ? basesym : buf, symbuf))
304 				fprintf(stderr,"%s: %s\n", dp->d_name, symbuf);
305 		} else {
306 			if (symlink(basesymlen>=0 ? basesym : buf, dp->d_name) < 0)
307 				warn("%s", dp->d_name);
308 		}
309 next:
310 	;
311 	}
312 
313 	closedir(df);
314 	return(0);
315 }
316 
317 void
usage(void)318 usage(void)
319 {
320 	(void)fprintf(stderr, "usage: %s [-e except] [-si] fromdir [todir]\n",
321 	    __progname);
322 	exit(1);
323 }
324