]>
Commit | Line | Data |
---|---|---|
6a5fa4e0 MG |
1 | /* |
2 | * tclXchmod.c -- | |
3 | * | |
4 | * Chmod, chown and chgrp Tcl commands. | |
5 | *----------------------------------------------------------------------------- | |
6 | * Copyright 1992 Karl Lehenbauer and Mark Diekhans. | |
7 | * | |
8 | * Permission to use, copy, modify, and distribute this software and its | |
9 | * documentation for any purpose and without fee is hereby granted, provided | |
10 | * that the above copyright notice appear in all copies. Karl Lehenbauer and | |
11 | * Mark Diekhans make no representations about the suitability of this | |
12 | * software for any purpose. It is provided "as is" without express or | |
13 | * implied warranty. | |
14 | *----------------------------------------------------------------------------- | |
15 | * $Id: tclXchmod.c,v 2.0 1992/10/16 04:50:26 markd Rel $ | |
16 | *----------------------------------------------------------------------------- | |
17 | */ | |
18 | ||
19 | #include "tclxint.h" | |
20 | ||
21 | /* | |
22 | * Prototypes of internal functions. | |
23 | */ | |
24 | static int | |
25 | ConvSymMode _ANSI_ARGS_((Tcl_Interp *interp, | |
26 | char *symMode, | |
27 | int modeVal)); | |
28 | ||
29 | \f | |
30 | /* | |
31 | *----------------------------------------------------------------------------- | |
32 | * | |
33 | * ConvSymMode -- | |
34 | * Parse and convert symbolic file permissions as specified by chmod(C). | |
35 | * | |
36 | * Parameters: | |
37 | * o interp - Pointer to the current interpreter, error messages will be | |
38 | * returned in the result. | |
39 | * o symMode - The symbolic permissions to parse. | |
40 | * o modeVal - The existing permissions value on a file. | |
41 | * | |
42 | * Results: | |
43 | * The new permissions, or -1 if invalid permissions where supplied. | |
44 | * | |
45 | *----------------------------------------------------------------------------- | |
46 | */ | |
47 | static int | |
48 | ConvSymMode (interp, symMode, modeVal) | |
49 | Tcl_Interp *interp; | |
50 | char *symMode; | |
51 | int modeVal; | |
52 | ||
53 | { | |
54 | int user, group, other; | |
55 | char operator, *scanPtr; | |
56 | int rwxMask, ugoMask, setUID, sticky, locking; | |
57 | int newMode; | |
58 | ||
59 | scanPtr = symMode; | |
60 | ||
61 | while (*scanPtr != '\0') { | |
62 | user = group = other = FALSE; | |
63 | ||
64 | /* | |
65 | * Scan who field. | |
66 | */ | |
67 | while (! ((*scanPtr == '+') || | |
68 | (*scanPtr == '-') || | |
69 | (*scanPtr == '='))) { | |
70 | switch (*scanPtr) { | |
71 | case 'a': | |
72 | user = group = other = TRUE; | |
73 | break; | |
74 | case 'u': | |
75 | user = TRUE; | |
76 | break; | |
77 | case 'g': | |
78 | group = TRUE; | |
79 | break; | |
80 | case 'o': | |
81 | other = TRUE; | |
82 | break; | |
83 | default: | |
84 | goto invalidMode; | |
85 | } | |
86 | scanPtr++; | |
87 | } | |
88 | ||
89 | /* | |
90 | * If none where specified, that means all. | |
91 | */ | |
92 | ||
93 | if (! (user || group || other)) | |
94 | user = group = other = TRUE; | |
95 | ||
96 | operator = *scanPtr++; | |
97 | ||
98 | /* | |
99 | * Decode the permissions | |
100 | */ | |
101 | ||
102 | rwxMask = 0; | |
103 | setUID = sticky = locking = FALSE; | |
104 | ||
105 | /* | |
106 | * Scan permissions field | |
107 | */ | |
108 | while (! ((*scanPtr == ',') || (*scanPtr == 0))) { | |
109 | switch (*scanPtr) { | |
110 | case 'r': | |
111 | rwxMask |= 4; | |
112 | break; | |
113 | case 'w': | |
114 | rwxMask |= 2; | |
115 | break; | |
116 | case 'x': | |
117 | rwxMask |= 1; | |
118 | break; | |
119 | case 's': | |
120 | setUID = TRUE; | |
121 | break; | |
122 | case 't': | |
123 | sticky = TRUE; | |
124 | break; | |
125 | case 'l': | |
126 | locking = TRUE; | |
127 | break; | |
128 | default: | |
129 | goto invalidMode; | |
130 | } | |
131 | scanPtr++; | |
132 | } | |
133 | ||
134 | /* | |
135 | * Build mode map of specified values. | |
136 | */ | |
137 | ||
138 | newMode = 0; | |
139 | ugoMask = 0; | |
140 | if (user) { | |
141 | newMode |= rwxMask << 6; | |
142 | ugoMask |= 0700; | |
143 | } | |
144 | if (group) { | |
145 | newMode |= rwxMask << 3; | |
146 | ugoMask |= 0070; | |
147 | } | |
148 | if (other) { | |
149 | newMode |= rwxMask; | |
150 | ugoMask |= 0007; | |
151 | } | |
152 | if (setUID && user) | |
153 | newMode |= 04000; | |
154 | if ((setUID || locking) && group) | |
155 | newMode |= 02000; | |
156 | if (sticky) | |
157 | newMode |= 01000; | |
158 | ||
159 | /* | |
160 | * Add to cumulative mode based on operator. | |
161 | */ | |
162 | ||
163 | if (operator == '+') | |
164 | modeVal |= newMode; | |
165 | else if (operator == '-') | |
166 | modeVal &= ~newMode; | |
167 | else if (operator == '=') | |
168 | modeVal |= (modeVal & ugoMask) | newMode; | |
169 | if (*scanPtr == ',') | |
170 | scanPtr++; | |
171 | } | |
172 | ||
173 | return modeVal; | |
174 | ||
175 | invalidMode: | |
176 | Tcl_AppendResult (interp, "invalid file mode \"", symMode, "\"", | |
177 | (char *) NULL); | |
178 | return -1; | |
179 | } | |
180 | \f | |
181 | /* | |
182 | *----------------------------------------------------------------------------- | |
183 | * | |
184 | * Tcl_ChmodCmd -- | |
185 | * Implements the TCL chmod command: | |
186 | * chmod mode filelist | |
187 | * | |
188 | * Results: | |
189 | * Standard TCL results, may return the UNIX system error message. | |
190 | * | |
191 | *----------------------------------------------------------------------------- | |
192 | */ | |
193 | int | |
194 | Tcl_ChmodCmd (clientData, interp, argc, argv) | |
195 | ClientData clientData; | |
196 | Tcl_Interp *interp; | |
197 | int argc; | |
198 | char **argv; | |
199 | { | |
200 | int idx, modeVal, fileArgc, absMode; | |
201 | char **fileArgv; | |
202 | struct stat fileStat; | |
203 | ||
204 | if (argc != 3) { | |
205 | Tcl_AppendResult (interp, tclXWrongArgs, argv [0], | |
206 | " mode filelist", (char *) NULL); | |
207 | return TCL_ERROR; | |
208 | } | |
209 | ||
210 | if (isdigit (argv [1][0])) { | |
211 | if (Tcl_GetInt (interp, argv [1], &modeVal) != TCL_OK) | |
212 | return TCL_ERROR; | |
213 | absMode = TRUE; | |
214 | } else | |
215 | absMode = FALSE; | |
216 | ||
217 | if (Tcl_SplitList (interp, argv [2], &fileArgc, &fileArgv) != TCL_OK) | |
218 | return TCL_ERROR; | |
219 | ||
220 | for (idx = 0; idx < fileArgc; idx++) { | |
221 | if (!absMode) { | |
222 | if (stat (fileArgv [idx], &fileStat) != 0) | |
223 | goto fileError; | |
224 | modeVal = ConvSymMode (interp, argv [1], fileStat.st_mode & 07777); | |
225 | if (modeVal < 0) | |
226 | goto errorExit; | |
227 | } | |
228 | if (chmod (fileArgv [idx], (unsigned short) modeVal) < 0) | |
229 | goto fileError; | |
230 | } | |
231 | ||
232 | exitPoint: | |
233 | ckfree ((char *) fileArgv); | |
234 | return TCL_OK; | |
235 | ||
236 | fileError: | |
237 | /* | |
238 | * Error accessing file, assumes file name is fileArgv [idx]. | |
239 | */ | |
240 | Tcl_AppendResult (interp, fileArgv [idx], ": ", Tcl_UnixError (interp), | |
241 | (char *) NULL); | |
242 | ||
243 | errorExit: | |
244 | ckfree ((char *) fileArgv); | |
245 | return TCL_ERROR; | |
246 | } | |
247 | \f | |
248 | /* | |
249 | *----------------------------------------------------------------------------- | |
250 | * | |
251 | * Tcl_ChownCmd -- | |
252 | * Implements the TCL chown command: | |
253 | * chown owner filelist | |
254 | * chown {owner group} filelist | |
255 | * | |
256 | * Results: | |
257 | * Standard TCL results, may return the UNIX system error message. | |
258 | * | |
259 | *----------------------------------------------------------------------------- | |
260 | */ | |
261 | int | |
262 | Tcl_ChownCmd (clientData, interp, argc, argv) | |
263 | ClientData clientData; | |
264 | Tcl_Interp *interp; | |
265 | int argc; | |
266 | char **argv; | |
267 | { | |
268 | int idx, ownArgc, fileArgc; | |
269 | char **ownArgv, **fileArgv = NULL; | |
270 | struct stat fileStat; | |
271 | int useOwnerGrp, chGroup, ownerId, groupId; | |
272 | struct passwd *passwdPtr; | |
273 | struct group *groupPtr; | |
274 | int result = TCL_ERROR; | |
275 | ||
276 | if (argc != 3) { | |
277 | Tcl_AppendResult (interp, tclXWrongArgs, argv [0], | |
278 | " owner|{owner group} filelist", (char *) NULL); | |
279 | return TCL_ERROR; | |
280 | } | |
281 | ||
282 | if (Tcl_SplitList (interp, argv[1], &ownArgc, &ownArgv) != TCL_OK) | |
283 | return TCL_ERROR; | |
284 | if ((ownArgc < 1) || (ownArgc > 2)) { | |
285 | interp->result = "owner arg should be: owner or {owner group}"; | |
286 | goto exitPoint; | |
287 | } | |
288 | if (ownArgc == 2) { | |
289 | useOwnerGrp = (ownArgv [1][0] == '\0'); | |
290 | chGroup = TRUE; | |
291 | } else | |
292 | chGroup = FALSE; | |
293 | ||
294 | /* | |
295 | * Get the owner id, either convert the name or use it as an integer. | |
296 | */ | |
297 | passwdPtr = getpwnam (ownArgv [0]); | |
298 | if (passwdPtr != NULL) | |
299 | ownerId = passwdPtr->pw_uid; | |
300 | else { | |
301 | if (!Tcl_StrToInt (ownArgv [0], 10, &ownerId)) { | |
302 | Tcl_AppendResult (interp, "unknown user id: ", ownArgv [0], | |
303 | (char *) NULL); | |
304 | goto exitPoint; | |
305 | } | |
306 | } | |
307 | /* | |
308 | * Get the group id, this is either the specified id or name, or the | |
309 | * if associated with the specified user. | |
310 | */ | |
311 | if (chGroup) { | |
312 | if (useOwnerGrp) { | |
313 | if (passwdPtr == NULL) { | |
314 | passwdPtr = getpwuid (ownerId); | |
315 | if (passwdPtr != NULL) { | |
316 | Tcl_AppendResult (interp, "unknown user id: ", | |
317 | ownArgv [0], (char *) NULL); | |
318 | goto exitPoint; | |
319 | } | |
320 | } | |
321 | groupId = passwdPtr->pw_gid; | |
322 | } else { | |
323 | groupPtr = getgrnam (ownArgv [1]); | |
324 | if (groupPtr != NULL) | |
325 | groupId = groupPtr->gr_gid; | |
326 | else { | |
327 | if (!Tcl_StrToInt (ownArgv [1], 10, &groupId)) { | |
328 | Tcl_AppendResult (interp, "unknown group id: ", | |
329 | ownArgv [1], (char *) NULL); | |
330 | goto exitPoint; | |
331 | } | |
332 | } | |
333 | } | |
334 | } | |
335 | if (Tcl_SplitList (interp, argv [2], &fileArgc, &fileArgv) != TCL_OK) | |
336 | goto exitPoint; | |
337 | ||
338 | for (idx = 0; idx < fileArgc; idx++) { | |
339 | if (!chGroup) { | |
340 | if (stat (fileArgv [idx], &fileStat) != 0) { | |
341 | Tcl_AppendResult (interp, fileArgv [idx], ": ", | |
342 | Tcl_UnixError (interp), (char *) NULL); | |
343 | goto exitPoint; | |
344 | } | |
345 | groupId = fileStat.st_gid; | |
346 | } | |
347 | ||
348 | if (chown (fileArgv[idx], ownerId, groupId) < 0) { | |
349 | Tcl_AppendResult (interp, fileArgv [idx], ": ", | |
350 | Tcl_UnixError (interp), (char *) NULL); | |
351 | goto exitPoint; | |
352 | } | |
353 | ||
354 | } /* Modify each file */ | |
355 | ||
356 | result = TCL_OK; | |
357 | exitPoint: | |
358 | ckfree ((char *) ownArgv); | |
359 | if (fileArgv != NULL) | |
360 | ckfree ((char *) fileArgv); | |
361 | return result; | |
362 | } | |
363 | \f | |
364 | /* | |
365 | *----------------------------------------------------------------------------- | |
366 | * | |
367 | * Tcl_ChgrpCmd -- | |
368 | * Implements the TCL chgrp command: | |
369 | * chgrp group filelist | |
370 | * | |
371 | * Results: | |
372 | * Standard TCL results, may return the UNIX system error message. | |
373 | * | |
374 | *----------------------------------------------------------------------------- | |
375 | */ | |
376 | int | |
377 | Tcl_ChgrpCmd (clientData, interp, argc, argv) | |
378 | ClientData clientData; | |
379 | Tcl_Interp *interp; | |
380 | int argc; | |
381 | char **argv; | |
382 | { | |
383 | int idx, fileArgc, groupId, result = TCL_ERROR; | |
384 | char **fileArgv; | |
385 | struct stat fileStat; | |
386 | struct group *groupPtr; | |
387 | ||
388 | if (argc < 3) { | |
389 | Tcl_AppendResult (interp, tclXWrongArgs, argv [0], | |
390 | " group filelist", (char *) NULL); | |
391 | return TCL_ERROR; | |
392 | } | |
393 | ||
394 | groupPtr = getgrnam (argv [1]); | |
395 | if (groupPtr != NULL) | |
396 | groupId = groupPtr->gr_gid; | |
397 | else { | |
398 | if (!Tcl_StrToInt (argv [1], 10, &groupId)) { | |
399 | Tcl_AppendResult (interp, "unknown group id: ", argv [1], | |
400 | (char *) NULL); | |
401 | return TCL_ERROR; | |
402 | } | |
403 | } | |
404 | if (Tcl_SplitList (interp, argv [2], &fileArgc, &fileArgv) != TCL_OK) | |
405 | return TCL_ERROR; | |
406 | ||
407 | for (idx = 0; idx < fileArgc; idx++) { | |
408 | if ((stat (fileArgv [idx], &fileStat) != 0) || | |
409 | (chown (fileArgv[idx], fileStat.st_uid, groupId) < 0)) { | |
410 | Tcl_AppendResult (interp, fileArgv [idx], ": ", | |
411 | Tcl_UnixError (interp), (char *) NULL); | |
412 | goto exitPoint; | |
413 | } | |
414 | } /* Modify each file */ | |
415 | ||
416 | result = TCL_OK; | |
417 | exitPoint: | |
418 | ckfree ((char *) fileArgv); | |
419 | return result; | |
420 | } |