1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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 mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
24 * Alternatively, the contents of this file may be used under the terms of
25 * either of the GNU General Public License Version 2 or later (the "GPL"),
26 * or 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 ***** */
38 ** Netscape portable install command.
40 ** Brendan Eich, 7/20/95
42 #include <stdio.h> /* OSF/1 requires this before grp.h, so put it first */
55 #include <sys/types.h>
76 #if !defined(S_ISLNK) && defined(S_IFLNK)
77 #define S_ISLNK(a) (((a) & S_IFMT) == S_IFLNK)
81 #ifndef _DIRECTORY_SEPARATOR
82 #define _DIRECTORY_SEPARATOR "/"
83 #endif /* _DIRECTORY_SEPARATOR */
85 #ifdef NEED_FCHMOD_PROTO
86 extern int fchmod(int fildes
, mode_t mode
);
93 "usage: %s [-C cwd] [-L linkprefix] [-m mode] [-o owner] [-g group]\n"
94 " %*s [-DdltR] file [file ...] directory\n",
95 program
, (int) strlen(program
), "");
100 mkdirs(char *path
, mode_t mode
)
107 /* strip trailing "/." */
109 if(l
> 1 && path
[l
- 1] == '.' && path
[l
- 2] == '/')
112 while (*path
== '/' && path
[1] == '/')
114 for (cp
= strrchr(path
, '/'); cp
&& cp
!= path
&& *(cp
- 1) == '/'; cp
--);
115 if (cp
&& cp
!= path
) {
117 if ((lstat(path
, &sb
) < 0 || !S_ISDIR(sb
.st_mode
)) &&
118 mkdirs(path
, mode
) < 0) {
124 res
= mkdir(path
, mode
);
125 if ((res
!= 0) && (errno
== EEXIST
))
138 pw
= getpwnam(owner
);
141 uid
= strtol(owner
, &cp
, 0);
142 if (uid
== 0 && cp
== owner
)
143 fail("cannot find uid for %s", owner
);
154 gr
= getgrnam(group
);
157 gid
= strtol(group
, &cp
, 0);
158 if (gid
== 0 && cp
== group
)
159 fail("cannot find gid for %s", group
);
164 copyfile( char *name
, char *toname
, mode_t mode
, char *group
, char *owner
,
165 int dotimes
, uid_t uid
, gid_t gid
)
167 int fromfd
, tofd
= -1, cc
, wc
, exists
;
168 char buf
[BUFSIZ
], *bp
;
169 struct stat sb
, tosb
;
172 exists
= (lstat(toname
, &tosb
) == 0);
174 fromfd
= open(name
, O_RDONLY
);
175 if (fromfd
< 0 || fstat(fromfd
, &sb
) < 0)
176 fail("cannot access %s", name
);
178 if (S_ISREG(tosb
.st_mode
)) {
179 /* See if we can open it. This is more reliable than 'access'. */
180 tofd
= open(toname
, O_CREAT
| O_WRONLY
, 0666);
183 (void) (S_ISDIR(tosb
.st_mode
) ? rmdir
: unlink
)(toname
);
187 tofd
= open(toname
, O_CREAT
| O_WRONLY
, 0666);
189 fail("cannot create %s", toname
);
193 while ((cc
= read(fromfd
, bp
, sizeof buf
)) > 0)
195 while ((wc
= write(tofd
, bp
, (unsigned int)cc
)) > 0)
202 fail("cannot write to %s", toname
);
205 fail("cannot read from %s", name
);
207 if (ftruncate(tofd
, sb
.st_size
) < 0)
208 fail("cannot truncate %s", toname
);
212 utb
.actime
= sb
.st_atime
;
213 utb
.modtime
= sb
.st_mtime
;
214 if (utime(toname
, &utb
) < 0)
215 fail("cannot set times of %s", toname
);
218 if (fchmod(tofd
, mode
) < 0)
220 if (chmod(toname
, mode
) < 0)
222 fail("cannot change mode of %s", toname
);
224 if ((owner
|| group
) && fchown(tofd
, uid
, gid
) < 0)
225 fail("cannot change owner of %s", toname
);
227 /* Must check for delayed (NFS) write errors on close. */
229 fail("cannot write to %s", toname
);
232 if (chmod(toname
, (mode
& (S_IREAD
| S_IWRITE
))) < 0)
233 fail("cannot change mode of %s", toname
);
236 utb
.actime
= sb
.st_atime
;
237 utb
.modtime
= sb
.st_mtime
;
238 if (utime(toname
, &utb
) < 0)
239 fail("cannot set times of %s", toname
);
245 copydir( char *from
, char *to
, mode_t mode
, char *group
, char *owner
,
246 int dotimes
, uid_t uid
, gid_t gid
)
252 char *base
, *destdir
, *direntry
, *destentry
;
254 base
= xbasename(from
);
256 /* create destination directory */
257 destdir
= xmalloc((unsigned int)(strlen(to
) + 1 + strlen(base
) + 1));
258 sprintf(destdir
, "%s%s%s", to
, _DIRECTORY_SEPARATOR
, base
);
259 if (mkdirs(destdir
, mode
) != 0) {
260 fail("cannot make directory %s\n", destdir
);
265 if (!(dir
= opendir(from
))) {
266 fail("cannot open directory %s\n", from
);
271 direntry
= xmalloc((unsigned int)PATH_MAX
);
272 destentry
= xmalloc((unsigned int)PATH_MAX
);
274 while ((ep
= readdir(dir
)))
276 if (strcmp(ep
->d_name
, ".") == 0 || strcmp(ep
->d_name
, "..") == 0)
279 sprintf(direntry
, "%s/%s", from
, ep
->d_name
);
280 sprintf(destentry
, "%s%s%s", destdir
, _DIRECTORY_SEPARATOR
, ep
->d_name
);
282 if (stat(direntry
, &sb
) == 0 && S_ISDIR(sb
.st_mode
))
283 copydir( direntry
, destdir
, mode
, group
, owner
, dotimes
, uid
, gid
);
285 copyfile( direntry
, destentry
, mode
, group
, owner
, dotimes
, uid
, gid
);
295 main(int argc
, char **argv
)
297 int onlydir
, dodir
, dolink
, dorelsymlink
, dotimes
, opt
, len
, lplen
, tdlen
, bnlen
, exists
, fromfd
, tofd
, cc
, wc
;
299 char *linkprefix
, *owner
, *group
, *cp
, *cwd
, *todir
, *toname
, *name
, *base
, *linkname
, *bp
, buf
[BUFSIZ
];
302 struct stat sb
, tosb
, fromsb
;
306 cwd
= linkname
= linkprefix
= owner
= group
= 0;
307 onlydir
= dodir
= dolink
= dorelsymlink
= dotimes
= lplen
= 0;
309 while ((opt
= getopt(argc
, argv
, "C:DdlL:Rm:o:g:t")) != EOF
) {
325 lplen
= strlen(linkprefix
);
329 dolink
= dorelsymlink
= 1;
332 mode
= strtoul(optarg
, &cp
, 8);
333 if (mode
== 0 && cp
== optarg
)
352 if (argc
< 2 - onlydir
)
355 todir
= argv
[argc
-1];
356 if ((stat(todir
, &sb
) < 0 || !S_ISDIR(sb
.st_mode
)) &&
357 mkdirs(todir
, 0777) < 0) {
358 fail("cannot make directory %s", todir
);
365 #ifndef GETCWD_CANT_MALLOC
366 cwd
= getcwd(0, PATH_MAX
);
368 cwd
= malloc(PATH_MAX
+ 1);
369 cwd
= getcwd(cwd
, PATH_MAX
);
372 cwd
= malloc(PATH_MAX
+ 1);
379 #ifndef GETCWD_CANT_MALLOC
380 todir
= getcwd(0, PATH_MAX
);
382 todir
= malloc(PATH_MAX
+ 1);
383 todir
= getcwd(todir
, PATH_MAX
);
386 todir
= malloc(PATH_MAX
+ 1);
387 todir
= getwd(todir
);
389 tdlen
= strlen(todir
);
391 tdlen
= strlen(todir
);
393 uid
= owner
? touid(owner
) : (uid_t
)(-1);
394 gid
= group
? togid(group
) : (gid_t
)(-1);
399 base
= xbasename(name
);
400 bnlen
= strlen(base
);
401 toname
= xmalloc((unsigned int)(tdlen
+ 1 + bnlen
+ 1));
402 sprintf(toname
, "%s%s%s", todir
, _DIRECTORY_SEPARATOR
, base
);
403 exists
= (lstat(toname
, &tosb
) == 0);
406 /* -d means create a directory, always */
407 if (exists
&& !S_ISDIR(tosb
.st_mode
)) {
408 (void) unlink(toname
);
411 if (!exists
&& mkdir(toname
, mode
) < 0)
412 fail("cannot make directory %s", toname
);
413 if ((owner
|| group
) && chown(toname
, uid
, gid
) < 0)
414 fail("cannot change owner of %s", toname
);
416 if (access(name
, R_OK
) != 0) {
417 fail("cannot access %s", name
);
420 /* source is absolute pathname, link to it directly */
424 /* -L implies -l and prefixes names with a $cwd arg. */
426 linkname
= xmalloc((unsigned int)(len
+ 1));
427 sprintf(linkname
, "%s/%s", linkprefix
, name
);
428 } else if (dorelsymlink
) {
429 /* Symlink the relative path from todir to source name. */
430 linkname
= xmalloc(PATH_MAX
);
433 /* todir is absolute: skip over common prefix. */
434 lplen
= relatepaths(todir
, cwd
, linkname
);
435 strcpy(linkname
+ lplen
, name
);
437 /* todir is named by a relative path: reverse it. */
438 reversepath(todir
, name
, len
, linkname
);
442 len
= strlen(linkname
);
447 /* Check for a pre-existing symlink with identical content. */
448 if ((exists
&& (!S_ISLNK(tosb
.st_mode
) ||
449 readlink(toname
, buf
, sizeof buf
) != len
||
450 strncmp(buf
, name
, (unsigned int)len
) != 0)) ||
451 ((stat(name
, &fromsb
) == 0) &&
452 (fromsb
.st_mtime
> tosb
.st_mtime
))) {
453 (void) (S_ISDIR(tosb
.st_mode
) ? rmdir
: unlink
)(toname
);
456 if (!exists
&& symlink(name
, toname
) < 0)
457 fail("cannot make symbolic link %s", toname
);
459 if ((owner
|| group
) && lchown(toname
, uid
, gid
) < 0)
460 fail("cannot change owner of %s", toname
);
468 /* Copy from name to toname, which might be the same file. */
469 if( stat(name
, &sb
) == 0 && S_IFDIR
& sb
.st_mode
)
471 /* then is directory: must explicitly create destination dir */
472 /* and manually copy files over */
473 copydir( name
, todir
, mode
, group
, owner
, dotimes
, uid
, gid
);
477 copyfile(name
, toname
, mode
, group
, owner
, dotimes
, uid
, gid
);