unstack - fix ipcvecs
[minix.git] / commands / chmod / chmod.c
blob8daf4541cb2deba8ecf21bfe650361f119c56bb6
1 /* chmod - Change file modes Author: V. Archer */
3 /* Copyright 1991 by Vincent Archer
4 * You may freely redistribute this software, in source or binary
5 * form, provided that you do not alter this copyright mention in any
6 * way.
7 */
9 #include <sys/types.h>
10 #include <sys/stat.h>
11 #include <dirent.h>
12 #include <errno.h>
13 #include <limits.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <unistd.h>
17 #include <minix/minlib.h>
18 #include <stdio.h>
20 #ifndef S_ISLNK
21 #define S_ISLNK(mode) 0
22 #define lstat stat
23 #endif
25 #define USR_MODES (S_ISUID|S_IRWXU)
26 #define GRP_MODES (S_ISGID|S_IRWXG)
27 #define EXE_MODES (S_IXUSR|S_IXGRP|S_IXOTH)
28 #ifdef S_ISVTX
29 #define ALL_MODES (USR_MODES|GRP_MODES|S_IRWXO|S_ISVTX)
30 #else
31 #define ALL_MODES (USR_MODES|GRP_MODES|S_IRWXO)
32 #endif
35 /* Common variables */
36 char *symbolic;
37 mode_t new_mode, u_mask;
38 int rflag, errors;
39 struct stat st;
40 char path[PATH_MAX + 1];
42 int do_change(char *name);
43 void usage(void);
45 /* Parse a P1003.2 4.7.7-conformant symbolic mode. */
46 mode_t parsemode(const char *symbolic, mode_t oldmode)
48 mode_t who, mask, newmode, tmpmask;
49 char action;
51 newmode = oldmode & ALL_MODES;
52 while (*symbolic) {
53 who = 0;
54 for (; *symbolic; symbolic++) {
55 if (*symbolic == 'a') {
56 who |= ALL_MODES;
57 continue;
59 if (*symbolic == 'u') {
60 who |= USR_MODES;
61 continue;
63 if (*symbolic == 'g') {
64 who |= GRP_MODES;
65 continue;
67 if (*symbolic == 'o') {
68 who |= S_IRWXO;
69 continue;
71 break;
73 if (!*symbolic || *symbolic == ',') usage();
74 while (*symbolic) {
75 if (*symbolic == ',') break;
76 switch (*symbolic) {
77 default:
78 usage();
79 case '+':
80 case '-':
81 case '=': action = *symbolic++;
83 mask = 0;
84 for (; *symbolic; symbolic++) {
85 if (*symbolic == 'u') {
86 tmpmask = newmode & S_IRWXU;
87 mask |= tmpmask | (tmpmask << 3) | (tmpmask << 6);
88 symbolic++;
89 break;
91 if (*symbolic == 'g') {
92 tmpmask = newmode & S_IRWXG;
93 mask |= tmpmask | (tmpmask >> 3) | (tmpmask << 3);
94 symbolic++;
95 break;
97 if (*symbolic == 'o') {
98 tmpmask = newmode & S_IRWXO;
99 mask |= tmpmask | (tmpmask >> 3) | (tmpmask >> 6);
100 symbolic++;
101 break;
103 if (*symbolic == 'r') {
104 mask |= S_IRUSR | S_IRGRP | S_IROTH;
105 continue;
107 if (*symbolic == 'w') {
108 mask |= S_IWUSR | S_IWGRP | S_IWOTH;
109 continue;
111 if (*symbolic == 'x') {
112 mask |= EXE_MODES;
113 continue;
115 if (*symbolic == 's') {
116 mask |= S_ISUID | S_ISGID;
117 continue;
119 if (*symbolic == 'X') {
120 if (S_ISDIR(oldmode) || (oldmode & EXE_MODES))
121 mask |= EXE_MODES;
122 continue;
124 #ifdef S_ISVTX
125 if (*symbolic == 't') {
126 mask |= S_ISVTX;
127 who |= S_ISVTX;
128 continue;
130 #endif
131 break;
133 switch (action) {
134 case '=':
135 if (who)
136 newmode &= ~who;
137 else
138 newmode = 0;
139 case '+':
140 if (who)
141 newmode |= who & mask;
142 else
143 newmode |= mask & (~u_mask);
144 break;
145 case '-':
146 if (who)
147 newmode &= ~(who & mask);
148 else
149 newmode &= ~mask | u_mask;
152 if (*symbolic) symbolic++;
154 return(newmode);
158 /* Main module. The single option possible (-R) does not warrant a call to
159 * the getopt() stuff.
161 int main(int argc, char *argv[])
163 int ex_code = 0;
165 argc--;
166 argv++;
168 if (argc && strcmp(*argv, "-R") == 0) {
169 argc--;
170 argv++;
171 rflag = 1;
172 } else
173 rflag = 0;
175 if (!argc--) usage();
176 if (!strcmp(argv[0], "--")) { /* Allow chmod -- -r, as in Draft11 example */
177 if (!argc--) usage();
178 argv++;
180 symbolic = *argv++;
181 if (!argc) usage();
183 if (*symbolic >= '0' && *symbolic <= '7') {
184 new_mode = 0;
185 while (*symbolic >= '0' && *symbolic <= '7')
186 new_mode = (new_mode << 3) | (*symbolic++ & 07);
187 if (*symbolic) usage();
188 new_mode &= ALL_MODES;
189 symbolic = (char *) 0;
190 } else
191 u_mask = umask(0);
193 while (argc--)
194 if (do_change(*argv++)) ex_code = 1;
195 return(ex_code);
199 /* Apply a mode change to a given file system element. */
200 int do_change(name)
201 char *name;
203 mode_t m;
204 DIR *dirp;
205 struct dirent *entp;
206 char *namp;
208 if (lstat(name, &st)) {
209 perror(name);
210 return(1);
212 if (S_ISLNK(st.st_mode) && rflag) return(0); /* Note: violates POSIX. */
213 if (!symbolic)
214 m = new_mode;
215 else
216 m = parsemode(symbolic, st.st_mode);
217 if (chmod(name, m)) {
218 perror(name);
219 errors = 1;
220 } else
221 errors = 0;
223 if (S_ISDIR(st.st_mode) && rflag) {
224 if (!(dirp = opendir(name))) {
225 perror(name);
226 return(1);
228 if (name != path) strcpy(path, name);
229 namp = path + strlen(path);
230 *namp++ = '/';
231 while (entp = readdir(dirp))
232 if (entp->d_name[0] != '.' ||
233 (entp->d_name[1] &&
234 (entp->d_name[1] != '.' || entp->d_name[2]))) {
235 strcpy(namp, entp->d_name);
236 errors |= do_change(path);
238 closedir(dirp);
239 *--namp = '\0';
241 return(errors);
245 /* Display Posix prototype */
246 void usage()
248 std_err("Usage: chmod [-R] mode file...\n");
249 exit(1);