/* scandir.c Copyright (C) Codemist Ltd., 1994-2002 */
/*
*
* Directory scanning code for use in CSL-related utilities.
*
* A C Norman
*/
/*
* This code may be used and modified, and redistributed in binary
* or source form, subject to the "CCL Public License", which should
* accompany it. This license is a variant on the BSD license, and thus
* permits use of code derived from this in either open and commercial
* projects: but it does require that updates to this code be made
* available back to the originators of the package.
* Before merging other code in with this or linking this code
* with other packages or libraries please check that the license terms
* of the other material are compatible with those of this.
*/
/* Signature: 163eeafe 10-Oct-2002 */
#include "sys.h"
/*
* If I am to process directories I need a set of routines that will
* scan sub-directories for me. This is (necessarily?) dependent on
* the operating system I am running under, hence the conditional compilation
* here. The specification I want is:
* void scan_directory(char *dir,
* void (*proc)(char *name, int why, long int size));
*
* This is called with a file- or directory-name as its first argument
* and a function as its second.
* It calls the function for every directory and every file that can be found
* rooted from the given place. If the file to scan is specified as NULL
* the current directory is processed. I also arrange that an input string
* "." (on Windows, DOS and Unix) or "@" (Archimedes) is treated as a request
* to scan the whole of the current directory.
* When a simple file is found the procedure is called with the name of the
* file, why=0, and the length (in bytes) of the file. For a directory
* the function is called with why=1, then the contents of the directory are
* processed. For directories the size information will be 0. There is no
* guarantee of useful behaviour if some of the files to be scanned are
* flagged as "invisible" or "not readable" or if they are otherwise special.
*
* I also provide a similar function scan_files() with the same arguments that
* does just the same except that it does not recurse into sub-directories,
* but if the name originally passed is that of a directory then all the
* files in it will be scanned.
*/
/*
* When scan_directory calls the procedure it has been passed, it will have
* set scan_leafstart to the offset in the passed filename where the
* original directory ended and the new information starts.
*/
int scan_leafstart = 0;
/* #define SCAN_FILE 0 */ /* In scandir.h - listed here for emphasis */
/* #define SCAN_STARTDIR 1 */
/* #define SCAN_ENDDIR 2 */
/*
* I use a (static) flag to indicate how sub-directories should be
* handled, and what to do about case. By default I fold to lower case
* on windows. setting hostcase non-zero causes case to be preserved.
*/
static int recursive_scan, hostcase = 0;
/*
* The following is an expression of despair! ANSI C (all the way back from
* 1989, and based on good practise from before then, mandates that
* realloc should behave as malloc if its first arg is NULL. But STILL there
* are C libraries out there which do not honour this and which crash
* in such cases. Thus this veneer ought to be unnecessary but in reality
* is a useful safety net!
*/
static void *my_realloc(void *p, int len)
{
if (p == NULL) return (*malloc_hook)(len);
else return (*realloc_hook)(p, len);
}
void set_hostcase(int fg)
{
hostcase = fg;
}
#ifdef __LCC__
static char filename[LONGEST_LEGAL_FILENAME];
static struct _finddata_t *found_files = NULL;
static int n_found_files = 0, max_found_files = 0;
#define TABLE_INCREMENT 50
static int more_files(void)
{
if (n_found_files > max_found_files - 5)
{ struct _finddata_t *fnew = (struct _finddata_t *)
my_realloc((void *)found_files,
sizeof(struct _finddata_t) *
(max_found_files + TABLE_INCREMENT));
if (fnew == NULL) return 1; /* failure flag */
found_files = fnew;
max_found_files += TABLE_INCREMENT;
}
return 0;
}
int MS_CDECL alphasort_files(const void *a, const void *b)
{
const struct _finddata_t *fa = (const struct _finddata_t *)a,
*fb = (const struct _finddata_t *)b;
return strncmp(fb->name, fa->name, sizeof(fa->name));
}
static void exall(int namelength,
void (*proc)(char *name, int why, long int size))
/*
* This procedure scans a directory-full of files, calling the given procedure
* to process each one it finds.
*/
{
int rootlen = namelength;
long code;
int first = n_found_files;
long fh;
struct _finddata_t found;
fh = _findfirst(filename, &found);
code = fh;
while (code != -1)
{ if (more_files()) break;
found_files[n_found_files++] = found;
code = _findnext(fh, &found);
}
_findclose(fh);
qsort((void *)&found_files[first],
n_found_files-first,
sizeof(struct _finddata_t),
alphasort_files);
while (rootlen>=0 && filename[rootlen]!='\\') rootlen--;
while (n_found_files != first)
{ char *p = (char *)&found_files[--n_found_files].name;
int c;
namelength = rootlen+1;
/*
* I fold DOS filenames into lower case because it does not matter much
* to DOS and I think it looks better - furthermore it helps when I move
* archives to other systems.
*/
while ((c = *p++) != 0)
{ if (!hostcase) if (isupper(c)) c = tolower(c);
filename[namelength++] = (char)c;
}
filename[namelength] = 0;
if (found_files[n_found_files].attrib & _A_SUBDIR)
{ if (found_files[n_found_files].name[0] != '.')
/*
* I filter out directory names that start with '.'.
* This is to avoid calamity with recursion though chains such as .\.\.\.....
*/
{ proc(filename, SCAN_STARTDIR, 0);
if (!recursive_scan) continue;
strcpy(&filename[namelength], "\\*.*");
/*
* Append "\*.*" to the directory-name and try again, thereby scanning
* its contents.
*/
exall(namelength+4, proc);
filename[namelength] = 0;
proc(filename, SCAN_ENDDIR, 0);
}
}
else proc(filename, SCAN_FILE, found_files[n_found_files].size);
}
return;
}
void scan_directory(char *dir,
void (*proc)(char *name, int why, long int size))
{
recursive_scan = 1;
if (dir==NULL || strcmp(dir,".")==0)
{ dir = "*.*";
scan_leafstart = 0;
}
else scan_leafstart = strlen(dir)+1;
strcpy(filename, dir);
exall(strlen(filename), proc);
}
void scan_files(char *dir,
void (*proc)(char *name, int why, long int size))
{
recursive_scan = 0;
if (dir==NULL || strcmp(dir,".")==0)
{ strcpy(filename, "*.*");
scan_leafstart = 0;
}
else
{ scan_leafstart = strlen(dir);
strcpy(filename, dir);
strcpy(filename+scan_leafstart, "\\*.*");
scan_leafstart++;
}
exall(strlen(filename), proc);
}
#define SCANDIR_KNOWN 1
#else
#ifdef WINDOWS_NT
/*
* Note that I put the test for WINDOWS_NT before the test for MS_DOS
* so that if it happens that the symbol MS_DOS is defined that fact will
* not cause too much confusion.
*/
static char filename[LONGEST_LEGAL_FILENAME];
static WIN32_FIND_DATA *found_files = NULL;
static int n_found_files = 0, max_found_files = 0;
#define TABLE_INCREMENT 50
static int more_files(void)
{
if (n_found_files > max_found_files - 5)
{ WIN32_FIND_DATA *fnew = (WIN32_FIND_DATA *)
my_realloc((void *)found_files,
sizeof(WIN32_FIND_DATA)*
(max_found_files + TABLE_INCREMENT));
if (fnew == NULL) return 1; /* failure flag */
found_files = fnew;
max_found_files += TABLE_INCREMENT;
}
return 0;
}
/*
* Anybody compiling using Microsoft Visual C++ had better note that
* the type declared in the Microsoft header files for qsort insists
* on a __cdecl here. Ugh.
*/
int MS_CDECL alphasort_files(const void *a, const void *b)
{
const WIN32_FIND_DATA *fa = (const WIN32_FIND_DATA *)a,
*fb = (const WIN32_FIND_DATA *)b;
return strncmp(fb->cFileName, fa->cFileName, sizeof(fa->cFileName));
}
static void exall(int namelength,
void (*proc)(char *name, int why, long int size))
/*
* This procedure scans a directory-full of files, calling the given procedure
* to process each one it finds.
*/
{
WIN32_FIND_DATA found;
int rootlen = namelength, first = n_found_files;
HANDLE hSearch = FindFirstFile(filename, &found);
if (hSearch == INVALID_HANDLE_VALUE) return; /* No files found at all */
for (;;)
{ if (more_files()) break;
found_files[n_found_files++] = found;
if (!FindNextFile(hSearch, &found)) break;
}
FindClose(hSearch);
qsort((void *)&found_files[first],
n_found_files-first,
sizeof(WIN32_FIND_DATA),
alphasort_files);
while (rootlen>=0 && filename[rootlen]!='\\') rootlen--;
while (n_found_files != first)
{ char *p = (char *)&found_files[--n_found_files].cFileName;
int c;
/*
* Fill out filename with the actual name I grabbed, i.e. with
* wild-cards expanded.
*/
namelength = rootlen+1;
/*
* I fold DOS filenames into lower case because it does not matter much
* to DOS and I think it looks better - furthermore it helps when I move
* archives to other systems. So I do the same on NT.
*/
while ((c = *p++) != 0)
{ if (!hostcase) if (isupper(c)) c = tolower(c);
filename[namelength++] = (char)c;
}
filename[namelength] = 0;
if (found_files[n_found_files].dwFileAttributes &
FILE_ATTRIBUTE_DIRECTORY)
{ if (found_files[n_found_files].cFileName[0] != '.')
/*
* I filter out directory names that start with '.'.
* This is to avoid calamity with recursion though chains such as .\.\.\.....
*/
{ proc(filename, SCAN_STARTDIR, 0);
if (!recursive_scan) continue;
strcpy(&filename[namelength], "\\*.*");
/*
* Append "\*.*" to the directory-name and try again, thereby scanning
* its contents.
*/
exall(namelength+4, proc);
filename[namelength] = 0;
proc(filename, SCAN_ENDDIR, 0);
}
}
else proc(filename, SCAN_FILE,
found_files[n_found_files].nFileSizeLow);
}
return;
}
void scan_directory(char *dir,
void (*proc)(char *name, int why, long int size))
{
recursive_scan = 1;
if (dir==NULL || strcmp(dir,".")==0)
{ dir = "*.*";
scan_leafstart = 0;
}
else scan_leafstart = strlen(dir)+1;
strcpy(filename, dir);
exall(strlen(filename), proc);
}
void scan_files(char *dir,
void (*proc)(char *name, int why, long int size))
{
recursive_scan = 0;
if (dir==NULL || strcmp(dir,".")==0)
{ strcpy(filename, "*.*");
scan_leafstart = 0;
}
else
{ scan_leafstart = strlen(dir);
strcpy(filename, dir);
if (filename[scan_leafstart-1] == '\\')
{ /* Root directory */
strcpy(filename+scan_leafstart, "*.*");
--scan_leafstart;
}
else strcpy(filename+scan_leafstart, "\\*.*");
scan_leafstart++;
}
exall(strlen(filename), proc);
}
#define SCANDIR_KNOWN 1
#else /* WINDOWS_NT */
#ifdef MS_DOS
/*
* This will be OK for both Zortech and Watcom. The entry for GCCWIN refers
* to Cygnus cygwin32 "Windows GCC" together with the windows32api header
* files and libraries. However note well that the cygwin32 version of this
* code has not yet been got working and the status of various bits of
* Windows code in that environment remains slightly delicate as of 1Q97.
*/
#if defined __WATCOMC__ || defined _MSC_VER || defined GCCWIN
static char filename[LONGEST_LEGAL_FILENAME];
static WIN32_FIND_DATA *found_files = NULL;
static int n_found_files = 0, max_found_files = 0;
#define TABLE_INCREMENT 50
static int more_files(void)
{
if (n_found_files > max_found_files - 5)
{ WIN32_FIND_DATA *fnew = (WIN32_FIND_DATA *)
my_realloc((void *)found_files,
sizeof(WIN32_FIND_DATA) *
(max_found_files + TABLE_INCREMENT));
if (fnew == NULL) return 1; /* failure flag */
found_files = fnew;
max_found_files += TABLE_INCREMENT;
}
return 0;
}
int MS_CDECL alphasort_files(const void *a, const void *b)
{
const WIN32_FIND_DATA *fa = (const WIN32_FIND_DATA *)a,
*fb = (const WIN32_FIND_DATA *)b;
return strncmp(fb->cFileName, fa->cFileName, sizeof(fa->cFileName));
}
static void exall(int namelength,
void (*proc)(char *name, int why, long int size))
/*
* This procedure scans a directory-full of files, calling the given procedure
* to process each one it finds.
*/
{
int rootlen = namelength;
int code, first = n_found_files;
HANDLE fh;
WIN32_FIND_DATA found;
fh = FindFirstFile(filename, &found);
code = (fh != INVALID_HANDLE_VALUE);
while (code)
{ if (more_files()) break;
found_files[n_found_files++] = found;
code = FindNextFile(fh, &found);
}
FindClose(fh);
qsort((void *)&found_files[first],
n_found_files-first,
sizeof(WIN32_FIND_DATA),
alphasort_files);
while (rootlen>=0 && filename[rootlen]!='\\') rootlen--;
while (n_found_files != first)
{ char *p = (char *)&found_files[--n_found_files].cFileName;
int c;
/*
* Fill out filename with the actual name I grabbed, i.e. with
* wild-cards expanded.
*/
namelength = rootlen+1;
/*
* I fold DOS filenames into lower case because it does not matter much
* to DOS and I think it looks better - furthermore it helps when I move
* archives to other systems.
*/
while ((c = *p++) != 0)
{ if (!hostcase) if (isupper(c)) c = tolower(c);
filename[namelength++] = (char)c;
}
filename[namelength] = 0;
if (found_files[n_found_files].dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{ if (found_files[n_found_files].cFileName[0] != '.')
/*
* I filter out directory names that start with '.'.
* This is to avoid calamity with recursion though chains such as .\.\.\.....
*/
{ proc(filename, SCAN_STARTDIR, 0);
if (!recursive_scan) continue;
strcpy(&filename[namelength], "\\*.*");
/*
* Append "\*.*" to the directory-name and try again, thereby scanning
* its contents.
*/
exall(namelength+4, proc);
filename[namelength] = 0;
proc(filename, SCAN_ENDDIR, 0);
}
}
else proc(filename, SCAN_FILE, found_files[n_found_files].nFileSizeLow);
}
return;
}
void scan_directory(char *dir,
void (*proc)(char *name, int why, long int size))
{
recursive_scan = 1;
if (dir==NULL || strcmp(dir,".")==0)
{ dir = "*.*";
scan_leafstart = 0;
}
else scan_leafstart = strlen(dir)+1;
strcpy(filename, dir);
exall(strlen(filename), proc);
}
void scan_files(char *dir,
void (*proc)(char *name, int why, long int size))
{
recursive_scan = 0;
if (dir==NULL || strcmp(dir,".")==0)
{ strcpy(filename, "*.*");
scan_leafstart = 0;
}
else
{ scan_leafstart = strlen(dir);
strcpy(filename, dir);
strcpy(filename+scan_leafstart, "\\*.*");
scan_leafstart++;
}
exall(strlen(filename), proc);
}
#else
#ifdef GCC386
/*
* BEWARE: this version will not recognise Windows 95 long file-names
* and so there is very real possibility of muddle.
*/
static char filename[LONGEST_LEGAL_FILENAME];
static struct ffblk *found_files = NULL;
static int n_found_files = 0, max_found_files = 0;
#define TABLE_INCREMENT 50
static int more_files(void)
{
if (n_found_files > max_found_files - 5)
{ struct ffblk *fnew = (struct ffblk *)
my_realloc((void *)found_files,
sizeof(struct ffblk) *
(max_found_files + TABLE_INCREMENT));
if (fnew == NULL) return 1; /* failure flag */
found_files = fnew;
max_found_files += TABLE_INCREMENT;
}
return 0;
}
int alphasort_files(const void *a, const void *b)
{
const struct ffblk *fa = (const struct ffblk *)a,
*fb = (const struct ffblk *)b;
return strncmp(fb->ff_name, fa->ff_name, sizeof(fa->ff_name));
}
static void exall(int namelength,
void (*proc)(char *name, int why, long int size))
/*
* This procedure scans a directory-full of files, calling the given procedure
* to process each one it finds.
*/
{
int rootlen = namelength;
int code, first = n_found_files;
struct ffblk found;
code = findfirst(filename, &found, FA_DIREC);
while (code == 0)
{ if (more_files()) break;
found_files[n_found_files++] = found;
code = findnext(&found);
}
qsort((void *)&found_files[first],
n_found_files-first,
sizeof(struct ffblk),
alphasort_files);
while (rootlen>=0 && filename[rootlen]!='\\') rootlen--;
while (n_found_files != first)
{ char *p = (char *)&found_files[--n_found_files].ff_name;
int c;
/*
* Fill out filename with the actual name I grabbed, i.e. with
* wild-cards expanded.
*/
namelength = rootlen+1;
/*
* I fold DOS filenames into lower case because it does not matter much
* to DOS and I think it looks better - furthermore it helps when I move
* archives to other systems.
*/
while ((c = *p++) != 0)
{ if (!hostcase) if (isupper(c)) c = tolower(c);
filename[namelength++] = (char)c;
}
filename[namelength] = 0;
if (found_files[n_found_files].ff_attrib & FA_DIREC)
{ if (found_files[n_found_files].ff_name[0] != '.')
/*
* I filter out directory names that start with '.'.
* This is to avoid calamity with recursion though chains such as .\.\.\.....
*/
{ proc(filename, SCAN_STARTDIR, 0);
if (!recursive_scan) continue;
strcpy(&filename[namelength], "\\*.*");
/*
* Append "\*.*" to the directory-name and try again, thereby scanning
* its contents.
*/
exall(namelength+4, proc);
filename[namelength] = 0;
proc(filename, SCAN_ENDDIR, 0);
}
}
else proc(filename, SCAN_FILE, found_files[n_found_files].ff_fsize);
}
return;
}
void scan_directory(char *dir,
void (*proc)(char *name, int why, long int size))
{
recursive_scan = 1;
if (dir==NULL || strcmp(dir,".")==0)
{ dir = "*.*";
scan_leafstart = 0;
}
else scan_leafstart = strlen(dir)+1;
strcpy(filename, dir);
exall(strlen(filename), proc);
}
void scan_files(char *dir,
void (*proc)(char *name, int why, long int size))
{
recursive_scan = 0;
if (dir==NULL || strcmp(dir,".")==0)
{ strcpy(filename, "*.*");
scan_leafstart = 0;
}
else
{ scan_leafstart = strlen(dir);
strcpy(filename, dir);
strcpy(filename+scan_leafstart, "\\*.*");
scan_leafstart++;
}
exall(strlen(filename), proc);
}
#else /* __WATCOMC__ */
static char filename[LONGEST_LEGAL_FILENAME];
static struct find_t *found_files = NULL;
static int n_found_files = 0, max_found_files = 0;
#define TABLE_INCREMENT 50
static int more_files(void)
{
if (n_found_files > max_found_files - 5)
{ struct find_t *fnew = (struct find_t *)
my_realloc((void *)found_files,
sizeof(struct find_t) *
(max_found_files + TABLE_INCREMENT));
if (fnew == NULL) return 1; /* failure flag */
found_files = fnew;
max_found_files += TABLE_INCREMENT;
}
return 0;
}
int alphasort_files(const void *a, const void *b)
{
const struct find_t *fa = (const struct find_t *)a,
*fb = (const struct find_t *)b;
return strncmp(fb->name, fa->name, sizeof(fa->name));
}
static void exall(int namelength,
void (*proc)(char *name, int why, long int size))
/*
* This procedure scans a directory-full of files, calling the given procedure
* to process each one it finds.
*/
{
int rootlen = namelength;
int code, first = n_found_files;
struct find_t found;
#ifdef FA_DIREC
code = _dos_findfirst(filename, FA_DIREC, &found);
#else
code = _dos_findfirst(filename, _A_SUBDIR, &found);
#endif
while (code == 0)
{ if (more_files()) break;
found_files[n_found_files++] = found;
code = _dos_findnext(&found);
}
qsort((void *)&found_files[first],
n_found_files-first,
sizeof(struct find_t),
alphasort_files);
while (rootlen>=0 && filename[rootlen]!='\\') rootlen--;
while (n_found_files != first)
{ char *p = (char *)&found_files[--n_found_files].name;
int c;
/*
* Fill out filename with the actual name I grabbed, i.e. with
* wild-cards expanded.
*/
namelength = rootlen+1;
/*
* I fold DOS filenames into lower case because it does not matter much
* to DOS and I think it looks better - furthermore it helps when I move
* archives to other systems.
*/
while ((c = *p++) != 0)
{ if (!hostcase) if (isupper(c)) c = tolower(c);
filename[namelength++] = (char)c;
}
filename[namelength] = 0;
#ifdef FA_DIREC
if (found_files[n_found_files].attribute & FA_DIREC)
#else
if (found_files[n_found_files].attrib & _A_SUBDIR)
#endif
{ if (found_files[n_found_files].name[0] != '.')
/*
* I filter out directory names that start with '.'.
* This is to avoid calamity with recursion though chains such as .\.\.\.....
*/
{ proc(filename, SCAN_STARTDIR, 0);
if (!recursive_scan) continue;
strcpy(&filename[namelength], "\\*.*");
/*
* Append "\*.*" to the directory-name and try again, thereby scanning
* its contents.
*/
exall(namelength+4, proc);
filename[namelength] = 0;
proc(filename, SCAN_ENDDIR, 0);
}
}
else proc(filename, SCAN_FILE, found_files[n_found_files].size);
}
return;
}
void scan_directory(char *dir,
void (*proc)(char *name, int why, long int size))
{
recursive_scan = 1;
if (dir==NULL || strcmp(dir,".")==0)
{ dir = "*.*";
scan_leafstart = 0;
}
else scan_leafstart = strlen(dir)+1;
strcpy(filename, dir);
exall(strlen(filename), proc);
}
void scan_files(char *dir,
void (*proc)(char *name, int why, long int size))
{
recursive_scan = 0;
if (dir==NULL || strcmp(dir,".")==0)
{ strcpy(filename, "*.*");
scan_leafstart = 0;
}
else
{ scan_leafstart = strlen(dir);
strcpy(filename, dir);
strcpy(filename+scan_leafstart, "\\*.*");
scan_leafstart++;
}
exall(strlen(filename), proc);
}
#endif /* __WATCOMC__ */
#endif /* GCC386 */
#define SCANDIR_KNOWN 1
#else /* MS_DOS */
#ifdef UNIX
static char filename[LONGEST_LEGAL_FILENAME];
/*
* The code here uses opendir, readdir and closedir and as such ought to
* be Posix compatible. The macro USE_DIRECT_H can cause an older variant
* on this idea to be used. BUt it may need adjustment for different
* systems.
*/
#ifdef USE_DIRECT_H
#include <sys/types.h>
#include <sys/dir.h>
#else
#include <dirent.h>
#endif
static char **found_files = NULL;
int n_found_files = 0, max_found_files = 0;
#define TABLE_INCREMENT 50
static int more_files(void)
{
if (n_found_files > max_found_files - 5)
{ char **fnew = (char **)
my_realloc((void *)found_files,
sizeof(char *) *
(max_found_files + TABLE_INCREMENT));
if (fnew == NULL) return 1; /* failure flag */
found_files = fnew;
max_found_files += TABLE_INCREMENT;
}
return 0;
}
int alphasort_files(const void *a, const void *b)
{
const char *fa = *(const char **)a,
*fb = *(const char **)b;
return strcmp(fb, fa);
}
extern int stat(const char *, struct stat*);
static void scan_file(int namelength,
void (*proc)(char *name, int why, long int size));
static void exall(int namelength,
void (*proc)(char *name, int why, long int size))
{
DIR *d;
#ifdef USE_DIRECT_H
struct direct *dd;
#else
struct dirent *dd;
#endif
int i, j;
int rootlen = namelength, first = n_found_files;
proc(filename, SCAN_STARTDIR, 0);
d = opendir(filename);
if (d != NULL)
{ while ((dd = readdir(d)) != NULL)
{ char *leafname = dd->d_name;
char *copyname;
/*
* readdir hands back both "." and ".." but I had better not recurse
* into either!
*/
if (strcmp(leafname, ".") == 0 ||
strcmp(leafname, "..") == 0) continue;
if (more_files()) break;
copyname = (char *)malloc(1+strlen(leafname));
if (copyname == NULL) break;
strcpy(copyname, leafname);
found_files[n_found_files++] = copyname;
}
closedir(d);
}
qsort((void *)&found_files[first],
n_found_files-first,
sizeof(char *),
alphasort_files);
filename[rootlen] = '/';
while (n_found_files != first)
{ char *p = found_files[--n_found_files];
int c;
namelength = rootlen+1;
while ((c = *p++) != 0) filename[namelength++] = (char)c;
free((void *)found_files[n_found_files]);
filename[namelength] = 0;
scan_file(namelength, proc);
}
filename[rootlen] = 0;
proc(filename, SCAN_ENDDIR, 0);
}
#ifndef S_IFMT
# ifdef __S_IFMT
# define S_IFMT __S_IFMT
# endif
#endif
#ifndef S_IFDIR
# ifdef __S_IFDIR
# define S_IFDIR __S_IFDIR
# endif
#endif
#ifndef S_IFREG
# ifdef __S_IFREG
# define S_IFREG __S_IFREG
# endif
#endif
static void scan_file(int namelength,
void (*proc)(char *name, int why, long int size))
{
struct stat buf;
stat(filename, &buf);
if ((buf.st_mode & S_IFMT) == S_IFDIR)
{ if (!recursive_scan) proc(filename, SCAN_STARTDIR, 0);
else exall(namelength, proc);
}
else if ((buf.st_mode & S_IFMT) == S_IFREG)
proc(filename, SCAN_FILE, buf.st_size);
/* else fprintf(stderr, "Mode of %s is %o\n", filename, buf.st_mode); */
}
void scan_directory(char *dir,
void (*proc)(char *name, int why, long int size))
{
recursive_scan = 1;
if (dir==NULL || strcmp(dir, ".")==0) dir = ".";
scan_leafstart = strlen(dir)+1;
strcpy(filename, dir);
scan_file(scan_leafstart-1, proc);
}
void scan_files(char *dir,
void (*proc)(char *name, int why, long int size))
{
recursive_scan = 0;
if (dir==NULL || strcmp(dir, ".")==0) dir = ".";
scan_leafstart = strlen(dir)+1;
strcpy(filename, dir);
exall(scan_leafstart-1, proc);
}
#define SCANDIR_KNOWN 1
#else /* UNIX */
#ifdef __arm
/*
* I want to be able to find the currently-selected directory - if
* only so I can restore it at the end of processing. Yes I do know
* that RISCOS is moving away from the concept of a current directory,
* but this is a command-line utility at present, at least.
*/
static void find_current_directory(char dir[])
{
os_gbpbstr block;
char drivename[64];
char name[32];
/*
* osgbpb(5) reads the name of the current disc - which I need as the first
* part of a full-rooted file-path.
*/
block.action = 5;
block.file_handle = 0;
block.data_addr = drivename;
os_gbpb(&block);
for (;;)
{ int len;
/*
* osgbpb(6) reads the name of the currently selected directory - but just
* the trailing component.
*/
block.action = 6;
block.file_handle = 0;
block.data_addr = name;
os_gbpb(&block);
len = name[1];
/* Here I trim trailing blanks from the name */
while (name[len + 1] == ' ' && len > 0) len--;
if (dir[0] == 0)
{ memcpy(dir, &name[2], len);
dir[len] = 0;
}
else
{ memmove(&dir[1 + len], dir, strlen(dir) + 1);
memcpy(dir, &name[2], len);
dir[len] = '.';
}
/*
* When I find that the director name is '$' I conclude that I must be
* at the top of the tree, and I exit.
*/
if (len == 1 && name[2] == '$')
{ len = drivename[0];
memmove(&dir[2 + len], dir, strlen(dir) + 1);
memcpy(&dir[1], &drivename[1], len);
dir[0] = ':';
dir[len+1] = '.';
return;
}
/*
* Mostly I select the parent directory, and by reading names of
* successive parents I build up a complete name.
*/
system("dir ^");
}
}
#define AT_ONCE 80
#define NAME_LENGTH 12
static char filename[LONGEST_LEGAL_FILENAME];
static char dirsave[LONGEST_LEGAL_FILENAME+4];
/* Original directory the user had selected */
static void exall(int namelength,
void (*proc)(char *name, int why, long int size))
/*
* This procedure scans a directory-full of files, calling the given procedure
* to process each one it finds.
*/
{
os_filestr file_status;
/*
* osfile(5) reads information about a file - in particular it allows me to
* tell if I have a simple file or a directory.
*/
file_status.action = 5;
file_status.name = filename;
if (os_file(&file_status) != NULL) file_status.action = 99;
switch (file_status.action)
{
default:
/* fprintf(stderr, "\nBad return code from osfile\n"); */
return;
/* case 0: fprintf(stderr, "\nFile %s not found\n", filename); */
/* exit(EXIT_FAILURE); */
case 0xff: /* Protected file */
case 1: proc(filename, SCAN_FILE, file_status.start); /* Simple file */
return;
case 2: /* Here we have a directory to scan */
proc(filename, SCAN_STARTDIR, 0);
if (!recursive_scan) return;
{ char workvec[NAME_LENGTH*AT_ONCE];
os_gbpbstr block;
block.seq_point = 0;
for (;;)
{ char *p = workvec;
block.action = 9;
block.file_handle = (int)filename;/* Scan given directory */
block.data_addr = workvec;
block.number = AT_ONCE;
block.buf_len = NAME_LENGTH*AT_ONCE;
block.wild_fld = "*";
filename[namelength] = 0;
/*
* osgbpb(9) reads (several) file-names from the specified directory.
*/
os_gbpb(&block);
if (block.number == 0) break; /* Nothing transfered */
while (block.number != 0)
{ int len = strlen(p);
filename[namelength] = '.';
strcpy(&filename[namelength+1], p);
p += len + 1;
/*
* I concatenate the name of the new directory on the end of my current path
* and recurse into it.
*/
exall(namelength + len + 1, proc);
block.number--;
}
if (block.seq_point == -1) break;
}
filename[namelength] = 0;
}
proc(filename, SCAN_ENDDIR, 0);
return;
}
}
void scan_directory(char *dir,
void (*proc)(char *name, int why, long int size))
{
recursive_scan = 1;
strcpy(dirsave, "dir ");
find_current_directory(dirsave+4); /* Find initial directory */
system(dirsave); /* Restore initial directory */
strcpy(filename, dir==NULL ? "@" : dir);
scan_leafstart = strcmp(dir, "@")==0 ? 2 : 0;
exall(strlen(filename), proc);
system(dirsave); /* Restore initial directory */
}
void scan_files(char *dir,
void (*proc)(char *name, int why, long int size))
{
recursive_scan = 0;
strcpy(dirsave, "dir ");
find_current_directory(dirsave+4); /* Find initial directory */
system(dirsave); /* Restore initial directory */
strcpy(filename, dir==NULL ? "@" : dir);
scan_leafstart = strcmp(dir, "@")==0 ? 2 : 0;
exall(strlen(filename), proc);
system(dirsave); /* Restore initial directory */
}
#define SCANDIR_KNOWN 1
#else /* __arm */
#ifdef ATARI
static char filename[LONGEST_LEGAL_FILENAME];
static void exall(int32 namelength,
void (*proc)(char *name, int why, long int size))
/*
* This procedure scans a directory-full of files, calling the given procedure
* to process each one it finds.
*/
{
int32 rootlen = namelength;
int code;
struct FILEINFO found;
code = dfind(&found, filename, FA_DIREC);
while (rootlen>=0 && filename[rootlen]!='\\') rootlen--;
/*
* If I used _dos_findfirst I could allow recursion here,
* and if I call findfirst(filename, FA_DIREC) then directories can
* be found as well as just files.
*/
while (code == 0)
{ char *p = (char *)&found.name;
int c;
/*
* Fill out filename with the actual name I grabbed, i.e. with
* wild-cards expanded.
*/
namelength = rootlen+1;
/*
* I fold Atari filenames into lower case because it does not matter much
* to Atari and I think it looks better - furthermore it helps when I move
* archives to other systems.
*/
while ((c = *p++) != 0)
{ if (!hostcase) c = tolower(c);
filename[namelength++] = (char)c;
}
filename[namelength] = 0;
if (found.attr & FA_DIREC)
{ if (found.name[0]!='.')
/*
* I filter out directory names that start with '.'.
* This is to avoid calamity with recursion though chains such as .\.\.\.....
*/
{ proc(filename, SCAN_STARTDIR, 0);
if (!recursive_scan) continue;
strcpy(&filename[namelength], "\\*.*");
/*
* Append "\*.*" to the directory-name and try again, thereby scanning
* its contents.
*/
exall(namelength+4, proc);
filename[namelength] = 0;
proc(filename, SCAN_ENDDIR, 0);
}
}
else proc(filename, SCAN_FILE, found.size);
code = dnext(&found);
}
return;
}
void scan_directory(char *dir,
void (*proc)(char *name, int why, long int size))
{
recursive_scan = 1;
if (dir==NULL || strcmp(dir,".")==0)
{ dir = "*.*";
scan_leafstart = 0;
}
else scan_leafstart = strlen(dir)+1;
strcpy(filename, dir);
exall(strlen(filename), proc);
}
void scan_files(char *dir,
void (*proc)(char *name, int why, long int size))
{
recursive_scan = 0;
if (dir==NULL || strcmp(dir,".")==0)
{ dir = "*.*";
scan_leafstart = 0;
}
else scan_leafstart = strlen(dir)+1;
strcpy(filename, dir);
exall(strlen(filename), proc);
}
#define SCANDIR_KNOWN 1
#else /* ATARI */
#ifdef macintosh
/*
* I have place-holder routines here, so that at some stage proper versions
* can be written and installed. By leaving the Unix code in as comment I hope
* to help whoever gets around to building the Macintosh-specific versions...
*/
static char filename[LONGEST_LEGAL_FILENAME];
#ifdef THIS_NEEDS_RE_WRITING_FOR_MACINTOSH
static int remove_dot(struct direct *name)
{
if (strcmp(name->d_name, ".")==0 || strcmp(name->d_name, "..")==0) return 0;
return 1;
}
#endif
/* int alphasort(struct direct **, struct direct **); */
/* extern int stat(const char *, struct stat*); */
static void scan_file(int namelength,
void (*proc)(char *name, int why, long int size));
static void exall(int namelength,
void (*proc)(char *name, int why, long int size))
{
#ifdef THIS_NEEDS_RE_WRITING_FOR_MACINTOSH
struct direct **namelist;
int i, j;
proc(filename, SCAN_STARTDIR, 0);
if (!recursive_scan) return;
i = scandir(filename, &namelist, remove_dot, alphasort);
for(j=0; j<i; j++)
{ char *leafname = namelist[j]->d_name;
filename[namelength] = '/';
strcpy(&filename[namelength+1], leafname);
scan_file(namelength+1+strlen(leafname), proc);
free(namelist[j]);
}
free(namelist);
filename[namelength] = 0;
proc(filename, SCAN_ENDDIR, 0);
#endif
}
static void scan_file(int namelength,
void (*proc)(char *name, int why, long int size))
{
#ifdef THIS_NEEDS_RE_WRITING_FOR_MACINTOSH
struct stat buf;
stat(filename, &buf);
if ((buf.st_mode & S_IFMT) == S_IFDIR) exall(namelength, proc);
else if ((buf.st_mode & S_IFMT) == S_IFREG)
proc(filename, SCAN_FILE, buf.st_size);
/* else fprintf(stderr, "Mode of %s is %o\n", filename, buf.st_mode); */
#endif
}
void scan_directory(char *dir,
void (*proc)(char *name, int why, long int size))
{
recursive_scan = 1;
if (dir==NULL || strcmp(dir, ".")==0) dir = ".";
scan_leafstart = strlen(dir)+1;
strcpy(filename, dir);
scan_file(scan_leafstart-1, proc);
}
void scan_files(char *dir,
void (*proc)(char *name, int why, long int size))
{
recursive_scan = 0;
if (dir==NULL || strcmp(dir, ".")==0) dir = ".";
scan_leafstart = strlen(dir)+1;
strcpy(filename, dir);
scan_file(scan_leafstart-1, proc);
}
#define SCANDIR_KNOWN 1
#endif /* macintosh */
#endif /* ATARI */
#endif /* __arm */
#endif /* UNIX */
#endif /* MS_DOS */
#endif /* WINDOWS_NT */
#endif /* __LCC__ */
#ifndef SCANDIR_KNOWN
#error "Directory scanning machine-specific code not present"
#endif
/* end of scandir.c */