/* 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