/*---------------------------------------------------------------------------
  Module FmDirs.c                                                           

  (c) Simon Marlow 1990-1993

  functions for manipulating directory lists, and some other utilities
  related to the file system.
-----------------------------------------------------------------------------*/

#include <memory.h>
#include <string.h>
#include <stdlib.h>

#include <X11/Intrinsic.h>

#include "Fm.h"

/*-----------------------------------------------------------------------------
  STATIC DATA                                       
-----------------------------------------------------------------------------*/

static SortType sort_type;
static Boolean dirs_first;

/*-----------------------------------------------------------------------------
  PRIVATE FUNCTIONS
-----------------------------------------------------------------------------*/

static int comp(FileRec **fr1, FileRec **fr2)
{
  FileRec *fl1 = *fr1, *fl2 = *fr2;

  if (dirs_first) {
    if (S_ISDIR(fl1->stats.st_mode)) {
      if (!S_ISDIR(fl2->stats.st_mode))
	return -1;
    }
    else if (S_ISDIR(fl2->stats.st_mode))
      return 1;
  }
	
  switch (sort_type) {
  case SortByName:
    return strcmp(fl1->name, fl2->name);
  case SortBySize:
    return (int)(fl2->stats.st_size - fl1->stats.st_size);
  case SortByMTime:
    return (int)(fl2->stats.st_mtime - fl1->stats.st_mtime);
  }

  return 0;
}

/*-----------------------------------------------------------------------------
  PUBLIC FUNCTIONS
-----------------------------------------------------------------------------*/

/* Read in the directory for the file window specified. Note that since we have
   the stats available for the directory, we could simply check the modification
   time, and only read in the directory if necessay. This isn't worth it though-
   the time taken to stat all the files (which still needs to be done) far
   outweighs the time to read in the dir */

Boolean readDirectory(FileWindowRec *fw)
{
  FileList fl = NULL;
  DIR *dir;
  struct dirent *entry;
  int i, m;

  if (stat(fw->directory, &fw->stats))
    goto error2;

  if (chdir(fw->directory))
    goto error2;

  if (!(dir = opendir(".")))
    goto error2;

  for(i = 0; entry = readdir(dir); i++) {
    fl = (FileRec **) XTREALLOC(fl, (i+1)*sizeof(FileRec *));
    fl[i] = (FileRec *) XtMalloc(sizeof(FileRec));
    strcpy(fl[i]->name, entry->d_name);
    if (lstat(entry->d_name, &(fl[i]->stats)))
      goto error1;
    if (S_ISLNK(fl[i]->stats.st_mode)) {
      fl[i]->sym_link = True;
      if (stat(entry->d_name, &(fl[i]->stats)))
	error("Warning", "funny link in this directory.");
    }
    else
      fl[i]->sym_link = False;
    fl[i]->selected = False;
  }

  if (closedir(dir))
    goto error1;

  fw->files = fl;
  fw->n_files = i;
  return True;

 error1:
  for(m = 0; m <= i; m++)
    XTFREE(fl[m]);
  XTFREE(fl);

 error2:
  return False;
} 

/*-----------------------------------------------------------------------------
  Remove either files or directories from a FileList
-----------------------------------------------------------------------------*/

void filterDirectory(FileWindowRec *fw, FilterType type)
{
  FileList fl = NULL, oldfl = fw->files;
  int n = 0, m = 0;

#ifdef DEBUG_MALLOC
  fprintf(stderr,"entering filterDirectory: %lu\n",malloc_inuse(NULL));
#endif

  for (; m < fw->n_files; m++) {
    if (
	( !strcmp(oldfl[m]->name,".") && (type == Directories) ) ||
	( strcmp(oldfl[m]->name,".") && 
	 (
	  !strcmp(oldfl[m]->name,"..") ||
	  (
	   (fw->show_hidden || (oldfl[m]->name[0] != '.')) &&
	   (
	    (S_ISDIR(oldfl[m]->stats.st_mode) && (type != Files)) ||
	    (!S_ISDIR(oldfl[m]->stats.st_mode) && type != Directories)
	    )
	   )
	  )
	 )
	) {
      fl = (FileList) XTREALLOC(fl, (n+1)*sizeof(FileRec *));
      fl[n] = oldfl[m];
      n++;
    }
    else
      XTFREE(oldfl[m]);
  }
  XTFREE(oldfl);
  
#ifdef DEBUG_MALLOC
  fprintf(stderr,"exiting filterDirectory: %lu\n",malloc_inuse(NULL));
#endif

  fw->files = fl;
  fw->n_files = n;
}  


/*-----------------------------------------------------------------------------
  Sort a directory according to the sort type and dfirst flag
-----------------------------------------------------------------------------*/

void sortDirectory(FileList fl, int n, SortType type, Boolean dfirst)
{
  sort_type = type;
  dirs_first = dfirst;
  qsort(fl, n, sizeof(FileRec *), comp);
}


/*-----------------------------------------------------------------------------
  Check permission for an operation, equivalent to UNIX access()
-----------------------------------------------------------------------------*/

int permission(struct stat *stats, int perms)
{
  int mode = stats->st_mode;
  int result = 0;

  if (user.uid == stats->st_uid) {
    if (mode & S_IRUSR)
      result |= P_READ;
    if (mode & S_IWUSR)
      result |= P_WRITE;
    if (mode & S_IXUSR)
      result |= P_EXECUTE;
  } 

  else if (user.gid == stats->st_gid) {
    if (mode & S_IRGRP)
      result |= P_READ;
    if (mode & S_IWGRP)
      result |= P_WRITE;
    if (mode & S_IXGRP)
      result |= P_EXECUTE;
  } 

  else {
    if (mode & S_IROTH)
      result |= P_READ;
    if (mode & S_IWOTH)
      result |= P_WRITE;
    if (mode & S_IXOTH)
      result |= P_EXECUTE;
  } 

  return (result & perms) == perms;
}

/*-----------------------------------------------------------------------------
  Filename substitution                   
  TODO: check length of area before substitution
-----------------------------------------------------------------------------*/
void fnSub(char *fn)
{
  char *s = fn, *t;
  int l = strlen(user.home);

  if (user.home)
    for (;*s ;s++)
      if (*s == '~') {
	for (t = s + strlen(s); t > s; t--) /* have to copy backwards here */
	  *(t+l-1) = *t;
	memcpy(s, user.home, l);
	s += l;
      }
}

void makePermissionsString(char *s, int perms)
{
  s[0] = perms & S_IRUSR ? 'r' : '-'; 
  s[1] = perms & S_IWUSR ? 'w' : '-'; 
  s[2] = perms & S_IXUSR ? 'x' : '-'; 

  s[3] = perms & S_IRGRP ? 'r' : '-'; 
  s[4] = perms & S_IWGRP ? 'w' : '-'; 
  s[5] = perms & S_IXGRP ? 'x' : '-'; 

  s[6] = perms & S_IROTH ? 'r' : '-'; 
  s[7] = perms & S_IWOTH ? 'w' : '-'; 
  s[8] = perms & S_IXOTH ? 'x' : '-'; 
  
  s[9] = '\0';
}

/*---------------------------------------------------------------------------*/

void freeFileList(FileWindowRec *fw)
{
  int i;

  if (fw->files) {
    for (i = 0; i < fw->n_files; i++)
      XTFREE(fw->files[i]);
    XTFREE(fw->files);
    fw->files = NULL;
  }
  fw->n_files = 0;
}  

