diff options
Diffstat (limited to 'command/chmod.c')
-rw-r--r-- | command/chmod.c | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/command/chmod.c b/command/chmod.c new file mode 100644 index 0000000..9e2972e --- /dev/null +++ b/command/chmod.c @@ -0,0 +1,232 @@ +#include "args.h" +#include "command.h" +#include "lslib.h" + +#include <stdlib.h> +#include <sys/types.h> +#include <pwd.h> +#include <unistd.h> +#include <dirent.h> +#include <errno.h> +#include <string.h> + +enum method { + ADD, + SUB, + SET +}; + +static struct { + bool recurse; + bool list_changed; + bool verbose; + bool quiet; + enum method method; + mode_t mode; +} flags; + +static void help (void) { + printf("Usage: chmod [-Rcvf] MODE[,MODE]... FILE...\n\n"); + printf("MODE is octal number (bit pattern sstrwxrwxrwx) or [ugoa]{+|-|=}[rwxXst]\n\n"); + printf("\t-R\tRecurse\n"); + printf("\t-c\tList changed files\n"); + printf("\t-v\tVerbose\n"); + printf("\t-f\tHide errors\n"); +} + +static int short_arg(char c, char* next) { + UNUSED(next); + switch (c) { + case 'R': + flags.recurse = true; + break; + case 'c': + flags.list_changed = true; + break; + case 'v': + flags.verbose = true; + break; + case 'f': + flags.quiet = true; + break; + case 'r': + case 'w': + case 'x': + case 'X': + case 't': + case 's': + return ARG_IGNORE; + default: + return ARG_INVALID; + } + return ARG_UNUSED; +} + +static void chmod_file(char* path) { + int save; + struct stat s; + DIR* d; + struct dirent* file; + mode_t mode = 0; + + save = push_path_buffer(path); + + if (lstat(get_path_buffer(), &s) < 0) { + if (!flags.quiet) { + error_s("cannot stat '%s': %s", get_path_buffer(), strerror(errno)); + } + pop_path_buffer(save); + return; + } + + if (flags.method == SET) { + mode = flags.mode;; + } else if (flags.method == ADD) { + mode = s.st_mode | flags.mode; + } else if (flags.method == SUB) { + mode = s.st_mode & ~flags.mode; + } + + if (chmod(get_path_buffer(), mode) < 0) { + if (!flags.quiet) { + error_s("cannot chmod '%s': %s", get_path_buffer(), strerror(errno)); + } + pop_path_buffer(save); + return; + } else if (flags.list_changed) { + printf("chmod: changed '%s' to %o\n", get_path_buffer(), mode); + } + + if (!flags.recurse) { + pop_path_buffer(save); + return; + } + + if (!S_ISDIR(s.st_mode)) { + pop_path_buffer(save); + return; + } + + d = opendir(get_path_buffer()); + if (d == NULL) { + if (!flags.quiet) { + error_s("cannot open dir '%s': %s", get_path_buffer(), strerror(errno)); + } + pop_path_buffer(save); + return; + } + + while ((file = readdir(d)) != NULL) { + if (is_dot_dir(file->d_name)) continue; + chmod_file(file->d_name); + } + + closedir(d); + pop_path_buffer(save); +} + +static mode_t parse_mode(char** input) { + mode_t mode = 00000; + char* str = *input; + + switch (*str) { + case '=': + flags.method = SET; + break; + case '+': + flags.method = ADD; + break; + case '-': + flags.method = SUB; + break; + default: + flags.method = SET; + mode = get_mode(str); + goto end; + } + +next: + str++; + switch (*str) { + case 'r': + mode |= 00444; + goto next; + case 'w': + mode |= 00200; + goto next; + case 'x': + mode |= 00111; + goto next; + case 'X': + mode |= 00000; + goto next; + case 's': + mode |= 06000; + goto next; + case 't': + mode |= 01000; + goto next; + case '\0': + case ',': + goto end; + default: + error("invalid option: %c", *str); + } + +end: + while (true) { + + if (*str == '\0') { + *input = NULL; + break; + } + + if (*str == ',') { + *input = ++str; + break; + } + + str++; + } + + return mode; +} + +COMMAND(chmod_cmd) { + + int start, i; + + flags.recurse = false; + flags.list_changed = false; + flags.verbose = false; + flags.quiet = false; + flags.mode = 0; + + start = parse_args(argc, argv, help, short_arg, NULL); + + if (argc - start < 1) { + if (!flags.quiet) { + error("no mode provided"); + } else { + return EXIT_FAILURE; + } + } + + do { + flags.mode |= parse_mode(&argv[start]); + } while (argv[start] != NULL); + + if (argc - start < 2) { + if (!flags.quiet) { + error("no files passed"); + } else { + return EXIT_FAILURE; + } + } + + for (i = start + 1; i < argc; i++) { + chmod_file(argv[i]); + } + + return EXIT_SUCCESS; +} |