1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is the Netscape Portable Runtime (NSPR).
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998-2000
20 * the Initial Developer. All Rights Reserved.
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
39 ** Netscape portable install command.
41 ** Brendan Eich, 7/20/95
43 #include <stdio.h> /* OSF/1 requires this before grp.h, so put it first */
52 #include <sys/types.h>
57 #ifdef USE_REENTRANT_LIBC
59 #endif /* USE_REENTRANT_LIBC */
70 * Does getcwd() take NULL as the first argument and malloc
73 #if !defined(DARWIN) && !defined(NEXTSTEP) && !defined(VMS)
74 #define GETCWD_CAN_MALLOC
81 ** balazs.pataki@sztaki.hu: The getcwd is broken in NEXTSTEP (returns 0),
82 ** when called on a mounted fs. Did anyone notice this? Here's an ugly
85 #define getcwd(b,s) my_getcwd(b,s)
88 my_getcwd (char *buf
, size_t size
)
90 FILE *pwd
= popen("pwd", "r");
91 char *result
= fgets(buf
, size
, pwd
);
94 buf
[strlen(buf
)-1] = '\0';
101 #if defined(LINUX) || defined(__GNU__) || defined(__GLIBC__)
105 #if defined(SCO) || defined(UNIXWARE) || defined(SNI) || defined(NCR) || defined(NEC) || defined(NEXTSTEP)
106 #if !defined(S_ISLNK) && defined(S_IFLNK)
107 #define S_ISLNK(a) (((a) & S_IFMT) == S_IFLNK)
112 extern int fchmod(int fildes
, mode_t mode
);
116 #define d_ino d_stat.st_ino
123 "usage: %s [-C cwd] [-L linkprefix] [-m mode] [-o owner] [-g group]\n"
124 " %*s [-DdltR] file [file ...] directory\n",
125 program
, (int)strlen(program
), "");
130 mkdirs(char *path
, mode_t mode
)
136 while (*path
== '/' && path
[1] == '/')
138 for (cp
= strrchr(path
, '/'); cp
&& cp
!= path
&& cp
[-1] == '/'; cp
--)
140 if (cp
&& cp
!= path
) {
142 if ((stat(path
, &sb
) < 0 || !S_ISDIR(sb
.st_mode
)) &&
143 mkdirs(path
, mode
) < 0) {
148 res
= mkdir(path
, mode
);
149 if ((res
!= 0) && (errno
== EEXIST
))
162 pw
= getpwnam(owner
);
165 uid
= strtol(owner
, &cp
, 0);
166 if (uid
== 0 && cp
== owner
)
167 fail("cannot find uid for %s", owner
);
178 gr
= getgrnam(group
);
181 gid
= strtol(group
, &cp
, 0);
182 if (gid
== 0 && cp
== group
)
183 fail("cannot find gid for %s", group
);
188 main(int argc
, char **argv
)
190 int onlydir
, dodir
, dolink
, dorelsymlink
, dotimes
, opt
, len
, lplen
, tdlen
, bnlen
, exists
, fromfd
, tofd
, cc
, wc
;
192 char *linkprefix
, *owner
, *group
, *cp
, *cwd
, *todir
, *toname
, *name
, *base
, *linkname
, *bp
, buf
[BUFSIZ
];
195 struct stat sb
, tosb
;
199 cwd
= linkname
= linkprefix
= owner
= group
= 0;
200 onlydir
= dodir
= dolink
= dorelsymlink
= dotimes
= lplen
= 0;
202 while ((opt
= getopt(argc
, argv
, "C:DdlL:Rm:o:g:t")) != EOF
) {
218 lplen
= strlen(linkprefix
);
222 dolink
= dorelsymlink
= 1;
225 mode
= strtoul(optarg
, &cp
, 8);
226 if (mode
== 0 && cp
== optarg
)
245 if (argc
< 2 - onlydir
)
248 todir
= argv
[argc
-1];
249 if ((stat(todir
, &sb
) < 0 || !S_ISDIR(sb
.st_mode
)) &&
250 mkdirs(todir
, 0777) < 0) {
251 fail("cannot make directory %s", todir
);
257 #ifdef GETCWD_CAN_MALLOC
258 cwd
= getcwd(0, PATH_MAX
);
260 cwd
= malloc(PATH_MAX
+ 1);
261 cwd
= getcwd(cwd
, PATH_MAX
);
265 #ifdef GETCWD_CAN_MALLOC
266 todir
= getcwd(0, PATH_MAX
);
268 todir
= malloc(PATH_MAX
+ 1);
269 todir
= getcwd(todir
, PATH_MAX
);
271 tdlen
= strlen(todir
);
273 tdlen
= strlen(todir
);
275 uid
= owner
? touid(owner
) : -1;
276 gid
= group
? togid(group
) : -1;
281 base
= xbasename(name
);
282 bnlen
= strlen(base
);
283 toname
= (char*)xmalloc(tdlen
+ 1 + bnlen
+ 1);
284 sprintf(toname
, "%s/%s", todir
, base
);
285 exists
= (lstat(toname
, &tosb
) == 0);
288 /* -d means create a directory, always */
289 if (exists
&& !S_ISDIR(tosb
.st_mode
)) {
290 (void) unlink(toname
);
293 if (!exists
&& mkdir(toname
, mode
) < 0)
294 fail("cannot make directory %s", toname
);
295 if ((owner
|| group
) && chown(toname
, uid
, gid
) < 0)
296 fail("cannot change owner of %s", toname
);
299 /* source is absolute pathname, link to it directly */
303 /* -L implies -l and prefixes names with a $cwd arg. */
305 linkname
= (char*)xmalloc(len
+ 1);
306 sprintf(linkname
, "%s/%s", linkprefix
, name
);
307 } else if (dorelsymlink
) {
308 /* Symlink the relative path from todir to source name. */
309 linkname
= (char*)xmalloc(PATH_MAX
);
312 /* todir is absolute: skip over common prefix. */
313 lplen
= relatepaths(todir
, cwd
, linkname
);
314 strcpy(linkname
+ lplen
, name
);
316 /* todir is named by a relative path: reverse it. */
317 reversepath(todir
, name
, len
, linkname
);
321 len
= strlen(linkname
);
326 /* Check for a pre-existing symlink with identical content. */
328 (!S_ISLNK(tosb
.st_mode
) ||
329 readlink(toname
, buf
, sizeof buf
) != len
||
330 strncmp(buf
, name
, len
) != 0)) {
331 (void) (S_ISDIR(tosb
.st_mode
) ? rmdir
: unlink
)(toname
);
334 if (!exists
&& symlink(name
, toname
) < 0)
335 fail("cannot make symbolic link %s", toname
);
337 if ((owner
|| group
) && lchown(toname
, uid
, gid
) < 0)
338 fail("cannot change owner of %s", toname
);
346 /* Copy from name to toname, which might be the same file. */
347 fromfd
= open(name
, O_RDONLY
);
348 if (fromfd
< 0 || fstat(fromfd
, &sb
) < 0)
349 fail("cannot access %s", name
);
350 if (exists
&& (!S_ISREG(tosb
.st_mode
) || access(toname
, W_OK
) < 0))
351 (void) (S_ISDIR(tosb
.st_mode
) ? rmdir
: unlink
)(toname
);
352 tofd
= open(toname
, O_CREAT
| O_WRONLY
, 0666);
354 fail("cannot create %s", toname
);
357 while ((cc
= read(fromfd
, bp
, sizeof buf
)) > 0) {
358 while ((wc
= write(tofd
, bp
, cc
)) > 0) {
364 fail("cannot write to %s", toname
);
367 fail("cannot read from %s", name
);
369 if (ftruncate(tofd
, sb
.st_size
) < 0)
370 fail("cannot truncate %s", toname
);
372 ** On OpenVMS we can't chmod() until the file is closed, and we
373 ** have to utime() last since fchown/chmod alter the timestamps.
377 utb
.actime
= sb
.st_atime
;
378 utb
.modtime
= sb
.st_mtime
;
379 if (utime(toname
, &utb
) < 0)
380 fail("cannot set times of %s", toname
);
383 if (fchmod(tofd
, mode
) < 0)
385 if (chmod(toname
, mode
) < 0)
387 fail("cannot change mode of %s", toname
);
389 if ((owner
|| group
) && fchown(tofd
, uid
, gid
) < 0)
390 fail("cannot change owner of %s", toname
);
392 /* Must check for delayed (NFS) write errors on close. */
394 fail("cannot write to %s", toname
);
397 if (chmod(toname
, mode
) < 0)
398 fail("cannot change mode of %s", toname
);
400 utb
.actime
= sb
.st_atime
;
401 utb
.modtime
= sb
.st_mtime
;
402 if (utime(toname
, &utb
) < 0)
403 fail("cannot set times of %s", toname
);
417 ** Pathname subroutines.
419 ** Brendan Eich, 8/29/95
425 fail(char *format
, ...)
430 #ifdef USE_REENTRANT_LIBC
435 fprintf(stderr
, "%s: ", program
);
436 va_start(ap
, format
);
437 vfprintf(stderr
, format
, ap
);
441 #ifdef USE_REENTRANT_LIBC
443 fprintf(stderr
, ": %s", r_strerror_r
);
445 fprintf(stderr
, ": %s", strerror(errno
));
453 getcomponent(char *path
, char *name
)
462 } while (*path
!= '/' && *path
!= '\0');
470 #ifdef UNIXWARE_READDIR_BUFFER_TOO_SMALL
471 /* Sigh. The static buffer in Unixware's readdir is too small. */
472 struct dirent
* readdir(DIR *d
)
474 static struct dirent
*buf
= NULL
;
475 #define MAX_PATH_LEN 1024
479 buf
= (struct dirent
*) malloc(sizeof(struct dirent
) + MAX_PATH_LEN
)
481 return(readdir_r(d
, buf
));
486 ino2name(ino_t ino
, char *dir
)
494 fail("cannot read parent directory");
496 if (!(ep
= readdir(dp
)))
497 fail("cannot find current directory");
498 if (ep
->d_ino
== ino
)
501 name
= xstrdup(ep
->d_name
);
509 void *p
= malloc(size
);
511 fail("cannot allocate %u bytes", size
);
518 return strcpy((char*)xmalloc(strlen(s
) + 1), s
);
522 xbasename(char *path
)
526 while ((cp
= strrchr(path
, '/')) && cp
[1] == '\0')
528 if (!cp
) return path
;
536 fail("cannot change directory to %s", dir
);
540 relatepaths(char *from
, char *to
, char *outpath
)
546 assert(*from
== '/' && *to
== '/');
547 for (cp
= to
, cp2
= from
; *cp
== *cp2
; cp
++, cp2
++)
550 while (cp
[-1] != '/')
553 /* closest common ancestor is /, so use full pathname */
554 len
= strlen(strcpy(outpath
, to
));
555 if (outpath
[len
] != '/') {
556 outpath
[len
++] = '/';
561 while ((cp2
= getcomponent(cp2
, buf
)) != 0) {
562 strcpy(outpath
+ len
, "../");
565 while ((cp
= getcomponent(cp
, buf
)) != 0) {
566 sprintf(outpath
+ len
, "%s/", buf
);
567 len
+= strlen(outpath
+ len
);
574 reversepath(char *inpath
, char *name
, int len
, char *outpath
)
580 cp
= strcpy(outpath
+ PATH_MAX
- (len
+ 1), name
);
582 while ((cp2
= getcomponent(cp2
, buf
)) != 0) {
583 if (strcmp(buf
, ".") == 0)
585 if (strcmp(buf
, "..") == 0) {
586 if (stat(".", &sb
) < 0)
587 fail("cannot stat current directory");
588 name
= ino2name(sb
.st_ino
, "..");
597 strncpy(cp
, "../", 3);