//
//  Fixperms - set and get permissions on lists of files
//  Copyright (C) 1993-1994  Anthony Edward Hall (aehall@seattleu.edu)
//
//  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 of the License, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// $Id: setperms.C,v 1.13 1994/01/29 16:44:02 aehall Exp $

static char rcsid[] = "$Id: setperms.C,v 1.13 1994/01/29 16:44:02 aehall Exp $";

#include <stdio.h>
#include <sys/stat.h>  // for file mode stuff
#include <unistd.h>    // for chown and readlink
#include <dirent.h>    // for directory stuff and NAME_MAX and PATH_MAX
#include <string.h>
#include <sys/sysmacros.h>  // for major & minor
#include "file.H"
#include "fixperms.H"
#include "setperms.H"

// dummy function to prevent "rcsid not used" warning on compile
void setperms_C_rcsid()
  {
  printf("%s",rcsid);
  };

char get_type(unsigned int mode)  // return type of file
  {
  if (S_ISREG(mode))   // regular
    return '-';
  if (S_ISDIR(mode))   // directory
    return 'd';
  if (S_ISLNK(mode))   // link
    return 'l';
  if (S_ISCHR(mode))   // character device
    return 'c';
  if (S_ISBLK(mode))   // block device
    return 'b';
  if (S_ISSOCK(mode))  // socket
    return 's';
  if (S_ISFIFO(mode))  // FIFO
    return 'p';
  return '?';          // unknown
  }; // get_type

char *get_link_name(char *fname, int display_perror)
  {
  char *tmpname=NULL;
  int linksize;

  if ((linksize=readlink(fname,tmpname,NAME_MAX+PATH_MAX+1)) >= 0)
    {
    tmpname=new(char[MAX_NAME_LEN]);
    tmpname[linksize]='\0';
    }
  else
    {
    if (display_perror)
      perror(fname);
    return NULL;
    };
  return tmpname;
  }; // end get_link_name

// check file type & major/minor
void check_f_type(char *fname, char file_type, char *type, struct stat fstats)
  {
  char tmp_type;

  if ((tmp_type=get_type(fstats.st_mode)) != file_type)
    fprintf(stderr,"%s: should be a %s\n",fname,type); // wrong file type
  }; // end check_f_type

//check major/minor device numbers of a file
void check_maj_min(char *fname, int fmajor, int fminor, dev_t rdev)
  {
  int minor=0, major=0;

  major=major(rdev);
  minor=minor(rdev);
  if ((major != fmajor) || (minor != fminor))
    fprintf(stderr,"%s: should be major %d and minor %d\n",\
            fname,fmajor,fminor);  // major/minor wrong
  }; // end check_maj_min

// set file to specified permissions
int setfile(file *ofptr, int options)
  {
  struct stat fstats;
  char *linkname=NULL;

  if (stat(ofptr->fname,&fstats))
    {
    perror(ofptr->fname);  // file error
    return 1;
    };
  ofptr->output(stdout,ofptr,options);
  if (O_IS_CHECK_ONLY(options))
    {           // only check file
    if ((fstats.st_mode & 0007777) != ofptr->fstats.st_mode) // check mode
      fprintf(stderr,"%s: mode should be %4.4lo\n",ofptr->fname,\
              ofptr->fstats.st_mode);
    if (fstats.st_uid != ofptr->fstats.st_uid) // check uid
      fprintf(stderr,"%s: owner should be %d\n",ofptr->fname,\
              ofptr->fstats.st_uid);
    if (fstats.st_gid != ofptr->fstats.st_gid) // check gid
      fprintf(stderr,"%s: group should be %d\n",ofptr->fname,\
              ofptr->fstats.st_gid);
    }
  else
    {           // change owner/group/mode of file
    chown(ofptr->fname,ofptr->fstats.st_uid,ofptr->fstats.st_gid);
    chmod(ofptr->fname,ofptr->fstats.st_mode);
    };
  switch (ofptr->ftype)
    {
    case '-':           // regular
      check_f_type(ofptr->fname,'-',"regular file",fstats);
      break;
    case 'd':           // directory
      check_f_type(ofptr->fname,'d',"directory",fstats);
      break;
    case 'l':           // link
      linkname=new(char[MAX_NAME_LEN]);
      if ((linkname=get_link_name(ofptr->fname,TRUE)) != NULL)
        if (strcmp(linkname,ofptr->linkname)) // check real and listed link
          fprintf(stderr,"%s: link should point to %s\n",ofptr->fname,\
                  ofptr->linkname);
      delete(linkname);
      break;
    case 'c':           // character device
      check_f_type(ofptr->fname,'c',"character device",fstats);
      check_maj_min(ofptr->fname,ofptr->major,ofptr->minor,fstats.st_rdev);
      break;
    case 'b':           // block device
      check_f_type(ofptr->fname,'b',"block device",fstats);
      check_maj_min(ofptr->fname,ofptr->major,ofptr->minor,fstats.st_rdev);
      break;
    case 's':           // socket
      check_f_type(ofptr->fname,'s',"socket",fstats);
      break;
    case 'p':           // FIFO
      check_f_type(ofptr->fname,'p',"FIFO",fstats);
      break;
    case '?':           // unknown = ERROR!!
      fprintf(stderr,"%s: file type unknown\n",ofptr->fname);
      break;
    default:
      break;
    };
  return 0;
  }; // end setfile

// reverse string
char *strrev(char *string)
  {
  char *tmpstring;
  int i,j;

  j=strlen(string);
  tmpstring=new(char[strlen(string)]);
  for (i=0; i < strlen(string); i++)
    {
    *(tmpstring+i) = *(string+j-1);
    j-=1;
    };
  tmpstring[strlen(string)]='\0';
  return tmpstring;
  }; // end strrev

// if filename includes a wildcard
int wild_match(file *fptr, int options)
  {
  DIR *dptr;
  struct dirent *dir;
  char *dirname, *wildname;
  int i=0, j=0, match=TRUE, one_match=FALSE;

  dirname=new(char[MAX_NAME_LEN]);
  wildname=new(char[MAX_NAME_LEN]);
  dirname=strrev(strchr(strrev(fptr->fname),'/')+1);
  strcpy(wildname,strrchr(fptr->fname,'/') + 1);
  if ((dptr=opendir(dirname)) == NULL)
    {
    perror(dirname);
    return 1;
    };
  while((dir=readdir(dptr)) != NULL)  // WILDCARD MATCHING
    {
    if (strcmp(dir->d_name,".") && strcmp(dir->d_name,".."))
      {
      match=TRUE;  i=0;  j=0;
      while ((wildname[i] != 0) && (match == TRUE))
        {
        switch (wildname[i])
          {
          case '*':  // cycle through wildcarded characters
            i+=1;
            while ((dir->d_name[j] != wildname[i]) && \
                   (dir->d_name[j] != 0))
              {
              j+=1;
              };
            break;
          default:   // cycle through non-wildcarded characters
            if (dir->d_name[j] != wildname[i])
              match=FALSE;
            j+=1;
            i+=1;
            break;
          };
        };
      if (j != strlen(dir->d_name))
        match=FALSE;     // didn't finish string
      if (match)
        {
        one_match = TRUE;  // had at least ONE match
        sprintf(fptr->fname,"%s/%s",dirname,dir->d_name);
        setfile(fptr,options);
        };
      };
    };
  closedir(dptr);
  if (!one_match) // if nothing matched the wildcard...
    fprintf(stderr,"%s/%s: no match\n",dirname,wildname);
  delete(wildname);
  delete(dirname);
  return 0;
  }; // end wild_match

// set permissions on specified pacakge
int setpackage(char *package, int options)
  {
  FILE *pptr;
  file fptr;
  char *pname;

  pname=new(char[MAX_NAME_LEN]);
  if (!strcmp(package,"-"))
    {
    strcpy(package,"stdin");
    pptr=stdin;
    }
  else
    {
    sprintf(pname,"%s/%s.%s",PERMS_DIR,package,PERMS_SUFFIX);
    if ((pptr=fopen(pname,"r")) == NULL) // open package for read
      {
      perror(pname);
      return 1;
      };
    };
  if (!O_IS_QUIET(options))
    if (O_IS_CHECK_ONLY(options))
      fprintf(stdout,"\n%s: checking permissions\n",package);
    else
      fprintf(stdout,"\n%s: fixing permissions\n",package);
  while (!feof(pptr))
    {
    fptr.input(pptr,&fptr);
    if (!feof(pptr))    // check if end of file
      if (strchr(fptr.fname,'*')) // if filename contains a wildcard
        wild_match(&fptr,options);
      else
        setfile(&fptr,options);
    };
  if (strcmp(package,"stdin"))
    {
    fclose(pptr);  // close package file
    };
  if (!O_IS_QUIET(options))
    if (O_IS_CHECK_ONLY(options))
      fprintf(stdout,"%s: permissions checked\n",package);
    else
      fprintf(stdout,"%s: permissions set\n",package);
  delete(pname);
  return 0;
  }; // end setpackage

// set permissions on all, specified package(s), or stdin
int setperms(char *package, int options)
  {
  DIR *dptr;
  struct dirent *dir;
  char *tmpstr;
  int error=0;

  if (!strcmp(package,"all"))
    {                                  // set permissions on all packages
    if ((dptr=opendir(PERMS_DIR)) == NULL)
      {
      perror(PERMS_DIR);
      return 1;
      };
    tmpstr=new(char[MAX_NAME_LEN]);
    while ((dir=readdir(dptr)) != NULL)
      {
      sprintf(tmpstr,".%s",PERMS_SUFFIX);  // parse packages only
      if ((int)dir->d_name+strlen(dir->d_name)-strlen(tmpstr) - \
          (int)strstr(dir->d_name,tmpstr) == 0)
        {
        strcpy(dir->d_name,strrev((char *)(strchr(strrev(dir->d_name),'.')+1)));
        strcpy(package,dir->d_name);
        error=setpackage(package,options);
        };
      };
    delete(tmpstr);
    closedir(dptr);
    }
  else
    {
    error=setpackage(package,options);
    };
  return error;
  }; // end setperms

// end $Id: setperms.C,v 1.13 1994/01/29 16:44:02 aehall Exp $
