/*
    ####             #    #     # #
    #   #            #    #       #          The FreeWare C library for
    #   #  ##   ###  #  # #     # ###             RISC OS machines
    #   # #  # #     # #  #     # #  #   ___________________________________
    #   # ####  ###  ##   #     # #  #
    #   # #        # # #  #     # #  #    Please refer to the accompanying
    ####   ### ####  #  # ##### # ###    documentation for conditions of use
    ________________________________________________________________________

    File:    Filing.ScanDir.c
    Author:  Copyright  1995 Sergio Monesi, Julian Smith
    Version: 1.02 (13 Sep 1995)
    Purpose: Scans a directory tree calling the specified functions.
    Mods:    13 Sep 1995 - JS - Detects Desk_filing_scan_PRUNE from functions,
                                and passes a 'void *reference' to them.
*/

#include <stdio.h>
#include <string.h>
#include "Desk.Core.h"
#include "Desk.Filing.h"
#include "Desk.Error2.h"


#define Desk_BUF__SIZE 2048      /* size of the buffer where the directory entries are read every time */

typedef struct {
  Desk_filing_scan_startdirfn startdir;
  Desk_filing_scan_foundfilefn foundfile;
  Desk_filing_scan_enddirfn enddir;
  void *reference;
  Desk_bool	skipdirerrors;	// If set, we catch and ignore dir-read errors.
} Desk_scandir_funcs;

static Desk_os_error *Desk_Filing__ScanDirRec(const char *dirname, Desk_filing_fulldirentry *dirdata, Desk_scandir_funcs *funcs)
{
 int readnum=0, offset=0;
 char direntries[Desk_BUF__SIZE];
 Desk_os_error *er;
 Desk_filing_fulldirentry *actptr=NULL;

 if (funcs->startdir!=NULL) {
   if (er=funcs->startdir(dirname, dirdata, funcs->reference), er!=NULL)	{
     if (er == Desk_filing_scan_PRUNE) return NULL;
     return er;
   }
 }

 do {
   if (readnum==0) {
     if (offset==-1) {
       /* no more objects to read */
       break;
     }
     readnum=255;
     if ( funcs->skipdirerrors)
     {
     	Desk_Error2_TryCatch
     	(
     	Desk_Filing_ReadFullDirEntry(dirname, (Desk_filing_fulldirentry *)direntries, &readnum, &offset, Desk_BUF__SIZE, NULL);
     	,
     	readnum = 0;
     	)
     }
     else
     {
     	Desk_Filing_ReadFullDirEntry(dirname, (Desk_filing_fulldirentry *)direntries, &readnum, &offset, Desk_BUF__SIZE, NULL);
     }
     if (readnum==0) break;
     actptr=(Desk_filing_fulldirentry *)direntries;
   }
   else {
     actptr=(Desk_filing_fulldirentry *)(((int)(&actptr->name)+strlen(actptr->name)+4)&~3);
   }
   readnum--;

   if (funcs->foundfile!=NULL) {
     if (er=funcs->foundfile(dirname, actptr, funcs->reference), er!=NULL) {
       if (er==Desk_filing_scan_PRUNE) return NULL;
       return er;
     }
   }

   if (actptr->objtype & 2) {
     char newname[256];
     Desk_Filing_MakePath(newname, dirname, actptr->name);
     if (er=Desk_Filing__ScanDirRec(newname, actptr, funcs), er!=NULL) {
       return er;
     }
   }
 } while(Desk_bool_TRUE);

 if (funcs->enddir!=NULL) {
   if (er=funcs->enddir(dirname, dirdata, funcs->reference), er!=NULL) {
     if ( er==Desk_filing_scan_PRUNE) return NULL;
     return er;
   }
 }

 return NULL;
}

void	Desk_Filing_ScanDir2(const char *dirname,
                         Desk_filing_scan_startdirfn startdirproc,
                         Desk_filing_scan_foundfilefn foundfileproc,
                         Desk_filing_scan_enddirfn enddirproc,
                         Desk_bool skipdirerrors,
                         void *reference)
{
 Desk_scandir_funcs funcs;

 funcs.startdir		= startdirproc;
 funcs.foundfile	= foundfileproc;
 funcs.enddir		= enddirproc;
 funcs.reference	= reference;
 funcs.skipdirerrors	= skipdirerrors;
 {
   Desk_filing_fulldirentry dirdata;
   Desk_Filing_SingleFullDirEntry(dirname, &dirdata, sizeof(dirdata));
   Desk_Filing__ScanDirRec(dirname, &dirdata, &funcs);
 }

/*
 if (dirname[strlen(dirname)-1]!='$') {
   Desk_filing_fulldirentry dirdata;
   Desk_Filing_SingleFullDirEntry(dirname, &dirdata, sizeof(dirdata));
   Desk_Filing__ScanDirRec(dirname, &dirdata, &funcs);
 }
 else {
   // it could be possible to 'forge' the Desk_filing_fulldirentry for the root directory...
   Desk_Filing__ScanDirRec(dirname, NULL, &funcs);
   return;
 }
*/
}



void	Desk_Filing_ScanDir(const char *dirname,
                         Desk_filing_scan_startdirfn startdirproc,
                         Desk_filing_scan_foundfilefn foundfileproc,
                         Desk_filing_scan_enddirfn enddirproc,
                         void *reference)
{
	Desk_Filing_ScanDir2( dirname, startdirproc, foundfileproc, enddirproc, Desk_bool_FALSE, reference);
}
