/* $Id: statfile.c,v 1.2 2009/01/04 15:42:16 kiesling Exp $ */

/*
  This file is part of Ctalk.
  Copyright  2005-2009  Robert Kiesling, ctalk@ctalklang.org.
  Permission is granted to copy this software provided that this copyright
  notice is included in all source code modules.

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

  This library 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
  Lesser 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., 51 Franklin St., Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <errno.h>

/* From ctalk.h */
#define MAXMSG 8192

#ifdef __DJGPP__
#define PATHSEPCHAR ';'
#define DIRSEPCHAR '\\'
#else 
#define PATHSEPCHAR ':'
#define DIRSEPCHAR '/'
#endif

/* Prototypes. */
int is_dir (char *);
char *substrcpy (char *, char *, int, int);

#ifdef __GNUC__
void _error (char *, ...) __attribute__ ((noreturn));
#else
void _error (char *, ...);
#endif

#if defined(__GNUC__) && defined(__sparc__) && defined(__svr4__)
#define index strchr
#define rindex strrchr
#endif

int stat_err_s (char *path, struct stat *s_p) {
  int r;
  int errno_save = errno;
  r = stat (path, s_p);
  errno = errno_save;
  return r;
}

static struct stat *statbuf_ptr;

int file_exists (char *path) {

#if defined(__CYGWIN__) || defined (__DJGPP__)
  DIR *d;
  struct dirent *d_ent;
  char dname[FILENAME_MAX], fname[FILENAME_MAX], *p;

  if (is_dir (path))
    return 0;

  if ((p = rindex (path, '/')) != NULL) {
    substrcpy (dname, path, 0, p - path);
    strcpy (fname, p + 1);
  } else {
    strcpy (fname, path);
    strcpy (dname, "./");
  }

  if (is_dir (dname)) {

    if ((d = opendir (dname)) == NULL)
      _error ("file_exists: %s.\n", strerror (errno));

    while ((d_ent = readdir (d)) != NULL)
      if (!strcmp (d_ent -> d_name, fname)) {
      	closedir (d);
      	return 1;
      }
    
    closedir (d);
  }
  return 0;
#else

  int r;

  if (is_dir (path)) {
    return 0;
  }

  statbuf_ptr = calloc (1, sizeof (struct stat));

  if ((r = stat_err_s (path, statbuf_ptr)) == 0) {
    free (statbuf_ptr);
    return 1;
  } else {
    free (statbuf_ptr);
    return 0;
  }
#endif
}

int file_size (char *path) {
  struct stat statbuf;
  int r;

  if ((r = stat_err_s (path, &statbuf)) != 0)
    _error ("%s: %s.", path, strerror (errno));

  return statbuf.st_size;
}

int __ctalkIsDir (char *path) { return is_dir (path); }

int is_dir (char *path) {
  struct stat statbuf;
  int r;
  
  if ((r = stat_err_s (path, &statbuf)) == 0) {
    if (S_ISDIR (statbuf.st_mode)) 
      return 1;
  }

  return 0;
}

char *which (char *prog) {
  static char pname[FILENAME_MAX];
  char path_var[MAXMSG],
    dname[FILENAME_MAX],
    *p, *q;
  DIR *d;
  struct dirent *d_ent;

  if (getenv ("PATH"))
    strcpy (path_var, getenv ("PATH"));
  else
    return NULL;

  p = path_var;
  while (p) {
    memset ((void *)dname, 0, FILENAME_MAX * sizeof (char));
    if ((q = index (p, PATHSEPCHAR)) != NULL)
      strncpy (dname, p, q - p);
    else
      strcpy (dname, p);

    if (is_dir (dname)) {

      if ((d = opendir (dname)) == NULL)
	_error ("which: %s.\n", strerror (errno));

      while ((d_ent = readdir (d)) != NULL)
	if (!strcmp (d_ent -> d_name, prog)) {
	  sprintf (pname, "%s%c%s", dname, DIRSEPCHAR, d_ent -> d_name);
	  closedir (d);
	  return pname;
	}

      closedir (d);
    }

    p = (q) ? q + 1 : NULL;
  }

  return NULL;
}

char *pwd (void) {
  static char buf[FILENAME_MAX+1];
  return getcwd (buf, FILENAME_MAX);
}

char *expand_path (char *path) {
  static char expanded_path[FILENAME_MAX];
  char basename[FILENAME_MAX],
    dirpath[FILENAME_MAX],
    parentpath[FILENAME_MAX],
    homedir[FILENAME_MAX],
    *s_ptr;

  if (!strncmp (path, "..", 2)) {
    strcpy (dirpath, pwd ());
    s_ptr = rindex (dirpath, '/');
    if (s_ptr) {
      *s_ptr = 0;
      strcpy (parentpath, dirpath);
    } else {
      *parentpath = 0;
    }
    s_ptr = index (path, '/');
    if (s_ptr) {
      sprintf (expanded_path, "%s/%s", parentpath, s_ptr + 1);
    } else {
      strcpy (expanded_path, parentpath);
    }
    return expanded_path;
  }

  if (*path == '.') {
    strcpy (dirpath, pwd ());
    s_ptr = index (path, '/');
    if (s_ptr) {
      sprintf (expanded_path, "%s/%s", dirpath, s_ptr + 1);
    } else {
      sprintf (expanded_path, "%s", dirpath);
    }
    return expanded_path;
  }

  if (index (path, '~')) {
    strcpy (dirpath, pwd ());
    strcpy (basename, index (path, '/') + 1);
    strcpy (homedir, getenv ("HOME"));
    sprintf (expanded_path, "%s/%s", homedir, basename);
    return expanded_path;
  }

  strcpy (expanded_path, path);
  return expanded_path;
}

int file_mtime (char *path) {
  struct stat statbuf;
  int r;
  
  if ((r = stat_err_s (path, &statbuf)) == 0) {
    return (int) statbuf.st_mtime;
  }
  return r;
}

