+/*
+ * chmod [ugoa][+-=][rwxstugo] files
+ * change mode of files
+ */
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#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 */
+
+char *ms;
+int um;
+struct stat st;
+
+main(argc,argv)
+char **argv;
+{
+ register i;
+ register char *p;
+ int status = 0;
+
+ if (argc < 3) {
+ fprintf(stderr, "Usage: chmod [ugoa][+-=][rwxstugo] file ...\n");
+ exit(255);
+ }
+ ms = argv[1];
+ um = umask(0);
+ newmode(0);
+ for (i = 2; i < argc; i++) {
+ p = argv[i];
+ if (stat(p, &st) < 0) {
+ fprintf(stderr, "chmod: can't access %s\n", p);
+ ++status;
+ continue;
+ }
+ ms = argv[1];
+ if (chmod(p, newmode(st.st_mode)) < 0) {
+ fprintf(stderr, "chmod: can't change %s\n", p);
+ ++status;
+ continue;
+ }
+ }
+ exit(status);
+}
+
+newmode(nm)
+unsigned nm;
+{
+ register o, m, b;
+
+ m = abs();
+ if (!*ms)
+ return(m);
+ do {
+ m = who();
+ while (o = what()) {
+ b = where(nm);
+ switch (o) {
+ case '+':
+ nm |= b & m;
+ break;
+ case '-':
+ nm &= ~(b & m);
+ break;
+ case '=':
+ nm &= ~m;
+ nm |= b & m;
+ break;
+ }
+ }
+ } while (*ms++ == ',');
+ if (*--ms) {
+ fprintf(stderr, "chmod: invalid mode\n");
+ exit(255);
+ }
+ return(nm);
+}
+
+abs()
+{
+ register c, i;
+
+ i = 0;
+ while ((c = *ms++) >= '0' && c <= '7')
+ i = (i << 3) + (c - '0');
+ ms--;
+ return(i);
+}
+
+who()
+{
+ register 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 om;
+{
+ register 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 's':
+ m |= SETID;
+ continue;
+ case 't':
+ m |= STICKY;
+ continue;
+ default:
+ ms--;
+ return m;
+ }
+}