sbase

suckless unix tools
git clone git://git.2f30.org/sbase.git
Log | Files | Refs | README | LICENSE

commit 26723ba0dc8a1a94fc7031d7a0973d35cc0b4519
parent ea7a807a6d811bd49ec3c2ae0453974fc4978015
Author: Connor Lane Smith <cls@lubutu.com>
Date:   Sat Jun 11 00:30:07 +0100

symbolic chmod, thanks pancake
Diffstat:
LICENSE | 1+
chmod.1 | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
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; +}