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