commit 26723ba0dc8a1a94fc7031d7a0973d35cc0b4519
parent ea7a807a6d811bd49ec3c2ae0453974fc4978015
Author: Connor Lane Smith <cls@lubutu.com>
Date: Sat, 11 Jun 2011 00:30:07 +0100
symbolic chmod, thanks pancake
Diffstat:
M | LICENSE | | | 1 | + |
M | chmod.1 | | | 63 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++-------- |
M | chmod.c | | | 98 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------- |
3 files changed, 138 insertions(+), 24 deletions(-)
diff --git a/LICENSE b/LICENSE
@@ -5,6 +5,7 @@ MIT/X Consortium License
© 2011 stateless <stateless@archlinux.us>
© 2011 Rob Pilling <robpilling@gmail.com>
© 2011 Hiltjo Posthuma <hiltjo@codemadness.org>
+© 2011 pancake <pancake@youterm.com>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
diff --git a/chmod.1 b/chmod.1
@@ -4,18 +4,65 @@ chmod \- change file mode
.SH SYNOPSIS
.B chmod
.RB [ \-Rr ]
-.RI mode
+.I octal
+.RI [ file ...]
+.P
+.B chmod
+.RB [ \-Rr ]
+.RB [ ugoa ][ +-= ][ rwxs ]
.RI [ file ...]
.SH DESCRIPTION
.B chmod
-changes the file mode for the given files. The
-.I mode
-is a four digit octal number derived from its comprising bits.
+changes the file mode for the given files.
.P
-The first digit defines the setuid (4) and setgid (2) attributes. The second
-digit defines the owner's permissions: read (4), write (2), and execute (1); the
-third defines permissions for others in the file's group; and the fourth for all
-other users. Leading zeroes may be omitted.
+If the mode is an
+.I octal
+number, the modes are set according to that number's comprising bits. The first
+digit defines the setuid (4) and setgid (2) attributes. The second digit
+defines the owner's permissions: read (4), write (2), and execute (1); the third
+defines permissions for others in the file's group; and the fourth for all other
+users. Leading zeroes may be omitted.
+.P
+Alternatively the mode may be symbolic. The symbol meanings are:
+.TP
+.B u
+modifies owner permissions.
+.PD 0
+.TP
+.B g
+modifies group permissions.
+.TP
+.B o
+modifies other user permissions.
+.TP
+.B a
+modifies all user permissions.
+.PD
+.TP
+.B +
+adds the given permissions to the mode.
+.PD 0
+.TP
+.B -
+removes the given permissions from the mode.
+.TP
+.B =
+sets the mode to the given permissions.
+.PD
+.TP
+.B r
+read permissions.
+.PD 0
+.TP
+.B w
+write permissions.
+.TP
+.B x
+execute permissions.
+.TP
+.B s
+setuid and setgid attributes.
+.PD
.SH OPTIONS
.TP
.B \-R, \-r
diff --git a/chmod.c b/chmod.c
@@ -7,15 +7,16 @@
#include "util.h"
static void chmodr(const char *);
+static void parsemode(const char *);
static bool rflag = false;
+static char oper = '=';
static mode_t mode = 0;
int
main(int argc, char *argv[])
{
char c;
- int octal;
while((c = getopt(argc, argv, "Rr")) != -1)
switch(c) {
@@ -28,21 +29,8 @@ main(int argc, char *argv[])
}
if(optind == argc)
eprintf("usage: %s [-Rr] mode [file...]\n", argv[0]);
- octal = estrtol(argv[optind++], 8);
-
- /* posix doesn't specify modal bits */
- if(octal & 04000) mode |= S_ISUID;
- if(octal & 02000) mode |= S_ISGID;
- if(octal & 00400) mode |= S_IRUSR;
- if(octal & 00200) mode |= S_IWUSR;
- if(octal & 00100) mode |= S_IXUSR;
- if(octal & 00040) mode |= S_IRGRP;
- if(octal & 00020) mode |= S_IWGRP;
- if(octal & 00010) mode |= S_IXGRP;
- if(octal & 00004) mode |= S_IROTH;
- if(octal & 00002) mode |= S_IWOTH;
- if(octal & 00001) mode |= S_IXOTH;
+ parsemode(argv[optind++]);
for(; optind < argc; optind++)
chmodr(argv[optind]);
return EXIT_SUCCESS;
@@ -51,8 +39,86 @@ main(int argc, char *argv[])
void
chmodr(const char *path)
{
- if(chmod(path, mode) == -1)
+ struct stat st;
+
+ if(stat(path, &st) == -1)
+ eprintf("stat %s:", path);
+
+ switch(oper) {
+ case '+':
+ st.st_mode |= mode;
+ break;
+ case '-':
+ st.st_mode &= ~mode;
+ break;
+ }
+ if(chmod(path, st.st_mode) == -1)
eprintf("chmod %s:", path);
if(rflag)
recurse(path, chmodr);
}
+
+void
+parsemode(const char *str)
+{
+ char *end;
+ const char *p;
+ int octal;
+ mode_t mask = 0;
+
+ octal = strtol(str, &end, 8);
+ if(*end == '\0') {
+ if(octal & 04000) mode |= S_ISUID;
+ if(octal & 02000) mode |= S_ISGID;
+ if(octal & 00400) mode |= S_IRUSR;
+ if(octal & 00200) mode |= S_IWUSR;
+ if(octal & 00100) mode |= S_IXUSR;
+ if(octal & 00040) mode |= S_IRGRP;
+ if(octal & 00020) mode |= S_IWGRP;
+ if(octal & 00010) mode |= S_IXGRP;
+ if(octal & 00004) mode |= S_IROTH;
+ if(octal & 00002) mode |= S_IWOTH;
+ if(octal & 00001) mode |= S_IXOTH;
+ return;
+ }
+ for(p = str; *p; p++)
+ switch(*p) {
+ /* masks */
+ case 'u':
+ mask |= S_IRWXU;
+ break;
+ case 'g':
+ mask |= S_IRWXG;
+ break;
+ case 'o':
+ mask |= S_IRWXO;
+ break;
+ case 'a':
+ mask |= S_IRWXU|S_IRWXG|S_IRWXO;
+ break;
+ /* opers */
+ case '+':
+ case '-':
+ case '=':
+ oper = *p;
+ break;
+ /* modes */
+ case 'r':
+ mode |= S_IRUSR|S_IRGRP|S_IROTH;
+ break;
+ case 'w':
+ mode |= S_IWUSR|S_IWGRP|S_IWOTH;
+ break;
+ case 'x':
+ mode |= S_IXUSR|S_IXGRP|S_IXOTH;
+ break;
+ case 's':
+ mode |= S_ISUID|S_ISGID;
+ break;
+ /* error */
+ default:
+ eprintf("%s: invalid mode\n", str);
+ }
+ if(mask)
+ mode &= mask;
+}