/* modechange.c -- file mode manipulation
   Copyright (C) 1989, 1990 Free Software Foundation, Inc.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software Foundation,
   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

/* Written by David MacKenzie <djm@ai.mit.edu> */

/* The ASCII mode string is compiled into a linked list of `struct
   modechange', which can then be applied to each file to be changed.
   We do this instead of re-parsing the ASCII string for each file
   because the compiled form requires less computation to use; when
   changing the mode of many files, this probably results in a
   performance gain. */

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

#include <stdlib.h>


/* Return file mode OLDMODE, adjusted as indicated by the list of change
   operations CHANGES.  If OLDMODE is a directory, the type `X'
   change affects it even if no execute bits were set in OLDMODE.
   The returned value has the S_IFMT bits cleared. */

unsigned short
mode_adjust (oldmode, changes)
     unsigned oldmode;
     const struct mode_change *changes;
{
  unsigned short newmode;	/* The adjusted mode and one operand. */
  unsigned short value;		/* The other operand. */

  newmode = oldmode & 07777;

  for (; changes; changes = changes->next)
    {
      if (changes->flags & MODE_COPY_EXISTING)
	{
	  /* Isolate in `value' the bits in `newmode' to copy, given in
	     the mask `changes->value'. */
	  value = newmode & changes->value;

	  if (changes->value & 00700)
	    /* Copy `u' permissions onto `g' and `o'. */
	    value |= (value >> 3) | (value >> 6);
	  else if (changes->value & 00070)
	    /* Copy `g' permissions onto `u' and `o'. */
	    value |= (value << 3) | (value >> 3);
	  else
	    /* Copy `o' permissions onto `u' and `g'. */
	    value |= (value << 3) | (value << 6);

	  /* In order to change only `u', `g', or `o' permissions,
	     or some combination thereof, clear unselected bits.
	     This can not be done in mode_compile because the value
	     to which the `changes->affected' mask is applied depends
	     on the old mode of each file. */
	  value &= changes->affected;
	}
      else
	{
	  value = changes->value;
	  /* If `X', do not affect the execute bits if the file is not a
	     directory and no execute bits are already set. */
	  if ((changes->flags & MODE_X_IF_ANY_X)
	      && !S_ISDIR (oldmode)
	      && (newmode & 00111) == 0)
	    value &= ~00111;	/* Clear the execute bits. */
	}

      switch (changes->op)
	{
	case '=':
	  /* Preserve the previous values in `newmode' of bits that are
	     not affected by this change operation. */
	  newmode = (newmode & ~changes->affected) | value;
	  break;
	case '+':
	  newmode |= value;
	  break;
	case '-':
	  newmode &= ~value;
	  break;
	}
    }
  return newmode;
}



syntax highlighted by Code2HTML, v. 0.9.1