chmode.c (was Re: chmod args)

Ian Stewartson gis at datlog.co.uk
Thu Jun 8 03:52:09 AEST 1989


In article <21508 at iuvax.cs.indiana.edu> regoli at silver.bacs.indiana.edu (michael regoli) writes:
>In article <1771 at papaya.bbn.com> rsalz at bbn.com (Rich Salz) writes:
>| Why hasn't anyone written a chmod(1) that takes a bloody "rwsr-xr-x" string?
>
>if an industrious soul wants to make improvements to allow more than
>any combination of "rwxrwxrwx" (e.g., "rwsr-xr-x") the patches would
>be most appreciated.

Attached is a modified version of some source which came over in 
comp.os.minix (I think) which supports Sys Vr2 absolute and symbolic modes and
the new string mode all in one.  I've tested it as far as I can.  The only
problem one may have with it are the include files which were based on
POSIX on Unix SysVr2 so one may have to hack about.

Hope it is of some interest.

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  chmod.c
# Wrapped by gis at dlvax2 on Wed Jun  7 18:44:50 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'chmod.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'chmod.c'\"
else
echo shar: Extracting \"'chmod.c'\" \(8300 characters\)
sed "s/^X//" >'chmod.c' <<'END_OF_FILE'
X/*
X *  chmod.c             Author: James da Silva (ihnp4!killer!jaime)
X *
X *  A Version of chmod up to Sys Vr2 with ls string mode enchancement
X *  Updated by Ian Stewartson (gis at datlog.co.uk)
X */
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <stdio.h>
X#include <stdlib.h>
X#include <ctype.h>
X#include <string.h>
X
X#define WHO_USER	1			/* Set user		*/
X#define WHO_GROUP	2			/* Set group		*/
X#define WHO_OTHER	4			/* Set others		*/
X#define WHO_ALL		7			/* Set all		*/
X
Xstruct stat	st;			/* structure returned by stat() */
Xchar		*u_mode;		/* New mode argument		*/
Xchar		*pname;
Xmode_t		newmode;
Xmode_t		num_absolute ();
Xmode_t		str_absolute ();
Xmode_t		symbolic ();
Xmode_t		applyop ();
Xvoid		usage ();
Xvoid		badmode ();
Xint		isabsolute = 0;
X
Xmain(argc, argv)
Xint	argc;
Xchar	**argv;
X{
X    int		i;
X    int		error = 0;
X
X    pname = *(argv++);
X
X    if (argc < 3)
X	usage ();
X
X    u_mode = *argv;        /* save pointer to mode arg */
X
X/* check for octal mode */
X
X    if (isabsolute = (isdigit(*u_mode) && (*u_mode <= '7')) )
X        newmode = num_absolute ();
X    
X    else if ((strlen (u_mode) == 9) && ((*u_mode == 'r') || (*u_mode == '-')))
X    {
X	isabsolute = 1;
X        newmode = str_absolute ();
X    }
X
X/* apply the mode to all files listed */
X
X    for (i = 2; i < argc; i++)
X    {
X        if (stat (*(++argv), &st))    /* get current file mode */
X        {
X            fprintf (stderr, "%s: cannot find `%s'\n", pname, *argv);
X	    ++error;
X            continue;
X        }
X
X/* calculate new mode for this file */
X
X        if (!isabsolute)
X            newmode = symbolic (st.st_mode);
X
X        if (chmod (*argv, newmode))  /* change the mode */
X	{
X            fprintf (stderr, "%s: cannot chmod `%s'\n", pname, *argv);
X	    ++error;
X	}
X    }
X
X    exit (error);
X}
X
X
X/*
X * num_absolute - Interprets an octal mode.  The file modes will be set to
X * this value.
X */
X
Xmode_t	num_absolute ()
X{
X    mode_t	m = 0;
X    char	*s = u_mode;
X
X/* convert octal string to integer */
X
X    while (isdigit(*s) && (*s <= '7'))
X        m = m * 8 + (*(s++) - '0');
X
X/* if something else is there, choke */
X
X    if (*s)
X	badmode (s);
X
X    return m;
X}
X
X
X/*
X * symbolic
X *
X * Processes symbolic mode of the form (in EBNF):
X *      <symbolic> ::= <pgroup> { ',' <pgroup> }.
X *      <pgroup> ::= [ <who> ] <op> <permissions> { <op> <permissions> }.
X *
X *      <who> ::= <whoch> { <whoch> }.
X *      <whoch> ::= 'a' | 'u' | 'g' | 'o'.
X *
X *      <op> ::= '+' | '-' | '='.
X *
X *      <permissions> ::= <permch> { <permch> }.
X *      <permch> ::= 'r' | 'w' | 'x' | 's' | 't' | 'u' | 'g' | 'o'.
X *
X * If <who> is omitted, 'a' is assumed, BUT umask()ed bits are uneffected.
X * If <op> is '=', all unspecified permissions are turned off for this <who>.
X * For permissions 'u', 'g', and 'o', the permissions are taken from the
X * specified set.  i.e.  o=g sets the permissions for other the same as for
X * group.
X */
X
Xmode_t	symbolic(mode)
Xmode_t	mode;
X{
X    int		haspcopy;
X    int		who;
X    mode_t	u_mask = umask (0);		/* get the umasked bits	*/
X    mode_t	emask;
X    mode_t	partial;			/* RXW flags		*/
X    mode_t	other;				/* sst flags		*/
X    char	*s = u_mode;
X    char	op;
X
X    do      /* pgroup */
X    {
X	who = 0;
X
X/* Process until we reach an operator (get the who) */
X
X        while (strchr ("+-=", *s) == (char *)NULL)
X        {
X            switch (*s)
X            {
X                case 'a':	who |= WHO_ALL;		break;
X                case 'u':	who |= WHO_USER;	break;
X                case 'g':	who |= WHO_GROUP;	break;
X                case 'o':	who |= WHO_OTHER;	break;
X
X                default:
X		    badmode (s);
X            }
X
X            s++;
X        }
X
X/* If who is not defined - set all */
X
X        if (!who)
X        {
X	    who = WHO_ALL;
X            emask = ~u_mask;    /* effective umask */
X        }
X
X        else
X	    emask = ~0;
X
X
X/* process each given operator */
X
X        while (*s && (strchr ("+-=", *s) != (char *)NULL))
X        {
X            op = *(s++);
X            partial = 0;
X            other = 0;
X            haspcopy = 0;
X
X            /* collect the specified permissions */
X
X            while (*s && (strchr ("rwxstugo", *s) != (char *)NULL))
X            {
X
X/* Berkeley only allows one of 'u' 'g' or 'o' as permissions */
X
X                if ((*s && (strchr ("ugo", *s) != (char *)NULL)) &&
X		    (haspcopy++))
X		    badmode (s);
X
X/* Process the permissions */
X
X                switch (*s)
X                {
X                    case 'r':
X			partial |= S_IROTH;
X			break;
X
X                    case 'w':
X			partial |= S_IWOTH;
X			break;
X
X                    case 'x':
X			partial |= S_IXOTH;
X			break;
X
X                    case 't':
X			other |= S_ISVTX;
X			break;
X
X                    case 'u':
X			partial |= (mode & S_IRWXU) >> 6;
X			other |= mode & S_ISUID;
X                        break;
X
X                    case 'g':
X			partial |= (mode & S_IRWXG) >> 3;
X			other |= mode & S_ISGID;
X			break;
X
X                    case 'o':
X			partial |= (mode & S_IRWXO);
X			break;
X
X                    case 's':
X			if (who & WHO_USER)
X			    other |= S_ISUID;
X
X			if (who & WHO_GROUP)
X			    other |= S_ISGID;
X
X			break;
X
X                    default:
X			badmode (s);
X                }
X
X                s++;
X            }
X
X/* apply the op using the affected bits and masks */
X
X            if (who & WHO_USER)
X                mode = applyop (mode, op, (other | (partial << 6)), emask,
X				(S_IRWXU | S_ISUID));
X
X            if (who & WHO_GROUP)
X                mode = applyop (mode, op, (other | (partial << 3)), emask,
X				(S_IRWXG | S_ISGID));
X
X            if (who & WHO_OTHER)
X                mode = applyop (mode, op, (other | partial), emask,
X				S_IRWXO);
X        }
X
X    } while (*(s++) == ',');
X
X/* not at end - choke */
X
X    if (*(--s))
X	badmode(s);
X
X    return mode;
X}
X
X
X/*
X * applyop
X *
X * applies the operator to the current mode using the specified bitset
X * and mask.  'bits' will contain 1's in every bit affected by the
X * operator '+', '-', or '='.  In the case of '=', msk is used to
X * determine which bits will be forced off. 'emask' is the effective
X * umask.
X */
X
Xmode_t	applyop (mode, op, bits, emask, msk)
Xchar	op;
Xmode_t	mode, bits, emask, msk;
X{
X    if (op == '+')
X	return mode | bits & emask;		/* turn these bits on */
X
X    if (op == '-')
X	return mode & ~(bits & emask);		/* turn these off */
X
X    if (op == '=')
X    {
X	mode |= bits & emask;			/* turn these bits on */
X	return mode & ~(~bits & msk & emask);	/* others off */
X    }
X
X/* should never get here (famous last words) */
X
X    fprintf (stderr, "%s: panic: bad op `%c' passed\n", pname, op);
X    return mode;
X}
X
X
X/*
X * usage - Prints a terse usage message and exits.
X */
X
Xvoid	usage ()
X{
X    fprintf (stderr, "Usage: %s [absolute-mode | symbolic-mode | string-mode] files\n", pname);
X    exit(1);
X}
X
X
X/*
X * badmode
X *
X * Called when the parser chokes on the given mode.
X * Prints a message showing the offending character and exits.
X */
X
Xvoid	badmode (s)
Xchar	*s;
X{
X    int		sp;
X    char	buffer[80];
X
X    sp = s - u_mode + strlen (pname) + 21;
X    sp = sp > 79 ? 79 : sp;          /* check for buffer overflow */
X
X    memset (buffer, ' ', 80);
X    buffer[sp] = 0;
X
X    fprintf (stderr, "%s: badly formed mode `%s'\n", pname, u_mode);
X    fprintf (stderr, "%s^\n", buffer);
X    exit (1);
X}
X
X/*
X * str_absolute - Interprets an ls string mode.  The file modes will be set to
X * this value.
X */
X
Xmode_t	str_absolute ()
X{
X    mode_t	m = 0;			/* New mode			*/
X    char	*s = u_mode;
X    int		i;
X
X    for (i = 0; i < 9; ++i, ++s)
X    {
X	if (*s == '-')
X	    continue;
X
X	switch (i % 3)
X	{
X	    case 0:
X		if (*s != 'r')
X		    badmode (s);
X		
X		m |= (S_IRUSR >> ((i / 3) * 3));
X		break;
X
X	    case 1:
X		if (*s != 'w')
X		    badmode (s);
X		
X		m |= (S_IWUSR >> ((i / 3) * 3));
X		break;
X
X/* For x flag, there are a number of options x, s, S, t, T depending on
X * mulitple values so we need the position in the string as well
X */
X
X	    case 2:
X		if (islower(*s))
X		    m |= (S_IXUSR >> ((i / 3) * 3));
X
X		switch (*s)
X		{
X		    default:
X			badmode (s);
X
X		    case 't':
X		    case 'T':
X			m |= S_ISVTX;
X			break;
X
X
X		    case 'S':
X		    case 's':
X			if (!(i / 3))
X			    m |= S_ISUID;
X
X			else if ((i / 3) == 1)
X			    m |= S_ISGID;
X
X			else 
X			    badmode (s);
X			break;
X
X		    case 'x':
X			break;
X		}
X
X		break;
X		    
X	}
X    }
X
X    return m;
X}
END_OF_FILE
if test 8300 -ne `wc -c <'chmod.c'`; then
    echo shar: \"'chmod.c'\" unpacked with wrong size!
fi
# end of 'chmod.c'
fi
echo shar: End of shell archive.
exit 0

Regards,
Ian Stewartson
Data Logic Ltd, Queens House, Greenhill Way, Harrow, Middlesex, HA1 1YR, UK.
(Phone) +44 1 863 0383 (Telex) 888103 (Fax) +44 1 861 2010
(Network) gis at datlog.co.uk or ukc!datlog!gis



More information about the Alt.sources mailing list