master xplshn/aruu / shared / libutil / mode.c
  1/* See LICENSE file for copyright and license details. */
  2#include <stdlib.h>
  3#include <string.h>
  4#include <sys/stat.h>
  5#include <unistd.h>
  6
  7#include "../util.h"
  8
  9mode_t
 10getumask(void)
 11{
 12  mode_t mask = umask(0);
 13  umask(mask);
 14  return mask;
 15}
 16
 17mode_t
 18parsemode(const char *str, mode_t mode, mode_t mask)
 19{
 20  char       *end;
 21  const char *p = str;
 22  int         octal, op;
 23  mode_t      who, perm, clear;
 24
 25  octal = strtol(str, &end, 8);
 26  if (*end == '\0') {
 27    if (octal < 0 || octal > 07777)
 28      eprintf("%s: invalid mode\n", str);
 29    return octal;
 30  }
 31next:
 32  /* first, determine which bits we will be modifying */
 33  for (who = 0; *p; p++) {
 34    switch (*p) {
 35      /* masks */
 36      case 'u':
 37        who |= S_IRWXU | S_ISUID;
 38        continue;
 39      case 'g':
 40        who |= S_IRWXG | S_ISGID;
 41        continue;
 42      case 'o':
 43        who |= S_IRWXO | S_ISVTX;
 44        continue;
 45      case 'a':
 46        who |= S_IRWXU | S_ISUID | S_IRWXG | S_ISGID | S_IRWXO | S_ISVTX;
 47        continue;
 48    }
 49    break;
 50  }
 51  if (who) {
 52    clear = who;
 53  } else {
 54    clear = S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO;
 55    who   = ~mask;
 56  }
 57  while (*p) {
 58    switch (*p) {
 59      /* opers */
 60      case '=':
 61      case '+':
 62      case '-':
 63        op = (int)*p;
 64        break;
 65      default:
 66        eprintf("%s: invalid mode\n", str);
 67    }
 68
 69    perm = 0;
 70    switch (*++p) {
 71      /* copy */
 72      case 'u':
 73        if (mode & S_IRUSR)
 74          perm |= S_IRUSR | S_IRGRP | S_IROTH;
 75        if (mode & S_IWUSR)
 76          perm |= S_IWUSR | S_IWGRP | S_IWOTH;
 77        if (mode & S_IXUSR)
 78          perm |= S_IXUSR | S_IXGRP | S_IXOTH;
 79        if (mode & S_ISUID)
 80          perm |= S_ISUID | S_ISGID;
 81        p++;
 82        break;
 83      case 'g':
 84        if (mode & S_IRGRP)
 85          perm |= S_IRUSR | S_IRGRP | S_IROTH;
 86        if (mode & S_IWGRP)
 87          perm |= S_IWUSR | S_IWGRP | S_IWOTH;
 88        if (mode & S_IXGRP)
 89          perm |= S_IXUSR | S_IXGRP | S_IXOTH;
 90        if (mode & S_ISGID)
 91          perm |= S_ISUID | S_ISGID;
 92        p++;
 93        break;
 94      case 'o':
 95        if (mode & S_IROTH)
 96          perm |= S_IRUSR | S_IRGRP | S_IROTH;
 97        if (mode & S_IWOTH)
 98          perm |= S_IWUSR | S_IWGRP | S_IWOTH;
 99        if (mode & S_IXOTH)
100          perm |= S_IXUSR | S_IXGRP | S_IXOTH;
101        p++;
102        break;
103      default:
104        for (; *p; p++) {
105          switch (*p) {
106            /* modes */
107            case 'r':
108              perm |= S_IRUSR | S_IRGRP | S_IROTH;
109              break;
110            case 'w':
111              perm |= S_IWUSR | S_IWGRP | S_IWOTH;
112              break;
113            case 'x':
114              perm |= S_IXUSR | S_IXGRP | S_IXOTH;
115              break;
116            case 'X':
117              if (S_ISDIR(mode) || mode & (S_IXUSR | S_IXGRP | S_IXOTH))
118                perm |= S_IXUSR | S_IXGRP | S_IXOTH;
119              break;
120            case 's':
121              perm |= S_ISUID | S_ISGID;
122              break;
123            case 't':
124              perm |= S_ISVTX;
125              break;
126            default:
127              goto apply;
128          }
129        }
130    }
131
132  apply:
133    /* apply */
134    switch (op) {
135      case '=':
136        mode &= ~clear;
137        /* fallthrough */
138      case '+':
139        mode |= perm & who;
140        break;
141      case '-':
142        mode &= ~(perm & who);
143        break;
144    }
145    /* if we hit a comma, move on to the next clause */
146    if (*p == ',') {
147      p++;
148      goto next;
149    }
150  }
151  return mode & ~S_IFMT;
152}