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

    File:    Filing.ScanDirDir.c
    Author:  Copyright  1995 Sergio Monesi
    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"

#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_scandir_funcs;

static Desk_os_error *Desk_Filing__ScanDirByDirRec(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;

 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;
     er=Desk_Filing_ReadFullDirEntry(dirname, (Desk_filing_fulldirentry *)direntries, &readnum, &offset, Desk_BUF__SIZE, NULL);
     if (er!=NULL) {
       return er;
     }
     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;
     }
   }
 } 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;
   }
 }

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

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

 return NULL;
/* cc reports 'actptr' may be used before being set */
}

Desk_os_error *Desk_Filing_ScanDirByDir(const char *dirname,
                              Desk_filing_scan_startdirfn startdirproc,
                              Desk_filing_scan_foundfilefn foundfileproc,
                              Desk_filing_scan_enddirfn enddirproc,
                              void *reference)
{
 Desk_os_error *er;
 Desk_scandir_funcs funcs;

 funcs.startdir=startdirproc;
 funcs.foundfile=foundfileproc;
 funcs.enddir=enddirproc;
 funcs.reference=reference;
 
 if (dirname[strlen(dirname)-1]!='$') {
   Desk_filing_fulldirentry dirdata;
   if (er=Desk_Filing_SingleFullDirEntry(dirname, &dirdata, sizeof(dirdata)), er!=NULL)
     return er;
   return Desk_Filing__ScanDirByDirRec(dirname, &dirdata, &funcs);
 }
 else {
   /* it could be possible to 'forge' the Desk_filing_fulldirentry for the root directory... */
   return Desk_Filing__ScanDirByDirRec(dirname, NULL, &funcs);
 }
}
