1/* 2 * @(#)msd_dir.c 1.4 87/11/06 Public Domain. 3 * 4 * A public domain implementation of BSD directory routines for 5 * MS-DOS. Written by Michael Rendell ({uunet,utai}michael@garfield), 6 * August 1897 7 * 8 * Modified by Ian Stewartson, Data Logic (istewart@datlog.co.uk). 9 * 10 * Updates: 1. To support OS/2 1.x 11 * 2. To support HPFS long filenames 12 * 3. To support OS/2 2.x 13 * 4. To support TurboC 14 * 5. To support Windows NT 15 */ 16 17#include <sys/types.h> 18#include <sys/stat.h> 19#include <stdio.h> 20#include <stdlib.h> 21 22#include <malloc.h> 23 24#include <string.h> 25#include <limits.h> 26#include <ctype.h> 27#include <errno.h> 28#include <dirent.h> 29 30 31#define WIN32_LEAN_AND_MEAN 32#include <windows.h> 33 34#define FILE_NAME_E cFileName 35#define OS_CloseFH(a) FindClose (a) 36#define FIND_BUFFER WIN32_FIND_DATA 37#define DISABLE_HARD_ERRORS SetErrorMode (0) 38#define ENABLE_HARD_ERRORS SetErrorMode (SEM_FAILCRITICALERRORS | \ 39 SEM_NOOPENFILEERRORBOX); 40 41# define ERROR_EMPTY_DIR ERROR_FILE_NOT_FOUND 42 43# define ATTRIBUTES (_A_SUBDIR | _A_HIDDEN | _A_SYSTEM | \ 44 _A_NORMAL | _A_RDONLY | _A_ARCH) 45 46/* 47 * missing ?? 48 */ 49 50#ifndef ENOTDIR 51# define ENOTDIR 120 /* Not a directory */ 52#endif 53 54#ifndef S_IFMT 55# define S_IFMT 0xf000 /* type of file */ 56#endif 57 58#ifndef S_ISDIR 59# define S_ISDIR(m) ((((m) & S_IFMT) == S_IFDIR)) 60#endif 61 62/* 63 * Internals 64 */ 65 66typedef struct _dircontents DIRCONT; 67static void free_dircontents (DIRCONT *); 68 69/* 70 * Open the directory stream 71 */ 72 73DIR * 74opendir (name) 75 const char *name; 76{ 77 struct stat statb; 78 DIR *dirp; 79 char *last; 80 DIRCONT *dp; 81 char *nbuf; 82 int len = strlen (name); 83 unsigned long rc; 84 FIND_BUFFER dtabuf; 85 HANDLE d_handle; 86 bool HPFS = FALSE; 87 88 if (!len) 89 { 90 errno = ENOTDIR; 91 return (DIR *)NULL; 92 } 93 94 if ((nbuf = malloc (len + 5)) == (char *)NULL) 95 return (DIR *) NULL; 96 97 strcpy (nbuf, name); 98 last = &nbuf[len - 1]; 99 100/* Ok, DOS is very picky about its directory names. The following are 101 * valid. 102 * 103 * c:/ 104 * c:. 105 * c:name/name1 106 * 107 * c:name/ is not valid 108 */ 109 110 if (((*last == '\\') || (*last == '/')) && (len > 1) && 111 (!((len == 3) && (name[1] == ':')))) 112 *(last--) = 0; 113 114/* Check its a directory */ 115 116 DISABLE_HARD_ERRORS; 117 rc = stat (nbuf, &statb); 118 ENABLE_HARD_ERRORS; 119 120 if (rc) 121 { 122 free (nbuf); 123 return (DIR *) NULL; 124 } 125 126 if (!S_ISDIR (statb.st_mode)) 127 { 128 free (nbuf); 129 errno = ENOTDIR; 130 return (DIR *)NULL; 131 } 132 133 if ((dirp = (DIR *) malloc (sizeof (DIR))) == (DIR *) NULL) 134 { 135 free (nbuf); 136 return (DIR *) NULL; 137 } 138 139/* Set up to find everything */ 140 141 if ((*last != '\\') && (*last != '/')) 142 strcat (last, "/"); 143 144 strcat (last, "*.*"); 145 146/* Find the file system type */ 147 148 HPFS = IsHPFSFileSystem (nbuf); 149 150 dirp->dd_loc = 0; 151 dirp->dd_cp = (DIRCONT *) NULL; 152 dirp->dd_contents = (DIRCONT *) NULL; 153 154 DISABLE_HARD_ERRORS; 155 156 d_handle = FindFirstFile (nbuf, &dtabuf); 157 rc = (d_handle == INVALID_HANDLE_VALUE) ? GetLastError () : 0; 158 159 ENABLE_HARD_ERRORS; 160 161/* Check for errors */ 162 163 if (rc) 164 { 165 free (nbuf); 166 167/* Empty directory */ 168 169#if defined (ERROR_EMPTY_DIR) 170 if (rc == ERROR_EMPTY_DIR) 171 return dirp; 172#endif 173 174 free (dirp); 175 return (DIR *) NULL; 176 } 177 178/* Process the directory */ 179 180 do 181 { 182 if (((dp = (DIRCONT *) malloc (sizeof (DIRCONT))) == (DIRCONT *)NULL) || 183 ((dp->_d_entry = strdup (dtabuf.FILE_NAME_E)) == (char *) NULL)) 184 { 185 if (dp->_d_entry != (char *)NULL) 186 free ((char *)dp); 187 188 free (nbuf); 189 free_dircontents (dirp->dd_contents); 190 191 OS_CloseFH (d_handle); 192 return (DIR *) NULL; 193 } 194 195 if (!HPFS) 196 strlwr (dp->_d_entry); 197 198 if (dirp->dd_contents != (DIRCONT *) NULL) 199 dirp->dd_cp = dirp->dd_cp->_d_next = dp; 200 201 else 202 dirp->dd_contents = dirp->dd_cp = dp; 203 204 dp->_d_next = (DIRCONT *) NULL; 205 206 } while (FindNextFile (d_handle, &dtabuf)); 207 208 dirp->dd_cp = dirp->dd_contents; 209 free (nbuf); 210 211 OS_CloseFH (d_handle); 212 return dirp; 213} 214 215 216/* 217 * Close the directory stream 218 */ 219 220int 221closedir (dirp) 222 DIR *dirp; 223{ 224 if (dirp != (DIR *)NULL) 225 { 226 free_dircontents (dirp->dd_contents); 227 free ((char *)dirp); 228 } 229 230 return 0; 231} 232 233/* 234 * Read the next record from the stream 235 */ 236 237struct dirent * 238readdir (dirp) 239 DIR *dirp; 240{ 241 static struct dirent dp; 242 243 if ((dirp == (DIR *)NULL) || (dirp->dd_cp == (DIRCONT *) NULL)) 244 return (struct dirent *) NULL; 245 246 dp.d_reclen = strlen (strcpy (dp.d_name, dirp->dd_cp->_d_entry)); 247 dp.d_off = dirp->dd_loc * 32; 248 dp.d_ino = (ino_t)++dirp->dd_loc; 249 dirp->dd_cp = dirp->dd_cp->_d_next; 250 251 return &dp; 252} 253 254/* 255 * Restart the directory stream 256 */ 257 258void 259rewinddir (dirp) 260 DIR *dirp; 261{ 262 seekdir (dirp, (off_t)0); 263} 264 265/* 266 * Move to a know position in the stream 267 */ 268 269void 270seekdir (dirp, off) 271 DIR *dirp; 272 off_t off; 273{ 274 long i = off; 275 DIRCONT *dp; 276 277 if ((dirp == (DIR *)NULL) || (off < 0L)) 278 return; 279 280 for (dp = dirp->dd_contents; (--i >= 0) && (dp != (DIRCONT *)NULL); 281 dp = dp->_d_next) 282 ; 283 284 dirp->dd_loc = off - (i + 1); 285 dirp->dd_cp = dp; 286} 287 288/* 289 * Get the current position 290 */ 291 292off_t 293telldir(dirp) 294 DIR *dirp; 295{ 296 return (dirp == (DIR *)NULL) ? (off_t) -1 : dirp->dd_loc; 297} 298 299/* 300 * Release the internal structure 301 */ 302 303static void 304free_dircontents (dp) 305 DIRCONT *dp; 306{ 307 DIRCONT *odp; 308 309 while ((odp = dp) != (DIRCONT *)NULL) 310 { 311 if (dp->_d_entry != (char *)NULL) 312 free (dp->_d_entry); 313 314 dp = dp->_d_next; 315 free ((char *)odp); 316 } 317} 318 319 320/* 321 * Windows NT version 322 */ 323 324bool 325IsHPFSFileSystem (directory) 326 char *directory; 327{ 328 char bName[4]; 329 DWORD flags; 330 DWORD maxname; 331 BOOL rc; 332 unsigned int nDrive; 333 char szCurDir [MAX_PATH]; 334 335 if (isalpha (directory[0]) && (directory[1] == ':')) 336 nDrive = toupper (directory[0]) - '@'; 337 338 else 339 { 340 GetCurrentDirectory (MAX_PATH, szCurDir); 341 nDrive = szCurDir[0] - 'A' + 1; 342 } 343 344/* Set up the drive name */ 345 346 strcpy (bName, "x:\\"); 347 bName[0] = (char) (nDrive + '@'); 348 349/* Read the volume info, if we fail - assume non-HPFS */ 350 351 DISABLE_HARD_ERRORS; 352 353 rc = GetVolumeInformation (bName, (LPTSTR)NULL, 0, (LPDWORD)NULL, 354 &maxname, &flags, (LPTSTR)NULL, 0); 355 ENABLE_HARD_ERRORS; 356 357 return ((rc) && (flags & (FS_CASE_SENSITIVE | FS_CASE_IS_PRESERVED))) 358 ? TRUE : FALSE; 359} 360 361