Minix1.5/commands/chmod.c

Compare this file to the similar file:
Show the results in this format:

/* chmod - change mode		Author: James da Silva */


/* Author James da Silva (ihnp4!killer!jaime)
 *
 *  a (hopefully) 7th Edition Unix compatible chmod for Minix.
 */

#include <sys/types.h>
#include <sys/stat.h>

#define isop(c)         ((c=='+')||(c=='-')||(c=='='))
#define isperm(c)       ((c=='r')||(c=='w')||(c=='x')||(c=='s')||(c=='t')||\
                         (c=='u')||(c=='g')||(c=='o'))

/* The bits associated with user, group, other */
#define U_MSK   (0700 | S_ISUID)
#define G_MSK   (0070 | S_ISGID)
#define O_MSK    0007

typedef unsigned short bitset;	/* type used for modes */

struct stat st;			/* structure returned by stat() */
char *pname, *arg;
bitset newmode, absolute(), symbolic();
int isabsolute;

main(argc, argv)
int argc;
char **argv;
{
  int i;

  pname = *(argv++);
  if (argc < 3) usage();
  arg = *argv;			/* save pointer to mode arg */

  /* Check for octal mode */
  if (isabsolute = ((*arg >= '0') && (*arg <= '7'))) newmode = absolute();

  /* Apply the mode to all files listed */
  for (i = 2; i < argc; i++) {
	argv++;
	if (stat(*argv, &st)) {	/* get current file mode */
		printf("%s: cannot find `%s'\n", pname, *argv);
		exit(1);
	}

	/* Calculate new mode for this file */
	if (!isabsolute) newmode = symbolic(st.st_mode);

	if (chmod(*argv, newmode)) {	/* change the mode */
		printf("%s: cannot chmod `%s'\n", pname, *argv);
		exit(1);
	}
  }
  exit(0);
}


/* Absolute interprets an octal mode.
 * The file modes will be set to this value.
 */
bitset absolute()
{
  bitset m;
  char *s;

  m = 0;
  s = arg;

  /* Convert octal string to integer */
  while ((*s >= '0') && (*s <= '7')) m = m * 8 + (*(s++) - '0');

  /* If something else is there, choke */
  if (*s) badmode(s);

  return m;
}


/* Symbolic
 *
 * Processes symbolic mode of the form (in EBNF):
 *      <symbolic> ::= <pgroup> { ',' <pgroup> }.
 *      <pgroup> ::= [ <who> ] <op> <permissions> { <op> <permissions> }.
 *
 *      <who> ::= <whoch> { <whoch> }.
 *      <whoch> ::= 'a' | 'u' | 'g' | 'o'.
 *
 *      <op> ::= '+' | '-' | '='.
 *
 *      <permissions> ::= <permch> { <permch> }.
 *      <permch> ::= 'r' | 'w' | 'x' | 's' | 't' | 'u' | 'g' | 'o'.
 *
 * If <who> is omitted, 'a' is assumed, BUT umask()ed bits are uneffected.
 * If <op> is '=', all unspecified permissions are turned off for this <who>.
 * For permissions 'u', 'g', and 'o', the permissions are taken from the
 * specified set.  i.e.  o=g sets the permissions for other the same as for
 * group.
 *
 * Pain in the duff, isn't it?
 */
bitset symbolic(mode)
bitset mode;
{
  int g, o, u, haswho, haspcopy;
  bitset u_mask, emask, partial, other, applyop();
  char *s, c, op;

  s = arg;
  u_mask = umask(0);		/* get the umasked bits */

  do {				/* pgroup */
	haswho = u = g = o = 0;

	while (!isop(*s)) {
		/* We must have a 'who' then */
		haswho = 1;
		switch (*s) {
		    case 'a':	u = g = o = 1;	break;
		    case 'u':	u = 1;	break;
		    case 'g':	g = 1;	break;
		    case 'o':	o = 1;	break;

		    default:	badmode(s);
		}
		s++;
	}

	if (!haswho) {
		u = g = o = 1;	/* assume all */
		emask = ~u_mask;/* effective umask */
	} else
		emask = ~0;


	/* Process each given operator */
	while (isop(*s)) {
		op = *(s++);
		other = partial = haspcopy = 0;

		/* Collect the specified permissions */

		while (isperm(*s)) {

			/* Berkeley only allows one of 'u' 'g' or 'o'
			 * as permissions */

			if ((*s == 'u') || (*s == 'g') || (*s == 'o'))
				if (haspcopy)
					badmode(s);
				else
					haspcopy = 1;

			switch (*s) {
			    case 'r':
				partial |= 4;
				break;
			    case 'w':
				partial |= 2;
				break;
			    case 'x':
				partial |= 1;
				break;

			    case 'u':
				partial |= (mode & U_MSK & ~S_ISUID) >> 6;
				other |= mode & S_ISUID;
				break;
			    case 'g':
				partial |= (mode & G_MSK & ~S_ISGID) >> 3;
				other |= mode & S_ISGID;
				break;
			    case 'o':
				partial |= (mode & O_MSK);
				break;

#ifdef S_ISVTX
			    case 't':
				other |= S_ISVTX;
				break;
#endif

			    case 's':
				if (u) other |= S_ISUID;
				if (g) other |= S_ISGID;
				break;

			    default:	badmode(s);
			}
			s++;
		}

		/* Apply the op using the affected bits and masks */
		if (u)
			mode = applyop(mode, op, (other | (partial << 6)), emask, U_MSK);
		if (g)
			mode = applyop(mode, op, (other | (partial << 3)), emask, G_MSK);
		if (o)
			mode = applyop(mode, op, (other | partial), emask, O_MSK);
	}

  } while (*(s++) == ',');

  /* Not at end - choke */

  if (*(--s)) badmode(s);

  return mode;
}


/* Applyop
 *
 * applies the operator to the current mode using the specified bitset
 * and mask.  'bits' will contain 1's in every bit affected by the
 * operator '+', '-', or '='.  In the case of '=', msk is used to
 * determine which bits will be forced off. 'emask' is the effective
 * umask.
 */
bitset applyop(mode, op, bits, emask, msk)
char op;
bitset mode, bits, emask, msk;
{
  switch (op) {
      case '+':
	mode |= bits & emask;	/* turn these bits on */
	break;
      case '-':
	mode &= ~(bits & emask);/* turn these off */
	break;
      case '=':
	mode |= bits & emask;	/* turn these bits on */
	mode &= ~(~bits & msk & emask);	/* others off */
	break;
      default:			/* should never get here (famous last words) */
	printf("%s: panic: bad op `%c' passed\n", pname, op);
  }
  return mode;
}


/* Usage
 *
 * Prints a terse usage message and exits.
 */
usage()
{
  printf("Usage: %s [absolute-mode | symbolic-mode] files\n", pname);
  exit(1);
}


/* Badmode
 *
 * Called when the parser chokes on the given mode.
 * Prints a message showing the offending character and exits.
 */
badmode(s)
char *s;
{
  int i, sp;
  char buffer[80], *bp;

  sp = s - arg + strlen(pname) + 21;
  sp = sp > 79 ? 79 : sp;	/* check for buffer overflow */

  for (i = 0, bp = buffer; i < sp; i++, bp++) *bp = ' ';
  *bp = '\0';

  printf("%s: badly formed mode `%s'\n", pname, arg);
  printf("%s^\n", buffer);
  exit(1);
}