home *** CD-ROM | disk | FTP | other *** search
- /*
- * tclXchmod.c --
- *
- * Chmod, chown and chgrp Tcl commands.
- *-----------------------------------------------------------------------------
- * Copyright 1991-1993 Karl Lehenbauer and Mark Diekhans.
- *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation for any purpose and without fee is hereby granted, provided
- * that the above copyright notice appear in all copies. Karl Lehenbauer and
- * Mark Diekhans make no representations about the suitability of this
- * software for any purpose. It is provided "as is" without express or
- * implied warranty.
- *-----------------------------------------------------------------------------
- * $Id: tclXchmod.c,v 3.0 1993/11/19 06:58:22 markd Rel $
- *-----------------------------------------------------------------------------
- */
-
- #include "tclExtdInt.h"
-
- /*
- * Type used for returning parsed owner informtion.
- */
- typedef struct {
- int changeGroup;
- uid_t userId;
- gid_t groupId;
- } ownerInfo_t;
-
- /*
- * Prototypes of internal functions.
- */
- static int
- ConvSymMode _ANSI_ARGS_((Tcl_Interp *interp,
- char *symMode,
- int modeVal));
-
- static int
- ConvertGroupId _ANSI_ARGS_((Tcl_Interp *interp,
- char *strId,
- gid_t *groupIdPtr));
-
- static int
- ConvertUserGroup _ANSI_ARGS_((Tcl_Interp *interp,
- char *ownerGroupList,
- ownerInfo_t *ownerInfoPtr));
-
- /*
- *-----------------------------------------------------------------------------
- *
- * ConvSymMode --
- * Parse and convert symbolic file permissions as specified by chmod(C).
- *
- * Parameters:
- * o interp - Pointer to the current interpreter, error messages will be
- * returned in the result.
- * o symMode - The symbolic permissions to parse.
- * o modeVal - The existing permissions value on a file.
- *
- * Results:
- * The new permissions, or -1 if invalid permissions where supplied.
- *
- *-----------------------------------------------------------------------------
- */
- static int
- ConvSymMode (interp, symMode, modeVal)
- Tcl_Interp *interp;
- char *symMode;
- int modeVal;
-
- {
- int user, group, other;
- char operator, *scanPtr;
- int rwxMask, ugoMask, setUID, sticky, locking;
- int newMode;
-
- scanPtr = symMode;
-
- while (*scanPtr != '\0') {
- user = group = other = FALSE;
-
- /*
- * Scan who field.
- */
- while (! ((*scanPtr == '+') ||
- (*scanPtr == '-') ||
- (*scanPtr == '='))) {
- switch (*scanPtr) {
- case 'a':
- user = group = other = TRUE;
- break;
- case 'u':
- user = TRUE;
- break;
- case 'g':
- group = TRUE;
- break;
- case 'o':
- other = TRUE;
- break;
- default:
- goto invalidMode;
- }
- scanPtr++;
- }
-
- /*
- * If none where specified, that means all.
- */
-
- if (! (user || group || other))
- user = group = other = TRUE;
-
- operator = *scanPtr++;
-
- /*
- * Decode the permissions
- */
-
- rwxMask = 0;
- setUID = sticky = locking = FALSE;
-
- /*
- * Scan permissions field
- */
- while (! ((*scanPtr == ',') || (*scanPtr == 0))) {
- switch (*scanPtr) {
- case 'r':
- rwxMask |= 4;
- break;
- case 'w':
- rwxMask |= 2;
- break;
- case 'x':
- rwxMask |= 1;
- break;
- case 's':
- setUID = TRUE;
- break;
- case 't':
- sticky = TRUE;
- break;
- case 'l':
- locking = TRUE;
- break;
- default:
- goto invalidMode;
- }
- scanPtr++;
- }
-
- /*
- * Build mode map of specified values.
- */
-
- newMode = 0;
- ugoMask = 0;
- if (user) {
- newMode |= rwxMask << 6;
- ugoMask |= 0700;
- }
- if (group) {
- newMode |= rwxMask << 3;
- ugoMask |= 0070;
- }
- if (other) {
- newMode |= rwxMask;
- ugoMask |= 0007;
- }
- if (setUID && user)
- newMode |= 04000;
- if ((setUID || locking) && group)
- newMode |= 02000;
- if (sticky)
- newMode |= 01000;
-
- /*
- * Add to cumulative mode based on operator.
- */
-
- if (operator == '+')
- modeVal |= newMode;
- else if (operator == '-')
- modeVal &= ~newMode;
- else if (operator == '=')
- modeVal |= (modeVal & ugoMask) | newMode;
- if (*scanPtr == ',')
- scanPtr++;
- }
-
- return modeVal;
-
- invalidMode:
- Tcl_AppendResult (interp, "invalid file mode \"", symMode, "\"",
- (char *) NULL);
- return -1;
- }
-
- /*
- *-----------------------------------------------------------------------------
- *
- * Tcl_ChmodCmd --
- * Implements the TCL chmod command:
- * chmod mode filelist
- *
- * Results:
- * Standard TCL results, may return the UNIX system error message.
- *
- *-----------------------------------------------------------------------------
- */
- int
- Tcl_ChmodCmd (clientData, interp, argc, argv)
- ClientData clientData;
- Tcl_Interp *interp;
- int argc;
- char **argv;
- {
- int idx, modeVal, fileArgc, absMode;
- char **fileArgv, *fileName;
- struct stat fileStat;
- Tcl_DString tildeBuf;
-
- Tcl_DStringInit (&tildeBuf);
-
- if (argc != 3) {
- Tcl_AppendResult (interp, tclXWrongArgs, argv [0],
- " mode filelist", (char *) NULL);
- return TCL_ERROR;
- }
-
- if (ISDIGIT (argv [1][0])) {
- if (Tcl_GetInt (interp, argv [1], &modeVal) != TCL_OK)
- return TCL_ERROR;
- absMode = TRUE;
- } else
- absMode = FALSE;
-
- if (Tcl_SplitList (interp, argv [2], &fileArgc, &fileArgv) != TCL_OK)
- return TCL_ERROR;
-
- for (idx = 0; idx < fileArgc; idx++) {
- fileName = Tcl_TildeSubst (interp, fileArgv [idx], &tildeBuf);
- if (fileName == NULL)
- goto errorExit;
-
- if (!absMode) {
- if (stat (fileName, &fileStat) != 0)
- goto fileError;
- modeVal = ConvSymMode (interp, argv [1], fileStat.st_mode & 07777);
- if (modeVal < 0)
- goto errorExit;
- }
- if (chmod (fileName, (unsigned short) modeVal) < 0)
- goto fileError;
-
- Tcl_DStringFree (&tildeBuf);
- }
-
- ckfree ((char *) fileArgv);
- return TCL_OK;
-
- fileError:
- /*
- * Error accessing file, assumes file name is fileArgv [idx].
- */
- Tcl_AppendResult (interp, fileArgv [idx], ": ", Tcl_PosixError (interp),
- (char *) NULL);
-
- errorExit:
- Tcl_DStringFree (&tildeBuf);
- ckfree ((char *) fileArgv);
- return TCL_ERROR;
- }
-
- /*
- *-----------------------------------------------------------------------------
- *
- * ConvertGroupId --
- * Convert a group name of string id number to a group id.
- *-----------------------------------------------------------------------------
- */
- static int
- ConvertGroupId (interp, strId, groupIdPtr)
- Tcl_Interp *interp;
- char *strId;
- gid_t *groupIdPtr;
- {
- int tmpId;
- struct group *groupPtr;
-
- groupPtr = getgrnam (strId);
- if (groupPtr != NULL) {
- *groupIdPtr = groupPtr->gr_gid;
- } else {
- if (!Tcl_StrToInt (strId, 10, &tmpId))
- goto unknownGroup;
- *groupIdPtr = tmpId;
- if ((int) (*groupIdPtr) != tmpId)
- goto unknownGroup;
- }
- endpwent ();
- return TCL_OK;
-
- unknownGroup:
- Tcl_AppendResult (interp, "unknown group id: ", strId, (char *) NULL);
- endpwent ();
- return TCL_ERROR;
- }
-
- /*
- *-----------------------------------------------------------------------------
- *
- * ConvertUserGroup --
- * Convert the owner/group parameter for the chmod command. If is one of
- * {owner}. {owner group} or {owner {}}
- *-----------------------------------------------------------------------------
- */
- static int
- ConvertUserGroup (interp, userGroupList, ownerInfoPtr)
- Tcl_Interp *interp;
- char *userGroupList;
- ownerInfo_t *ownerInfoPtr;
- {
- int ownArgc, tmpId;
- char **ownArgv;
- struct passwd *passwdPtr;
-
- if (Tcl_SplitList (interp, userGroupList, &ownArgc, &ownArgv) != TCL_OK)
- return TCL_ERROR;
-
- if ((ownArgc < 1) || (ownArgc > 2)) {
- interp->result = "owner arg should be: user or {user group}";
- goto errorExit;
- }
-
- ownerInfoPtr->changeGroup = (ownArgc == 2);
-
- passwdPtr = getpwnam (ownArgv [0]);
- if (passwdPtr != NULL) {
- ownerInfoPtr->userId = passwdPtr->pw_uid;
- } else {
- if (!Tcl_StrToInt (ownArgv [0], 10, &tmpId))
- goto unknownUser;
- ownerInfoPtr->userId = tmpId;
- if ((int) ownerInfoPtr->userId != tmpId)
- goto unknownUser;
- }
-
- if (ownerInfoPtr->changeGroup && (ownArgv [1][0] != '\0')) {
- if (ConvertGroupId (interp, ownArgv [1],
- &ownerInfoPtr->groupId) != TCL_OK)
- goto errorExit;
- } else {
- if (passwdPtr != NULL) {
- ownerInfoPtr->groupId = passwdPtr->pw_gid;
- } else {
- passwdPtr = getpwuid (ownerInfoPtr->userId);
- if (passwdPtr == NULL)
- goto noUserForGroup;
- }
- }
- ckfree (ownArgv);
- endpwent ();
- return TCL_OK;
-
- unknownUser:
- Tcl_AppendResult (interp, "unknown user id: ", ownArgv [0], (char *) NULL);
- goto errorExit;
-
- noUserForGroup:
- Tcl_AppendResult (interp, "can't find group for user id: ", ownArgv [0],
- (char *) NULL);
-
- errorExit:
- ckfree (ownArgv);
- endpwent ();
- return TCL_ERROR;
- }
-
- /*
- *-----------------------------------------------------------------------------
- *
- * Tcl_ChownCmd --
- * Implements the TCL chown command:
- * chown user filelist
- * chown {user group} filelist
- *
- * Results:
- * Standard TCL results, may return the UNIX system error message.
- *
- *-----------------------------------------------------------------------------
- */
- int
- Tcl_ChownCmd (clientData, interp, argc, argv)
- ClientData clientData;
- Tcl_Interp *interp;
- int argc;
- char **argv;
- {
- int idx, fileArgc;
- char **fileArgv, *fileName;
- ownerInfo_t ownerInfo;
- struct stat fileStat;
- Tcl_DString tildeBuf;
-
- Tcl_DStringInit (&tildeBuf);
-
- if (argc != 3) {
- Tcl_AppendResult (interp, tclXWrongArgs, argv [0],
- " user|{user group} filelist", (char *) NULL);
- return TCL_ERROR;
- }
-
- if (ConvertUserGroup (interp, argv [1], &ownerInfo) != TCL_OK)
- return TCL_ERROR;
-
- if (Tcl_SplitList (interp, argv [2], &fileArgc, &fileArgv) != TCL_OK)
- return TCL_ERROR;
-
- for (idx = 0; idx < fileArgc; idx++) {
- fileName = Tcl_TildeSubst (interp, fileArgv [idx], &tildeBuf);
- if (fileName == NULL)
- goto errorExit;
-
- if (!ownerInfo.changeGroup) {
- if (stat (fileName, &fileStat) != 0)
- goto fileError;
- ownerInfo.groupId = fileStat.st_gid;
- }
- if (chown (fileName, ownerInfo.userId, ownerInfo.groupId) < 0)
- goto fileError;
-
- Tcl_DStringFree (&tildeBuf);
- }
-
- ckfree ((char *) fileArgv);
- return TCL_OK;
-
- fileError:
- Tcl_AppendResult (interp, fileArgv [idx], ": ",
- Tcl_PosixError (interp), (char *) NULL);
- errorExit:
- Tcl_DStringFree (&tildeBuf);
- ckfree ((char *) fileArgv);
- return TCL_ERROR;;
- }
-
- /*
- *-----------------------------------------------------------------------------
- *
- * Tcl_ChgrpCmd --
- * Implements the TCL chgrp command:
- * chgrp group filelist
- *
- * Results:
- * Standard TCL results, may return the UNIX system error message.
- *
- *-----------------------------------------------------------------------------
- */
- int
- Tcl_ChgrpCmd (clientData, interp, argc, argv)
- ClientData clientData;
- Tcl_Interp *interp;
- int argc;
- char **argv;
- {
- int idx, fileArgc;
- gid_t groupId;
- char **fileArgv, *fileName;
- struct stat fileStat;
- Tcl_DString tildeBuf;
-
- Tcl_DStringInit (&tildeBuf);
-
- if (argc < 3) {
- Tcl_AppendResult (interp, tclXWrongArgs, argv [0],
- " group filelist", (char *) NULL);
- return TCL_ERROR;
- }
-
- if (ConvertGroupId (interp, argv [1], &groupId) != TCL_OK)
- return TCL_ERROR;
-
- if (Tcl_SplitList (interp, argv [2], &fileArgc, &fileArgv) != TCL_OK)
- return TCL_ERROR;
-
- for (idx = 0; idx < fileArgc; idx++) {
- fileName = Tcl_TildeSubst (interp, fileArgv [idx], &tildeBuf);
- if (fileName == NULL)
- goto errorExit;
-
- if ((stat (fileName, &fileStat) != 0) ||
- (chown (fileArgv[idx], fileStat.st_uid, groupId) < 0)) {
- Tcl_AppendResult (interp, fileArgv [idx], ": ",
- Tcl_PosixError (interp), (char *) NULL);
- goto fileError;
- }
-
- Tcl_DStringFree (&tildeBuf);
- }
-
- ckfree ((char *) fileArgv);
- return TCL_OK;
-
- fileError:
- Tcl_AppendResult (interp, fileArgv [idx], ": ",
- Tcl_PosixError (interp), (char *) NULL);
- errorExit:
- Tcl_DStringFree (&tildeBuf);
- ckfree ((char *) fileArgv);
- return TCL_ERROR;;
- }
-