/* Here is stuff for interfacing to directories.
** Copyright (c) 1993, 1994 by Olin Shivers.
*/

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

#include "libcig.h"
#include "scsh_aux.h"

/* Make sure our exports match up w/the implementation: */
#include "dirstuff1.h"

/* Linked list of malloc'd entries. */
struct scm_dirent_struct
    { char *fname;			/* File name */ 
      struct scm_dirent_struct *next;};	/* Next pointer */
typedef struct scm_dirent_struct scm_dirent_t;

void free_dirent_list(scm_dirent_t *entry)
{
     while(entry) {
	 scm_dirent_t *next = entry->next;
	 Free(entry);
	 entry = next;
	 }
     }

/* Returns [err, fnames, len]
** err is 0 for success, otw errno.
** fnames is a vector of strings (filenames), null terminated.
** len is the length of fnames.
*/
int open_dir(const char *dirname,  char ***fnames, int *len)
{
    scm_dirent_t *dep, *entries;
    struct dirent *dirent;
    char *fname, **dirvec, **vecp;
    DIR *d;
    int num_entries;
    int e; /* errno temp */

    if( NULL == (d = opendir(dirname)) ) {
      fnames = 0; len = 0;
      return errno;
      }
    
    entries = NULL; num_entries = 0;
    while( NULL != (dirent = readdir(d)) ) {
	if((strcmp(dirent->d_name,".") == 0) || (strcmp(dirent->d_name,"..") == 0))
	    continue;
	if( NULL == (dep=Alloc(scm_dirent_t)) )
	    {e=errno; goto lose2;}
	if( NULL == (fname=copystring(NULL, dirent->d_name)) ) goto lose1;
	dep->fname = fname;
	dep->next = entries;
	entries = dep; num_entries++;
	}
    closedir(d);
    
    /* Load the filenames into a vector and free the structs. */
    if( NULL == (dirvec = Malloc(char *, num_entries+1)) )
	{e=errno; goto lose3;}
    for(dep=entries, vecp=dirvec;  dep;  vecp++) {
	scm_dirent_t *next = dep->next;
	*vecp = dep->fname;
	Free(dep);
	dep = next;
	}
    dirvec[num_entries] = NULL;
    
    *fnames = dirvec;
    *len = num_entries;
    return 0;


  lose1: e = errno; Free(dep);
  lose2: closedir(d);
  lose3: free_dirent_list(entries);
    fnames = 0; len = 0;
    return e;
    }


#define DOTFILE(a) ((a) && *(a) == '.') /* Not a function. */

/* a <= b in the Unix filename ordering:
**   dotfiles come first, lexicographically ordered.
**   others come second, lexicographically ordered.
**
** This is for sorting filenames in directory listings.
*/

static int compare_fname(const void *aptr, const void *bptr)
{
    char const *a = * (char const * *) aptr;
    char const *b = * (char const * *) bptr;
    if( DOTFILE(a) )
	return DOTFILE(b) ? strcmp(a+1,b+1) : -1;
    return DOTFILE(b) ? 1 : strcmp(a,b);
}


void scm_sort_filevec(const char **dirvec, int nelts)
{
    qsort((char *) dirvec, nelts, sizeof(char*), compare_fname);
    }

#if 0
/* This one is a little more complex, but we don't use it because we
** never try to sort lists of filenames with . or .. in the list.
*/

/* Boolean function: a <= b in the Unix filename ordering:
**   . comes first
**   .. comes second
**   Other dotfiles come next, lexicographically ordered.
**   Non-dotfiles come last, lexicographically ordered.
**
** This is for sorting filenames in directory listings.
*/

static int comp1(const void *aptr, const void* bptr)
{
    char const *a = *(char const **)aptr;
    char const *b = *(char const **)bptr;

    if(streq(a,b)) return 0;

    if(DOTFILE(a))
	if( DOTFILE(b) )
	    return streq(a, ".") || 
                   (!streq(b, ".") && (streq(a, "..") || (!streq(b, "..") &&
							  (strcmp(a,b) <= 0))))
		       ? -1 : 1;
	else return -1;

    else return DOTFILE(b) ? 1 : strcmp(a,b);
}
#endif
