Annotation of src/usr.bin/lndir/lndir.c, Revision 1.5
1.5 ! ericj 1: /* $OpenBSD: lndir.c,v 1.4 1996/08/19 14:20:18 niklas Exp $ */
1.1 downsj 2: /* $XConsortium: lndir.c /main/15 1995/08/30 10:56:18 gildea $ */
1.5 ! ericj 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: */
1.1 downsj 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>
1.4 niklas 51: #include <stdlib.h>
1.1 downsj 52: #include <sys/types.h>
53: #include <sys/stat.h>
54: #include <sys/param.h>
1.5 ! ericj 55: #include <err.h>
1.1 downsj 56: #include <errno.h>
57: #include <dirent.h>
58: #include <stdarg.h>
59: #include <string.h>
1.5 ! ericj 60: #include <unistd.h>
! 61:
! 62: extern char *__progname;
1.1 downsj 63:
64: int silent = 0; /* -silent */
65: int ignore_links = 0; /* -ignorelinks */
66:
67: char *rcurdir;
68: char *curdir;
69:
1.5 ! ericj 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:
1.1 downsj 75: struct except {
76: char *name;
77: struct except *next;
78: };
1.5 ! ericj 79:
1.1 downsj 80: struct except *exceptions = (struct except *)NULL;
81:
1.5 ! ericj 82: int
! 83: main (argc, argv)
! 84: int argc;
! 85: char **argv;
1.1 downsj 86: {
1.5 ! ericj 87: int ch;
! 88: char *fn, *tn;
! 89: struct stat fs, ts;
! 90:
! 91: while (++argv, --argc) {
! 92: if ((strcmp(*argv, "-silent") == 0)
! 93: || (strcmp(*argv, "-s") == 0))
! 94: silent = 1;
! 95: else if ((strcmp(*argv, "-ignorelinks") == 0)
! 96: || (strcmp(*argv, "-i") == 0))
! 97: ignore_links = 1;
! 98: else if (strcmp(*argv, "-e") == 0) {
! 99: ++argv, --argc;
! 100:
! 101: if (argc < 2)
! 102: usage();
! 103: addexcept(*argv);
! 104: } else if (strcmp(*argv, "--") == 0) {
! 105: ++argv, --argc;
! 106: break;
! 107: } else
! 108: break;
! 109: }
! 110:
! 111:
! 112: if (argc < 1 || argc > 2)
! 113: usage();
! 114:
! 115: fn = argv[0];
! 116: if (argc == 2)
! 117: tn = argv[1];
! 118: else
! 119: tn = ".";
1.1 downsj 120:
1.5 ! ericj 121: /* to directory */
! 122: if (stat(tn, &ts) < 0)
! 123: errx(1, "%s", tn);
! 124: if (!(S_ISDIR(ts.st_mode)))
! 125: errx(2, "%s: Not a directory", tn);
! 126: if (chdir(tn) < 0)
! 127: errx(1, "%s", tn);
1.1 downsj 128:
1.5 ! ericj 129: /* from directory */
! 130: if (stat(fn, &fs) < 0)
! 131: errx(1, "%s", fn);
! 132: if (!(S_ISDIR(fs.st_mode)))
! 133: errx(2, "%s: Not a directory", fn);
1.1 downsj 134:
1.5 ! ericj 135: exit(dodir (fn, &fs, &ts, 0));
1.1 downsj 136: }
137:
1.5 ! ericj 138: int
! 139: equivalent(lname, rname)
1.1 downsj 140: char *lname;
141: char *rname;
142: {
143: char *s;
144:
145: if (!strcmp(lname, rname))
1.5 ! ericj 146: return(1);
1.1 downsj 147: for (s = lname; *s && (s = strchr(s, '/')); s++) {
148: while (s[1] == '/')
149: strcpy(s+1, s+2);
150: }
1.5 ! ericj 151: return(!strcmp(lname, rname));
1.1 downsj 152: }
153:
1.5 ! ericj 154: void
1.1 downsj 155: addexcept(name)
156: char *name;
157: {
158: struct except *new;
159:
160: new = (struct except *)malloc(sizeof(struct except));
161: if (new == (struct except *)NULL)
1.5 ! ericj 162: errx(1, "addexcept");
1.1 downsj 163: new->name = strdup(name);
164: if (new->name == (char *)NULL)
1.5 ! ericj 165: errx(1, "addexcept");
1.1 downsj 166:
167: new->next = exceptions;
168: exceptions = new;
169: }
170:
171:
1.5 ! ericj 172: /*
! 173: * Recursively create symbolic links from the current directory to the "from"
! 174: * directory. Assumes that files described by fs and ts are directories.
! 175: */
! 176: int
! 177: dodir(fn, fs, ts, rel)
! 178: char *fn; /* name of "from" directory, either absolute or
1.1 downsj 179: relative to cwd */
1.5 ! ericj 180: struct stat *fs, *ts; /* stats for the "from" directory and cwd */
! 181: int rel; /* if true, prepend "../" to fn before using */
1.1 downsj 182: {
183: DIR *df;
184: struct dirent *dp;
185: char buf[MAXPATHLEN + 1], *p;
186: char symbuf[MAXPATHLEN + 1];
187: char basesym[MAXPATHLEN + 1];
188: struct stat sb, sc;
189: int n_dirs;
190: int symlen;
191: int basesymlen = -1;
192: char *ocurdir;
193: struct except *cur;
194:
195: if ((fs->st_dev == ts->st_dev) && (fs->st_ino == ts->st_ino)) {
1.5 ! ericj 196: warn("%s: From and to directories are identical!", fn);
! 197: return(1);
1.1 downsj 198: }
199:
200: if (rel)
1.5 ! ericj 201: strcpy(buf, "../");
1.1 downsj 202: else
203: buf[0] = '\0';
1.5 ! ericj 204: strcat(buf, fn);
1.1 downsj 205:
1.5 ! ericj 206: if (!(df = opendir(buf))) {
! 207: warn("%s: Cannot opendir", buf);
! 208: return(1);
1.1 downsj 209: }
210:
1.5 ! ericj 211: p = buf + strlen(buf);
1.1 downsj 212: *p++ = '/';
213: n_dirs = fs->st_nlink;
1.5 ! ericj 214: while ((dp = readdir(df))) {
1.1 downsj 215: if (dp->d_name[strlen(dp->d_name) - 1] == '~')
216: continue;
1.3 downsj 217: for (cur = exceptions; cur != (struct except *)NULL;
218: cur = cur->next) {
219: if (!strcmp (dp->d_name, cur->name))
220: goto next; /* can't continue */
221: }
1.5 ! ericj 222: strcpy(p, dp->d_name);
1.1 downsj 223:
224: if (n_dirs > 0) {
1.5 ! ericj 225: if (stat(buf, &sb) < 0) {
! 226: warn("%s", buf);
1.1 downsj 227: continue;
228: }
229:
1.5 ! ericj 230: if (S_ISDIR(sb.st_mode)) {
1.1 downsj 231: /* directory */
232: n_dirs--;
233: if (dp->d_name[0] == '.' &&
234: (dp->d_name[1] == '\0' || (dp->d_name[1] == '.' &&
235: dp->d_name[2] == '\0')))
236: continue;
1.5 ! ericj 237: if (!strcmp(dp->d_name, "RCS"))
1.1 downsj 238: continue;
1.5 ! ericj 239: if (!strcmp(dp->d_name, "SCCS"))
1.1 downsj 240: continue;
1.5 ! ericj 241: if (!strcmp(dp->d_name, "CVS"))
1.1 downsj 242: continue;
1.5 ! ericj 243: if (!strcmp(dp->d_name, "CVS.adm"))
1.1 downsj 244: continue;
245: ocurdir = rcurdir;
246: rcurdir = buf;
1.5 ! ericj 247: curdir = silent ? buf : NULL;
1.1 downsj 248: if (!silent)
1.5 ! ericj 249: printf("%s:\n", buf);
! 250: if ((stat(dp->d_name, &sc) < 0) && (errno == ENOENT)) {
! 251: if (mkdir(dp->d_name, 0777) < 0 ||
! 252: stat(dp->d_name, &sc) < 0) {
! 253: warn("%s", dp->d_name);
1.1 downsj 254: curdir = rcurdir = ocurdir;
255: continue;
256: }
257: }
1.5 ! ericj 258: if (readlink(dp->d_name, symbuf, sizeof(symbuf) - 1) >= 0) {
! 259: (void)fprintf(stderr, "%s: is a link instead of a directory\n",
! 260: dp->d_name);
1.1 downsj 261: curdir = rcurdir = ocurdir;
262: continue;
263: }
1.5 ! ericj 264: if (chdir(dp->d_name) < 0) {
! 265: warn("%s", dp->d_name);
1.1 downsj 266: curdir = rcurdir = ocurdir;
267: continue;
268: }
1.5 ! ericj 269: dodir(buf, &sb, &sc, (buf[0] != '/'));
1.1 downsj 270: if (chdir ("..") < 0)
1.5 ! ericj 271: errx (1, "..");
1.1 downsj 272: curdir = rcurdir = ocurdir;
273: continue;
274: }
275: }
276:
277: /* non-directory */
1.5 ! ericj 278: symlen = readlink(dp->d_name, symbuf, sizeof(symbuf) - 1);
1.1 downsj 279: if (symlen >= 0)
280: symbuf[symlen] = '\0';
281:
1.5 ! ericj 282: /*
! 283: * The option to ignore links exists mostly because
! 284: * checking for them slows us down by 10-20%.
! 285: * But it is off by default because this really is a useful check.
! 286: */
1.1 downsj 287: if (!ignore_links) {
288: /* see if the file in the base tree was a symlink */
289: basesymlen = readlink(buf, basesym, sizeof(basesym) - 1);
290: if (basesymlen >= 0)
291: basesym[basesymlen] = '\0';
292: }
293:
294: if (symlen >= 0) {
295: /* Link exists in new tree. Print message if it doesn't match. */
1.5 ! ericj 296: if (!equivalent(basesymlen>=0 ? basesym : buf, symbuf))
! 297: (void)fprintf(stderr,"%s: %s\n", dp->d_name, symbuf);
1.1 downsj 298: } else {
1.5 ! ericj 299: if (symlink(basesymlen>=0 ? basesym : buf, dp->d_name) < 0)
! 300: warn("%s", dp->d_name);
1.1 downsj 301: }
302: next:
303: }
304:
1.5 ! ericj 305: closedir(df);
! 306: return(0);
1.1 downsj 307: }
308:
1.5 ! ericj 309: void
! 310: usage()
1.1 downsj 311: {
1.5 ! ericj 312: (void)fprintf(stderr, "usage: %s [-e except] [-si] fromdir todir\n",
! 313: __progname);
! 314: exit(1);
1.1 downsj 315: }