2 Copyright © 2013 Alastair Stuart
4 This program is open source software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
24 #include <sys/types.h>
27 #define VERSION "0.01"
44 mode_t user_flags
[] = {
50 mode_t group_flags
[] = {
56 mode_t other_flags
[] = {
64 return (ch
>= '0' && ch
<= '7');
67 void usage(char* program
)
69 printf("Usage: %s [options] [mode] [file ...]\n", program
);
70 printf("Changes file permissions.\n"
72 " -R Run recursively.\n"
74 " --help Print this message.\n"
75 " --version Show version info.\n"
77 "[mode] is in the form of [ugoa][+-=][rwx] or an octal number.\n");
80 struct symbolic_mode
* parse_mode_str(char* mode_str
)
82 struct symbolic_mode
*mode
= calloc(1, sizeof(struct symbolic_mode
));
86 for (i
= 0; i
< strlen(mode_str
); i
++)
100 mode
->who
= S_ISUID
| S_ISGID
| S_ISVTX
;
114 mode
->operation
= mode_str
[i
];
117 for ( ; i
< strlen(mode_str
); i
++)
122 mode
->perm
|= S_IROTH
;
125 mode
->perm
|= S_IWOTH
;
128 mode
->perm
|= S_IXOTH
;
139 mode_t
equals_op(mode_t original
, mode_t who
, mode_t perm
)
141 mode_t
new = original
;
142 mode_t
*flag_array
= NULL
;
147 flag_array
= user_flags
;
148 } else if (who
& S_ISGID
) {
149 flag_array
= group_flags
;
150 } else if (who
& S_ISVTX
) {
151 flag_array
= other_flags
;
156 // turn off all the flags for that type
157 for (int i
= 0; i
< 3; i
++)
159 new &= ~(flag_array
[i
]);
162 if (perm
& S_IROTH
) {
163 new |= flag_array
[0];
165 if (perm
& S_IWOTH
) {
166 new |= flag_array
[1];
168 if (perm
& S_IXOTH
) {
169 new |= flag_array
[2];
172 if (flag_array
== user_flags
) {
174 } else if (flag_array
== group_flags
) {
176 } else if (flag_array
== other_flags
) {
186 void do_chmod(char* file
, struct symbolic_mode
*mode
, mode_t change
, char* argv0
)
189 if (stat(file
, &st
) < 0) {
190 fprintf(stderr
, "%s: %s: %s\n",
191 argv0
, strerror(errno
), realpath(file
, NULL
));
195 mode_t final_mode
= 0;
198 final_mode
= st
.st_mode
;
200 switch (mode
->operation
)
203 final_mode
|= change
;
206 final_mode
&= ~change
;
209 final_mode
= equals_op(final_mode
, mode
->who
, mode
->perm
);
212 fprintf(stderr
, "%s: No operation specified.\n", argv0
);
219 if (chmod(file
, final_mode
) < 0) {
220 fprintf(stderr
, "%s: %s: %s\n",
221 argv0
, strerror(errno
), realpath(file
, NULL
));
225 if (S_ISDIR(st
.st_mode
) && flags
.recursive
) {
226 if (chdir(file
) == 0) {
228 struct dirent
*ep
= NULL
;
231 while ((ep
= readdir(dp
)))
233 if (!(strcmp(ep
->d_name
, ".") == 0 || strcmp(ep
->d_name
, "..") == 0)) {
234 do_chmod(ep
->d_name
, mode
, change
, argv0
);
239 fprintf(stderr
, "%s: %s: %s\n",
240 argv0
, strerror(errno
), realpath(file
, NULL
));
245 fprintf(stderr
, "%s: %s: %s\n",
246 argv0
, strerror(errno
), realpath(file
, NULL
));
251 int main(int argc
, char* argv
[])
253 flags
.recursive
= false;
254 static struct option long_options
[] = {
255 {"recursive", no_argument
, NULL
, 'R'},
256 {"help", no_argument
, NULL
, 1},
257 {"version", no_argument
, NULL
, 2},
262 while ((c
= getopt_long(argc
, argv
, "R",
263 long_options
, NULL
)) != -1)
268 flags
.recursive
= true;
274 printf("chmod (mutos) v"VERSION
"\n");
279 if ((argc
- optind
) < 2) {
280 fprintf(stderr
, "%s: missing operands\n"
281 "Run '%s --help' for usage.\n",
286 char* mode_str
= argv
[optind
];
289 // check if mode is in octal form
290 bool octal_mode
= true;
291 for (size_t i
= 0; i
< strlen(mode_str
); i
++)
293 if (!isoctal(mode_str
[i
])) {
299 // convert string to octal number
300 mode_t final_mode
= strtoul(mode_str
, NULL
, 8);
301 for (int i
= 0; i
< (argc
- optind
); i
++)
303 do_chmod(argv
[optind
], NULL
, final_mode
, argv
[0]);
314 struct symbolic_mode
*mode
= parse_mode_str(mode_str
);
317 fprintf(stderr
, "%s: Invalid mode: %s\n",
322 // if who is not specified, it defaults to user
323 if (mode
->who
== 0) {
329 if (mode
->who
& S_ISUID
) {
330 if (mode
->perm
& S_IROTH
) {
333 if (mode
->perm
& S_IWOTH
) {
336 if (mode
->perm
& S_IXOTH
) {
341 if (mode
->who
& S_ISGID
) {
342 if (mode
->perm
& S_IROTH
) {
345 if (mode
->perm
& S_IWOTH
) {
348 if (mode
->perm
& S_IXOTH
) {
353 if (mode
->who
& S_ISVTX
) {
354 if (mode
->perm
& S_IROTH
) {
357 if (mode
->perm
& S_IWOTH
) {
360 if (mode
->perm
& S_IXOTH
) {
365 for (int i
= 0; i
< (argc
- optind
); i
++)
367 do_chmod(argv
[optind
], mode
, change
, argv
[0]);