home *** CD-ROM | disk | FTP | other *** search
/ Complete Linux / Complete Linux.iso / docs / devel / tcl / tclx7_31.z / tclx7_31 / tcldev / tclX7.3a-p1 / src / tclXchmod.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-11-19  |  13.8 KB  |  513 lines

  1. /* 
  2.  * tclXchmod.c --
  3.  *
  4.  *    Chmod, chown and chgrp Tcl commands.
  5.  *-----------------------------------------------------------------------------
  6.  * Copyright 1991-1993 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 3.0 1993/11/19 06:58:22 markd Rel $
  16.  *-----------------------------------------------------------------------------
  17.  */
  18.  
  19. #include "tclExtdInt.h"
  20.  
  21. /*
  22.  * Type used for returning parsed owner informtion.
  23.  */
  24. typedef struct {
  25.     int    changeGroup;
  26.     uid_t  userId;
  27.     gid_t  groupId;
  28. } ownerInfo_t;
  29.  
  30. /*
  31.  * Prototypes of internal functions.
  32.  */
  33. static int
  34. ConvSymMode _ANSI_ARGS_((Tcl_Interp  *interp,
  35.                          char        *symMode,
  36.                          int          modeVal));
  37.  
  38. static int
  39. ConvertGroupId _ANSI_ARGS_((Tcl_Interp  *interp,
  40.                             char        *strId,
  41.                             gid_t       *groupIdPtr));
  42.  
  43. static int
  44. ConvertUserGroup _ANSI_ARGS_((Tcl_Interp  *interp,
  45.                               char        *ownerGroupList,
  46.                               ownerInfo_t *ownerInfoPtr));
  47.  
  48. /*
  49.  *-----------------------------------------------------------------------------
  50.  *
  51.  * ConvSymMode --
  52.  *      Parse and convert symbolic file permissions as specified by chmod(C).
  53.  *
  54.  * Parameters:
  55.  *   o interp - Pointer to the current interpreter, error messages will be
  56.  *     returned in the result.
  57.  *   o symMode - The symbolic permissions to parse.
  58.  *   o modeVal - The existing permissions value on a file.
  59.  *
  60.  * Results:
  61.  *      The new permissions, or -1 if invalid permissions where supplied.
  62.  *
  63.  *-----------------------------------------------------------------------------
  64.  */
  65. static int
  66. ConvSymMode (interp, symMode, modeVal)
  67.     Tcl_Interp  *interp;
  68.     char        *symMode;
  69.     int          modeVal;
  70.  
  71. {
  72.     int  user, group, other;
  73.     char operator, *scanPtr;
  74.     int  rwxMask, ugoMask, setUID, sticky, locking;
  75.     int  newMode;
  76.  
  77.     scanPtr = symMode;
  78.  
  79.     while (*scanPtr != '\0') {
  80.         user = group = other = FALSE;
  81.  
  82.         /* 
  83.          * Scan who field.
  84.          */
  85.         while (! ((*scanPtr == '+') || 
  86.                   (*scanPtr == '-') || 
  87.                   (*scanPtr == '='))) {
  88.             switch (*scanPtr) {
  89.                 case 'a':
  90.                     user = group = other = TRUE;
  91.                     break;
  92.                 case 'u':
  93.                     user = TRUE;
  94.                     break;
  95.                 case 'g':
  96.                     group = TRUE;
  97.                     break;
  98.                 case 'o':
  99.                     other = TRUE;
  100.                     break;
  101.                 default:
  102.                     goto invalidMode;
  103.             }
  104.             scanPtr++;
  105.         }
  106.  
  107.         /*
  108.          * If none where specified, that means all.
  109.          */
  110.  
  111.         if (! (user || group || other))
  112.             user = group = other = TRUE;
  113.  
  114.         operator = *scanPtr++;
  115.  
  116.         /* 
  117.          * Decode the permissions
  118.          */
  119.  
  120.         rwxMask = 0;
  121.         setUID = sticky = locking = FALSE;
  122.  
  123.         /* 
  124.          * Scan permissions field
  125.          */
  126.         while (! ((*scanPtr == ',') || (*scanPtr == 0))) {
  127.             switch (*scanPtr) {
  128.                 case 'r':
  129.                     rwxMask |= 4;
  130.                     break;
  131.                 case 'w':
  132.                     rwxMask |= 2;
  133.                     break;
  134.                 case 'x':
  135.                     rwxMask |= 1;
  136.                     break;
  137.                 case 's':
  138.                     setUID = TRUE;
  139.                     break;
  140.                 case 't':
  141.                     sticky = TRUE;
  142.                     break;
  143.                 case 'l':
  144.                     locking = TRUE;
  145.                     break;
  146.                 default:
  147.                     goto invalidMode;
  148.             }
  149.             scanPtr++;
  150.         }
  151.  
  152.         /*
  153.          * Build mode map of specified values.
  154.          */
  155.  
  156.         newMode = 0;
  157.         ugoMask = 0;
  158.         if (user) {
  159.             newMode |= rwxMask << 6;
  160.             ugoMask |= 0700;
  161.         }
  162.         if (group) {
  163.             newMode |= rwxMask << 3;
  164.             ugoMask |= 0070;
  165.         }
  166.         if (other) {
  167.             newMode |= rwxMask;
  168.             ugoMask |= 0007;
  169.         }
  170.         if (setUID && user)
  171.             newMode |= 04000;
  172.         if ((setUID || locking) && group)
  173.             newMode |= 02000;
  174.         if (sticky)
  175.             newMode |= 01000;
  176.  
  177.         /* 
  178.          * Add to cumulative mode based on operator.
  179.          */
  180.  
  181.         if (operator == '+')
  182.             modeVal |= newMode;
  183.         else if (operator == '-')
  184.             modeVal &= ~newMode;
  185.         else if (operator == '=')
  186.             modeVal |= (modeVal & ugoMask) | newMode;
  187.         if (*scanPtr == ',')
  188.             scanPtr++;
  189.     }
  190.  
  191.     return modeVal;
  192.  
  193.   invalidMode:
  194.     Tcl_AppendResult (interp, "invalid file mode \"", symMode, "\"",
  195.                       (char *) NULL);
  196.     return -1;
  197. }
  198.  
  199. /*
  200.  *-----------------------------------------------------------------------------
  201.  *
  202.  * Tcl_ChmodCmd --
  203.  *     Implements the TCL chmod command:
  204.  *     chmod mode filelist
  205.  *
  206.  * Results:
  207.  *  Standard TCL results, may return the UNIX system error message.
  208.  *
  209.  *-----------------------------------------------------------------------------
  210.  */
  211. int
  212. Tcl_ChmodCmd (clientData, interp, argc, argv)
  213.     ClientData   clientData;
  214.     Tcl_Interp  *interp;
  215.     int          argc;
  216.     char       **argv;
  217. {
  218.     int           idx, modeVal, fileArgc, absMode;
  219.     char        **fileArgv, *fileName;
  220.     struct stat   fileStat;
  221.     Tcl_DString   tildeBuf;
  222.  
  223.     Tcl_DStringInit (&tildeBuf);
  224.  
  225.     if (argc != 3) {
  226.         Tcl_AppendResult (interp, tclXWrongArgs, argv [0], 
  227.                           " mode filelist", (char *) NULL);
  228.         return TCL_ERROR;
  229.     }
  230.  
  231.     if (ISDIGIT (argv [1][0])) {
  232.         if (Tcl_GetInt (interp, argv [1], &modeVal) != TCL_OK)
  233.             return TCL_ERROR;
  234.         absMode = TRUE;
  235.     } else
  236.         absMode = FALSE;
  237.  
  238.     if (Tcl_SplitList (interp, argv [2], &fileArgc, &fileArgv) != TCL_OK)
  239.         return TCL_ERROR;
  240.  
  241.     for (idx = 0; idx < fileArgc; idx++) {
  242.         fileName = Tcl_TildeSubst (interp, fileArgv [idx], &tildeBuf);
  243.         if (fileName == NULL)
  244.             goto errorExit;
  245.         
  246.         if (!absMode) {
  247.             if (stat (fileName, &fileStat) != 0)
  248.                 goto fileError;
  249.             modeVal = ConvSymMode (interp, argv [1], fileStat.st_mode & 07777);
  250.             if (modeVal < 0)
  251.                 goto errorExit;
  252.         }
  253.         if (chmod (fileName, (unsigned short) modeVal) < 0)
  254.             goto fileError;
  255.  
  256.         Tcl_DStringFree (&tildeBuf);
  257.     }
  258.  
  259.     ckfree ((char *) fileArgv);
  260.     return TCL_OK;
  261.  
  262.   fileError:
  263.     /*
  264.      * Error accessing file, assumes file name is fileArgv [idx].
  265.      */
  266.     Tcl_AppendResult (interp, fileArgv [idx], ": ", Tcl_PosixError (interp),
  267.                       (char *) NULL);
  268.  
  269.   errorExit:
  270.     Tcl_DStringFree (&tildeBuf);
  271.     ckfree ((char *) fileArgv);
  272.     return TCL_ERROR;
  273. }
  274.  
  275. /*
  276.  *-----------------------------------------------------------------------------
  277.  *
  278.  * ConvertGroupId --
  279.  *   Convert a group name of string id number to a group id.
  280.  *-----------------------------------------------------------------------------
  281.  */
  282. static int
  283. ConvertGroupId (interp, strId, groupIdPtr)
  284.     Tcl_Interp  *interp;
  285.     char        *strId;
  286.     gid_t       *groupIdPtr;
  287. {
  288.     int             tmpId;
  289.     struct group   *groupPtr;
  290.  
  291.     groupPtr = getgrnam (strId);
  292.     if (groupPtr != NULL) {
  293.         *groupIdPtr = groupPtr->gr_gid;
  294.     } else {
  295.         if (!Tcl_StrToInt (strId, 10, &tmpId))
  296.             goto unknownGroup;
  297.         *groupIdPtr = tmpId;
  298.         if ((int) (*groupIdPtr) != tmpId)
  299.             goto unknownGroup;
  300.     }
  301.     endpwent ();
  302.     return TCL_OK;
  303.  
  304.   unknownGroup:
  305.     Tcl_AppendResult (interp, "unknown group id: ", strId, (char *) NULL);
  306.     endpwent ();
  307.     return TCL_ERROR;
  308. }
  309.  
  310. /*
  311.  *-----------------------------------------------------------------------------
  312.  *
  313.  * ConvertUserGroup --
  314.  *   Convert the owner/group parameter for the chmod command.  If is one of
  315.  *   {owner}. {owner group} or {owner {}}
  316.  *-----------------------------------------------------------------------------
  317.  */
  318. static int
  319. ConvertUserGroup (interp, userGroupList, ownerInfoPtr)
  320.     Tcl_Interp  *interp;
  321.     char        *userGroupList;
  322.     ownerInfo_t *ownerInfoPtr;
  323. {
  324.     int             ownArgc, tmpId;
  325.     char          **ownArgv;
  326.     struct passwd  *passwdPtr;
  327.  
  328.     if (Tcl_SplitList (interp, userGroupList, &ownArgc, &ownArgv) != TCL_OK)
  329.         return TCL_ERROR;
  330.  
  331.     if ((ownArgc < 1) || (ownArgc > 2)) {
  332.         interp->result = "owner arg should be: user or {user group}";
  333.         goto errorExit;
  334.     }
  335.  
  336.     ownerInfoPtr->changeGroup = (ownArgc == 2);
  337.  
  338.     passwdPtr = getpwnam (ownArgv [0]);
  339.     if (passwdPtr != NULL) {
  340.         ownerInfoPtr->userId = passwdPtr->pw_uid;
  341.     } else {
  342.         if (!Tcl_StrToInt (ownArgv [0], 10, &tmpId))
  343.             goto unknownUser;
  344.         ownerInfoPtr->userId = tmpId;
  345.         if ((int) ownerInfoPtr->userId != tmpId)
  346.             goto unknownUser;
  347.     }
  348.  
  349.     if (ownerInfoPtr->changeGroup && (ownArgv [1][0] != '\0')) {
  350.         if (ConvertGroupId (interp, ownArgv [1],
  351.                             &ownerInfoPtr->groupId) != TCL_OK)
  352.             goto errorExit;
  353.     } else {
  354.         if (passwdPtr != NULL) {
  355.             ownerInfoPtr->groupId = passwdPtr->pw_gid;
  356.         } else {
  357.             passwdPtr = getpwuid (ownerInfoPtr->userId);
  358.             if (passwdPtr == NULL)
  359.                 goto noUserForGroup;
  360.         }
  361.     }
  362.     ckfree (ownArgv);
  363.     endpwent ();
  364.     return TCL_OK;
  365.  
  366.   unknownUser:
  367.     Tcl_AppendResult (interp, "unknown user id: ", ownArgv [0], (char *) NULL);
  368.     goto errorExit;
  369.  
  370.   noUserForGroup:
  371.     Tcl_AppendResult (interp, "can't find group for user id: ", ownArgv [0],
  372.                       (char *) NULL);
  373.  
  374.   errorExit:
  375.     ckfree (ownArgv);
  376.     endpwent ();
  377.     return TCL_ERROR;
  378. }
  379.  
  380. /*
  381.  *-----------------------------------------------------------------------------
  382.  *
  383.  * Tcl_ChownCmd --
  384.  *     Implements the TCL chown command:
  385.  *     chown user filelist
  386.  *     chown {user group} filelist
  387.  *
  388.  * Results:
  389.  *  Standard TCL results, may return the UNIX system error message.
  390.  *
  391.  *-----------------------------------------------------------------------------
  392.  */
  393. int
  394. Tcl_ChownCmd (clientData, interp, argc, argv)
  395.     ClientData   clientData;
  396.     Tcl_Interp  *interp;
  397.     int          argc;
  398.     char       **argv;
  399. {
  400.     int            idx, fileArgc;
  401.     char         **fileArgv, *fileName;
  402.     ownerInfo_t    ownerInfo;
  403.     struct stat    fileStat;
  404.     Tcl_DString    tildeBuf;
  405.  
  406.     Tcl_DStringInit (&tildeBuf);
  407.  
  408.     if (argc != 3) {
  409.         Tcl_AppendResult (interp, tclXWrongArgs, argv [0], 
  410.                           " user|{user group} filelist", (char *) NULL);
  411.         return TCL_ERROR;
  412.     }
  413.     
  414.     if (ConvertUserGroup (interp, argv [1], &ownerInfo) != TCL_OK)
  415.         return TCL_ERROR;
  416.  
  417.     if (Tcl_SplitList (interp, argv [2], &fileArgc, &fileArgv) != TCL_OK)
  418.         return TCL_ERROR;
  419.  
  420.     for (idx = 0; idx < fileArgc; idx++) {
  421.         fileName = Tcl_TildeSubst (interp, fileArgv [idx], &tildeBuf);
  422.         if (fileName == NULL)
  423.             goto errorExit;
  424.         
  425.         if (!ownerInfo.changeGroup) {
  426.             if (stat (fileName, &fileStat) != 0)
  427.                 goto fileError;
  428.             ownerInfo.groupId = fileStat.st_gid;
  429.         }
  430.         if (chown (fileName, ownerInfo.userId, ownerInfo.groupId) < 0)
  431.                 goto fileError;
  432.  
  433.         Tcl_DStringFree (&tildeBuf);
  434.     }
  435.  
  436.     ckfree ((char *) fileArgv);
  437.     return TCL_OK;
  438.  
  439.   fileError:
  440.     Tcl_AppendResult (interp, fileArgv [idx], ": ",
  441.                       Tcl_PosixError (interp), (char *) NULL);
  442.   errorExit:
  443.     Tcl_DStringFree (&tildeBuf);
  444.     ckfree ((char *) fileArgv);
  445.     return TCL_ERROR;;
  446. }
  447.  
  448. /*
  449.  *-----------------------------------------------------------------------------
  450.  *
  451.  * Tcl_ChgrpCmd --
  452.  *     Implements the TCL chgrp command:
  453.  *     chgrp group filelist
  454.  *
  455.  * Results:
  456.  *  Standard TCL results, may return the UNIX system error message.
  457.  *
  458.  *-----------------------------------------------------------------------------
  459.  */
  460. int
  461. Tcl_ChgrpCmd (clientData, interp, argc, argv)
  462.     ClientData   clientData;
  463.     Tcl_Interp  *interp;
  464.     int          argc;
  465.     char       **argv;
  466. {
  467.     int            idx, fileArgc;
  468.     gid_t          groupId;
  469.     char         **fileArgv, *fileName;
  470.     struct stat    fileStat;
  471.     Tcl_DString    tildeBuf;
  472.  
  473.     Tcl_DStringInit (&tildeBuf);
  474.  
  475.     if (argc < 3) {
  476.         Tcl_AppendResult (interp, tclXWrongArgs, argv [0], 
  477.                           " group filelist", (char *) NULL);
  478.         return TCL_ERROR;
  479.     }
  480.  
  481.     if (ConvertGroupId (interp, argv [1], &groupId) != TCL_OK)
  482.         return TCL_ERROR;
  483.  
  484.     if (Tcl_SplitList (interp, argv [2], &fileArgc, &fileArgv) != TCL_OK)
  485.         return TCL_ERROR;
  486.  
  487.     for (idx = 0; idx < fileArgc; idx++) {
  488.         fileName = Tcl_TildeSubst (interp, fileArgv [idx], &tildeBuf);
  489.         if (fileName == NULL)
  490.             goto errorExit;
  491.         
  492.         if ((stat (fileName, &fileStat) != 0) ||
  493.                 (chown (fileArgv[idx], fileStat.st_uid, groupId) < 0)) {
  494.             Tcl_AppendResult (interp, fileArgv [idx], ": ",
  495.                               Tcl_PosixError (interp), (char *) NULL);
  496.             goto fileError;
  497.         }
  498.  
  499.         Tcl_DStringFree (&tildeBuf);
  500.     }
  501.  
  502.     ckfree ((char *) fileArgv);
  503.     return TCL_OK;
  504.  
  505.   fileError:
  506.     Tcl_AppendResult (interp, fileArgv [idx], ": ",
  507.                       Tcl_PosixError (interp), (char *) NULL);
  508.   errorExit:
  509.     Tcl_DStringFree (&tildeBuf);
  510.     ckfree ((char *) fileArgv);
  511.     return TCL_ERROR;;
  512. }
  513.