coreutils

General Software Utilities
git clone http://git.omkov.net/coreutils
Log | Tree | Refs | README | LICENCE | Download

AuthorJamozed <[email protected]>
Date2020-11-16 12:23:57
Commit72364dd43c95e99112f6fa96735ce1b03e541c5a
Parent70ce5b01e577b7b480ce65f9c471de1336660d47

chmod: Handle R flag

Diffstat

M src/chmod.c | 52 +++++++++++++++++++++++++++++++++++++++++++---------
M src/mkdir.c | 2 +-

2 files changed, 44 insertions, 10 deletions

diff --git a/src/chmod.c b/src/chmod.c
index c6dc1e0..a301e9a 100644
--- a/src/chmod.c
+++ b/src/chmod.c
@@ -1,4 +1,4 @@
-// chmod.c, version 1.0.0
+// chmod.c, version 1.0.1
 // OMKOV coreutils implementation of POSIX chmod
 // Copyright (C) 2020, Jakob Wakeling
 // All rights reserved.
@@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
 */
 
 /*
-	TODO Handle R flag
+	TODO Don't recursively change symlink permissions
 	TODO Handle copying permission
 */
 
@@ -39,14 +39,17 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
 #include "lib/mode.h"
 #include "lib/optget.h"
 
+#include <dirent.h>
 #include <sys/stat.h>
+#include <unistd.h>
 
 #include <errno.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 
-#define VERSION "1.0.0"
+#define VERSION "1.0.1"
 
 static struct lop lops[] = {
 	{ "help",    ARG_NUL, 256 },
@@ -57,8 +60,10 @@ static struct lop lops[] = {
 static bool Rflag;
 
 static mode_t mask;
+static bool   warned = false;
 
-static inline int setmode(const char *path, chmod_t *m);
+static inline int   setmode(const char *path, chmod_t *m);
+static inline char *mkpath(const char *path, const char *file);
 
 static void hlp(void);
 static void ver(void);
@@ -77,19 +82,20 @@ int main(int ac, char *av[]) { A0 = av[0];
 	mask = umask(0); chmod_t *m = strmode(av[opt.ind]);
 	if (!m) { error(1, "%s: invalid mode", av[opt.ind]); }
 
-	bool warned = false;
-	
 	for (char **p = &av[opt.ind + 1]; *p; ++p) {
 		if (setmode(*p, m)) { warn("%s: %s", *p, serr()); warned = true; }
 	}
 
-	if (m) { free(m); } return warned;
+	free(m); return warned;
 }
 
 /* Set the mode of the specified file */
 static inline int setmode(const char *path, chmod_t *m) {
+	struct stat st; register bool ln = false;
+	
 	// Get current permissions of file
-	struct stat st; if (stat(path, &st)) { return 1; };
+	if (lstat(path, &st)) { return 1; };
+	if (S_ISLNK(st.st_mode)) { if (stat(path, &st)) { return 1; } ln = true; }
 	mode_t mode = st.st_mode;
 
 	// Handle each chmod_t
@@ -114,7 +120,35 @@ static inline int setmode(const char *path, chmod_t *m) {
 	} end:;
 
 	// Set permissions of the file to the calculated mode
-	if (chmod(path, mode)) { return 1; } return 0;
+	if (chmod(path, mode)) { return 1; }
+	
+	// Recurse into directories if R flag is set
+	if (Rflag && !ln && S_ISDIR(st.st_mode)) {
+		DIR *di; if (!(di = opendir(path))) { return 1; }
+		
+		// For each entry in directory, recursively call setmode
+		for (struct dirent *de; (de = readdir(di));) {
+			if (de->d_name[0] == '.' && (de->d_name[1] == 0 ||
+				(de->d_name[1] == '.' && de->d_name[2] == 0))) { continue; }
+			
+			char *s = mkpath(path, de->d_name);
+			if (setmode(s, m)) { warn("%s: %s", s, serr()); warned = true; }
+			
+			free(s);
+		}
+		
+		closedir(di);
+	}
+	
+	return 0;
+}
+
+/* Allocate and make path from two components */
+static inline char *mkpath(const char *path, const char *file) {
+	register size_t pl = strlen(path); char *s;
+	s = (char *)malloc((pl + strlen(file) + 2) * sizeof (*s));
+	strcpy(s, path); if (s[pl - 1] != '/') { s[pl] = '/'; s[++pl] = 0; }
+	strcat(s, file); return s;
 }
 
 /* Print help information */
diff --git a/src/mkdir.c b/src/mkdir.c
index a29dd45..0d3fbf3 100644
--- a/src/mkdir.c
+++ b/src/mkdir.c
@@ -98,7 +98,7 @@ int main(int ac, char *av[]) { A0 = av[0];
 		if (makedir(*p, m)) { warn("%s: %s", *p, serr()); warned = true; }
 	}
 
-	if (m) { free(m); } return warned;
+	free(m); return warned;
 }
 
 /* Make directory with specified permissions */