- affected_bits = 0;
- ops_to_mask = 0;
- /* Turn on all the bits in `affected_bits' for each group given. */
- for (++mode_string;; ++mode_string)
- switch (*mode_string)
- {
- case 'u':
- affected_bits |= 04700;
- break;
- case 'g':
- affected_bits |= 02070;
- break;
- case 'o':
- affected_bits |= 01007;
- break;
- case 'a':
- affected_bits |= 07777;
- break;
- default:
- goto no_more_affected;
- }
-
- no_more_affected:
- /* If none specified, affect all bits, except perhaps those
- set in the umask. */
- if (affected_bits == 0)
- {
- affected_bits = 07777;
- ops_to_mask = masked_ops;
- }
-
- while (*mode_string == '=' || *mode_string == '+' || *mode_string == '-')
- {
- /* Add the element to the tail of the list, so the operations
- are performed in the correct order. */
- if (head == NULL)
- {
- head = talloc (struct mode_change);
- if (head == NULL)
- return MODE_MEMORY_EXHAUSTED;
- change = head;
- }
- else
- {
- change->next = talloc (struct mode_change);
- if (change->next == NULL)
- {
- mode_free (change);
- return MODE_MEMORY_EXHAUSTED;
- }
- change = change->next;
- }
-
- change->next = NULL;
- change->op = *mode_string; /* One of "=+-". */
- affected_masked = affected_bits;
- if (ops_to_mask & (*mode_string == '=' ? MODE_MASK_EQUALS
- : *mode_string == '+' ? MODE_MASK_PLUS
- : MODE_MASK_MINUS))
- affected_masked &= ~umask_value;
- change->affected = affected_masked;
- change->value = 0;
- change->flags = 0;
-
- /* Set `value' according to the bits set in `affected_masked'. */
- for (++mode_string;; ++mode_string)
- switch (*mode_string)
- {
- case 'r':
- change->value |= 00444 & affected_masked;
- break;
- case 'w':
- change->value |= 00222 & affected_masked;
- break;
- case 'X':
- change->flags |= MODE_X_IF_ANY_X;
- /* Fall through. */
- case 'x':
- change->value |= 00111 & affected_masked;
- break;
- case 's':
- /* Set the setuid/gid bits if `u' or `g' is selected. */
- change->value |= 06000 & affected_masked;
- break;
- case 't':
- /* Set the "save text image" bit if `o' is selected. */
- change->value |= 01000 & affected_masked;
- break;
- case 'u':
- /* Set the affected bits to the value of the `u' bits
- on the same file. */
- if (change->value)
- goto invalid;
- change->value = 00700;
- change->flags |= MODE_COPY_EXISTING;
- break;
- case 'g':
- /* Set the affected bits to the value of the `g' bits
- on the same file. */
- if (change->value)
- goto invalid;
- change->value = 00070;
- change->flags |= MODE_COPY_EXISTING;
- break;
- case 'o':
- /* Set the affected bits to the value of the `o' bits
- on the same file. */
- if (change->value)
- goto invalid;
- change->value = 00007;
- change->flags |= MODE_COPY_EXISTING;
- break;
- default:
- goto no_more_values;
- }
- no_more_values:;
- }
- } while (*mode_string == ',');
+ /* Which bits in the mode are operated on. */
+ mode_t affected = 0;
+
+ /* Turn on all the bits in `affected' for each group given. */
+ for (;; mode_string++)
+ switch (*mode_string)
+ {
+ default:
+ goto invalid;
+ case 'u':
+ affected |= S_ISUID | S_IRWXU;
+ break;
+ case 'g':
+ affected |= S_ISGID | S_IRWXG;
+ break;
+ case 'o':
+ affected |= S_ISVTX | S_IRWXO;
+ break;
+ case 'a':
+ affected |= CHMOD_MODE_BITS;
+ break;
+ case '=': case '+': case '-':
+ goto no_more_affected;
+ }
+ no_more_affected:;
+
+ do
+ {
+ char op = *mode_string++;
+ mode_t value;
+ char flag = MODE_COPY_EXISTING;
+ struct mode_change *change;
+
+ switch (*mode_string++)
+ {
+ case 'u':
+ /* Set the affected bits to the value of the `u' bits
+ on the same file. */
+ value = S_IRWXU;
+ break;
+ case 'g':
+ /* Set the affected bits to the value of the `g' bits
+ on the same file. */
+ value = S_IRWXG;
+ break;
+ case 'o':
+ /* Set the affected bits to the value of the `o' bits
+ on the same file. */
+ value = S_IRWXO;
+ break;
+
+ default:
+ value = 0;
+ flag = MODE_ORDINARY_CHANGE;
+
+ for (mode_string--;; mode_string++)
+ switch (*mode_string)
+ {
+ case 'r':
+ value |= S_IRUSR | S_IRGRP | S_IROTH;
+ break;
+ case 'w':
+ value |= S_IWUSR | S_IWGRP | S_IWOTH;
+ break;
+ case 'x':
+ value |= S_IXUSR | S_IXGRP | S_IXOTH;
+ break;
+ case 'X':
+ flag = MODE_X_IF_ANY_X;
+ break;
+ case 's':
+ /* Set the setuid/gid bits if `u' or `g' is selected. */
+ value |= S_ISUID | S_ISGID;
+ break;
+ case 't':
+ /* Set the "save text image" bit if `o' is selected. */
+ value |= S_ISVTX;
+ break;
+ default:
+ goto no_more_values;
+ }
+ no_more_values:;
+ }
+
+ change = &mc[used++];
+ change->op = op;
+ change->flag = flag;
+ change->affected = affected;
+ change->value = value;
+ change->mentioned = (affected ? affected & value : value);
+ }
+ while (*mode_string == '=' || *mode_string == '+'
+ || *mode_string == '-');
+
+ if (*mode_string != ',')
+ break;
+ }
+