Bug 1737692 [wpt PR 31378] - Update wpt metadata, a=testonly
[gecko.git] / config / nsinstall.c
blobca70dc40f8348b9cb0eadef9a463792f3d91422d
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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 ** Netscape portable install command.
7 **
8 ** Brendan Eich, 7/20/95
9 */
10 #include <stdio.h> /* OSF/1 requires this before grp.h, so put it first */
11 #include <assert.h>
12 #include <fcntl.h>
13 #include <errno.h>
14 #include <dirent.h>
15 #include <limits.h>
16 #include <grp.h>
17 #include <pwd.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <unistd.h>
22 #include <utime.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include "pathsub.h"
27 #ifdef HAVE_GETOPT_H
28 # include <getopt.h>
29 #endif
31 #ifdef SUNOS4
32 # include "sunos4.h"
33 #endif
35 #ifdef NEXTSTEP
36 # include <bsd/libc.h>
37 #endif
39 #ifdef __QNX__
40 # include <unix.h>
41 #endif
43 #ifdef NEED_S_ISLNK
44 # if !defined(S_ISLNK) && defined(S_IFLNK)
45 # define S_ISLNK(a) (((a)&S_IFMT) == S_IFLNK)
46 # endif
47 #endif
49 #ifndef _DIRECTORY_SEPARATOR
50 # define _DIRECTORY_SEPARATOR "/"
51 #endif /* _DIRECTORY_SEPARATOR */
53 #ifdef NEED_FCHMOD_PROTO
54 extern int fchmod(int fildes, mode_t mode);
55 #endif
57 static void usage(void) {
58 fprintf(stderr,
59 "usage: %s [-C cwd] [-L linkprefix] [-m mode] [-o owner] [-g group]\n"
60 " %*s [-DdltR] file [file ...] directory\n",
61 program, (int)strlen(program), "");
62 exit(2);
65 static int mkdirs(char* path, mode_t mode) {
66 char* cp;
67 struct stat sb;
68 int res;
69 int l;
71 /* strip trailing "/." */
72 l = strlen(path);
73 if (l > 1 && path[l - 1] == '.' && path[l - 2] == '/') path[l - 2] = 0;
75 while (*path == '/' && path[1] == '/') path++;
76 for (cp = strrchr(path, '/'); cp && cp != path && *(cp - 1) == '/'; cp--)
78 if (cp && cp != path) {
79 *cp = '\0';
80 if ((lstat(path, &sb) < 0 || !S_ISDIR(sb.st_mode)) &&
81 mkdirs(path, mode) < 0) {
82 return -1;
84 *cp = '/';
87 res = mkdir(path, mode);
88 if ((res != 0) && (errno == EEXIST))
89 return 0;
90 else
91 return res;
94 static uid_t touid(char* owner) {
95 struct passwd* pw;
96 uid_t uid;
97 char* cp;
99 pw = getpwnam(owner);
100 if (pw) return pw->pw_uid;
101 uid = strtol(owner, &cp, 0);
102 if (uid == 0 && cp == owner) fail("cannot find uid for %s", owner);
103 return uid;
106 static gid_t togid(char* group) {
107 struct group* gr;
108 gid_t gid;
109 char* cp;
111 gr = getgrnam(group);
112 if (gr) return gr->gr_gid;
113 gid = strtol(group, &cp, 0);
114 if (gid == 0 && cp == group) fail("cannot find gid for %s", group);
115 return gid;
118 static void copyfile(char* name, char* toname, mode_t mode, char* group,
119 char* owner, int dotimes, uid_t uid, gid_t gid) {
120 int fromfd, tofd = -1, cc, wc, exists;
121 char buf[BUFSIZ], *bp;
122 struct stat sb, tosb;
123 struct utimbuf utb;
125 exists = (lstat(toname, &tosb) == 0);
127 fromfd = open(name, O_RDONLY);
128 if (fromfd < 0 || fstat(fromfd, &sb) < 0) fail("cannot access %s", name);
129 if (exists) {
130 if (S_ISREG(tosb.st_mode)) {
131 /* See if we can open it. This is more reliable than 'access'. */
132 tofd = open(toname, O_CREAT | O_WRONLY, 0666);
134 if (tofd < 0) {
135 (void)(S_ISDIR(tosb.st_mode) ? rmdir : unlink)(toname);
138 if (tofd < 0) {
139 tofd = open(toname, O_CREAT | O_WRONLY, 0666);
140 if (tofd < 0) fail("cannot create %s", toname);
143 bp = buf;
144 while ((cc = read(fromfd, bp, sizeof buf)) > 0) {
145 while ((wc = write(tofd, bp, (unsigned int)cc)) > 0) {
146 if ((cc -= wc) == 0) break;
147 bp += wc;
149 if (wc < 0) fail("cannot write to %s", toname);
151 if (cc < 0) fail("cannot read from %s", name);
153 if (ftruncate(tofd, sb.st_size) < 0) fail("cannot truncate %s", toname);
154 #if !defined(VMS)
155 if (dotimes) {
156 utb.actime = sb.st_atime;
157 utb.modtime = sb.st_mtime;
158 if (utime(toname, &utb) < 0) fail("cannot set times of %s", toname);
160 # ifdef HAVE_FCHMOD
161 if (fchmod(tofd, mode) < 0)
162 # else
163 if (chmod(toname, mode) < 0)
164 # endif
165 fail("cannot change mode of %s", toname);
166 #endif
167 if ((owner || group) && fchown(tofd, uid, gid) < 0)
168 fail("cannot change owner of %s", toname);
170 /* Must check for delayed (NFS) write errors on close. */
171 if (close(tofd) < 0) fail("cannot write to %s", toname);
172 close(fromfd);
173 #if defined(VMS)
174 if (chmod(toname, (mode & (S_IREAD | S_IWRITE))) < 0)
175 fail("cannot change mode of %s", toname);
176 if (dotimes) {
177 utb.actime = sb.st_atime;
178 utb.modtime = sb.st_mtime;
179 if (utime(toname, &utb) < 0) fail("cannot set times of %s", toname);
181 #endif
184 static void copydir(char* from, char* to, mode_t mode, char* group, char* owner,
185 int dotimes, uid_t uid, gid_t gid) {
186 DIR* dir;
187 struct dirent* ep;
188 struct stat sb;
189 char *base, *destdir, *direntry, *destentry;
191 base = xbasename(from);
193 /* create destination directory */
194 destdir = xmalloc((unsigned int)(strlen(to) + 1 + strlen(base) + 1));
195 sprintf(destdir, "%s%s%s", to, _DIRECTORY_SEPARATOR, base);
196 if (mkdirs(destdir, mode) != 0) {
197 fail("cannot make directory %s\n", destdir);
198 free(destdir);
199 return;
202 if (!(dir = opendir(from))) {
203 fail("cannot open directory %s\n", from);
204 free(destdir);
205 return;
208 direntry = xmalloc((unsigned int)PATH_MAX);
209 destentry = xmalloc((unsigned int)PATH_MAX);
211 while ((ep = readdir(dir))) {
212 if (strcmp(ep->d_name, ".") == 0 || strcmp(ep->d_name, "..") == 0) continue;
214 sprintf(direntry, "%s/%s", from, ep->d_name);
215 sprintf(destentry, "%s%s%s", destdir, _DIRECTORY_SEPARATOR, ep->d_name);
217 if (stat(direntry, &sb) == 0 && S_ISDIR(sb.st_mode))
218 copydir(direntry, destdir, mode, group, owner, dotimes, uid, gid);
219 else
220 copyfile(direntry, destentry, mode, group, owner, dotimes, uid, gid);
223 free(destdir);
224 free(direntry);
225 free(destentry);
226 closedir(dir);
229 int main(int argc, char** argv) {
230 int onlydir, dodir, dolink, dorelsymlink, dotimes, opt, len, lplen, tdlen,
231 bnlen, exists;
232 mode_t mode = 0755;
233 char *linkprefix, *owner, *group, *cp, *cwd, *todir, *toname, *name, *base,
234 *linkname, buf[BUFSIZ];
235 uid_t uid;
236 gid_t gid;
237 struct stat sb, tosb, fromsb;
239 program = argv[0];
240 cwd = linkname = linkprefix = owner = group = 0;
241 onlydir = dodir = dolink = dorelsymlink = dotimes = lplen = 0;
243 while ((opt = getopt(argc, argv, "C:DdlL:Rm:o:g:t")) != EOF) {
244 switch (opt) {
245 case 'C':
246 cwd = optarg;
247 break;
248 case 'D':
249 onlydir = 1;
250 break;
251 case 'd':
252 dodir = 1;
253 break;
254 case 'L':
255 linkprefix = optarg;
256 lplen = strlen(linkprefix);
257 dolink = 1;
258 break;
259 case 'R':
260 dolink = dorelsymlink = 1;
261 break;
262 case 'm':
263 mode = strtoul(optarg, &cp, 8);
264 if (mode == 0 && cp == optarg) usage();
265 break;
266 case 'o':
267 owner = optarg;
268 break;
269 case 'g':
270 group = optarg;
271 break;
272 case 't':
273 dotimes = 1;
274 break;
275 default:
276 usage();
280 argc -= optind;
281 argv += optind;
282 if (argc < 2 - onlydir) usage();
284 todir = argv[argc - 1];
285 if ((stat(todir, &sb) < 0 || !S_ISDIR(sb.st_mode)) &&
286 mkdirs(todir, 0777) < 0) {
287 fail("cannot make directory %s", todir);
289 if (onlydir) return 0;
291 if (!cwd) {
292 #ifndef NEEDS_GETCWD
293 # ifndef GETCWD_CANT_MALLOC
294 cwd = getcwd(0, PATH_MAX);
295 # else
296 cwd = malloc(PATH_MAX + 1);
297 cwd = getcwd(cwd, PATH_MAX);
298 # endif
299 #else
300 cwd = malloc(PATH_MAX + 1);
301 cwd = getwd(cwd);
302 #endif
305 xchdir(todir);
306 #ifndef NEEDS_GETCWD
307 # ifndef GETCWD_CANT_MALLOC
308 todir = getcwd(0, PATH_MAX);
309 # else
310 todir = malloc(PATH_MAX + 1);
311 todir = getcwd(todir, PATH_MAX);
312 # endif
313 #else
314 todir = malloc(PATH_MAX + 1);
315 todir = getwd(todir);
316 #endif
317 tdlen = strlen(todir);
318 xchdir(cwd);
319 tdlen = strlen(todir);
321 uid = owner ? touid(owner) : (uid_t)(-1);
322 gid = group ? togid(group) : (gid_t)(-1);
324 while (--argc > 0) {
325 name = *argv++;
326 len = strlen(name);
327 base = xbasename(name);
328 bnlen = strlen(base);
329 toname = xmalloc((unsigned int)(tdlen + 1 + bnlen + 1));
330 sprintf(toname, "%s%s%s", todir, _DIRECTORY_SEPARATOR, base);
331 exists = (lstat(toname, &tosb) == 0);
333 if (dodir) {
334 /* -d means create a directory, always */
335 if (exists && !S_ISDIR(tosb.st_mode)) {
336 (void)unlink(toname);
337 exists = 0;
339 if (!exists && mkdir(toname, mode) < 0)
340 fail("cannot make directory %s", toname);
341 if ((owner || group) && chown(toname, uid, gid) < 0)
342 fail("cannot change owner of %s", toname);
343 } else if (dolink) {
344 if (access(name, R_OK) != 0) {
345 fail("cannot access %s", name);
347 if (*name == '/') {
348 /* source is absolute pathname, link to it directly */
349 linkname = 0;
350 } else {
351 if (linkprefix) {
352 /* -L prefixes names with a $cwd arg. */
353 len += lplen + 1;
354 linkname = xmalloc((unsigned int)(len + 1));
355 sprintf(linkname, "%s/%s", linkprefix, name);
356 } else if (dorelsymlink) {
357 /* Symlink the relative path from todir to source name. */
358 linkname = xmalloc(PATH_MAX);
360 if (*todir == '/') {
361 /* todir is absolute: skip over common prefix. */
362 lplen = relatepaths(todir, cwd, linkname);
363 strcpy(linkname + lplen, name);
364 } else {
365 /* todir is named by a relative path: reverse it. */
366 reversepath(todir, name, len, linkname);
367 xchdir(cwd);
370 len = strlen(linkname);
372 name = linkname;
375 /* Check for a pre-existing symlink with identical content. */
376 if (exists &&
377 (!S_ISLNK(tosb.st_mode) || readlink(toname, buf, sizeof buf) != len ||
378 strncmp(buf, name, (unsigned int)len) != 0 ||
379 ((stat(name, &fromsb) == 0) && (fromsb.st_mtime > tosb.st_mtime)))) {
380 (void)(S_ISDIR(tosb.st_mode) ? rmdir : unlink)(toname);
381 exists = 0;
383 if (!exists && symlink(name, toname) < 0)
384 fail("cannot make symbolic link %s", toname);
385 #ifdef HAVE_LCHOWN
386 if ((owner || group) && lchown(toname, uid, gid) < 0)
387 fail("cannot change owner of %s", toname);
388 #endif
390 if (linkname) {
391 free(linkname);
392 linkname = 0;
394 } else {
395 /* Copy from name to toname, which might be the same file. */
396 if (stat(name, &sb) == 0 && S_IFDIR & sb.st_mode) {
397 /* then is directory: must explicitly create destination dir */
398 /* and manually copy files over */
399 copydir(name, todir, mode, group, owner, dotimes, uid, gid);
400 } else {
401 copyfile(name, toname, mode, group, owner, dotimes, uid, gid);
405 free(toname);
408 free(cwd);
409 free(todir);
410 return 0;