summaryrefslogtreecommitdiff
path: root/command/chown.c
diff options
context:
space:
mode:
Diffstat (limited to 'command/chown.c')
-rw-r--r--command/chown.c187
1 files changed, 187 insertions, 0 deletions
diff --git a/command/chown.c b/command/chown.c
new file mode 100644
index 0000000..164e3b4
--- /dev/null
+++ b/command/chown.c
@@ -0,0 +1,187 @@
+#include "command.h"
+#include "lslib.h"
+
+#include <dirent.h>
+#include <grp.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <pwd.h>
+
+static struct {
+ bool recurse;
+ bool list_changed;
+ bool verbose;
+ bool quiet;
+ uid_t uid;
+ gid_t gid;
+} flags;
+
+static void help (void) {
+ printf("Usage: chown [-Rcvf]... USER[:[GRP]] FILE...\n\n");
+ printf("Change the owner and/or group of FILEs to USER and/or GRP\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;
+ default:
+ return ARG_INVALID;
+ }
+ return ARG_UNUSED;
+}
+
+static void chown_file(char* path) {
+ int save;
+ struct stat s;
+ DIR* d;
+ struct dirent* file;
+
+ save = push_path_buffer(path);
+
+ if (chown(get_path_buffer(), flags.uid, flags.gid) < 0) {
+ if (!flags.quiet) {
+ error_s("cannot chown '%s': %s", get_path_buffer(), strerror(errno));
+ }
+ pop_path_buffer(save);
+ return;
+ } else if (flags.list_changed) {
+ printf("chown: changed '%s' to %u:%u\n", get_path_buffer(), flags.uid, flags.gid);
+ }
+
+ if (!flags.recurse) {
+ pop_path_buffer(save);
+ return;
+ }
+
+ 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 (!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;
+ chown_file(file->d_name);
+ }
+
+ closedir(d);
+ pop_path_buffer(save);
+}
+
+static void parse_ownership(char* str) {
+ char* user = str;
+ char* group = NULL;
+ char* end = NULL;
+ unsigned long i;
+ struct passwd* p = NULL;
+ struct group* g = NULL;
+
+ for (i = 0; i < strlen(str); i++) {
+ if (str[i] == ':') {
+ str[i] = '\0';
+ group = &str[i] + 1;
+ break;
+ }
+ }
+
+ flags.uid = strtol(user, &end, 10);
+ if (end != user) goto group;
+
+ if ((p = getpwnam(user)) == NULL) {
+ error("invalid user '%s'", user);
+ } else {
+ flags.uid = p->pw_uid;
+ }
+
+group:
+
+ if (group == NULL) {
+ if (p == NULL) {
+ flags.gid = flags.uid;
+ } else {
+ flags.gid = p->pw_gid;
+ }
+ return;
+ }
+
+ flags.gid = strtol(group, &end, 10);
+ if (end != group) return;
+
+ if ((g = getgrnam(group)) == NULL) {
+ error("invalid group '%s'", group);
+ } else {
+ flags.gid = g->gr_gid;
+ }
+}
+
+COMMAND(chown_cmd) {
+
+ int start, i;
+
+ flags.recurse = false;
+ flags.list_changed = false;
+ flags.verbose = false;
+ flags.quiet = false;
+
+ start = parse_args(argc, argv, help, short_arg, NULL);
+
+ if (argc - start < 1) {
+ if (!flags.quiet) {
+ error("no onwership passed");
+ } else {
+ return EXIT_FAILURE;
+ }
+ }
+
+ parse_ownership(argv[start]);
+
+ if (argc - start < 1) {
+ if (!flags.quiet) {
+ error("no files passed");
+ } else {
+ return EXIT_FAILURE;
+ }
+ }
+
+ for (i = start + 1; i < argc; i++) {
+ chown_file(argv[i]);
+ }
+
+ return EXIT_SUCCESS;
+}