lazysphere/command/chmod.c

231 lines
4.6 KiB
C
Raw Normal View History

2023-05-12 21:38:41 +00:00
#include "command.h"
#include "lslib.h"
#include <stdlib.h>
#include <sys/types.h>
#include <pwd.h>
#include <unistd.h>
#include <dirent.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");
2023-05-15 01:43:02 +00:00
printf("MODE is octal number (bit pattern sstrwxrwxrwx) or {+|-|=}[rwxXst]\n\n");
2023-05-12 21:38:41 +00:00
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) {
2023-05-15 01:43:02 +00:00
error_s("cannot stat '%s'", get_path_buffer());
2023-05-12 21:38:41 +00:00
}
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) {
2023-05-15 01:43:02 +00:00
error_s("cannot chmod '%s'", get_path_buffer());
2023-05-12 21:38:41 +00:00
}
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) {
2023-05-15 01:43:02 +00:00
error_s("cannot open dir '%s'", get_path_buffer());
2023-05-12 21:38:41 +00:00
}
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;
}
2023-05-15 14:57:33 +00:00
COMMAND(chmod_main) {
2023-05-12 21:38:41 +00:00
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;
}