add getopt; clean up tree traversal; lint cleanups
[unix-history] / usr / src / bin / chmod / chmod.c
/*
* Copyright (c) 1980, 1988 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*/
#ifndef lint
char copyright[] =
"@(#) Copyright (c) 1980, 1988 Regents of the University of California.\n\
All rights reserved.\n";
#endif /* not lint */
#ifndef lint
static char sccsid[] = "@(#)chmod.c 5.7 (Berkeley) %G%";
#endif /* not lint */
/*
* chmod options mode files
* where
* mode is [ugoa][+-=][rwxXstugo] or an octal number
* options are -Rf
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/dir.h>
static int fflag, rflag, retval, um;
static char *modestring, *ms;
main(argc, argv)
int argc;
char **argv;
{
extern char *optarg;
extern int optind, opterr;
int ch;
/*
* since "-[rwx]" etc. are valid file modes, we don't let getopt(3)
* print error messages, and we mess around with optind as necessary.
*/
opterr = 0;
while ((ch = getopt(argc, argv, "Rf")) != EOF)
switch((char)ch) {
case 'R':
rflag++;
break;
case 'f':
fflag++;
break;
case '?':
default:
--optind;
goto done;
}
done: argv += optind;
argc -= optind;
if (argc < 2) {
fputs("usage: chmod [-Rf] [ugoa][+-=][rwxXstugo] file ...\n",
stderr);
exit(-1);
}
modestring = *argv;
um = umask(0);
(void)newmode((u_short)0);
while (*++argv)
change(*argv);
exit(retval);
}
change(file)
char *file;
{
register DIR *dirp;
register struct direct *dp;
struct stat buf;
if (lstat(file, &buf) || chmod(file, newmode(buf.st_mode))) {
err(file);
return;
}
if (rflag && ((buf.st_mode & S_IFMT) == S_IFDIR)) {
if (chdir(file) < 0 || !(dirp = opendir("."))) {
err(file);
return;
}
for (dp = readdir(dirp); dp; dp = readdir(dirp)) {
if (dp->d_name[0] == '.' && (!dp->d_name[1] ||
dp->d_name[1] == '.' && !dp->d_name[2]))
continue;
change(dp->d_name);
}
closedir(dirp);
if (chdir("..")) {
err("..");
exit(fflag ? 0 : -1);
}
}
}
err(s)
char *s;
{
if (fflag)
return;
fputs("chmod: ", stderr);
perror(s);
retval = -1;
}
newmode(nm)
u_short nm;
{
register int o, m, b;
ms = modestring;
m = abs();
if (*ms == '\0')
return (m);
do {
m = who();
while (o = what()) {
b = where((int)nm);
switch (o) {
case '+':
nm |= b & m;
break;
case '-':
nm &= ~(b & m);
break;
case '=':
nm &= ~m;
nm |= b & m;
break;
}
}
} while (*ms++ == ',');
if (*--ms) {
fputs("chmod: invalid mode.\n", stderr);
exit(-1);
}
return ((int)nm);
}
abs()
{
register int c, i;
i = 0;
while ((c = *ms++) >= '0' && c <= '7')
i = (i << 3) + (c - '0');
ms--;
return (i);
}
#define USER 05700 /* user's bits */
#define GROUP 02070 /* group's bits */
#define OTHER 00007 /* other's bits */
#define ALL 01777 /* all (note absence of setuid, etc) */
#define READ 00444 /* read permit */
#define WRITE 00222 /* write permit */
#define EXEC 00111 /* exec permit */
#define SETID 06000 /* set[ug]id */
#define STICKY 01000 /* sticky bit */
who()
{
register int m;
m = 0;
for (;;) switch (*ms++) {
case 'u':
m |= USER;
continue;
case 'g':
m |= GROUP;
continue;
case 'o':
m |= OTHER;
continue;
case 'a':
m |= ALL;
continue;
default:
ms--;
if (m == 0)
m = ALL & ~um;
return (m);
}
}
what()
{
switch (*ms) {
case '+':
case '-':
case '=':
return (*ms++);
}
return (0);
}
where(om)
register int om;
{
register int m;
m = 0;
switch (*ms) {
case 'u':
m = (om & USER) >> 6;
goto dup;
case 'g':
m = (om & GROUP) >> 3;
goto dup;
case 'o':
m = (om & OTHER);
dup:
m &= (READ|WRITE|EXEC);
m |= (m << 3) | (m << 6);
++ms;
return (m);
}
for (;;) switch (*ms++) {
case 'r':
m |= READ;
continue;
case 'w':
m |= WRITE;
continue;
case 'x':
m |= EXEC;
continue;
case 'X':
if ((om & S_IFDIR) || (om & EXEC))
m |= EXEC;
continue;
case 's':
m |= SETID;
continue;
case 't':
m |= STICKY;
continue;
default:
ms--;
return (m);
}
}