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/. */
7 ** Netscape portable install command.
9 ** Brendan Eich, 7/20/95
11 #include <stdio.h> /* OSF/1 requires this before grp.h, so put it first */
20 #include <sys/types.h>
25 #ifdef USE_REENTRANT_LIBC
27 #endif /* USE_REENTRANT_LIBC */
34 * Does getcwd() take NULL as the first argument and malloc
38 # define GETCWD_CAN_MALLOC
41 #if defined(LINUX) || defined(__GNU__) || defined(__GLIBC__)
46 # define d_ino d_stat.st_ino
49 static void usage(void) {
51 "usage: %s [-C cwd] [-L linkprefix] [-m mode] [-o owner] [-g group]\n"
52 " %*s [-DdltR] file [file ...] directory\n",
53 program
, (int)strlen(program
), "");
57 static int mkdirs(char* path
, mode_t mode
) {
62 while (*path
== '/' && path
[1] == '/') {
65 for (cp
= strrchr(path
, '/'); cp
&& cp
!= path
&& cp
[-1] == '/'; cp
--);
66 if (cp
&& cp
!= path
) {
68 if ((stat(path
, &sb
) < 0 || !S_ISDIR(sb
.st_mode
)) &&
69 mkdirs(path
, mode
) < 0) {
74 res
= mkdir(path
, mode
);
75 if ((res
!= 0) && (errno
== EEXIST
)) {
82 static uid_t
touid(char* owner
) {
91 uid
= strtol(owner
, &cp
, 0);
92 if (uid
== 0 && cp
== owner
) {
93 fail("cannot find uid for %s", owner
);
98 static gid_t
togid(char* group
) {
103 gr
= getgrnam(group
);
107 gid
= strtol(group
, &cp
, 0);
108 if (gid
== 0 && cp
== group
) {
109 fail("cannot find gid for %s", group
);
114 int main(int argc
, char** argv
) {
115 int onlydir
, dodir
, dolink
, dorelsymlink
, dotimes
, opt
, len
, lplen
, tdlen
,
116 bnlen
, exists
, fromfd
, tofd
, cc
, wc
;
118 char *linkprefix
, *owner
, *group
, *cp
, *cwd
, *todir
, *toname
, *name
, *base
,
119 *linkname
, *bp
, buf
[BUFSIZ
];
122 struct stat sb
, tosb
;
126 cwd
= linkname
= linkprefix
= owner
= group
= 0;
127 onlydir
= dodir
= dolink
= dorelsymlink
= dotimes
= lplen
= 0;
129 while ((opt
= getopt(argc
, argv
, "C:DdlL:Rm:o:g:t")) != EOF
) {
145 lplen
= strlen(linkprefix
);
149 dolink
= dorelsymlink
= 1;
152 mode
= strtoul(optarg
, &cp
, 8);
153 if (mode
== 0 && cp
== optarg
) {
173 if (argc
< 2 - onlydir
) {
177 todir
= argv
[argc
- 1];
178 if ((stat(todir
, &sb
) < 0 || !S_ISDIR(sb
.st_mode
)) &&
179 mkdirs(todir
, 0777) < 0) {
180 fail("cannot make directory %s", todir
);
187 #ifdef GETCWD_CAN_MALLOC
188 cwd
= getcwd(0, PATH_MAX
);
190 cwd
= malloc(PATH_MAX
+ 1);
191 cwd
= getcwd(cwd
, PATH_MAX
);
195 #ifdef GETCWD_CAN_MALLOC
196 todir
= getcwd(0, PATH_MAX
);
198 todir
= malloc(PATH_MAX
+ 1);
199 todir
= getcwd(todir
, PATH_MAX
);
202 tdlen
= strlen(todir
);
204 uid
= owner
? touid(owner
) : -1;
205 gid
= group
? togid(group
) : -1;
210 base
= xbasename(name
);
211 bnlen
= strlen(base
);
212 toname
= (char*)xmalloc(tdlen
+ 1 + bnlen
+ 1);
213 sprintf(toname
, "%s/%s", todir
, base
);
214 exists
= (lstat(toname
, &tosb
) == 0);
217 /* -d means create a directory, always */
218 if (exists
&& !S_ISDIR(tosb
.st_mode
)) {
219 (void)unlink(toname
);
222 if (!exists
&& mkdir(toname
, mode
) < 0) {
223 fail("cannot make directory %s", toname
);
225 if ((owner
|| group
) && chown(toname
, uid
, gid
) < 0) {
226 fail("cannot change owner of %s", toname
);
230 /* source is absolute pathname, link to it directly */
234 /* -L implies -l and prefixes names with a $cwd arg. */
236 linkname
= (char*)xmalloc(len
+ 1);
237 sprintf(linkname
, "%s/%s", linkprefix
, name
);
238 } else if (dorelsymlink
) {
239 /* Symlink the relative path from todir to source name. */
240 linkname
= (char*)xmalloc(PATH_MAX
);
243 /* todir is absolute: skip over common prefix. */
244 lplen
= relatepaths(todir
, cwd
, linkname
);
245 strcpy(linkname
+ lplen
, name
);
247 /* todir is named by a relative path: reverse it. */
248 reversepath(todir
, name
, len
, linkname
);
252 len
= strlen(linkname
);
257 /* Check for a pre-existing symlink with identical content. */
259 (!S_ISLNK(tosb
.st_mode
) || readlink(toname
, buf
, sizeof buf
) != len
||
260 strncmp(buf
, name
, len
) != 0)) {
261 (void)(S_ISDIR(tosb
.st_mode
) ? rmdir
: unlink
)(toname
);
264 if (!exists
&& symlink(name
, toname
) < 0) {
265 fail("cannot make symbolic link %s", toname
);
268 if ((owner
|| group
) && lchown(toname
, uid
, gid
) < 0) {
269 fail("cannot change owner of %s", toname
);
278 /* Copy from name to toname, which might be the same file. */
279 fromfd
= open(name
, O_RDONLY
);
280 if (fromfd
< 0 || fstat(fromfd
, &sb
) < 0) {
281 fail("cannot access %s", name
);
283 if (exists
&& (!S_ISREG(tosb
.st_mode
) || access(toname
, W_OK
) < 0)) {
284 (void)(S_ISDIR(tosb
.st_mode
) ? rmdir
: unlink
)(toname
);
286 tofd
= open(toname
, O_CREAT
| O_WRONLY
, 0666);
288 fail("cannot create %s", toname
);
292 while ((cc
= read(fromfd
, bp
, sizeof buf
)) > 0) {
293 while ((wc
= write(tofd
, bp
, cc
)) > 0) {
294 if ((cc
-= wc
) == 0) {
300 fail("cannot write to %s", toname
);
304 fail("cannot read from %s", name
);
307 if (ftruncate(tofd
, sb
.st_size
) < 0) {
308 fail("cannot truncate %s", toname
);
311 utb
.actime
= sb
.st_atime
;
312 utb
.modtime
= sb
.st_mtime
;
313 if (utime(toname
, &utb
) < 0) {
314 fail("cannot set times of %s", toname
);
318 if (fchmod(tofd
, mode
) < 0)
320 if (chmod(toname
, mode
) < 0)
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
);
327 /* Must check for delayed (NFS) write errors on close. */
328 if (close(tofd
) < 0) {
329 fail("cannot write to %s", toname
);
343 ** Pathname subroutines.
345 ** Brendan Eich, 8/29/95
350 void fail(char* format
, ...) {
354 #ifdef USE_REENTRANT_LIBC
359 fprintf(stderr
, "%s: ", program
);
360 va_start(ap
, format
);
361 vfprintf(stderr
, format
, ap
);
365 #ifdef USE_REENTRANT_LIBC
367 fprintf(stderr
, ": %s", r_strerror_r
);
369 fprintf(stderr
, ": %s", strerror(errno
));
376 char* getcomponent(char* path
, char* name
) {
385 } while (*path
!= '/' && *path
!= '\0');
388 while (*path
== '/') {
394 char* ino2name(ino_t ino
, char* dir
) {
401 fail("cannot read parent directory");
404 if (!(ep
= readdir(dp
))) {
405 fail("cannot find current directory");
407 if (ep
->d_ino
== ino
) {
411 name
= xstrdup(ep
->d_name
);
416 void* xmalloc(size_t size
) {
417 void* p
= malloc(size
);
419 fail("cannot allocate %u bytes", size
);
424 char* xstrdup(char* s
) { return strcpy((char*)xmalloc(strlen(s
) + 1), s
); }
426 char* xbasename(char* path
) {
429 while ((cp
= strrchr(path
, '/')) && cp
[1] == '\0') {
438 void xchdir(char* dir
) {
439 if (chdir(dir
) < 0) {
440 fail("cannot change directory to %s", dir
);
444 int relatepaths(char* from
, char* to
, char* outpath
) {
449 assert(*from
== '/' && *to
== '/');
450 for (cp
= to
, cp2
= from
; *cp
== *cp2
; cp
++, cp2
++)
454 while (cp
[-1] != '/') {
458 /* closest common ancestor is /, so use full pathname */
459 len
= strlen(strcpy(outpath
, to
));
460 if (outpath
[len
] != '/') {
461 outpath
[len
++] = '/';
466 while ((cp2
= getcomponent(cp2
, buf
)) != 0) {
467 strcpy(outpath
+ len
, "../");
470 while ((cp
= getcomponent(cp
, buf
)) != 0) {
471 sprintf(outpath
+ len
, "%s/", buf
);
472 len
+= strlen(outpath
+ len
);
478 void reversepath(char* inpath
, char* name
, int len
, char* outpath
) {
483 cp
= strcpy(outpath
+ PATH_MAX
- (len
+ 1), name
);
485 while ((cp2
= getcomponent(cp2
, buf
)) != 0) {
486 if (strcmp(buf
, ".") == 0) {
489 if (strcmp(buf
, "..") == 0) {
490 if (stat(".", &sb
) < 0) {
491 fail("cannot stat current directory");
493 name
= ino2name(sb
.st_ino
, "..");
502 memcpy(cp
, "../", 3);