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