/*
   <p>Portable directory access module.</p>

   <kbd>
   <b>environ - C programming support functions</b><br>
   Copyright (C) 2005  Martin C Gregorie
   
   This program is free software; you can redistribute it and/or
   modify it under the terms of the Lesser GNU General Public License.
   
   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 Lesser 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-1307, USA.
   </kbd>
  
   <p>Contact Martin Gregorie at <i>martin@gregorie.org</i></p>
*/

#include "environ.h"

#ifdef OS9
#include <strings.h>
#include <modes.h>
#endif

#ifdef MSDOS
#include <string.h>
#include <sys\stat.h>
#include <fcntl.h>
#include <dir.h>
#include <dos.h>
#endif

#ifdef LINUX
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <stdlib.h>
#endif

/*
    OS9/68K-specific versions of diropen, dirclose, dirread.
*/
#ifdef OS9

/*
   Open the directory <u>p</u> for reading.
   Returns a DIRPATH* pointer to the directory
   control block on success and null if an error ocurred.
*/
DIRPATH *diropen(char *p)    
{
    DIRPATH *dp;
    FILE    *fp;

    fp = fopen(p,"d");       /* open as directory */
    if (fp == NULL)          /* didn't open */
        return(NULL);

    /*
        Now we've got the directory open, set up the
        DIRPATH control block
    */

    dp = (DIRPATH*)calloc(1,sizeof(DIRPATH)); 
    if (dp == NULL)
        return(NULL);        /* couldn't get the space */
    dp->p = fp;              /* keep the directory file pointer */
    strcpy(dp->path,p);      /* and the path name */
    return(dp);
}

int dirclose(dp)
DIRPATH *dp;
{
    int temp;
    temp = fclose(dp->p);    /* close the directory */
    free(dp);                /* ditch the DIRPATH control block */
    return(temp);           /* return the fclose reply */
}

/*
   Read the next filename from the directory referred to
   by the DIRPATH pointer <u>dp</u>. The absolute filename
   is placed in the <u>name</u> buffer. Returns:
   <ul>
   <li>DIR_NULL if the directory entry is a deleted file</li>
   <li>DIR_FILE if it is a file</li>
   <li>DIR_SUBDIR if it is a directory</li>
   <li>DIR_EOF if the end of the directory was found</li>
   </ul>
*/
int dirread(dp,name)
DIRPATH *dp;
char    *name;
{
    int     r,a,i;
    char    fullname[NAMELTH];
    BYTE    temp[DIRENT];
    
    if (fread(temp,DIRENT,1,dp->p) == 0)
        r = DIR_EOF;
    else
    {
        for (i = 0; i < NAMESIZE && temp[i]; i++) 
        {
            if (temp[i] >= 0x80) 
                name[i] = temp[i] - 0x80;
            else
                name[i] = temp[i];
        }
        name[i] = NULL;
        strcpy(fullname,dp->path);
        strcat(fullname,PATHSEP);
        strcat(fullname,name);
        if (name[0] == NULL)
            r = DIR_NULL;
        else
        {
            a = access(fullname,S_IFDIR);
            if (a == 0)
                r = DIR_SUBDIR;
            else
                r = DIR_FILE;    
        }
    }
    return(r);
}

#endif        

/* 
    MS-DOS-specific versions of diropen, dirclose, dirread.
*/
#ifdef MSDOS

/*
   Open the directory <u>p</u> for reading.
   Returns a DIRPATH* pointer to the directory
   control block on success and null if an error ocurred.
*/
DIRPATH *diropen(p)    
char *p;
{
    DIRPATH      *dp;
    char         temp[NAMELTH];
    int          i;

    struct stat  statbuf;
    struct ffblk *fb;

    if (strlen(p) > (NAMELTH - 5))
        return(NULL);
    strcpy(temp,p);
    i = strlen(temp);
    if (i == 0)
        return(NULL);              /* no name supplied */
    if (temp[i-1] == '\\')
        temp[--i] = NULL;          /* remove trailing slash */
    if (i == 0)
        return(NULL);              /* only slash supplied */
    if (temp[i-1] != ':')
    {                              /* omit the following for devices */
        if (access(temp,0))
            return(NULL);          /* no such file */
        if (stat(temp,&statbuf))           
            return(NULL);          /* stat fails to find stuff */
        if (statbuf.st_mode & S_IFREG)
            return(NULL);          /* not a directory */
    }

    /*
        The path is known to point to a valid directory, so
        set up the controlling ffblk (needed by 'findfirst'
        and 'findnext') and the DIRPATH control block.
    */

    fb = (struct ffblk*)calloc(1,sizeof(struct ffblk));
    if (fb == NULL)
        return(NULL);              /* out of space */
    dp = (DIRPATH*)calloc(1,sizeof(DIRPATH)); 
    if (dp == NULL)
        return(NULL);              /* ditto */
    dp->p = fb;                    /* pointer to find... control block */
    dp->status = FIRSTCALL;        /* preset first time flag */
    strcat(temp,PATHSEP);          /* add the path separator */
    strcat(temp,"*.*");            /* and the search-all wildcard */
    strcpy(dp->path,temp);         /* save the path name */

    return(dp);
}

/*
   Close the directory, release the DIRPATH control block memory
   and return null.
*/
int dirclose(dp)
DIRPATH *dp;
{
    free(dp->p);
    dp->p = NULL;
    free(dp);
    return(NULL);
}

/*
   Read the next filename from the directory referred to
   by the DIRPATH pointer <u>dp</u>. The absolute filename
   is placed in the <u>name</u> buffer. Returns:
   <ul>
   <li>DIR_FILE if it is a file</li>
   <li>DIR_SUBDIR if it is a directory</li>
   <li>DIR_EOF if the end of the directory was found</li>
   </ul>
*/
int dirread(dp,name)        
DIRPATH *dp;
char    *name;
{
    int     r,
            i,
            whatfound;

    if (dp->status == FIRSTCALL)
    {
        r = findfirst(dp->path,dp->p,FA_DIREC);
        dp->status = NEXTCALL;
    }
    else
        r = findnext(dp->p);

    if (r)
        whatfound = DIR_EOF;
    else
    {
        for (i = 0; i < NAMESIZE && dp->p->ff_name[i]; i++)
            name[i] = dp->p->ff_name[i];
        name[i] = NULL;
        if (dp->p->ff_attrib & FA_DIREC)
            whatfound = DIR_SUBDIR;
        else
            whatfound = DIR_FILE;
    }
    return(whatfound);
}        
#endif        

/*
    Linux-specific versions of diropen, dirclose, dirread.
*/
#ifdef LINUX

/*
   Open the directory <u>p</u> for reading.
   Returns a DIRPATH* pointer to the directory
   control block on success and null if an error ocurred.
*/
DIRPATH *diropen(char *p)    
{
    DIRPATH *dp;
    DIR     *fp;

    fp = opendir(p);         /* open as directory */
    if (fp == NULL)          /* didn't open */
        return(NULL);

    /*
        Now we've got the directory open, set up the
        DIRPATH control block
    */

    dp = (DIRPATH*)calloc(1,sizeof(DIRPATH)); 
    if (dp == NULL)
        return(NULL);             /* couldn't get the space */
    dp->p = fp;                   /* keep the directory file pointer */
    strncpy(dp->path,p,NAMESIZE); /* and the path name */
    dp->path[NAMESIZE-1] = 0;     /* enforce termination */
    return(dp);
}

/*
   Close the directory, release the DIRPATH control block memory
   and return the result code from closing the directory.
   Zero is returned if the close was successful.
*/
int dirclose(DIRPATH *dp)
{
    int temp;
    temp = closedir(dp->p);  /* close the directory */
    free(dp);                /* ditch the DIRPATH control block */
    return(temp);            /* return the fclose reply */
}

/*
   Read the next filename from the directory referred to
   by the DIRPATH pointer <u>dp</u>. The absolute filename
   is placed in the <u>name</u> buffer. Returns:
   <ul>
   <li>DIR_NULL if the directory entry is a deleted file</li>
   <li>DIR_FILE if it is a file</li>
   <li>DIR_SUBDIR if it is a directory</li>
   <li>DIR_EOF if the end of the directory was found</li>
   </ul>
*/
int dirread(DIRPATH *dp, char *name)
{
    struct dirent *info;
    struct stat   st;
    int           r;
    size_t        n;
    DIR           *d;
    char          *fullname;

    info = readdir(dp->p);    
    if (info == NULL)
        r = DIR_EOF;
    else
    {
        if (strlen(info->d_name) == 0)
        {
       	    strcpy(name, "");
            r = DIR_NULL;
        }
        else
        {
    	    strncpy(name, info->d_name, NAMELTH);
    	    name[NAMELTH-1] = 0;
    	    n = strlen(info->d_name) + strlen(dp->path) + strlen(PATHSEP) + 1;
    	    fullname = (char*)calloc(n, sizeof(char));
    	    if (!fullname)
                error(progname(),
                      "dirread():",
                      "calloc failed to obtain space for pathname");

            strcpy(fullname,dp->path);
            strcat(fullname,PATHSEP);
            strcat(fullname,name);
            if (lstat(fullname,&st)<0)
            	error(progname(),
            	      "dirread(): lstat() failed accessing",
            	      fullname);

            r = DIR_NULL;
            if ((st.st_mode & S_IFMT) == S_IFDIR)
                r = DIR_SUBDIR;
            if ((st.st_mode & S_IFMT) == S_IFREG)
                r = DIR_FILE;
                
            free(fullname);
        }
    }
    return(r);
}

#endif        


