119840Sdist /*
266445Sbostic * Copyright (c) 1989, 1993, 1994
363597Sbostic * The Regents of the University of California. All rights reserved.
439829Sbostic *
542527Sbostic * %sccs.include.redist.c%
619840Sdist */
716242Slayer
819840Sdist #ifndef lint
963597Sbostic static char copyright[] =
1066445Sbostic "@(#) Copyright (c) 1989, 1993, 1994\n\
1163597Sbostic The Regents of the University of California. All rights reserved.\n";
1234043Sbostic #endif /* not lint */
1319840Sdist
1434043Sbostic #ifndef lint
15*66600Sbostic static char sccsid[] = "@(#)chmod.c 8.8 (Berkeley) 04/01/94";
1634043Sbostic #endif /* not lint */
1734043Sbostic
1816242Slayer #include <sys/types.h>
1916242Slayer #include <sys/stat.h>
2058142Sbostic
2158142Sbostic #include <err.h>
2252070Sbostic #include <errno.h>
2339829Sbostic #include <fts.h>
2439829Sbostic #include <stdio.h>
2552070Sbostic #include <stdlib.h>
2642010Sbostic #include <string.h>
2758142Sbostic #include <unistd.h>
2816242Slayer
2952070Sbostic void usage __P((void));
3052070Sbostic
3152070Sbostic int
main(argc,argv)3224503Ssam main(argc, argv)
3334043Sbostic int argc;
3452070Sbostic char *argv[];
3516242Slayer {
3666544Sbostic FTS *ftsp;
3766544Sbostic FTSENT *p;
3852070Sbostic mode_t *set;
3966544Sbostic long val;
4066544Sbostic int oct, omode;
4166543Sbostic int Hflag, Lflag, Pflag, Rflag, ch, fflag, fts_options, hflag, rval;
4252070Sbostic char *ep, *mode;
4316242Slayer
4466543Sbostic Hflag = Lflag = Pflag = Rflag = fflag = hflag = 0;
4566543Sbostic while ((ch = getopt(argc, argv, "HLPRXfgorstuwx")) != EOF)
4666445Sbostic switch (ch) {
4753782Selan case 'H':
4853782Selan Hflag = 1;
4966543Sbostic Lflag = Pflag = 0;
5053782Selan break;
5166543Sbostic case 'L':
5266543Sbostic Lflag = 1;
5366543Sbostic Hflag = Pflag = 0;
5466543Sbostic break;
5566543Sbostic case 'P':
5666543Sbostic Pflag = 1;
5766543Sbostic Hflag = Lflag = 0;
5866543Sbostic break;
5924503Ssam case 'R':
6066543Sbostic Rflag = 1;
6124503Ssam break;
6266543Sbostic case 'f': /* XXX: undocumented. */
6347072Sbostic fflag = 1;
6424503Ssam break;
6553782Selan case 'h':
6666543Sbostic /*
6766543Sbostic * In System V (and probably POSIX.2) the -h option
6866543Sbostic * causes chmod to change the mode of the symbolic
6966543Sbostic * link. 4.4BSD's symbolic links don't have modes,
7066543Sbostic * so it's an undocumented noop. Do syntax checking,
7166543Sbostic * though.
7266543Sbostic */
7353782Selan hflag = 1;
7453782Selan break;
7565163Sbostic /*
7666445Sbostic * XXX
7765163Sbostic * "-[rwx]" are valid mode commands. If they are the entire
7865163Sbostic * argument, getopt has moved past them, so decrement optind.
7965163Sbostic * Regardless, we're done argument processing.
8065163Sbostic */
8166445Sbostic case 'g': case 'o': case 'r': case 's':
8266445Sbostic case 't': case 'u': case 'w': case 'X': case 'x':
8366445Sbostic if (argv[optind - 1][0] == '-' &&
8466445Sbostic argv[optind - 1][1] == ch &&
8566445Sbostic argv[optind - 1][2] == '\0')
8665163Sbostic --optind;
8765163Sbostic goto done;
8834043Sbostic case '?':
8924503Ssam default:
9039829Sbostic usage();
9123482Smckusick }
9234043Sbostic done: argv += optind;
9334043Sbostic argc -= optind;
9434043Sbostic
9539829Sbostic if (argc < 2)
9639829Sbostic usage();
9739829Sbostic
9866543Sbostic fts_options = FTS_PHYSICAL;
9966543Sbostic if (Rflag) {
10066543Sbostic if (hflag)
10166543Sbostic errx(1,
10266543Sbostic "the -R and -h options may not be specified together.");
10366543Sbostic if (Hflag)
10466543Sbostic fts_options |= FTS_COMFOLLOW;
10566543Sbostic if (Lflag) {
10666543Sbostic fts_options &= ~FTS_PHYSICAL;
10766543Sbostic fts_options |= FTS_LOGICAL;
10866543Sbostic }
10966543Sbostic }
11066543Sbostic
11139829Sbostic mode = *argv;
11239829Sbostic if (*mode >= '0' && *mode <= '7') {
11366544Sbostic errno = 0;
11466544Sbostic val = strtol(mode, &ep, 8);
11566544Sbostic if (val > INT_MAX || val < 0)
11666544Sbostic errno = ERANGE;
11766544Sbostic if (errno)
11866544Sbostic err(1, "invalid file mode: %s", mode);
11966544Sbostic if (*ep)
12058142Sbostic errx(1, "invalid file mode: %s", mode);
12166544Sbostic omode = val;
12239829Sbostic oct = 1;
12339829Sbostic } else {
12458142Sbostic if ((set = setmode(mode)) == NULL)
12558142Sbostic errx(1, "invalid file mode: %s", mode);
12639829Sbostic oct = 0;
12716242Slayer }
12834043Sbostic
12953782Selan if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL)
13066543Sbostic err(1, NULL);
13166543Sbostic for (rval = 0; (p = fts_read(ftsp)) != NULL;) {
13266445Sbostic switch (p->fts_info) {
13353782Selan case FTS_D:
13466543Sbostic if (Rflag) /* Change it at FTS_DP. */
13566543Sbostic continue;
13666543Sbostic fts_set(ftsp, p, FTS_SKIP);
13753782Selan break;
13866543Sbostic case FTS_DNR: /* Warn, chmod, continue. */
139*66600Sbostic warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
14066543Sbostic rval = 1;
14166543Sbostic break;
14266543Sbostic case FTS_ERR: /* Warn, continue. */
14353782Selan case FTS_NS:
144*66600Sbostic warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
14566543Sbostic rval = 1;
14666543Sbostic continue;
14766543Sbostic case FTS_SL: /* Ignore. */
14866543Sbostic case FTS_SLNONE:
14966543Sbostic /*
15066543Sbostic * The only symlinks that end up here are ones that
15166543Sbostic * don't point to anything and ones that we found
15266543Sbostic * doing a physical walk.
15366543Sbostic */
15466543Sbostic continue;
15566543Sbostic default:
15653782Selan break;
15753782Selan }
15866543Sbostic if (chmod(p->fts_accpath, oct ? omode :
15966543Sbostic getmode(set, p->fts_statp->st_mode)) && !fflag) {
16066543Sbostic warn(p->fts_path);
16166543Sbostic rval = 1;
16266543Sbostic }
16366543Sbostic }
16466549Sbostic if (errno)
16566549Sbostic err(1, "fts_read");
16666543Sbostic exit(rval);
16716242Slayer }
16816242Slayer
16952070Sbostic void
usage()17039829Sbostic usage()
17116242Slayer {
17266543Sbostic (void)fprintf(stderr,
17366543Sbostic "usage: chmod [-R [-H | -L | -P]] mode file ...\n");
17439829Sbostic exit(1);
17516242Slayer }
176