More stuff
[oweals/busybox.git] / coreutils / chmod.c
1 /*
2  * Mini chmod implementation for busybox
3  *
4  * Copyright (C) 1998 by Erik Andersen <andersee@debian.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  *
20  */
21
22 #include <stdio.h>
23 #include <grp.h>
24 #include <pwd.h>
25 #include "internal.h"
26
27
28 static mode_t mode=7777;
29
30
31 static const char chmod_usage[] = "[-R] MODE[,MODE]... FILE...\n"
32 "Each MODE is one or more of the letters ugoa, one of the symbols +-= and\n"
33 "one or more of the letters rwxst.\n\n"
34  "\t-R\tchange files and directories recursively.\n";
35
36
37
38 static int fileAction(const char *fileName)
39 {
40     struct stat statBuf;
41     if ((stat(fileName, &statBuf) < 0) || 
42             (chmod(fileName, mode)) < 0) { 
43         perror(fileName);
44         return( FALSE);
45     }
46     return( TRUE);
47 }
48
49 /* [ugoa]{+|-|=}[rwxstl] */
50 int parse_mode( const char* s, mode_t* or, mode_t* and, int* group_execute) 
51 {
52         mode_t  mode = 0;
53         mode_t  groups = S_ISVTX;
54         char    type;
55         char    c;
56
57         do {
58                 for ( ; ; ) {
59                         switch ( c = *s++ ) {
60                         case '\0':
61                                 return (FALSE);
62                         case 'u':
63                                 groups |= S_ISUID|S_IRWXU;
64                                 continue;
65                         case 'g':
66                                 groups |= S_ISGID|S_IRWXG;
67                                 continue;
68                         case 'o':
69                                 groups |= S_IRWXO;
70                                 continue;
71                         case 'a':
72                                 groups |= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
73                                 continue;
74                         case '+':
75                         case '=':
76                         case '-':
77                                 type = c;
78                                 if ( groups == S_ISVTX ) /* The default is "all" */
79                                         groups |= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
80                                 break;
81                         default:
82                                 if ( c >= '0' && c <= '7' && mode == 0 && groups == S_ISVTX ) {
83                                         *and = 0;
84                                         *or = strtol(--s, 0, 010);
85                                         return (TRUE);
86                                 }
87                                 else
88                                         return (FALSE);
89                         }
90                         break;
91                 }
92
93                 while ( (c = *s++) != '\0' ) {
94                         switch ( c ) {
95                         case ',':
96                                 break;
97                         case 'r':
98                                 mode |= S_IRUSR|S_IRGRP|S_IROTH;
99                                 continue;
100                         case 'w':
101                                 mode |= S_IWUSR|S_IWGRP|S_IWOTH;
102                                 continue;
103                         case 'x':
104                                 mode |= S_IXUSR|S_IXGRP|S_IXOTH;
105                                 continue;
106                         case 's':
107                                 if ( group_execute != 0 && (groups & S_IRWXG) ) {
108                                         if ( *group_execute < 0 )
109                                                 return (FALSE);
110                                         if ( type != '-' ) {
111                                                 mode |= S_IXGRP;
112                                                 *group_execute = 1;
113                                         }
114                                 }
115                                 mode |= S_ISUID|S_ISGID;
116                                 continue;
117                         case 'l':
118                                 if ( *group_execute > 0 )
119                                         return (FALSE);
120                                 if ( type != '-' ) {
121                                         *and &= ~S_IXGRP;
122                                         *group_execute = -1;
123                                 }
124                                 mode |= S_ISGID;
125                                 groups |= S_ISGID;
126                                 continue;
127                         case 't':
128                                 mode |= S_ISVTX;
129                                 continue;
130                         default:
131                                 return (FALSE);
132                         }
133                         break;
134                 }
135                 switch ( type ) {
136                 case '=':
137                         *and &= ~(groups);
138                         /* fall through */
139                 case '+':
140                         *or |= mode & groups;
141                         break;
142                 case '-':
143                         *and &= ~(mode & groups);
144                         *or &= *and;
145                         break;
146                 }
147         } while ( c == ',' );
148         return (TRUE);
149 }
150
151
152 int chmod_main(int argc, char **argv)
153 {
154     int recursiveFlag=FALSE;
155     mode_t andWithMode = S_ISVTX|S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
156     mode_t orWithMode = 0;
157     char *invocationName=*argv;
158
159     if (argc < 3) {
160         fprintf(stderr, "Usage: %s %s", invocationName, chmod_usage);
161         exit( FALSE);
162     }
163     argc--;
164     argv++;
165
166     /* Parse options */
167     while (**argv == '-') {
168         while (*++(*argv)) switch (**argv) {
169             case 'R':
170                 recursiveFlag = TRUE;
171                 break;
172             default:
173                 fprintf(stderr, "Unknown option: %c\n", **argv);
174                 exit( FALSE);
175         }
176         argc--;
177         argv++;
178     }
179     
180     /* Find the selected group */
181     if ( parse_mode(*argv, &orWithMode, &andWithMode, 0) == FALSE ) {
182         fprintf(stderr, "%s: Unknown mode: %s\n", invocationName, *argv);
183         exit( FALSE);
184     }
185     mode &= andWithMode;
186     mode |= orWithMode;
187
188     /* Ok, ready to do the deed now */
189     if (argc <= 1) {
190         fprintf(stderr, "%s: too few arguments", invocationName);
191         exit( FALSE);
192     }
193     while (argc-- > 1) {
194         argv++;
195         recursiveAction( *argv, recursiveFlag, TRUE, fileAction, fileAction);
196     }
197     exit(TRUE);
198 }