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