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/. */
6 ** Netscape portable install command.
8 ** Brendan Eich, 7/20/95
10 #include <stdio.h> /* OSF/1 requires this before grp.h, so put it first */
23 #include <sys/types.h>
36 # include <bsd/libc.h>
44 # if !defined(S_ISLNK) && defined(S_IFLNK)
45 # define S_ISLNK(a) (((a) & S_IFMT) == S_IFLNK)
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
);
57 static void usage(void) {
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
), "");
65 static int mkdirs(char* path
, mode_t mode
) {
71 /* strip trailing "/." */
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
--);
77 if (cp
&& cp
!= path
) {
79 if ((lstat(path
, &sb
) < 0 || !S_ISDIR(sb
.st_mode
)) &&
80 mkdirs(path
, mode
) < 0) {
86 res
= mkdir(path
, mode
);
87 if ((res
!= 0) && (errno
== EEXIST
))
93 static uid_t
touid(char* owner
) {
99 if (pw
) return pw
->pw_uid
;
100 uid
= strtol(owner
, &cp
, 0);
101 if (uid
== 0 && cp
== owner
) fail("cannot find uid for %s", owner
);
105 static gid_t
togid(char* group
) {
110 gr
= getgrnam(group
);
111 if (gr
) return gr
->gr_gid
;
112 gid
= strtol(group
, &cp
, 0);
113 if (gid
== 0 && cp
== group
) fail("cannot find gid for %s", group
);
117 static void copyfile(char* name
, char* toname
, mode_t mode
, char* group
,
118 char* owner
, int dotimes
, uid_t uid
, gid_t gid
) {
119 int fromfd
, tofd
= -1, cc
, wc
, exists
;
120 char buf
[BUFSIZ
], *bp
;
121 struct stat sb
, tosb
;
124 exists
= (lstat(toname
, &tosb
) == 0);
126 fromfd
= open(name
, O_RDONLY
);
127 if (fromfd
< 0 || fstat(fromfd
, &sb
) < 0) fail("cannot access %s", name
);
129 if (S_ISREG(tosb
.st_mode
)) {
130 /* See if we can open it. This is more reliable than 'access'. */
131 tofd
= open(toname
, O_CREAT
| O_WRONLY
, 0666);
134 (void)(S_ISDIR(tosb
.st_mode
) ? rmdir
: unlink
)(toname
);
138 tofd
= open(toname
, O_CREAT
| O_WRONLY
, 0666);
139 if (tofd
< 0) fail("cannot create %s", toname
);
143 while ((cc
= read(fromfd
, bp
, sizeof buf
)) > 0) {
144 while ((wc
= write(tofd
, bp
, (unsigned int)cc
)) > 0) {
145 if ((cc
-= wc
) == 0) break;
148 if (wc
< 0) fail("cannot write to %s", toname
);
150 if (cc
< 0) fail("cannot read from %s", name
);
152 if (ftruncate(tofd
, sb
.st_size
) < 0) fail("cannot truncate %s", toname
);
155 utb
.actime
= sb
.st_atime
;
156 utb
.modtime
= sb
.st_mtime
;
157 if (utime(toname
, &utb
) < 0) fail("cannot set times of %s", toname
);
160 if (fchmod(tofd
, mode
) < 0)
162 if (chmod(toname
, mode
) < 0)
164 fail("cannot change mode of %s", toname
);
166 if ((owner
|| group
) && fchown(tofd
, uid
, gid
) < 0)
167 fail("cannot change owner of %s", toname
);
169 /* Must check for delayed (NFS) write errors on close. */
170 if (close(tofd
) < 0) fail("cannot write to %s", toname
);
173 if (chmod(toname
, (mode
& (S_IREAD
| S_IWRITE
))) < 0)
174 fail("cannot change mode of %s", toname
);
176 utb
.actime
= sb
.st_atime
;
177 utb
.modtime
= sb
.st_mtime
;
178 if (utime(toname
, &utb
) < 0) fail("cannot set times of %s", toname
);
183 static void copydir(char* from
, char* to
, mode_t mode
, char* group
, char* owner
,
184 int dotimes
, uid_t uid
, gid_t gid
) {
188 char *base
, *destdir
, *direntry
, *destentry
;
190 base
= xbasename(from
);
192 /* create destination directory */
193 destdir
= xmalloc((unsigned int)(strlen(to
) + 1 + strlen(base
) + 1));
194 sprintf(destdir
, "%s%s%s", to
, _DIRECTORY_SEPARATOR
, base
);
195 if (mkdirs(destdir
, mode
) != 0) {
196 fail("cannot make directory %s\n", destdir
);
201 if (!(dir
= opendir(from
))) {
202 fail("cannot open directory %s\n", from
);
207 direntry
= xmalloc((unsigned int)PATH_MAX
);
208 destentry
= xmalloc((unsigned int)PATH_MAX
);
210 while ((ep
= readdir(dir
))) {
211 if (strcmp(ep
->d_name
, ".") == 0 || strcmp(ep
->d_name
, "..") == 0) continue;
213 sprintf(direntry
, "%s/%s", from
, ep
->d_name
);
214 sprintf(destentry
, "%s%s%s", destdir
, _DIRECTORY_SEPARATOR
, ep
->d_name
);
216 if (stat(direntry
, &sb
) == 0 && S_ISDIR(sb
.st_mode
))
217 copydir(direntry
, destdir
, mode
, group
, owner
, dotimes
, uid
, gid
);
219 copyfile(direntry
, destentry
, mode
, group
, owner
, dotimes
, uid
, gid
);
228 int main(int argc
, char** argv
) {
229 int onlydir
, dodir
, dolink
, dorelsymlink
, dotimes
, opt
, len
, lplen
, tdlen
,
232 char *linkprefix
, *owner
, *group
, *cp
, *cwd
, *todir
, *toname
, *name
, *base
,
233 *linkname
, buf
[BUFSIZ
];
236 struct stat sb
, tosb
, fromsb
;
239 cwd
= linkname
= linkprefix
= owner
= group
= 0;
240 onlydir
= dodir
= dolink
= dorelsymlink
= dotimes
= lplen
= 0;
242 while ((opt
= getopt(argc
, argv
, "C:DdlL:Rm:o:g:t")) != EOF
) {
255 lplen
= strlen(linkprefix
);
259 dolink
= dorelsymlink
= 1;
262 mode
= strtoul(optarg
, &cp
, 8);
263 if (mode
== 0 && cp
== optarg
) usage();
281 if (argc
< 2 - onlydir
) usage();
283 todir
= argv
[argc
- 1];
284 if ((stat(todir
, &sb
) < 0 || !S_ISDIR(sb
.st_mode
)) &&
285 mkdirs(todir
, 0777) < 0) {
286 fail("cannot make directory %s", todir
);
288 if (onlydir
) return 0;
292 # ifndef GETCWD_CANT_MALLOC
293 cwd
= getcwd(0, PATH_MAX
);
295 cwd
= malloc(PATH_MAX
+ 1);
296 cwd
= getcwd(cwd
, PATH_MAX
);
299 cwd
= malloc(PATH_MAX
+ 1);
306 # ifndef GETCWD_CANT_MALLOC
307 todir
= getcwd(0, PATH_MAX
);
309 todir
= malloc(PATH_MAX
+ 1);
310 todir
= getcwd(todir
, PATH_MAX
);
313 todir
= malloc(PATH_MAX
+ 1);
314 todir
= getwd(todir
);
316 tdlen
= strlen(todir
);
318 tdlen
= strlen(todir
);
320 uid
= owner
? touid(owner
) : (uid_t
)(-1);
321 gid
= group
? togid(group
) : (gid_t
)(-1);
326 base
= xbasename(name
);
327 bnlen
= strlen(base
);
328 toname
= xmalloc((unsigned int)(tdlen
+ 1 + bnlen
+ 1));
329 sprintf(toname
, "%s%s%s", todir
, _DIRECTORY_SEPARATOR
, base
);
330 exists
= (lstat(toname
, &tosb
) == 0);
333 /* -d means create a directory, always */
334 if (exists
&& !S_ISDIR(tosb
.st_mode
)) {
335 (void)unlink(toname
);
338 if (!exists
&& mkdir(toname
, mode
) < 0)
339 fail("cannot make directory %s", toname
);
340 if ((owner
|| group
) && chown(toname
, uid
, gid
) < 0)
341 fail("cannot change owner of %s", toname
);
343 if (access(name
, R_OK
) != 0) {
344 fail("cannot access %s", name
);
347 /* source is absolute pathname, link to it directly */
351 /* -L prefixes names with a $cwd arg. */
353 linkname
= xmalloc((unsigned int)(len
+ 1));
354 sprintf(linkname
, "%s/%s", linkprefix
, name
);
355 } else if (dorelsymlink
) {
356 /* Symlink the relative path from todir to source name. */
357 linkname
= xmalloc(PATH_MAX
);
360 /* todir is absolute: skip over common prefix. */
361 lplen
= relatepaths(todir
, cwd
, linkname
);
362 strcpy(linkname
+ lplen
, name
);
364 /* todir is named by a relative path: reverse it. */
365 reversepath(todir
, name
, len
, linkname
);
369 len
= strlen(linkname
);
374 /* Check for a pre-existing symlink with identical content. */
376 (!S_ISLNK(tosb
.st_mode
) || readlink(toname
, buf
, sizeof buf
) != len
||
377 strncmp(buf
, name
, (unsigned int)len
) != 0 ||
378 ((stat(name
, &fromsb
) == 0) && (fromsb
.st_mtime
> tosb
.st_mtime
)))) {
379 (void)(S_ISDIR(tosb
.st_mode
) ? rmdir
: unlink
)(toname
);
382 if (!exists
&& symlink(name
, toname
) < 0)
383 fail("cannot make symbolic link %s", toname
);
385 if ((owner
|| group
) && lchown(toname
, uid
, gid
) < 0)
386 fail("cannot change owner of %s", toname
);
394 /* Copy from name to toname, which might be the same file. */
395 if (stat(name
, &sb
) == 0 && S_IFDIR
& sb
.st_mode
) {
396 /* then is directory: must explicitly create destination dir */
397 /* and manually copy files over */
398 copydir(name
, todir
, mode
, group
, owner
, dotimes
, uid
, gid
);
400 copyfile(name
, toname
, mode
, group
, owner
, dotimes
, uid
, gid
);