/*
    zdir.c - directory functions
    Copyright (C) 2002-2011  Ladislav Vaiz <ok1zia@nagano.cz>
    and authors of glibc as seen bellow

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    version 2 as published by the Free Software Foundation.

*/

/* Copyright (C) 1992-1998, 2000, 2002 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C 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.1 of the License, or (at your option) any later version.

   The GNU C 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 Lesser General Public
   License along with the GNU C Library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307 USA.  */

#include <zdir.h>

#include <zfile.h>
#include <zpath.h>
#include <zstr.h>

#include <string.h>

#ifdef Z_MSC_MINGW
#define __builtin_expect(expression, value) (expression)
#pragma warning(disable : 4996)
#define _CRT_SECURE_NO_WARNINGS
#endif

//#include <stdlib.h>
//#include <string.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>

#ifdef Z_HAVE_DIRECT_H
#include <direct.h>
#endif


#ifndef Z_HAVE_DIRENT_H

DIR *opendir(char *name){
    size_t base_length;
	char *all;
	DIR *dir = 0;

    if(!name || !*name) {
        errno = EINVAL;
        return NULL;
    }
    
    base_length = strlen(name);
    /* search pattern must end with suitable wildcard */
    all = strchr("/\\", name[base_length - 1]) ? "*" : "/*";

    if((dir = (DIR *) malloc(sizeof *dir)) != 0 &&
       (dir->name = (char *) malloc(base_length + strlen(all) + 1)) != 0)
    {
        strcat(strcpy(dir->name, name), all);

        if((dir->handle = (long) _findfirst(dir->name, &dir->info)) != -1)
        {
            dir->result.d_name[0] = '\0';
        }
        else /* rollback */
        {
            free(dir->name);
            free(dir);
            dir = 0;
        }
    }
    else /* rollback */
    {
        free(dir);
        dir   = 0;
        errno = ENOMEM;
    }
    return dir;
}


struct dirent *readdir(DIR *dir){
	struct dirent *result = 0;

    if(dir && dir->handle != -1)
    {
        if(!dir->result.d_name[0] != '\0' || _findnext(dir->handle, &dir->info) != -1)
        {
            result = &dir->result;
			z_strlcpy(result->d_name, dir->info.name, Z_MAX_PATH);
        }	
		errno = 0;
    }
    else
    {
        errno = EBADF;
    }

    return result;
}

int closedir(DIR *dir){
	int result = -1;

    if(dir)
    {
        if(dir->handle != -1)
        {
            result = _findclose(dir->handle);
        }

        free(dir->name);
        free(dir);
    }

    if(result == -1) /* map all errors to EBADF */
    {
        errno = EBADF;
    }

    return result;
}
#endif

#ifndef _D_ALLOC_NAMLEN
#define _D_ALLOC_NAMLEN(d) (sizeof((d)->d_name)+1)
#endif


int z_scandir (char *dir, struct dirent ***namelist, int (*select) (const char *dir, const struct dirent *), int (*cmp) (const void *, const void *))
{
    DIR *dp;
    struct dirent **v;// = NULL;
    size_t vsize = 0, i;
    struct dirent *d;
    int save;
   
	v = NULL;
    
	z_wokna(dir);
    dp = opendir(dir);
    if (dp == NULL) return -1;

    save = errno;
    errno = 0;

    i = 0;
    while (1){
        struct dirent *vnew;
        size_t dsize;
        
        d = readdir(dp);
        if (!d) break;
        if (select != NULL && (*select)(dir, d) == 0) continue;

        /* Ignore errors from select or readdir */
        errno = 0;

        if (__builtin_expect (i == vsize, 0)){
            struct dirent **new_;
            if (vsize == 0) vsize = 10;
            else vsize *= 2;
            new_ = (struct dirent **) realloc (v, vsize * sizeof (*v));
            if (new_ == NULL) break;
            v = new_;
        }

        dsize = &d->d_name[_D_ALLOC_NAMLEN (d)] - (char *) d;
        vnew = (struct dirent *) malloc (dsize);
        if (vnew == NULL) break;
        v[i++] = (struct dirent *) memcpy (vnew, d, dsize);
    }

    if (__builtin_expect (errno, 0) != 0)
    {
        save = errno;
        while (i > 0) free (v[--i]);
        free (v);
        i = -1;
    }
    else
    {
      /* Sort the list if we have a comparison function to sort with.  */
        if (cmp != NULL) qsort (v, i, sizeof (*v), cmp);
        *namelist = v;
    }

    closedir (dp);
    errno = save;
    return i;
}

void z_free_namelist(struct dirent ***namelist, int *namelistlen){
    int i;

    for (i = 0; i < *namelistlen; i++)     
        free((*namelist)[i]);

    free(*namelist);
    *namelist = NULL;
    *namelistlen = 0;
}

int z_select_dir_func(const char *base, const struct dirent *de){
    struct stat st;
	char *full;
    
	if (strcmp(de->d_name, ".") == 0) return 0;
	if (strcmp(de->d_name, "..") == 0) return 0;
	if (strcmp(de->d_name, ".svn") == 0) return 0;

	full = g_strconcat(base, "/", de->d_name, NULL);
    z_wokna(full);

    if (stat(full, &st)) {
		g_free(full);
        return 0;
    }
	g_free(full);

	if (!S_ISDIR(st.st_mode)) return 0;

    return 1;
}

int z_select_file_func(const char *base, const struct dirent *de){
    struct stat st;
	char *full = g_strconcat(base, "/", de->d_name, NULL);

    z_wokna(full);
    if (stat(full, &st)) {
		g_free(full);
        return 0;
    }
	g_free(full);

	if (!S_ISREG(st.st_mode)) return 0;

    return 1;
}

int z_compare_name_func(const void *d1, const void *d2){
    const struct dirent **pde1, **pde2;
    
    pde1=(const struct dirent **)d1;
    pde2=(const struct dirent **)d2;

    return strcmp( (*pde1)->d_name, (*pde2)->d_name);
}

                                                                                                                                     
int z_scandir_alphasort(const void *a, const void *b)
{
  return strcoll ((*(const struct dirent **) a)->d_name,
                  (*(const struct dirent **) b)->d_name);
}


int z_mkdir(const char *s, int mode){
#ifdef Z_MSC_MINGW
	return _mkdir(s);
#else
	return mkdir(s, mode);
#endif
}

int z_mkdir_p(const char *s, int mode) {
    struct stat st;
    gchar *dir,*c, *wdir;
	char sep[2];
	int ret;

    /*dbg("mkdir_p '%s'\n", s);*/

	wdir = g_strdup(s);
	z_wokna(wdir);
	
    if (!stat(wdir, &st)){
        if (S_ISDIR(st.st_mode)) {
			g_free(wdir);
			return(0);
		}
	}

    dir = g_strdup(wdir);
	strcpy(sep, "/");
	z_wokna(sep);
	c=strrchr(dir, sep[0]);
    if (c==NULL) {
		ret = z_mkdir(wdir,mode);
        g_free(dir);
		g_free(wdir);
        return(-1);
    }
    *c='\0';
    if (strlen(dir)==0) {
        g_free(dir);
		g_free(wdir);
        return(-1);
    }
    z_mkdir_p(dir,mode);
	ret = z_mkdir(wdir,mode);
    g_free(dir);
	g_free(wdir);
    return(ret);
}


int z_fmkdir_p(const char *filename, int mode){
    gchar *dir,*d;
    int ret;
	char sep[2];
    
    /*dbg("fmkdir_p '%s'\n", filename);*/
    dir = g_strdup(filename);
	z_wokna(dir);
	strcpy(sep, "/");
	z_wokna(sep);
    d = strrchr(dir, sep[0]);
    ret = -1;
    if (d){
        *d = '\0';
        ret = z_mkdir_p(dir, mode);
	}
    g_free(dir);
    return ret;
}
