/*   -*- c -*-
 * 
 *  ----------------------------------------------------------------------
 *  Directory and file flag utilities.
 *  ----------------------------------------------------------------------
 *
 *  Copyright (c) 2002-2003 by PuhPuh
 *  
 *  This code is copyrighted property of the author.  It can still
 *  be used for any non-commercial purpose following conditions:
 *  
 *      1) This copyright notice is not removed.
 *      2) Source code follows any distribution of the software
 *         if possible.
 *      3) Copyright notice above is found in the documentation
 *         of the distributed software.
 *  
 *  Any express or implied warranties are disclaimed.  Author is
 *  not liable for any direct or indirect damages caused by the use
 *  of this software.
 *
 *  ----------------------------------------------------------------------
 *
 */


#include "ccincludes.h"
#include "ccbuffer.h"
#include "ccxstream.h"
#include "ccxmltrans.h"

static char *cc_xstream_file_name_to_string(const char *fn);
static char *cc_xstream_path_name_to_components(const char *path);
static char *cc_xstream_file_component_to_tag(const char *component, unsigned int level);

static char *cc_xstream_file_name_to_string(const char *fn)
{
  struct CcBufferRec buf[1];
  char *r, c, *hlp2;
  const char *hlp;

  if (strchr(fn, '/') == NULL)
    {
      if (strcmp(fn, "") != 0)
	hlp = fn;
      else
	hlp = CC_XSTREAM_VERSION_STRING;
    }
  else
    {
      hlp = strrchr(fn, '/');
      hlp++;
    }
  hlp2 = cc_xstrdup(hlp);
  hlp = hlp2;
  cc_buffer_init(buf);
  c = 0;
  for (/*NOTHING*/; *hlp; hlp++)
    {
      if ((*hlp == ':') || (*hlp == '.') || (*hlp == ',') || (*hlp == ';') ||
	  (*hlp == '(') || (*hlp == ')') || (*hlp == '-') || (*hlp == '[') ||
	  (*hlp == ']') || (*hlp == '{') || (*hlp == '}') || (*hlp == '&') ||
	  (*hlp == '%') || (*hlp == '?') || (*hlp == '#') || (*hlp == '=') ||
	  (*hlp == '*') || (*hlp == '@') || (*hlp == '!') || (*hlp == '&') ||
	  (*hlp == '+') || (*hlp == '\''))
	{
	  c = *hlp;
	  cc_buffer_append(buf, &c, 1);
	}
      else if (isdigit(*hlp))
	{
	  c = *hlp;
	  cc_buffer_append(buf, &c, 1);
	}
      else if (isalpha(*hlp))
	{
	  if (isalpha(c))
	    c = *hlp;
	  else
	    c = toupper(*hlp);
	  cc_buffer_append(buf, &c, 1);
	}
      else
	{
	  if ((c != ' ') && (c != 0))
	    {
	      c = ' ';
	      cc_buffer_append(buf, &c, 1);
	    }
	}
    }
  r = cc_xmemdup(cc_buffer_ptr(buf), cc_buffer_len(buf));
  cc_xfree(hlp2);
  cc_buffer_uninit(buf);
  return r;
}


static char *cc_xstream_file_component_to_tag(const char *component, unsigned int level)
{
  struct CcBufferRec buf[1];
  char nb[16], *r;
  char *xml_component;

  cc_buffer_init(buf);
  cc_buffer_append_string(buf, "<COMPONENT LEVEL=\"");
  snprintf(nb, sizeof (nb), "%u", level);
  cc_buffer_append_string(buf, nb);
  cc_buffer_append_string(buf, "\">");
  xml_component = cc_xstream_xml_encode(component);
  cc_buffer_append_string(buf, xml_component);
  cc_xfree(xml_component);
  cc_buffer_append_string(buf, "</COMPONENT>");
  r = cc_xmemdup(cc_buffer_ptr(buf), cc_buffer_len(buf));
  cc_buffer_uninit(buf);
  return r;
}

static char *cc_xstream_path_name_to_components(const char *path)
{
  const char *hlp;
  char *c1, *c2;
  char nb[16], *r;
  unsigned int count;
  struct CcBufferRec buf[1];

  count = 0;
  cc_buffer_init(buf);
  while ((hlp = strchr(path, '/')) != NULL)
    {
      c1 = cc_xmemdup(path, hlp - path);
      path = hlp + 1;
      if ((strlen(c1) > 0) && (strcmp(c1, ".") != 0))
	{
	  c2 = cc_xstream_file_component_to_tag(c1, ++count);
	  cc_buffer_append_string(buf, c2);
	  cc_xfree(c2);
	}
      cc_xfree(c1);
    }
  if ((strlen(path) > 0) && (strcmp(path, ".") != 0))
    {
      c2 = cc_xstream_file_component_to_tag(path, ++count);
      cc_buffer_append_string(buf, c2);
      cc_xfree(c2);
    }
  cc_buffer_prepend_string(buf, "\">");
  snprintf(nb, sizeof (nb), "%u", count);
  cc_buffer_prepend_string(buf, nb);
  cc_buffer_prepend_string(buf, "<PATHNAME DEPTH=\"");
  cc_buffer_append_string(buf, "</PATHNAME>");
  r = cc_xmemdup(cc_buffer_ptr(buf), cc_buffer_len(buf));
  cc_buffer_uninit(buf);
  return r;
}

CcStringList cc_xstream_string_list_add(CcStringList lst, const char *str)
{
  CcStringList x;

  x = cc_xcalloc(1, sizeof (*x));
  x->s = cc_xstrdup(str);
  x->next = lst;
  return x;
}

CcStringList cc_xstream_read_directory(const char *path)
{
  DIR *dir;
  struct dirent *item;
  CcStringList r, x;

  r = NULL;
  dir = opendir(path);
  if (dir == NULL)
    return NULL;
  while ((item = readdir(dir)) != NULL)
    {
      if ((item->d_name[0] != '\0') &&
	  (strcmp(item->d_name, ".") != 0) &&
	  (strcmp(item->d_name, "..") != 0))
	{
	  x = cc_xcalloc(1, sizeof (*x));
	  x->s = cc_xstrdup(item->d_name);
	  x->next = r;
	  r = x;
	}
    }
  closedir(dir);
  return r;
}

char *cc_xstream_file_info(CcXstreamProg prog, const char *path, const char *relativepath)
{
  struct stat st;
  struct CcBufferRec buf[1];
  CC_UINT_64_TYPE_NAME t;
  char b[16], *r, *name, *rname, *tmp;
  int sr;

  r = NULL;
  if ((prog != NULL) && prog->follow_symlinks)
    sr = stat(path, &st);
  else
    sr = lstat(path, &st);
  if (sr == 0)
    {
      tmp = cc_xstream_file_name_to_string(relativepath);
      name = cc_xstream_xml_encode(tmp);
      cc_xfree(tmp);
      if (relativepath == NULL)
	rname = NULL;
      else if (strlen(relativepath) == 0)
	rname = cc_xstrdup("<PATHNAME DEPTH=\"0\"></PATHNAME>");
      else
	rname = cc_xstream_path_name_to_components(relativepath);
      if (S_ISREG(st.st_mode))
	{
	  cc_buffer_init(buf);
	  cc_buffer_append_string(buf, "<DIRECTORYITEM>");
	  cc_buffer_append_string(buf, "<NAME>");
	  cc_buffer_append_string(buf, name);
	  cc_buffer_append_string(buf, "</NAME>");
	  if (rname != NULL)
	    {
	      cc_buffer_append_string(buf, rname);
	    }
	  cc_buffer_append_string(buf, "<ATTRIB>file</ATTRIB>");
	  cc_buffer_append_string(buf, "<SIZE>");
	  t = (CC_UINT_64_TYPE_NAME)(cc_xstream_file_size(path));
	  snprintf(b, sizeof (b), CC_UINT_64_PRINTF_FORMAT, t);
	  cc_buffer_append_string(buf, b);
	  cc_buffer_append_string(buf, "</SIZE>");
	  cc_buffer_append_string(buf, "<TIME>");
	  cc_buffer_append_string(buf, "<ACCESS>");
	  t = (CC_UINT_64_TYPE_NAME)(st.st_atime);
	  snprintf(b, sizeof (b), CC_UINT_64_PRINTF_FORMAT, t);
	  cc_buffer_append_string(buf, b);
	  cc_buffer_append_string(buf, "</ACCESS>");
	  cc_buffer_append_string(buf, "<MODIFICATION>");
	  t = (CC_UINT_64_TYPE_NAME)(st.st_mtime);
	  snprintf(b, sizeof (b), CC_UINT_64_PRINTF_FORMAT, t);
	  cc_buffer_append_string(buf, b);
	  cc_buffer_append_string(buf, "</MODIFICATION>");
	  cc_buffer_append_string(buf, "<CHANGE>");
	  t = (CC_UINT_64_TYPE_NAME)(st.st_ctime);
	  snprintf(b, sizeof (b), CC_UINT_64_PRINTF_FORMAT, t);
	  cc_buffer_append_string(buf, b);
	  cc_buffer_append_string(buf, "</CHANGE>");
	  cc_buffer_append_string(buf, "</TIME>");
	  cc_buffer_append_string(buf, "<INFO></INFO>");
	  cc_buffer_append_string(buf, "</DIRECTORYITEM>");
	  r = cc_xmemdup(cc_buffer_ptr(buf), cc_buffer_len(buf));
	  cc_buffer_uninit(buf);
	}
      else if (S_ISDIR(st.st_mode))
	{
	  cc_buffer_init(buf);
	  cc_buffer_append_string(buf, "<DIRECTORYITEM>");
	  cc_buffer_append_string(buf, "<NAME>");
	  cc_buffer_append_string(buf, name);
	  cc_buffer_append_string(buf, "</NAME>");
	  if (rname != NULL)
	    {
	      cc_buffer_append_string(buf, rname);
	    }
	  cc_buffer_append_string(buf, "<ATTRIB>directory</ATTRIB>");
	  cc_buffer_append_string(buf, "<INFO></INFO>");
	  cc_buffer_append_string(buf, "</DIRECTORYITEM>");
	  r = cc_xmemdup(cc_buffer_ptr(buf), cc_buffer_len(buf));
	  cc_buffer_uninit(buf);
	}
      cc_xfree(name);
      cc_xfree(rname);
    }
  return r;
}

int cc_xstream_path_is_directory(CcXstreamProg prog, const char *path)
{
  struct stat st;

  if ((prog != NULL) && prog->follow_symlinks)
    return ((stat(path, &st) == 0) && (S_ISDIR(st.st_mode)));
  else
    return ((lstat(path, &st) == 0) && (S_ISDIR(st.st_mode)));
}

int cc_xstream_path_is_file(CcXstreamProg prog, const char *path)
{
  struct stat st;

  if ((prog != NULL) && prog->follow_symlinks)
    return ((stat(path, &st) == 0) && (S_ISREG(st.st_mode)));
  else
    return ((lstat(path, &st) == 0) && (S_ISREG(st.st_mode)));
}

off_t cc_xstream_open_file_size(FILE *f)
{
#ifndef __CYGWIN__
  struct stat st;

  if (fstat(fileno(f), &st) == 0)
    return st.st_size;
  return 0;
#else
  off_t c, r;

  c = ftello(f);
  if (fseeko(f, 0, SEEK_END) != 0)
    return 0;
  r = ftello(f);
  fseeko(f, c, SEEK_SET);
  return r;
#endif
}

off_t cc_xstream_file_size(const char *path)
{
#ifndef __CYGWIN__
  struct stat st;

  if (stat(path, &st) == 0)
    return st.st_size;
  return 0;
#else
  FILE *f;
  off_t r;

  f = fopen(path, "r");
  if (f != NULL)
    {
      r = cc_xstream_open_file_size(f);
      fclose(f);
      return r;
    }
  return 0;
#endif
}

/* eof (ccxfile.c) */
