xref: /original-bsd/bin/chmod/chmod.c (revision 612877e1)
1 /*
2  * Copyright (c) 1989, 1993, 1994
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char copyright[] =
10 "@(#) Copyright (c) 1989, 1993, 1994\n\
11 	The Regents of the University of California.  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)chmod.c	8.8 (Berkeley) 04/01/94";
16 #endif /* not lint */
17 
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 
21 #include <err.h>
22 #include <errno.h>
23 #include <fts.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 
29 void usage __P((void));
30 
31 int
32 main(argc, argv)
33 	int argc;
34 	char *argv[];
35 {
36 	FTS *ftsp;
37 	FTSENT *p;
38 	mode_t *set;
39 	long val;
40 	int oct, omode;
41 	int Hflag, Lflag, Pflag, Rflag, ch, fflag, fts_options, hflag, rval;
42 	char *ep, *mode;
43 
44 	Hflag = Lflag = Pflag = Rflag = fflag = hflag = 0;
45 	while ((ch = getopt(argc, argv, "HLPRXfgorstuwx")) != EOF)
46 		switch (ch) {
47 		case 'H':
48 			Hflag = 1;
49 			Lflag = Pflag = 0;
50 			break;
51 		case 'L':
52 			Lflag = 1;
53 			Hflag = Pflag = 0;
54 			break;
55 		case 'P':
56 			Pflag = 1;
57 			Hflag = Lflag = 0;
58 			break;
59 		case 'R':
60 			Rflag = 1;
61 			break;
62 		case 'f':		/* XXX: undocumented. */
63 			fflag = 1;
64 			break;
65 		case 'h':
66 			/*
67 			 * In System V (and probably POSIX.2) the -h option
68 			 * causes chmod to change the mode of the symbolic
69 			 * link.  4.4BSD's symbolic links don't have modes,
70 			 * so it's an undocumented noop.  Do syntax checking,
71 			 * though.
72 			 */
73 			hflag = 1;
74 			break;
75 		/*
76 		 * XXX
77 		 * "-[rwx]" are valid mode commands.  If they are the entire
78 		 * argument, getopt has moved past them, so decrement optind.
79 		 * Regardless, we're done argument processing.
80 		 */
81 		case 'g': case 'o': case 'r': case 's':
82 		case 't': case 'u': case 'w': case 'X': case 'x':
83 			if (argv[optind - 1][0] == '-' &&
84 			    argv[optind - 1][1] == ch &&
85 			    argv[optind - 1][2] == '\0')
86 				--optind;
87 			goto done;
88 		case '?':
89 		default:
90 			usage();
91 		}
92 done:	argv += optind;
93 	argc -= optind;
94 
95 	if (argc < 2)
96 		usage();
97 
98 	fts_options = FTS_PHYSICAL;
99 	if (Rflag) {
100 		if (hflag)
101 			errx(1,
102 		"the -R and -h options may not be specified together.");
103 		if (Hflag)
104 			fts_options |= FTS_COMFOLLOW;
105 		if (Lflag) {
106 			fts_options &= ~FTS_PHYSICAL;
107 			fts_options |= FTS_LOGICAL;
108 		}
109 	}
110 
111 	mode = *argv;
112 	if (*mode >= '0' && *mode <= '7') {
113 		errno = 0;
114 		val = strtol(mode, &ep, 8);
115 		if (val > INT_MAX || val < 0)
116 			errno = ERANGE;
117 		if (errno)
118 			err(1, "invalid file mode: %s", mode);
119 		if (*ep)
120 			errx(1, "invalid file mode: %s", mode);
121 		omode = val;
122 		oct = 1;
123 	} else {
124 		if ((set = setmode(mode)) == NULL)
125 			errx(1, "invalid file mode: %s", mode);
126 		oct = 0;
127 	}
128 
129 	if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL)
130 		err(1, NULL);
131 	for (rval = 0; (p = fts_read(ftsp)) != NULL;) {
132 		switch (p->fts_info) {
133 		case FTS_D:
134 			if (Rflag)		/* Change it at FTS_DP. */
135 				continue;
136 			fts_set(ftsp, p, FTS_SKIP);
137 			break;
138 		case FTS_DNR:			/* Warn, chmod, continue. */
139 			warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
140 			rval = 1;
141 			break;
142 		case FTS_ERR:			/* Warn, continue. */
143 		case FTS_NS:
144 			warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
145 			rval = 1;
146 			continue;
147 		case FTS_SL:			/* Ignore. */
148 		case FTS_SLNONE:
149 			/*
150 			 * The only symlinks that end up here are ones that
151 			 * don't point to anything and ones that we found
152 			 * doing a physical walk.
153 			 */
154 			continue;
155 		default:
156 			break;
157 		}
158 		if (chmod(p->fts_accpath, oct ? omode :
159 		    getmode(set, p->fts_statp->st_mode)) && !fflag) {
160 			warn(p->fts_path);
161 			rval = 1;
162 		}
163 	}
164 	if (errno)
165 		err(1, "fts_read");
166 	exit(rval);
167 }
168 
169 void
170 usage()
171 {
172 	(void)fprintf(stderr,
173 	    "usage: chmod [-R [-H | -L | -P]] mode file ...\n");
174 	exit(1);
175 }
176