/* chmog.c
 * chmod chown and chgrp of files
 *
 * Usage: chmog mode owner group file [file ... ]
 *
 */

#define NO_PROTOTYPE

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include  <stdfun.h> 
#include <errno.h>
#include <pwd.h>
#include <grp.h>

#define FALSE		0
#define TRUE		1

#define MODE_SIZE	6

/* rwx rwx rwx for (user) (group) (others).
 * Each rwx is an octal digit
 *
 */


	/* Array indicies of oo_mode[] for octal digits */
/* DO NOT CHANGE THESE DIGITS */
#define DIGIT_TYPE0	0	/* digit values: 0-1 */
#define DIGIT_TYPE	1	/* digit values: 0-6 */

#define DIGIT_EXEC	2	/* digit values: 0-7 */

/* Access permissions */
#define DIGIT_USER	3	/* digit values: 0-7 */
#define DIGIT_GROUP	4	/* digit values: 0-7 */
#define DIGIT_OTHERS	5	/* digit values: 0-7 */

/* Additive permission values for an access digit */
#define READ_ACCESS	04
#define WRITE_ACCESS	02
#define EXEC_ACCESS	01

/* Additive values for the exec digit */
#define SETUID_EXEC		04
#define SETGID_EXEC		02
#define SETSTICKYBIT_EXEC	01

extern void perror();

char oo_mode[MODE_SIZE + 2];
char uclass[5], op[2], pclass[6];

char _sccsid[] = { " chmog.c 1.3 9/22/97 " };

int main( argc, argv )
int argc;
char *argv[];
{
	extern int stat(), chmod(), chown();
	extern struct passwd *getpwnam();
	extern struct group *getgrnam();

	int modetype();
	int modmode();

	struct passwd *pws;
	struct group *gps;
	struct stat *pst_buf, st_buf;
	int of_mode;
	int mode, ownerid, groupid;

	/* Boolean */
	int octal_mode;

	int i;
	mode = 0;

	if( argc < 5 )    {
		fprintf( stderr, "Usage: %s mode owner group file [file ...]\n", argv[0] );
		exit( 1 );
	}

	/* Check modestring argv[1] syntax */
	switch( mode = modetype( argv[1] ) )    {
		case -1:
			/* Mode is not octal but symbolic */
			octal_mode = FALSE;
#ifdef DEBUG
printf( "octal_mode=FALSE: modetype sets uclass=(%s), op=(%s), pclass=(%s)\n", uclass, op, pclass );
#endif
			break;
		case -2:
			fprintf( stderr, "%s: invalid user class '%s'\n", argv[0], uclass );
			exit( 1 );
		case -3:
			fprintf( stderr, "%s: invalid permissions '%s'\n", argv[0], pclass );
			exit( 1 );
		default:
			/* mode is now set to the mode to be set for files */
			octal_mode = TRUE;
			break;
	}

	/* Check existence of user */
	if( (pws = getpwnam( argv[2] )) == (struct passwd *)0 )    {
		fprintf( stderr, "%s: no such user '%s'\n", argv[0], argv[2] );
		exit( 1 );
	}
	/* Check existence of group */
	if( (gps = getgrnam( argv[3] )) == (struct group *)0 )    {
		fprintf( stderr, "%s: no such group '%s'\n", argv[0], argv[3] );
		exit( 1 );
	}

	/* Get the numerical id's */
	ownerid = pws->pw_uid;
	groupid = gps->gr_gid;

	/* Loop on file/directory arguments */
	i = 4;
	while( i < argc )    {

		if( octal_mode == FALSE )    {

			/* Mode change is given symbolically.
			 * Using operators + and - we need to get
			 * the mode of the file or directory.
			 */
			pst_buf = &st_buf;
			if( stat( argv[i], pst_buf ) < 0 )    {
				perror( argv[0] );
				errno = 0;
				/* But continue processing */
			}

			/* Get the 'old file mode', print in octal rep
			 * to array oo_mode
			 */
			of_mode = pst_buf->st_mode;
			sprintf( oo_mode, "%o", of_mode );

#ifdef DEBUG
printf( "octal_mode=FALSE: got file stats, old mode=(%s)\n", oo_mode );
#endif
			if( (mode = modmode( of_mode )) < 0 )    {
				fprintf( stderr, "%s: missing unary operator\n", argv[0] );
				exit( 1 );
			}
#ifdef DEBUG
printf( "octal_mode=FALSE: got new mode (%s) from old\n", oo_mode );
#endif
			sscanf( oo_mode, "%o", &mode );
#ifdef DEBUG
printf( "oo_mode sscanf'd for octal mode %o set for %s\n", mode, argv[i] );
#endif
		}

		if( chmod( argv[i], mode ) < 0 )    {
			perror( argv[0] );
			errno = 0;
			/* But continue processing */
		}
		if( chown( argv[i], ownerid, groupid ) < 0 )    {
			perror( argv[0] );
			errno = 0;
			/* But continue processing */
		}
		++i;

	} /* End while processing file arguments */

	exit( 0 );
	return( 0 );

} /* End main() */

/* We want all valid mode syntax for standard utility 'chmod'
 *
 * chmod [augo][+|-|=][xrwstl] file ...
 *
 *	[agou] = any combination (4 max) no repetition, possibly empty
 *	[+|-|=] = any one operator
 *	[xrwst] = any combination (6 max) no repetition, possibly empty
 *	NB The l option for this last group is not supported by chown()
 *
 * chmod [d]ddd file ...
 */
int modetype( mdstr )
char *mdstr;
{
	int isoctal();

	char *s;
	int i, k;

	s = mdstr;

	if( isoctal( s ) )    {
		sscanf( s, "%o", &i );
		return( i );
	}

	/* A symbolic string of system utility chmod.
	 * Convert it to the int that must be passed to chmod()
	 * First parse into 3 substrings: uclass, op and pclass.
	 */
	i = 0;
	while( s[i] != '+' && s[i] != '-' && s[i] != '=' )    {
		uclass[i] = s[i];
		++i;
	}

	op[0] = s[i++];

	k = 0;
	while( s[i] != '\0' )    {
		pclass[k] = s[i];
		++i;
		++k;
	}

	/* a = all
	 * u = user
	 * g = group
	 * o = others
	 */
	if( *uclass != '\0' && strcspn( uclass, "aogu" ) != 0 )  {
		/* uclass can be empty, but if it is not and there are chars
		 * other than aogu, there are invalid chars in uclass.
		 */
		return( -2 );
	}
	/* If pclass is empty remove all permissions,
	 * and if there are chars other than any of "rwxst",
	 * there are invalid chars in pclass
	 */
	if( *pclass == '\0' )
		return( -1 );
	if( strcspn( pclass, "rwxst" ) != 0 )  {
		return( -3 );
	}

	return( -1 );
	
} /* End modetype() */

int isoctal( s )
char *s;
{
	while( *s != '\0' )    {
		if( ! isdigit( *s ) || (int)( *s ) > (int)( '7' ) )
			return( FALSE );
		++s;
	}
	return( TRUE );

} /* End isoctal() */

/* Make the new mode that is to be set if the 'mode argument' argv[1]
 * is of symbolic type
 *
 * NB Fri May 28 15:02:43 EDT 1993
 * This function should work by bit shifting see ulisp's resword.h and
 * ./chmogdir.c.
 */
int modmode( of_mode )
int of_mode;
{
	int moddigit();


	if( strchr( uclass, 'a' ) != NULL || *uclass == '\0' )    {
		of_mode = moddigit( of_mode, DIGIT_OTHERS );
		of_mode = moddigit( of_mode, DIGIT_GROUP );
		of_mode = moddigit( of_mode, DIGIT_USER );
	}
	else    {
		if( strchr( uclass, 'u' ) != NULL )    {
			of_mode = moddigit( of_mode, DIGIT_USER );
		}
		if( strchr( uclass, 'g' ) != NULL )    {
			of_mode = moddigit( of_mode, DIGIT_GROUP );
		}
		if( strchr( uclass, 'o' ) != NULL )    {
			of_mode = moddigit( of_mode, DIGIT_OTHERS );
		}
	}


	/* modes: u+s, g+s, */
	if( strchr( pclass, 's' ) != NULL && strchr( pclass, 'u' ) != NULL )    {
		of_mode = moddigit( of_mode, DIGIT_EXEC );
	}
	else if( strchr( pclass, 's' ) != NULL && strchr( pclass, 'g' ) != NULL )    {
		of_mode = moddigit( of_mode, DIGIT_EXEC );
	}

	/* mode: u+t */
	if( strchr( pclass, 't' ) != NULL )    {
		of_mode = moddigit( of_mode, DIGIT_EXEC );
	}

#ifdef DEBUG
printf( "modmode(): newmode=(octal)(%s)\n", oo_mode );
#endif

	return( of_mode );

} /* End modmode() */

int moddigit( of_mode, d )
int of_mode, d;
{
	int od;

	if( d == DIGIT_EXEC )    {

		
		/* Read the digit at index d in oo_mode as an int */
		sscanf( &oo_mode[ d ], "%1d", &od );
		/*
		od = oo_mode[ d ];
		*/
		switch( *op )    {
			case '+':
				if( strchr( pclass, 's' ) != NULL && strchr( uclass, 'u' ) != NULL )    {
					if( od < SETUID_EXEC )
						oo_mode[ d ] = (char)(oo_mode[ d ] + SETUID_EXEC );
				}
				else if( strchr( pclass, 's' ) != NULL && strchr( uclass, 'g' ) != NULL )    {
					if( od == 0 || od == 1 || od == 4 || od == 5 )
						oo_mode[ d ] = (char)(oo_mode[ d ] + SETGID_EXEC );
				}
				else if( strchr( pclass, 't' ) != NULL && strchr( uclass, 'u' ) != NULL )    {
					if( od == 0 || od == 2 || od == 4 || od == 6 )
						oo_mode[ d ] = (char)(oo_mode[ d ] + SETSTICKYBIT_EXEC );
				}
				else
					return( of_mode );
				break;
			case '-':
				if( strchr( pclass, 's' ) != NULL && strchr( uclass, 'u' ) != NULL )    {
					if( od >= SETUID_EXEC )
						oo_mode[ d ] = (char)(oo_mode[ d ] - SETUID_EXEC );
				}
				else if( strchr( pclass, 's' ) != NULL && strchr( uclass, 'g' ) != NULL )    {
					if( od == 2 || od == 3 || od == 6 || od == 7 )
						oo_mode[ d ] = (char)(oo_mode[ d ] - SETGID_EXEC );
				}
				else if( strchr( pclass, 't' ) != NULL && strchr( uclass, 'u' ) != NULL )    {
					if( od == 1 || od == 3 || od == 5 || od == 7 )
						oo_mode[ d ] = (char)(oo_mode[ d ] - SETSTICKYBIT_EXEC );
				}
				else
					return( of_mode );
				break;
			default:
				return( -1 );
		}
		return( of_mode );

	}	/* End if d == DIGIT_EXEC */

	/* p_mode = value of one digit in the complete octal mode */
	if( strchr( pclass, 'r' ) != NULL )    {
		/* Read the digit at index d in oo_mode as an int */
		sscanf( &oo_mode[ d ], "%1d", &od );
		/*
		od = oo_mode[ d ];
		*/
		switch( *op )    {
			case '+':
				if( od < READ_ACCESS )
					oo_mode[ d ] = (char)(oo_mode[ d ] + READ_ACCESS );
				break;
			case '-':
				if( od >= READ_ACCESS )
				oo_mode[ d ] = (char)(oo_mode[ d ] - READ_ACCESS );
				break;
			case '=':
				oo_mode[ d ] = (char)READ_ACCESS;
				break;
			default:
				return( -1 );
		}
	}
	if( strchr( pclass, 'w' ) != NULL )    {
		/* Read the digit at index d in oo_mode as an int */
		sscanf( &oo_mode[ d ], "%1d", &od );
		/*
		od = oo_mode[ d ];
		*/
		switch( *op )    {
			case '+':
				if( od == 0 || od == 1 || od == 4 || od == 5 )
					oo_mode[ d ] = (char)(oo_mode[ d ] + WRITE_ACCESS );
				break;
			case '-':
				if( od == 2 || od == 3 || od == 6 || od == 7 )
					oo_mode[ d ] = (char)(oo_mode[ d ] - WRITE_ACCESS );
				break;
			case '=':
				oo_mode[ d ] = (char)WRITE_ACCESS;
				break;
			default:
				return( -1 );
		}
	}
	if( strchr( pclass, 'x' ) != NULL )    {
		/* Read the digit at index d in oo_mode as an int */
		sscanf( &oo_mode[ d ], "%1d", &od );
		/*
		od = oo_mode[ d ];
		*/
		switch( *op )    {
			case '+':
				if( od == 0 || od == 2 || od == 4 || od == 6 )
					oo_mode[ d ] = (char)(oo_mode[ d ] + EXEC_ACCESS );
				break;
			case '-':
				if( od == 1 || od == 3 || od == 5 || od == 7 )
					oo_mode[ d ] = (char)(oo_mode[ d ] - EXEC_ACCESS );
				break;
			case '=':
				oo_mode[ d ] = (char)EXEC_ACCESS;
				break;
			default:
				return( -1 );
		}
	}
	return( of_mode );

} /* End moddigit() */


Return to Home Page
Return to Metayoga Page
Return to C Language Page


The URL for this document is:
http://graham.main.nc.us/~bhammel/graham/CPROGS/chmog.html
Created: 1997
Last Updated: May 28, 2000 Email me, Bill Hammel at
bhammel@graham.main.nc.us
READ WARNING BEFORE SENDING E-MAIL