59f7dc1c6effb2a05bec3fd4a5f7092a69b16e09
[fw/sdcc] / support / cpp / winnt / dirent.c
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
66 typedef struct _dircontents     DIRCONT;
67 static void                     free_dircontents (DIRCONT *);
68
69 /*
70  * Open the directory stream
71  */
72
73 DIR *
74 opendir (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
220 int
221 closedir (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
237 struct dirent *
238 readdir (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
258 void
259 rewinddir (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
269 void
270 seekdir (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
292 off_t
293 telldir(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
303 static void
304 free_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
324 bool
325 IsHPFSFileSystem (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