commit f3d05ffd0ac4226f9064be5c71606ab9b7d12d92
Author:     Michael Forney <[email protected]>
AuthorDate: Mon Jan 6 13:08:38 2020 -0800
Commit:     Michael Forney <[email protected]>
CommitDate: Mon Jan 6 13:47:26 2020 -0800

    chmod: Implement X perm symbol
    
    Instead of clearing the format bits before calling parsemode, leave
    them in so we can differentiate between directories and other files,
    then clear the format bits in the result.

diff --git a/chmod.1 b/chmod.1
index 4805ce0..fa4131d 100644
--- a/chmod.1
+++ b/chmod.1
@@ -47,7 +47,7 @@ If
 .Ar mode
 is
 .Em symbolic
-"[ugoa]*[+-=][rwxst]*"
+"[ugoa]*[+-=][rwxXst]*"
 .Bl -tag -width Ds
 .It u|g|o|a
 owner | group | other (non-group) | everyone
@@ -55,6 +55,8 @@ owner | group | other (non-group) | everyone
 add | remove | set
 .It r|w|x|s|t
 read | write | execute | setuid and setgid | sticky
+.It X
+execute, if directory or at least one execute bit is already set
 .El
 .Sh OPTIONS
 .Bl -tag -width Ds
diff --git a/chmod.c b/chmod.c
index 2a0085d..512a7ea 100644
--- a/chmod.c
+++ b/chmod.c
@@ -13,7 +13,7 @@ chmodr(const char *path, struct stat *st, void *data, struct 
recursor *r)
 {
        mode_t m;
 
-       m = parsemode(modestr, st->st_mode & ~S_IFMT, mask);
+       m = parsemode(modestr, st->st_mode, mask);
        if (chmod(path, m) < 0) {
                weprintf("chmod %s:", path);
                ret = 1;
@@ -50,8 +50,8 @@ main(int argc, char *argv[])
                        case 'P':
                                r.follow = (*argv)[i];
                                break;
-                       case 'r': case 'w': case 'x': case 's': case 't':
-                               /* -[rwxst] are valid modes, so we're done */
+                       case 'r': case 'w': case 'x': case 'X': case 's': case 
't':
+                               /* -[rwxXst] are valid modes, so we're done */
                                if (i == 1)
                                        goto done;
                                /* fallthrough */
diff --git a/libutil/mode.c b/libutil/mode.c
index 5ba8eb1..b3632ad 100644
--- a/libutil/mode.c
+++ b/libutil/mode.c
@@ -113,6 +113,10 @@ next:
                                case 'x':
                                        perm |= S_IXUSR|S_IXGRP|S_IXOTH;
                                        break;
+                               case 'X':
+                                       if (S_ISDIR(mode) || mode & 
(S_IXUSR|S_IXGRP|S_IXOTH))
+                                               perm |= S_IXUSR|S_IXGRP|S_IXOTH;
+                                       break;
                                case 's':
                                        perm |= S_ISUID|S_ISGID;
                                        break;
@@ -144,5 +148,5 @@ apply:
                        goto next;
                }
        }
-       return mode;
+       return mode & ~S_IFMT;
 }

Reply via email to