2 * files.c - file operation builtins
4 * This file is part of zsh, the Z shell.
6 * Copyright (c) 1996-1997 Andrew Main
9 * Permission is hereby granted, without written agreement and without
10 * license or royalty fees, to use, copy, modify, and distribute this
11 * software and to distribute modified versions of this software for any
12 * purpose, provided that the above copyright notice and the following
13 * two paragraphs appear in all copies of this software.
15 * In no event shall Andrew Main or the Zsh Development Group be liable
16 * to any party for direct, indirect, special, incidental, or consequential
17 * damages arising out of the use of this software and its documentation,
18 * even if Andrew Main and the Zsh Development Group have been advised of
19 * the possibility of such damage.
21 * Andrew Main and the Zsh Development Group specifically disclaim any
22 * warranties, including, but not limited to, the implied warranties of
23 * merchantability and fitness for a particular purpose. The software
24 * provided hereunder is on an "as is" basis, and Andrew Main and the
25 * Zsh Development Group have no obligation to provide maintenance,
26 * support, updates, enhancements, or modifications.
32 typedef int (*MoveFunc
) _((char const *, char const *));
33 typedef int (*RecurseFunc
) _((char *, char *, struct stat
const *, void *));
36 extern int link
_((const char *, const char *));
37 extern int symlink
_((const char *, const char *));
38 extern int rename
_((const char *, const char *));
50 for(c
= a
; c
!= EOF
&& c
!= '\n'; )
52 return a
== 'y' || a
== 'Y';
59 bin_sync(UNUSED(char *nam
), UNUSED(char **args
), UNUSED(Options ops
), UNUSED(int func
))
69 bin_mkdir(char *nam
, char **args
, Options ops
, UNUSED(int func
))
71 mode_t oumask
= umask(0);
72 mode_t mode
= 0777 & ~oumask
;
76 if(OPT_ISSET(ops
,'m')) {
77 char *str
= OPT_ARG(ops
,'m'), *ptr
;
79 mode
= zstrtol(str
, &ptr
, 8);
81 zwarnnam(nam
, "invalid mode `%s'", str
);
85 for(; *args
; args
++) {
86 char *ptr
= strchr(*args
, 0);
88 while(ptr
> *args
+ (**args
== '/') && *--ptr
== '/')
90 if(OPT_ISSET(ops
,'p')) {
96 while(*ptr
&& *ptr
!= '/')
99 err
|= domkdir(nam
, *args
, mode
, 1);
105 e
= domkdir(nam
, *args
, mode
| 0300, 1);
114 err
|= domkdir(nam
, *args
, mode
, 0);
121 domkdir(char *nam
, char *path
, mode_t mode
, int p
)
125 char const *rpath
= unmeta(path
);
130 if(!stat(rpath
, &st
) && S_ISDIR(st
.st_mode
))
134 err
= mkdir(path
, mode
) ? errno
: 0;
138 zwarnnam(nam
, "cannot make directory `%s': %e", path
, err
);
146 bin_rmdir(char *nam
, char **args
, UNUSED(Options ops
), UNUSED(int func
))
150 for(; *args
; args
++) {
151 char *rpath
= unmeta(*args
);
154 zwarnnam(nam
, "%s: %e", *args
, ENAMETOOLONG
);
156 } else if(rmdir(rpath
)) {
157 zwarnnam(nam
, "cannot remove directory `%s': %e", *args
, errno
);
164 /* ln and mv builtins */
169 #define MV_NODIRS (1<<0)
170 #define MV_FORCE (1<<1)
171 #define MV_INTERACTIVE (1<<2)
172 #define MV_ASKNW (1<<3)
173 #define MV_ATOMIC (1<<4)
174 #define MV_NOCHASETARGET (1<<5)
177 * bin_ln actually does three related jobs: hard linking, symbolic
178 * linking, and renaming. If called as mv it renames, otherwise
179 * it looks at the -s option. If hard linking, it will refuse to
180 * attempt linking to a directory unless the -d option is given.
184 * Option compatibility: BSD systems settled on using mostly-standardised
185 * options across multiple commands to deal with symlinks; see, eg,
186 * symlink(7) on a *BSD system for details. Per this, to work on a link
187 * directly we use "-h" and "ln -hsf" will not follow the target if it
188 * points to a directory. GNU settled on using -n for ln(1), so we
189 * have "ln -nsf". We handle them both.
191 * Logic compared against that of FreeBSD's ln.c, compatible license.
196 bin_ln(char *nam
, char **args
, Options ops
, int func
)
199 int flags
, have_dir
, err
= 0;
200 char **a
, *ptr
, *rp
, *buf
;
206 move
= (MoveFunc
) rename
;
207 flags
= OPT_ISSET(ops
,'f') ? 0 : MV_ASKNW
;
210 flags
= OPT_ISSET(ops
,'f') ? MV_FORCE
: 0;
212 if(OPT_ISSET(ops
,'h') || OPT_ISSET(ops
,'n'))
213 flags
|= MV_NOCHASETARGET
;
214 if(OPT_ISSET(ops
,'s'))
215 move
= (MoveFunc
) symlink
;
219 move
= (MoveFunc
) link
;
220 if(!OPT_ISSET(ops
,'d'))
224 if(OPT_ISSET(ops
,'i') && !OPT_ISSET(ops
,'f'))
225 flags
|= MV_INTERACTIVE
;
226 for(a
= args
; a
[1]; a
++) ;
229 if(rp
&& !stat(rp
, &st
) && S_ISDIR(st
.st_mode
)) {
231 if((flags
& MV_NOCHASETARGET
)
232 && !lstat(rp
, &st
) && S_ISLNK(st
.st_mode
)) {
234 * So we have "ln -h" with the target being a symlink pointing
235 * to a directory; if there are multiple sources but the target
236 * is a symlink, then it's an error as we're not following
237 * symlinks; if OTOH there's just one source, then we need to
238 * either fail EEXIST or if "-f" given then remove the target.
242 zwarnnam(nam
, "%s: %e", *a
, errno
);
245 if(flags
& MV_FORCE
) {
250 zwarnnam(nam
, "%s: %e", *a
, errno
);
254 /* Normal case, target is a directory, chase into it */
260 zwarnnam(nam
, "last of many arguments must be a directory");
264 ptr
= strrchr(args
[0], '/');
270 return domove(nam
, move
, args
[0], args
[1], flags
);
274 buf
= appstr(buf
, "/");
276 for(; *args
; args
++) {
278 ptr
= strrchr(*args
, '/');
285 buf
= appstr(buf
, ptr
);
286 err
|= domove(nam
, move
, *args
, buf
, flags
);
294 domove(char *nam
, MoveFunc move
, char *p
, char *q
, int flags
)
299 pbuf
= ztrdup(unmeta(p
));
301 if(flags
& MV_NODIRS
) {
303 if(lstat(pbuf
, &st
) || S_ISDIR(st
.st_mode
)) {
304 zwarnnam(nam
, "%s: %e", p
, errno
);
309 if(!lstat(qbuf
, &st
)) {
310 int doit
= flags
& MV_FORCE
;
311 if(S_ISDIR(st
.st_mode
)) {
312 zwarnnam(nam
, "%s: cannot overwrite directory", q
);
315 } else if(flags
& MV_INTERACTIVE
) {
316 nicezputs(nam
, stderr
);
317 fputs(": replace `", stderr
);
318 nicezputs(q
, stderr
);
319 fputs("'? ", stderr
);
326 } else if((flags
& MV_ASKNW
) &&
327 !S_ISLNK(st
.st_mode
) &&
328 access(qbuf
, W_OK
)) {
329 nicezputs(nam
, stderr
);
330 fputs(": replace `", stderr
);
331 nicezputs(q
, stderr
);
332 fprintf(stderr
, "', overriding mode %04o? ",
333 mode_to_octal(st
.st_mode
));
341 if(doit
&& !(flags
& MV_ATOMIC
))
344 if(move(pbuf
, qbuf
)) {
345 zwarnnam(nam
, "%s: %e", p
, errno
);
353 /* general recursion */
355 struct recursivecmd
{
360 RecurseFunc dirpre_func
;
361 RecurseFunc dirpost_func
;
362 RecurseFunc leaf_func
;
368 recursivecmd(char *nam
, int opt_noerr
, int opt_recurse
, int opt_safe
,
369 char **args
, RecurseFunc dirpre_func
, RecurseFunc dirpost_func
,
370 RecurseFunc leaf_func
, void *magic
)
375 struct recursivecmd reccmd
;
378 reccmd
.opt_noerr
= opt_noerr
;
379 reccmd
.opt_recurse
= opt_recurse
;
380 reccmd
.opt_safe
= opt_safe
;
381 reccmd
.dirpre_func
= dirpre_func
;
382 reccmd
.dirpost_func
= dirpost_func
;
383 reccmd
.leaf_func
= leaf_func
;
384 reccmd
.magic
= magic
;
387 ds
.dirfd
= ds
.level
= -1;
388 if (opt_recurse
|| opt_safe
) {
389 if ((ds
.dirfd
= open(".", O_RDONLY
|O_NOCTTY
)) < 0 &&
390 zgetdir(&ds
) && *ds
.dirname
!= '/')
391 ds
.dirfd
= open("..", O_RDONLY
|O_NOCTTY
);
393 for(; !errflag
&& !(err
& 2) && *args
; args
++) {
397 s
= strrchr(rp
, '/');
399 while (*s
== '/' && s
> rp
)
401 while (*s
!= '/' && s
> rp
)
408 e
= lchdir(s
> rp
? rp
: "/", &ds
, 1);
415 d
.dirfd
= d
.level
= -1;
416 err
|= recursivecmd_doone(&reccmd
, *args
, s
+ 1, &d
, 0);
420 } else if(!opt_noerr
)
421 zwarnnam(nam
, "%s: %e", *args
, errno
);
423 err
|= recursivecmd_doone(&reccmd
, *args
, rp
, &ds
, 0);
425 err
|= recursivecmd_doone(&reccmd
, *args
, rp
, &ds
, 1);
428 if ((err
& 2) && ds
.dirfd
>= 0 && restoredir(&ds
) && zchdir(pwd
)) {
432 zwarn("failed to chdir(%s): %e", pwd
, errno
);
442 recursivecmd_doone(struct recursivecmd
const *reccmd
,
443 char *arg
, char *rp
, struct dirsav
*ds
, int first
)
445 struct stat st
, *sp
= NULL
;
447 if(reccmd
->opt_recurse
&& !lstat(rp
, &st
)) {
448 if(S_ISDIR(st
.st_mode
))
449 return recursivecmd_dorec(reccmd
, arg
, rp
, &st
, ds
, first
);
452 return reccmd
->leaf_func(arg
, rp
, sp
, reccmd
->magic
);
457 recursivecmd_dorec(struct recursivecmd
const *reccmd
,
458 char *arg
, char *rp
, struct stat
const *sp
, struct dirsav
*ds
, int first
)
467 err1
= reccmd
->dirpre_func(arg
, rp
, sp
, reccmd
->magic
);
471 err
= -lchdir(rp
, ds
, !first
);
473 if(!reccmd
->opt_noerr
)
474 zwarnnam(reccmd
->nam
, "%s: %e", arg
, errno
);
479 dsav
.ino
= dsav
.dev
= 0;
481 dsav
.dirfd
= dsav
.level
= -1;
484 if(!reccmd
->opt_noerr
)
485 zwarnnam(reccmd
->nam
, "%s: %e", arg
, errno
);
488 int arglen
= strlen(arg
) + 1;
490 while (!errflag
&& (fn
= zreaddir(d
, 1))) {
491 int l
= strlen(fn
) + 1;
492 files
= hrealloc(files
, fileslen
, fileslen
+ l
);
493 strcpy(files
+ fileslen
, fn
);
497 for (fn
= files
; !errflag
&& !(err
& 2) && fn
< files
+ fileslen
;) {
498 int l
= strlen(fn
) + 1;
499 VARARR(char, narg
, arglen
+ l
);
502 narg
[arglen
-1] = '/';
503 strcpy(narg
+ arglen
, fn
);
505 err
|= recursivecmd_doone(reccmd
, narg
, fn
, &dsav
, 0);
508 hrealloc(files
, fileslen
, 0);
510 zsfree(dsav
.dirname
);
513 if (restoredir(ds
)) {
514 if(!reccmd
->opt_noerr
)
515 zwarnnam(reccmd
->nam
, "failed to return to previous directory: %e",
519 return err
| reccmd
->dirpost_func(arg
, rp
, sp
, reccmd
->magic
);
524 recurse_donothing(UNUSED(char *arg
), UNUSED(char *rp
), UNUSED(struct stat
const *sp
), UNUSED(void *magic
))
540 rm_leaf(char *arg
, char *rp
, struct stat
const *sp
, void *magic
)
542 struct rmmagic
*rmm
= magic
;
545 if(!rmm
->opt_unlinkdir
|| !rmm
->opt_force
) {
551 if(!rmm
->opt_unlinkdir
&& S_ISDIR(sp
->st_mode
)) {
554 zwarnnam(rmm
->nam
, "%s: %e", arg
, EISDIR
);
557 if(rmm
->opt_interact
) {
558 nicezputs(rmm
->nam
, stderr
);
559 fputs(": remove `", stderr
);
560 nicezputs(arg
, stderr
);
561 fputs("'? ", stderr
);
565 } else if(!rmm
->opt_force
&&
566 !S_ISLNK(sp
->st_mode
) &&
568 nicezputs(rmm
->nam
, stderr
);
569 fputs(": remove `", stderr
);
570 nicezputs(arg
, stderr
);
571 fprintf(stderr
, "', overriding mode %04o? ",
572 mode_to_octal(sp
->st_mode
));
579 if(unlink(rp
) && !rmm
->opt_force
) {
580 zwarnnam(rmm
->nam
, "%s: %e", arg
, errno
);
588 rm_dirpost(char *arg
, char *rp
, UNUSED(struct stat
const *sp
), void *magic
)
590 struct rmmagic
*rmm
= magic
;
592 if(rmm
->opt_interact
) {
593 nicezputs(rmm
->nam
, stderr
);
594 fputs(": remove `", stderr
);
595 nicezputs(arg
, stderr
);
596 fputs("'? ", stderr
);
601 if(rmdir(rp
) && !rmm
->opt_force
) {
602 zwarnnam(rmm
->nam
, "%s: %e", arg
, errno
);
610 bin_rm(char *nam
, char **args
, Options ops
, UNUSED(int func
))
616 rmm
.opt_force
= OPT_ISSET(ops
,'f');
617 rmm
.opt_interact
= OPT_ISSET(ops
,'i') && !OPT_ISSET(ops
,'f');
618 rmm
.opt_unlinkdir
= OPT_ISSET(ops
,'d');
619 err
= recursivecmd(nam
, OPT_ISSET(ops
,'f'),
620 OPT_ISSET(ops
,'r') && !OPT_ISSET(ops
,'d'),
622 args
, recurse_donothing
, rm_dirpost
, rm_leaf
, &rmm
);
623 return OPT_ISSET(ops
,'f') ? 0 : err
;
636 chown_dochown(char *arg
, char *rp
, UNUSED(struct stat
const *sp
), void *magic
)
638 struct chownmagic
*chm
= magic
;
640 if(chown(rp
, chm
->uid
, chm
->gid
)) {
641 zwarnnam(chm
->nam
, "%s: %e", arg
, errno
);
649 chown_dolchown(char *arg
, char *rp
, UNUSED(struct stat
const *sp
), void *magic
)
651 struct chownmagic
*chm
= magic
;
653 if(lchown(rp
, chm
->uid
, chm
->gid
)) {
654 zwarnnam(chm
->nam
, "%s: %e", arg
, errno
);
662 static unsigned long getnumeric(char *p
, int *errp
)
670 ret
= strtoul(p
, &p
, 10);
675 enum { BIN_CHOWN
, BIN_CHGRP
};
679 bin_chown(char *nam
, char **args
, Options ops
, int func
)
681 struct chownmagic chm
;
682 char *uspec
= ztrdup(*args
), *p
= uspec
;
686 if(func
== BIN_CHGRP
) {
690 end
= strchr(uspec
, ':');
692 end
= strchr(uspec
, '.');
703 chm
.uid
= pwd
->pw_uid
;
706 chm
.uid
= getnumeric(p
, &err
);
708 zwarnnam(nam
, "%s: no such user", p
);
716 if(!pwd
&& !(pwd
= getpwuid(chm
.uid
))) {
717 zwarnnam(nam
, "%s: no such user", uspec
);
721 chm
.gid
= pwd
->pw_gid
;
722 } else if(p
[0] == ':' && !p
[1]) {
729 chm
.gid
= grp
->gr_gid
;
732 chm
.gid
= getnumeric(p
, &err
);
734 zwarnnam(nam
, "%s: no such group", p
);
744 return recursivecmd(nam
, 0, OPT_ISSET(ops
,'R'), OPT_ISSET(ops
,'s'),
745 args
+ 1, OPT_ISSET(ops
, 'h') ? chown_dolchown
: chown_dochown
, recurse_donothing
,
746 OPT_ISSET(ops
, 'h') ? chown_dolchown
: chown_dochown
, &chm
);
749 /* module paraphernalia */
752 # define LN_OPTS "dfhins"
754 # define LN_OPTS "dfi"
757 static struct builtin bintab
[] = {
758 /* The names which overlap commands without necessarily being
759 * fully compatible. */
760 BUILTIN("chgrp", 0, bin_chown
, 2, -1, BIN_CHGRP
, "hRs", NULL
),
761 BUILTIN("chown", 0, bin_chown
, 2, -1, BIN_CHOWN
, "hRs", NULL
),
762 BUILTIN("ln", 0, bin_ln
, 1, -1, BIN_LN
, LN_OPTS
, NULL
),
763 BUILTIN("mkdir", 0, bin_mkdir
, 1, -1, 0, "pm:", NULL
),
764 BUILTIN("mv", 0, bin_ln
, 2, -1, BIN_MV
, "fi", NULL
),
765 BUILTIN("rm", 0, bin_rm
, 1, -1, 0, "dfirs", NULL
),
766 BUILTIN("rmdir", 0, bin_rmdir
, 1, -1, 0, NULL
, NULL
),
767 BUILTIN("sync", 0, bin_sync
, 0, 0, 0, NULL
, NULL
),
768 /* The "safe" zsh-only names */
769 BUILTIN("zf_chgrp", 0, bin_chown
, 2, -1, BIN_CHGRP
, "hRs", NULL
),
770 BUILTIN("zf_chown", 0, bin_chown
, 2, -1, BIN_CHOWN
, "hRs", NULL
),
771 BUILTIN("zf_ln", 0, bin_ln
, 1, -1, BIN_LN
, LN_OPTS
, NULL
),
772 BUILTIN("zf_mkdir", 0, bin_mkdir
, 1, -1, 0, "pm:", NULL
),
773 BUILTIN("zf_mv", 0, bin_ln
, 2, -1, BIN_MV
, "fi", NULL
),
774 BUILTIN("zf_rm", 0, bin_rm
, 1, -1, 0, "dfirs", NULL
),
775 BUILTIN("zf_rmdir", 0, bin_rmdir
, 1, -1, 0, NULL
, NULL
),
776 BUILTIN("zf_sync", 0, bin_sync
, 0, 0, 0, NULL
, NULL
),
780 static struct features module_features
= {
781 bintab
, sizeof(bintab
)/sizeof(*bintab
),
790 setup_(UNUSED(Module m
))
797 features_(Module m
, char ***features
)
799 *features
= featuresarray(m
, &module_features
);
805 enables_(Module m
, int **enables
)
807 return handlefeatures(m
, &module_features
, enables
);
821 return setfeatureenables(m
, &module_features
, NULL
);
826 finish_(UNUSED(Module m
))