Replace current verbose GPL stuff in libbb/*.c with one-line GPL boilerplate.
[oweals/busybox.git] / libbb / parse_mode.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * parse_mode implementation for busybox
4  *
5  * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
6  *
7  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
8  */
9
10 /* http://www.opengroup.org/onlinepubs/007904975/utilities/chmod.html */
11
12 #include <stdlib.h>
13 #include <assert.h>
14 #include <sys/stat.h>
15 #include "libbb.h"
16
17 #define FILEMODEBITS    (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
18
19 int bb_parse_mode(const char *s, mode_t *current_mode)
20 {
21         static const mode_t who_mask[] = {
22                 S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO, /* a */
23                 S_ISUID | S_IRWXU,              /* u */
24                 S_ISGID | S_IRWXG,              /* g */
25                 S_IRWXO                                 /* o */
26         };
27
28         static const mode_t perm_mask[] = {
29                 S_IRUSR | S_IRGRP | S_IROTH, /* r */
30                 S_IWUSR | S_IWGRP | S_IWOTH, /* w */
31                 S_IXUSR | S_IXGRP | S_IXOTH, /* x */
32                 S_IXUSR | S_IXGRP | S_IXOTH, /* X -- special -- see below */
33                 S_ISUID | S_ISGID,              /* s */
34                 S_ISVTX                                 /* t */
35         };
36
37         static const char who_chars[] = "augo";
38         static const char perm_chars[] = "rwxXst";
39
40         const char *p;
41
42         mode_t wholist;
43         mode_t permlist;
44         mode_t mask;
45         mode_t new_mode;
46         char op;
47
48         assert(s);
49
50         if (((unsigned int)(*s - '0')) < 8) {
51                 unsigned long tmp;
52                 char *e;
53
54                 tmp = strtol(s, &e, 8);
55                 if (*e || (tmp > 07777U)) { /* Check range and trailing chars. */
56                         return 0;
57                 }
58                 *current_mode = tmp;
59                 return 1;
60         }
61
62         mask = umask(0);
63         umask(mask);
64
65         new_mode = *current_mode;
66
67         /* Note: We allow empty clauses, and hence empty modes.
68          * We treat an empty mode as no change to perms. */
69
70         while (*s) {    /* Process clauses. */
71
72                 if (*s == ',') {        /* We allow empty clauses. */
73                         ++s;
74                         continue;
75                 }
76
77                 /* Get a wholist. */
78                 wholist = 0;
79
80         WHO_LIST:
81                 p = who_chars;
82                 do {
83                         if (*p == *s) {
84                                 wholist |= who_mask[(int)(p-who_chars)];
85                                 if (!*++s) {
86                                         return 0;
87                                 }
88                                 goto WHO_LIST;
89                         }
90                 } while (*++p);
91
92                 do {    /* Process action list. */
93                         if ((*s != '+') && (*s != '-')) {
94                                 if (*s != '=') {
95                                         return 0;
96                                 }
97                                 /* Since op is '=', clear all bits corresponding to the
98                                  * wholist, of all file bits if wholist is empty. */
99                                 permlist = ~FILEMODEBITS;
100                                 if (wholist) {
101                                         permlist = ~wholist;
102                                 }
103                                 new_mode &= permlist;
104                         }
105                         op = *s++;
106
107                         /* Check for permcopy. */
108                         p = who_chars + 1;      /* Skip 'a' entry. */
109                         do {
110                                 if (*p == *s) {
111                                         int i = 0;
112                                         permlist = who_mask[(int)(p-who_chars)]
113                                                          & (S_IRWXU | S_IRWXG | S_IRWXO)
114                                                          & new_mode;
115                                         do {
116                                                 if (permlist & perm_mask[i]) {
117                                                         permlist |= perm_mask[i];
118                                                 }
119                                         } while (++i < 3);
120                                         ++s;
121                                         goto GOT_ACTION;
122                                 }
123                         } while (*++p);
124
125                         /* It was not a permcopy, so get a permlist. */
126                         permlist = 0;
127
128                 PERM_LIST:
129                         p = perm_chars;
130                         do {
131                                 if (*p == *s) {
132                                         if ((*p != 'X')
133                                                 || (new_mode & (S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH))
134                                         ) {
135                                                 permlist |= perm_mask[(int)(p-perm_chars)];
136                                         }
137                                         if (!*++s) {
138                                                 break;
139                                         }
140                                         goto PERM_LIST;
141                                 }
142                         } while (*++p);
143
144                 GOT_ACTION:
145                         if (permlist) { /* The permlist was nonempty. */
146                                 mode_t tmp = ~mask;
147                                 if (wholist) {
148                                         tmp = wholist;
149                                 }
150                                 permlist &= tmp;
151
152                                 if (op == '-') {
153                                         new_mode &= ~permlist;
154                                 } else {
155                                         new_mode |= permlist;
156                                 }
157                         }
158                 } while (*s && (*s != ','));
159         }
160
161         *current_mode = new_mode;
162
163         return 1;
164 }