/* * 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) 4/21/88"; #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); } }