/*
    io.c - I/O routines
    Copyright (c) 1999,2006  Joey Schulze <joey@infodrom.org>

    This file is part of the Gerstensaft package.

    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., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*/

#include "beer.h"
#include "io.h"
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <malloc.h>
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>

struct s_file *get_dir (const char *dir)
{
  struct stat st;
  struct s_file *files, *fp;
  DIR *dirfd;
  struct dirent *entry;
  char *fname;

  files = NULL;
  if ((fname = (char *)malloc(strlen(dir)+256)) == NULL)
    return NULL;

  if (!stat (dir, &st)) {
    if (S_ISDIR (st.st_mode)) {
      if ((dirfd = opendir (dir)) != NULL) {
	while ((entry = readdir (dirfd)) != NULL) {
	  if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
	    continue;
	  if (!(config.dotfiles) && (entry->d_name[0] == '.'))
	    continue;
	  if ((fp = (struct s_file *)malloc(sizeof(struct s_file))) != NULL) {
	    fp->next = NULL;
	    if ((fp->name = (char *)malloc(strlen(entry->d_name)+1)) != NULL)
	      strcpy (fp->name, entry->d_name);
	    snprintf (fname, strlen(dir)+256, "%s/%s", dir, entry->d_name);
	    if (!lstat (fname, &st))
	      fp->size =  st.st_size;
	    else
	      fp->size =  0;

            if (S_ISLNK (st.st_mode)) {
	      if (!stat (fname, &st)) {
		if (S_ISDIR (st.st_mode))
		  fp->type = FILE_LINKDIR;
		else
		  fp->type = FILE_LINK;
	      } else
		fp->type = FILE_LINKDIR;
	    } else {
	      if (S_ISDIR (st.st_mode))
                fp->type = FILE_DIRECTORY;
            else
              fp->type = FILE_REGULAR;
	    }

	    if (files == 0)
	      files = fp;
	    else {
	      struct s_file *tmp, *pred;
	      
	      pred = 0;
	      tmp = files;
	      
	      while (tmp != 0)
		if (strcmp (fp->name, tmp->name) < 0)
		  break;
		else {
		  pred = tmp;
		  tmp = tmp->next;
		}
	      
	      if (pred) {
		pred->next = fp;
		fp->next = tmp;
	      } else {
		fp->next = tmp;
		files = fp;
	      }
	    }
	  }
	}
	closedir (dirfd);
      } else return NULL;
    } else return NULL;
  } else return NULL;

  if ((fp = (struct s_file *)malloc(sizeof(struct s_file))) != NULL) {
    fp->next = files;
    files = fp;
    if ((fp->name = (char *)malloc(3)) != NULL)
      strcpy (fp->name, "..");
    fp->size =  0;
    fp->type = FILE_UPDIR;
  }

  return files;
}

void free_dir (struct s_file *files)
{
  struct s_file *fp;

  for (fp=files; fp; fp=fp->next) {
    if (fp->name)
      free (fp->name);
    free (fp);
  }
}

char *check_and_adjust_dir (const char *dir)
{
  char *cp;
  int i;

  if (!dir || !strlen(dir))
    return NULL;

  /* Remove trailing '/' */
  for (i=strlen(dir)-1; i>0 && dir[i]=='/'; i--);

  if ((cp = (char *)malloc (i+2)) == NULL)
    return NULL;
  strncpy (cp, dir, i+1);
  *(cp+i+1) = '\0';

  return cp;
}

int list_contains_file (char **list, int length, const char *dir, const char *file)
{
  char *fname;
  int i;

  if (!dir || !file)
    return 0;

  if ((fname = (char *)malloc (strlen (dir) + strlen (file) +2)) == NULL)
    return 0;
  sprintf (fname, "%s/%s", dir, file);

  for (i=0; i<length; i++)
    if (!strcmp (list[i], fname)) {
      free (fname);
      return 1;
    }

  free (fname);
  return 0;
}

int exists (const char *dir, const char *file)
{
  char *fname;
  struct stat st;
  int ret;
 
  if (file) {
    if ((fname = (char *)malloc (strlen (dir) + strlen (file) +2)) == NULL)
      return 0;
    sprintf (fname, "%s/%s", dir, file);
  } else {
    fname = (char *)dir;
  }

  ret = stat (fname, &st);

  if (file)
    free (fname);

  return (ret == 0);
}

int is_dir (const char *dir, const char *file)
{
  char *fname;
  struct stat st;
 
  if (file) {
    if ((fname = (char *)malloc (strlen (dir) + strlen (file) +2)) == NULL)
      return 0;
    sprintf (fname, "%s/%s", dir, file);
  } else {
    fname = (char *)dir;
  }

  if (!stat (fname, &st))
    if (S_ISDIR (st.st_mode)) {
      if (file)
	free (fname);
      return 1;
    }

  if (file)
    free (fname);

  return 0;
}

/*
 *  The following routine is based on work from Andreas Fuchs <asf@bnet.at>
 *  which he put in the public domain.
 *
 *  Returns the amount of space a directory (with or without its subdirs)
 *  occupies.
 */
unsigned long int getdirsize (const char *dirname)
{
  unsigned long int size;
  DIR *d;
  struct dirent *dent;
  struct stat sbuf;
  char newdir[NAME_MAX];
	
  if (dirname == NULL || !strlen (dirname) || !(d = opendir ((dirname))))
    return 0;

  size = 0;
  while ((dent = readdir (d)) != NULL) {
    snprintf (newdir, sizeof(newdir), "%s/%s", dirname, dent->d_name);
    lstat (newdir, &sbuf);

    if (sbuf.st_mode & S_IFREG)
      size += sbuf.st_size;
    else if (S_ISDIR (sbuf.st_mode) && strcmp (dent->d_name, ".") && strcmp (dent->d_name, ".."))
      size += getdirsize (newdir);
  }

  return (size);
}

unsigned long int get_filesize (const char *name)
{
  struct stat st;

  if (!name)
    return 0;

  if (!stat (name, &st)) {
    if (S_ISDIR (st.st_mode)) {
      return getdirsize (name);
    } else
      return (st.st_size);
  }
  return 0;
}

char *escape_quotes (char *text)
{
  char *stext;
  char *cp, *xp;
  int count;

  for (cp=text,count=0; *cp; cp++)
    if (*cp == '\'')
      count++;

  if ((stext = (char *)malloc(strlen(text) + count*3 + 1)) == NULL)
    return NULL;

  memset (stext, 0, strlen(text) + count*3 + 1);

  for (cp=text,xp=stext; *cp; cp++)
    if (*cp == '\'') {
      *(xp++) = '\'';
      *(xp++) = '\\';
      *(xp++) = '\'';
      *(xp++) = '\'';
    } else
      *(xp++) = *cp;

  return stext;
}
