3396dcb8ecc57616232da28ade6f0bdd9473ea19
[platform/upstream/nspr.git] / nspr / config / nsinstall.c
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 /*
7 ** Netscape portable install command.
8 **
9 ** Brendan Eich, 7/20/95
10 */
11 #include <stdio.h>  /* OSF/1 requires this before grp.h, so put it first */
12 #include <assert.h>
13 #include <fcntl.h>
14 #include <grp.h>
15 #include <pwd.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <unistd.h>
19 #include <utime.h>
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <dirent.h>
23 #include <errno.h>
24 #include <stdarg.h>
25 #ifdef USE_REENTRANT_LIBC
26 #include "libc_r.h"
27 #endif /* USE_REENTRANT_LIBC */
28
29 #include "pathsub.h"
30
31 #define HAVE_FCHMOD
32
33 #if defined(BEOS)
34 #undef HAVE_FCHMOD
35 #endif
36
37 /*
38  * Does getcwd() take NULL as the first argument and malloc
39  * the result buffer?
40  */
41 #if !defined(DARWIN)
42 #define GETCWD_CAN_MALLOC
43 #endif
44
45 #if defined(LINUX) || defined(__GNU__) || defined(__GLIBC__) 
46 #include <getopt.h>
47 #endif
48
49 #if defined(SCO) || defined(UNIXWARE)
50 #if !defined(S_ISLNK) && defined(S_IFLNK)
51 #define S_ISLNK(a)      (((a) & S_IFMT) == S_IFLNK)
52 #endif
53 #endif
54
55 #ifdef QNX
56 #define d_ino d_stat.st_ino
57 #endif
58
59 static void
60 usage(void)
61 {
62     fprintf(stderr,
63         "usage: %s [-C cwd] [-L linkprefix] [-m mode] [-o owner] [-g group]\n"
64         "       %*s [-DdltR] file [file ...] directory\n",
65         program, (int)strlen(program), "");
66     exit(2);
67 }
68
69 static int
70 mkdirs(char *path, mode_t mode)
71 {
72     char *cp;
73     struct stat sb;
74     int res;
75     
76     while (*path == '/' && path[1] == '/')
77         path++;
78     for (cp = strrchr(path, '/'); cp && cp != path && cp[-1] == '/'; cp--)
79         ;
80     if (cp && cp != path) {
81         *cp = '\0';
82         if ((stat(path, &sb) < 0 || !S_ISDIR(sb.st_mode)) &&
83             mkdirs(path, mode) < 0) {
84             return -1;
85         }
86         *cp = '/';
87     }
88     res = mkdir(path, mode);
89     if ((res != 0) && (errno == EEXIST))
90         return 0;
91      else
92         return res;
93 }
94
95 static uid_t
96 touid(char *owner)
97 {
98     struct passwd *pw;
99     uid_t uid;
100     char *cp;
101
102     pw = getpwnam(owner);
103     if (pw)
104         return pw->pw_uid;
105     uid = strtol(owner, &cp, 0);
106     if (uid == 0 && cp == owner)
107         fail("cannot find uid for %s", owner);
108     return uid;
109 }
110
111 static gid_t
112 togid(char *group)
113 {
114     struct group *gr;
115     gid_t gid;
116     char *cp;
117
118     gr = getgrnam(group);
119     if (gr)
120         return gr->gr_gid;
121     gid = strtol(group, &cp, 0);
122     if (gid == 0 && cp == group)
123         fail("cannot find gid for %s", group);
124     return gid;
125 }
126
127 int
128 main(int argc, char **argv)
129 {
130     int onlydir, dodir, dolink, dorelsymlink, dotimes, opt, len, lplen, tdlen, bnlen, exists, fromfd, tofd, cc, wc;
131     mode_t mode = 0755;
132     char *linkprefix, *owner, *group, *cp, *cwd, *todir, *toname, *name, *base, *linkname, *bp, buf[BUFSIZ];
133     uid_t uid;
134     gid_t gid;
135     struct stat sb, tosb;
136     struct utimbuf utb;
137
138     program = argv[0];
139     cwd = linkname = linkprefix = owner = group = 0;
140     onlydir = dodir = dolink = dorelsymlink = dotimes = lplen = 0;
141
142     while ((opt = getopt(argc, argv, "C:DdlL:Rm:o:g:t")) != EOF) {
143         switch (opt) {
144           case 'C':
145             cwd = optarg;
146             break;
147           case 'D':
148             onlydir = 1;
149             break;
150           case 'd':
151             dodir = 1;
152             break;
153           case 'l':
154             dolink = 1;
155             break;
156           case 'L':
157             linkprefix = optarg;
158             lplen = strlen(linkprefix);
159             dolink = 1;
160             break;
161           case 'R':
162             dolink = dorelsymlink = 1;
163             break;
164           case 'm':
165             mode = strtoul(optarg, &cp, 8);
166             if (mode == 0 && cp == optarg)
167                 usage();
168             break;
169           case 'o':
170             owner = optarg;
171             break;
172           case 'g':
173             group = optarg;
174             break;
175           case 't':
176             dotimes = 1;
177             break;
178           default:
179             usage();
180         }
181     }
182
183     argc -= optind;
184     argv += optind;
185     if (argc < 2 - onlydir)
186         usage();
187
188     todir = argv[argc-1];
189     if ((stat(todir, &sb) < 0 || !S_ISDIR(sb.st_mode)) &&
190         mkdirs(todir, 0777) < 0) {
191         fail("cannot make directory %s", todir);
192     }
193     if (onlydir)
194         return 0;
195
196     if (!cwd) {
197 #ifdef GETCWD_CAN_MALLOC
198         cwd = getcwd(0, PATH_MAX);
199 #else
200         cwd = malloc(PATH_MAX + 1);
201         cwd = getcwd(cwd, PATH_MAX);
202 #endif
203     }
204     xchdir(todir);
205 #ifdef GETCWD_CAN_MALLOC
206     todir = getcwd(0, PATH_MAX);
207 #else
208     todir = malloc(PATH_MAX + 1);
209     todir = getcwd(todir, PATH_MAX);
210 #endif
211     tdlen = strlen(todir);
212     xchdir(cwd);
213     tdlen = strlen(todir);
214
215     uid = owner ? touid(owner) : -1;
216     gid = group ? togid(group) : -1;
217
218     while (--argc > 0) {
219         name = *argv++;
220         len = strlen(name);
221         base = xbasename(name);
222         bnlen = strlen(base);
223         toname = (char*)xmalloc(tdlen + 1 + bnlen + 1);
224         sprintf(toname, "%s/%s", todir, base);
225         exists = (lstat(toname, &tosb) == 0);
226
227         if (dodir) {
228             /* -d means create a directory, always */
229             if (exists && !S_ISDIR(tosb.st_mode)) {
230                 (void) unlink(toname);
231                 exists = 0;
232             }
233             if (!exists && mkdir(toname, mode) < 0)
234                 fail("cannot make directory %s", toname);
235             if ((owner || group) && chown(toname, uid, gid) < 0)
236                 fail("cannot change owner of %s", toname);
237         } else if (dolink) {
238             if (*name == '/') {
239                 /* source is absolute pathname, link to it directly */
240                 linkname = 0;
241             } else {
242                 if (linkprefix) {
243                     /* -L implies -l and prefixes names with a $cwd arg. */
244                     len += lplen + 1;
245                     linkname = (char*)xmalloc(len + 1);
246                     sprintf(linkname, "%s/%s", linkprefix, name);
247                 } else if (dorelsymlink) {
248                     /* Symlink the relative path from todir to source name. */
249                     linkname = (char*)xmalloc(PATH_MAX);
250
251                     if (*todir == '/') {
252                         /* todir is absolute: skip over common prefix. */
253                         lplen = relatepaths(todir, cwd, linkname);
254                         strcpy(linkname + lplen, name);
255                     } else {
256                         /* todir is named by a relative path: reverse it. */
257                         reversepath(todir, name, len, linkname);
258                         xchdir(cwd);
259                     }
260
261                     len = strlen(linkname);
262                 }
263                 name = linkname;
264             }
265
266             /* Check for a pre-existing symlink with identical content. */
267             if (exists &&
268                 (!S_ISLNK(tosb.st_mode) ||
269                  readlink(toname, buf, sizeof buf) != len ||
270                  strncmp(buf, name, len) != 0)) {
271                 (void) (S_ISDIR(tosb.st_mode) ? rmdir : unlink)(toname);
272                 exists = 0;
273             }
274             if (!exists && symlink(name, toname) < 0)
275                 fail("cannot make symbolic link %s", toname);
276 #ifdef HAVE_LCHOWN
277             if ((owner || group) && lchown(toname, uid, gid) < 0)
278                 fail("cannot change owner of %s", toname);
279 #endif
280
281             if (linkname) {
282                 free(linkname);
283                 linkname = 0;
284             }
285         } else {
286             /* Copy from name to toname, which might be the same file. */
287             fromfd = open(name, O_RDONLY);
288             if (fromfd < 0 || fstat(fromfd, &sb) < 0)
289                 fail("cannot access %s", name);
290             if (exists && (!S_ISREG(tosb.st_mode) || access(toname, W_OK) < 0))
291                 (void) (S_ISDIR(tosb.st_mode) ? rmdir : unlink)(toname);
292             tofd = open(toname, O_CREAT | O_WRONLY, 0666);
293             if (tofd < 0)
294                 fail("cannot create %s", toname);
295
296             bp = buf;
297             while ((cc = read(fromfd, bp, sizeof buf)) > 0) {
298                 while ((wc = write(tofd, bp, cc)) > 0) {
299                     if ((cc -= wc) == 0)
300                         break;
301                     bp += wc;
302                 }
303                 if (wc < 0)
304                     fail("cannot write to %s", toname);
305             }
306             if (cc < 0)
307                 fail("cannot read from %s", name);
308
309             if (ftruncate(tofd, sb.st_size) < 0)
310                 fail("cannot truncate %s", toname);
311             if (dotimes) {
312                 utb.actime = sb.st_atime;
313                 utb.modtime = sb.st_mtime;
314                 if (utime(toname, &utb) < 0)
315                     fail("cannot set times of %s", toname);
316             }
317 #ifdef HAVE_FCHMOD
318             if (fchmod(tofd, mode) < 0)
319 #else
320             if (chmod(toname, mode) < 0)
321 #endif
322                 fail("cannot change mode of %s", toname);
323             if ((owner || group) && fchown(tofd, uid, gid) < 0)
324                 fail("cannot change owner of %s", toname);
325
326             /* Must check for delayed (NFS) write errors on close. */
327             if (close(tofd) < 0)
328                 fail("cannot write to %s", toname);
329             close(fromfd);
330         }
331
332         free(toname);
333     }
334
335     free(cwd);
336     free(todir);
337     return 0;
338 }
339
340 /*
341 ** Pathname subroutines.
342 **
343 ** Brendan Eich, 8/29/95
344 */
345
346 char *program;
347
348 void
349 fail(char *format, ...)
350 {
351     int error;
352     va_list ap;
353
354 #ifdef USE_REENTRANT_LIBC
355     R_STRERROR_INIT_R();
356 #endif
357
358     error = errno;
359     fprintf(stderr, "%s: ", program);
360     va_start(ap, format);
361     vfprintf(stderr, format, ap);
362     va_end(ap);
363     if (error)
364
365 #ifdef USE_REENTRANT_LIBC
366     R_STRERROR_R(errno);
367         fprintf(stderr, ": %s", r_strerror_r);
368 #else
369         fprintf(stderr, ": %s", strerror(errno));
370 #endif
371
372     putc('\n', stderr);
373     exit(1);
374 }
375
376 char *
377 getcomponent(char *path, char *name)
378 {
379     if (*path == '\0')
380         return 0;
381     if (*path == '/') {
382         *name++ = '/';
383     } else {
384         do {
385             *name++ = *path++;
386         } while (*path != '/' && *path != '\0');
387     }
388     *name = '\0';
389     while (*path == '/')
390         path++;
391     return path;
392 }
393
394 #ifdef UNIXWARE_READDIR_BUFFER_TOO_SMALL
395 /* Sigh.  The static buffer in Unixware's readdir is too small. */
396 struct dirent * readdir(DIR *d)
397 {
398         static struct dirent *buf = NULL;
399 #define MAX_PATH_LEN 1024
400
401
402         if(buf == NULL)
403                 buf = (struct dirent *) malloc(sizeof(struct dirent) + MAX_PATH_LEN)
404 ;
405         return(readdir_r(d, buf));
406 }
407 #endif
408
409 char *
410 ino2name(ino_t ino, char *dir)
411 {
412     DIR *dp;
413     struct dirent *ep;
414     char *name;
415
416     dp = opendir("..");
417     if (!dp)
418         fail("cannot read parent directory");
419     for (;;) {
420         if (!(ep = readdir(dp)))
421             fail("cannot find current directory");
422         if (ep->d_ino == ino)
423             break;
424     }
425     name = xstrdup(ep->d_name);
426     closedir(dp);
427     return name;
428 }
429
430 void *
431 xmalloc(size_t size)
432 {
433     void *p = malloc(size);
434     if (!p)
435         fail("cannot allocate %u bytes", size);
436     return p;
437 }
438
439 char *
440 xstrdup(char *s)
441 {
442     return strcpy((char*)xmalloc(strlen(s) + 1), s);
443 }
444
445 char *
446 xbasename(char *path)
447 {
448     char *cp;
449
450     while ((cp = strrchr(path, '/')) && cp[1] == '\0')
451         *cp = '\0';
452     if (!cp) return path;
453     return cp + 1;
454 }
455
456 void
457 xchdir(char *dir)
458 {
459     if (chdir(dir) < 0)
460         fail("cannot change directory to %s", dir);
461 }
462
463 int
464 relatepaths(char *from, char *to, char *outpath)
465 {
466     char *cp, *cp2;
467     int len;
468     char buf[NAME_MAX];
469
470     assert(*from == '/' && *to == '/');
471     for (cp = to, cp2 = from; *cp == *cp2; cp++, cp2++)
472         if (*cp == '\0')
473             break;
474     while (cp[-1] != '/')
475         cp--, cp2--;
476     if (cp - 1 == to) {
477         /* closest common ancestor is /, so use full pathname */
478         len = strlen(strcpy(outpath, to));
479         if (outpath[len] != '/') {
480             outpath[len++] = '/';
481             outpath[len] = '\0';
482         }
483     } else {
484         len = 0;
485         while ((cp2 = getcomponent(cp2, buf)) != 0) {
486             strcpy(outpath + len, "../");
487             len += 3;
488         }
489         while ((cp = getcomponent(cp, buf)) != 0) {
490             sprintf(outpath + len, "%s/", buf);
491             len += strlen(outpath + len);
492         }
493     }
494     return len;
495 }
496
497 void
498 reversepath(char *inpath, char *name, int len, char *outpath)
499 {
500     char *cp, *cp2;
501     char buf[NAME_MAX];
502     struct stat sb;
503
504     cp = strcpy(outpath + PATH_MAX - (len + 1), name);
505     cp2 = inpath;
506     while ((cp2 = getcomponent(cp2, buf)) != 0) {
507         if (strcmp(buf, ".") == 0)
508             continue;
509         if (strcmp(buf, "..") == 0) {
510             if (stat(".", &sb) < 0)
511                 fail("cannot stat current directory");
512             name = ino2name(sb.st_ino, "..");
513             len = strlen(name);
514             cp -= len + 1;
515             strcpy(cp, name);
516             cp[len] = '/';
517             free(name);
518             xchdir("..");
519         } else {
520             cp -= 3;
521             strncpy(cp, "../", 3);
522             xchdir(buf);
523         }
524     }
525     strcpy(outpath, cp);
526 }