Blame | Last modification | View Log | RSS feed
/** @(#)msd_dir.c 1.4 87/11/06 Public Domain.** A public domain implementation of BSD directory routines for* MS-DOS. Written by Michael Rendell ({uunet,utai}michael@garfield),* August 1897** Modified by Ian Stewartson, Data Logic (istewart@datlog.co.uk).** Updates: 1. To support OS/2 1.x* 2. To support HPFS long filenames* 3. To support OS/2 2.x* 4. To support TurboC* 5. To support Windows NT*/#include <sys/types.h>#include <sys/stat.h>#include <stdio.h>#include <stdlib.h>#ifndef __TURBOC__# include <malloc.h>#endif#include <string.h>#include <limits.h>#include <ctype.h>#include <errno.h>#include <dirent.h>#ifdef __TURBOC__# include <dir.h>#endif#if defined (OS2) || defined (__OS2__)# define INCL_DOSFILEMGR# define INCL_DOSMISC# define INCL_DOSERRORS# include <os2.h># include <bseerr.h># if defined (__OS2__)# define DISABLE_HARD_ERRORS DosError (FERR_DISABLEHARDERR)# define ENABLE_HARD_ERRORS DosError (FERR_ENABLEHARDERR)# define FIND_BUFFER FILEFINDBUF3# else# define DISABLE_HARD_ERRORS DosError (HARDERROR_DISABLE)# define ENABLE_HARD_ERRORS DosError (HARDERROR_ENABLE)# define FIND_BUFFER FILEFINDBUF# endif# define ERROR_EMPTY_DIR ERROR_NO_MORE_FILES# define FILE_NAME_E achName# define OS_CloseFH(a) DosFindClose (a)#elif defined (WIN32)# include <windows.h># define FILE_NAME_E cFileName# define OS_CloseFH(a) FindClose (a)# define FIND_BUFFER WIN32_FIND_DATA# define DISABLE_HARD_ERRORS SetErrorMode (0)# define ENABLE_HARD_ERRORS SetErrorMode (SEM_FAILCRITICALERRORS | \SEM_NOOPENFILEERRORBOX);# define ERROR_EMPTY_DIR ERROR_FILE_NOT_FOUND#else# include <dos.h># define DISABLE_HARD_ERRORS# define ENABLE_HARD_ERRORS# define OS_CloseFH(a)# define ERROR_EMPTY_DIR 18#define ERROR_NO_MORE_FILES 18# if defined (__TURBOC__)# define FILE_NAME_E ff_name# define FIND_BUFFER struct ffblk# else# define FILE_NAME_E name# define FIND_BUFFER struct find_t# endif#endif#if defined (OS2) || defined (__OS2__)# define ATTRIBUTES (FILE_DIRECTORY | FILE_HIDDEN | FILE_SYSTEM | \FILE_NORMAL | FILE_READONLY | FILE_ARCHIVED)#elif defined (__TURBOC__)# define ATTRIBUTES (FA_RDONLY | FA_HIDDEN | FA_SYSTEM | \FA_DIREC | FA_ARCH)#else# define ATTRIBUTES (_A_SUBDIR | _A_HIDDEN | _A_SYSTEM | \_A_NORMAL | _A_RDONLY | _A_ARCH)#endif/** OS/2 2.x has these missing*/#ifndef ENOTDIR# define ENOTDIR 120 /* Not a directory */#endif#ifndef S_IFMT# define S_IFMT 0xf000 /* type of file */#endif#ifndef S_ISDIR# define S_ISDIR(m) ((((m) & S_IFMT) == S_IFDIR))#endif/** Internals*/typedef struct _dircontents DIRCONT;static void free_dircontents (DIRCONT *);/** Open the directory stream*/DIR *opendir (name)const char *name;{struct stat statb;DIR *dirp;char *last;DIRCONT *dp;char *nbuf;int len = strlen (name);unsigned long rc;FIND_BUFFER dtabuf;#if defined (__OS2__)HDIR d_handle = HDIR_SYSTEM;ULONG d_count = 1;bool HPFS = FALSE;#elif defined (OS2)HDIR d_handle = HDIR_SYSTEM;USHORT d_count = 1;bool HPFS = FALSE;#elif defined (WIN32)HANDLE d_handle;bool HPFS = FALSE;#endifif (!len){errno = ENOTDIR;return (DIR *)NULL;}if ((nbuf = malloc (len + 5)) == (char *)NULL)return (DIR *) NULL;strcpy (nbuf, name);last = &nbuf[len - 1];/* Ok, DOS is very picky about its directory names. The following are* valid.** c:/* c:.* c:name/name1** c:name/ is not valid*/if (((*last == '\\') || (*last == '/')) && (len > 1) &&(!((len == 3) && (name[1] == ':'))))*(last--) = 0;/* Check its a directory */DISABLE_HARD_ERRORS;rc = stat (nbuf, &statb);ENABLE_HARD_ERRORS;if (rc){free (nbuf);return (DIR *) NULL;}if (!S_ISDIR (statb.st_mode)){free (nbuf);errno = ENOTDIR;return (DIR *)NULL;}if ((dirp = (DIR *) malloc (sizeof (DIR))) == (DIR *) NULL){free (nbuf);return (DIR *) NULL;}/* Set up to find everything */if ((*last != '\\') && (*last != '/'))strcat (last, "/");strcat (last, "*.*");/* For OS/2, find the file system type */#if defined (OS2) || defined (__OS2__) || defined (WIN32)HPFS = IsHPFSFileSystem (nbuf);#endifdirp->dd_loc = 0;dirp->dd_cp = (DIRCONT *) NULL;dirp->dd_contents = (DIRCONT *) NULL;DISABLE_HARD_ERRORS;#if defined (__OS2__)rc = DosFindFirst (nbuf, &d_handle, ATTRIBUTES, &dtabuf,sizeof (FILEFINDBUF3), &d_count, FIL_STANDARD);#elif defined (OS2)rc = DosFindFirst (nbuf, &d_handle, ATTRIBUTES, &dtabuf,sizeof (FILEFINDBUF), &d_count, (ULONG)0);#elif defined (WIN32)d_handle = FindFirstFile (nbuf, &dtabuf);rc = (d_handle == INVALID_HANDLE_VALUE) ? GetLastError () : 0;#elif defined (__TURBOC__)rc = findfirst (nbuf, &dtabuf, ATTRIBUTES);#elserc = _dos_findfirst (nbuf, ATTRIBUTES, &dtabuf);#endifENABLE_HARD_ERRORS;/* Check for errors */if (rc){free (nbuf);/* Empty directory */#if defined (ERROR_EMPTY_DIR)if (rc == ERROR_EMPTY_DIR)return dirp;#endiffree (dirp);return (DIR *) NULL;}/* Process the directory */do{if (((dp = (DIRCONT *) malloc (sizeof (DIRCONT))) == (DIRCONT *)NULL) ||((dp->_d_entry = strdup (dtabuf.FILE_NAME_E)) == (char *) NULL)){if (dp->_d_entry != (char *)NULL)free ((char *)dp);free (nbuf);free_dircontents (dirp->dd_contents);OS_CloseFH (d_handle);return (DIR *) NULL;}#if defined (OS2) || defined (__OS2__) || defined (WIN32)if (!HPFS)strlwr (dp->_d_entry);#elsestrlwr (dp->_d_entry);#endifif (dirp->dd_contents != (DIRCONT *) NULL)dirp->dd_cp = dirp->dd_cp->_d_next = dp;elsedirp->dd_contents = dirp->dd_cp = dp;dp->_d_next = (DIRCONT *) NULL;#if defined (OS2) || defined (__OS2__)d_count = 1;} while (DosFindNext (d_handle, &dtabuf, sizeof (FILEFINDBUF),&d_count) == 0);#elif defined (WIN32)} while (FindNextFile (d_handle, &dtabuf));#elif defined (__TURBOC__)} while (findnext (&dtabuf) == 0);#else} while (_dos_findnext (&dtabuf) == 0);#endifdirp->dd_cp = dirp->dd_contents;free (nbuf);OS_CloseFH (d_handle);return dirp;}/** Close the directory stream*/int closedir (dirp)DIR *dirp;{if (dirp != (DIR *)NULL){free_dircontents (dirp->dd_contents);free ((char *)dirp);}return 0;}/** Read the next record from the stream*/struct dirent *readdir (dirp)DIR *dirp;{static struct dirent dp;if ((dirp == (DIR *)NULL) || (dirp->dd_cp == (DIRCONT *) NULL))return (struct dirent *) NULL;dp.d_reclen = strlen (strcpy (dp.d_name, dirp->dd_cp->_d_entry));dp.d_off = dirp->dd_loc * 32;dp.d_ino = (ino_t)++dirp->dd_loc;dirp->dd_cp = dirp->dd_cp->_d_next;return &dp;}/** Restart the directory stream*/void rewinddir (dirp)DIR *dirp;{seekdir (dirp, (off_t)0);}/** Move to a know position in the stream*/void seekdir (dirp, off)DIR *dirp;off_t off;{long i = off;DIRCONT *dp;if ((dirp == (DIR *)NULL) || (off < 0L))return;for (dp = dirp->dd_contents; (--i >= 0) && (dp != (DIRCONT *)NULL);dp = dp->_d_next);dirp->dd_loc = off - (i + 1);dirp->dd_cp = dp;}/** Get the current position*/off_t telldir(dirp)DIR *dirp;{return (dirp == (DIR *)NULL) ? (off_t) -1 : dirp->dd_loc;}/** Release the internal structure*/static void free_dircontents (dp)DIRCONT *dp;{DIRCONT *odp;while ((odp = dp) != (DIRCONT *)NULL){if (dp->_d_entry != (char *)NULL)free (dp->_d_entry);dp = dp->_d_next;free ((char *)odp);}}/** For OS/2, we need to know if we have to convert to lower case. This* only applies to non-HPFS (FAT, NETWARE etc) file systems.*/#if defined (OS2) || defined (__OS2__)/** Define the know FAT systems*/static char *FATSystems[] = {"FAT", "NETWARE", (char *)NULL};/** Check for Long filenames*/bool IsHPFSFileSystem (char *directory){ULONG lMap;BYTE bData[128];BYTE bName[3];int i;char *FName;unsigned long rc;#if defined (__OS2__)ULONG cbData;ULONG nDrive;PFSQBUFFER2 pFSQ = (PFSQBUFFER2)bData;#elseUSHORT cbData;USHORT nDrive;#endif#ifndef __WATCOMC__if ( _osmode == DOS_MODE )return FALSE;#endif/** Mike tells me there are IFS calls to determine this, but he carn't* remember which. So we read the partition info and check for HPFS.*/if (isalpha (directory[0]) && (directory[1] == ':'))nDrive = toupper (directory[0]) - '@';elseDosQCurDisk (&nDrive, &lMap);/* Set up the drive name */bName[0] = (char) (nDrive + '@');bName[1] = ':';bName[2] = 0;cbData = sizeof (bData);/* Read the info, if we fail - assume non-HPFS */DISABLE_HARD_ERRORS;# ifdef __OS2__rc = DosQFSAttach (bName, 0, FSAIL_QUERYNAME, pFSQ, &cbData);# elserc = DosQFSAttach (bName, 0, FSAIL_QUERYNAME, bData, &cbData, 0L);# endifENABLE_HARD_ERRORS;if (rc)return FALSE;# ifdef __OS2__FName = pFSQ->szName + pFSQ->cbName + 1;# elseFName = bData + (*((USHORT *) (bData + 2)) + 7);# endif#ifdef TESTprintf ("File System for <%s> = <%s>\n", directory, FName);#endiffor (i = 0; FATSystems[i] != (char *)NULL; i++){if (stricmp (FName, FATSystems[i]) == 0)return FALSE;}return TRUE;}#endif/** Windows NT version*/#if defined (WIN32)bool IsHPFSFileSystem (char *directory){char bName[4];DWORD flags;DWORD maxname;BOOL rc;unsigned int nDrive;char szCurDir [MAX_PATH];if (isalpha (directory[0]) && (directory[1] == ':'))nDrive = toupper (directory[0]) - '@';else{GetCurrentDirectory (MAX_PATH, szCurDir);nDrive = szCurDir[0] - 'A' + 1;}/* Set up the drive name */strcpy (bName, "x:\\");bName[0] = (char) (nDrive + '@');/* Read the volume info, if we fail - assume non-HPFS */DISABLE_HARD_ERRORS;rc = GetVolumeInformation (bName, (LPTSTR)NULL, 0, (LPDWORD)NULL,&maxname, &flags, (LPTSTR)NULL, 0);ENABLE_HARD_ERRORS;#ifdef TESTprintf ("File System flags for <%s> = <0x%.8lx> (%d)\n", directory,flags, rc);#endifreturn ((rc) && (flags & (FS_CASE_SENSITIVE | FS_CASE_IS_PRESERVED)))? TRUE : FALSE;}#endif/** Test program*/#ifdef TESTint main (int argc, char **argv){int i;struct dirent *cdp;DIR *dp;for (i = 1; i < argc; i++){#if defined (OS2) || defined (__OS2__) || defined (WIN32)printf ("IsHPFSFileSystem returns %d\n", IsHPFSFileSystem (argv[1]));#endifif ((dp = opendir (argv[i])) == (DIR *)NULL)printf ("Cannot open %s\n", argv[1]);else{while ((cdp = readdir (dp)) != (struct dirent *)NULL)printf ("Found %s\n", cdp->d_name);closedir (dp);}}return 0;}#endif