xref: /original-bsd/bin/chmod/chmod.c (revision b424313c)
1 /*
2  * Copyright (c) 1980, 1988 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #ifndef lint
8 char copyright[] =
9 "@(#) Copyright (c) 1980, 1988 Regents of the University of California.\n\
10  All rights reserved.\n";
11 #endif /* not lint */
12 
13 #ifndef lint
14 static char sccsid[] = "@(#)chmod.c	5.7 (Berkeley) 04/21/88";
15 #endif /* not lint */
16 
17 /*
18  * chmod options mode files
19  * where
20  *	mode is [ugoa][+-=][rwxXstugo] or an octal number
21  *	options are -Rf
22  */
23 #include <stdio.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <sys/dir.h>
27 
28 static int	fflag, rflag, retval, um;
29 static char	*modestring, *ms;
30 
31 main(argc, argv)
32 	int argc;
33 	char **argv;
34 {
35 	extern char *optarg;
36 	extern int optind, opterr;
37 	int ch;
38 
39 	/*
40 	 * since "-[rwx]" etc. are valid file modes, we don't let getopt(3)
41 	 * print error messages, and we mess around with optind as necessary.
42 	 */
43 	opterr = 0;
44 	while ((ch = getopt(argc, argv, "Rf")) != EOF)
45 		switch((char)ch) {
46 		case 'R':
47 			rflag++;
48 			break;
49 		case 'f':
50 			fflag++;
51 			break;
52 		case '?':
53 		default:
54 			--optind;
55 			goto done;
56 		}
57 done:	argv += optind;
58 	argc -= optind;
59 
60 	if (argc < 2) {
61 		fputs("usage: chmod [-Rf] [ugoa][+-=][rwxXstugo] file ...\n",
62 		    stderr);
63 		exit(-1);
64 	}
65 
66 	modestring = *argv;
67 	um = umask(0);
68 	(void)newmode((u_short)0);
69 
70 	while (*++argv)
71 		change(*argv);
72 	exit(retval);
73 }
74 
75 change(file)
76 	char *file;
77 {
78 	register DIR *dirp;
79 	register struct direct *dp;
80 	struct stat buf;
81 
82 	if (lstat(file, &buf) || chmod(file, newmode(buf.st_mode))) {
83 		err(file);
84 		return;
85 	}
86 	if (rflag && ((buf.st_mode & S_IFMT) == S_IFDIR)) {
87 		if (chdir(file) < 0 || !(dirp = opendir("."))) {
88 			err(file);
89 			return;
90 		}
91 		for (dp = readdir(dirp); dp; dp = readdir(dirp)) {
92 			if (dp->d_name[0] == '.' && (!dp->d_name[1] ||
93 			    dp->d_name[1] == '.' && !dp->d_name[2]))
94 				continue;
95 			change(dp->d_name);
96 		}
97 		closedir(dirp);
98 		if (chdir("..")) {
99 			err("..");
100 			exit(fflag ? 0 : -1);
101 		}
102 	}
103 }
104 
105 err(s)
106 	char *s;
107 {
108 	if (fflag)
109 		return;
110 	fputs("chmod: ", stderr);
111 	perror(s);
112 	retval = -1;
113 }
114 
115 newmode(nm)
116 	u_short nm;
117 {
118 	register int o, m, b;
119 
120 	ms = modestring;
121 	m = abs();
122 	if (*ms == '\0')
123 		return (m);
124 	do {
125 		m = who();
126 		while (o = what()) {
127 			b = where((int)nm);
128 			switch (o) {
129 			case '+':
130 				nm |= b & m;
131 				break;
132 			case '-':
133 				nm &= ~(b & m);
134 				break;
135 			case '=':
136 				nm &= ~m;
137 				nm |= b & m;
138 				break;
139 			}
140 		}
141 	} while (*ms++ == ',');
142 	if (*--ms) {
143 		fputs("chmod: invalid mode.\n", stderr);
144 		exit(-1);
145 	}
146 	return ((int)nm);
147 }
148 
149 abs()
150 {
151 	register int c, i;
152 
153 	i = 0;
154 	while ((c = *ms++) >= '0' && c <= '7')
155 		i = (i << 3) + (c - '0');
156 	ms--;
157 	return (i);
158 }
159 
160 #define	USER	05700	/* user's bits */
161 #define	GROUP	02070	/* group's bits */
162 #define	OTHER	00007	/* other's bits */
163 #define	ALL	01777	/* all (note absence of setuid, etc) */
164 
165 #define	READ	00444	/* read permit */
166 #define	WRITE	00222	/* write permit */
167 #define	EXEC	00111	/* exec permit */
168 #define	SETID	06000	/* set[ug]id */
169 #define	STICKY	01000	/* sticky bit */
170 
171 who()
172 {
173 	register int m;
174 
175 	m = 0;
176 	for (;;) switch (*ms++) {
177 	case 'u':
178 		m |= USER;
179 		continue;
180 	case 'g':
181 		m |= GROUP;
182 		continue;
183 	case 'o':
184 		m |= OTHER;
185 		continue;
186 	case 'a':
187 		m |= ALL;
188 		continue;
189 	default:
190 		ms--;
191 		if (m == 0)
192 			m = ALL & ~um;
193 		return (m);
194 	}
195 }
196 
197 what()
198 {
199 	switch (*ms) {
200 	case '+':
201 	case '-':
202 	case '=':
203 		return (*ms++);
204 	}
205 	return (0);
206 }
207 
208 where(om)
209 	register int om;
210 {
211 	register int m;
212 
213  	m = 0;
214 	switch (*ms) {
215 	case 'u':
216 		m = (om & USER) >> 6;
217 		goto dup;
218 	case 'g':
219 		m = (om & GROUP) >> 3;
220 		goto dup;
221 	case 'o':
222 		m = (om & OTHER);
223 	dup:
224 		m &= (READ|WRITE|EXEC);
225 		m |= (m << 3) | (m << 6);
226 		++ms;
227 		return (m);
228 	}
229 	for (;;) switch (*ms++) {
230 	case 'r':
231 		m |= READ;
232 		continue;
233 	case 'w':
234 		m |= WRITE;
235 		continue;
236 	case 'x':
237 		m |= EXEC;
238 		continue;
239 	case 'X':
240 		if ((om & S_IFDIR) || (om & EXEC))
241 			m |= EXEC;
242 		continue;
243 	case 's':
244 		m |= SETID;
245 		continue;
246 	case 't':
247 		m |= STICKY;
248 		continue;
249 	default:
250 		ms--;
251 		return (m);
252 	}
253 }
254