1 /*
2  * dirent.h - dirent API for Microsoft Visual Studio
3  *
4  * Copyright (C) 2006 Toni Ronkko
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files (the
8  * ``Software''), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included
15  * in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS
18  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20  * IN NO EVENT SHALL TONI RONKKO BE LIABLE FOR ANY CLAIM, DAMAGES OR
21  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23  * OTHER DEALINGS IN THE SOFTWARE.
24  *
25  *
26  * Mar 4, 2007, Toni Ronkko
27  * Bug fix: due to the strncpy_s() function this file only compiled in
28  * Visual Studio 2005.  Using the new string functions only when the
29  * compiler version allows.
30  *
31  * Nov  2, 2006, Toni Ronkko
32  * Major update: removed support for Watcom C, MS-DOS and Turbo C to
33  * simplify the file, updated the code to compile cleanly on Visual
34  * Studio 2005 with both unicode and multi-byte character strings,
35  * removed rewinddir() as it had a bug.
36  *
37  * Aug 20, 2006, Toni Ronkko
38  * Removed all remarks about MSVC 1.0, which is antiqued now.  Simplified
39  * comments by removing SGML tags.
40  *
41  * May 14 2002, Toni Ronkko
42  * Embedded the function definitions directly to the header so that no
43  * source modules need to be included in the Visual Studio project.  Removed
44  * all the dependencies to other projects so that this very header can be
45  * used independently.
46  *
47  * May 28 1998, Toni Ronkko
48  * First version.
49  */
50 #ifndef DIRENT_H
51 #define DIRENT_H
52 
53 #include <windows.h>
54 #include <tchar.h>
55 #include <string.h>
56 #include <assert.h>
57 
58 
59 typedef struct dirent {
60   /* name of current directory entry (a multi-byte character string) */
61   char d_name[MAX_PATH + 1];
62 
63   /* file attributes */
64   WIN32_FIND_DATA data;
65 } dirent;
66 
67 
68 typedef struct DIR {
69   /* current directory entry */
70   dirent current;
71 
72   /* is there an un-processed entry in current? */
73   int cached;
74 
75   /* file search handle */
76   HANDLE search_handle;
77 
78   /* search pattern (3 = zero terminator + pattern "\\*") */
79   TCHAR patt[MAX_PATH + 3];
80 } DIR;
81 
82 
83 static DIR *opendir (const char *dirname);
84 static struct dirent *readdir (DIR *dirp);
85 static int closedir (DIR *dirp);
86 
87 
88 /* use the new safe string functions introduced in Visual Studio 2005 */
89 #if defined(_MSC_VER) && _MSC_VER >= 1400
90 # define STRNCPY(dest,src,size) strncpy_s((dest),(size),(src),_TRUNCATE)
91 #else
92 # define STRNCPY(dest,src,size) strncpy((dest),(src),(size))
93 #endif
94 
95 
96 /*
97  * Open directory stream DIRNAME for read and return a pointer to the
98  * internal working area that is used to retrieve individual directory
99  * entries.
100  */
101 static DIR*
opendir(const char * dirname)102 opendir(
103     const char *dirname)
104 {
105   DIR *dirp;
106   assert (dirname != NULL);
107   assert (strlen (dirname) < MAX_PATH);
108 
109   /* construct new DIR structure */
110   dirp = (DIR*) malloc (sizeof (struct DIR));
111   if (dirp != NULL) {
112     TCHAR *p;
113 
114     /* prepare search pattern */
115 #ifdef _UNICODE
116 
117     /* convert directory name to wide character string */
118     MultiByteToWideChar(
119         CP_ACP,                                /* code page */
120         0,                                     /* conversion flags */
121         dirname,                               /* mb-string to convert */
122         -1,                                    /* length of mb-string */
123         dirp->patt,                            /* wc-string to produce */
124         MAX_PATH);                             /* max length of wc-string */
125     dirp->patt[MAX_PATH] = '\0';
126 
127     /* append search pattern to directory name */
128     p = wcschr (dirp->patt, '\0');
129     if (dirp->patt < p  &&  *(p-1) != '\\'  &&  *(p-1) != ':') {
130       *p++ = '\\';
131     }
132     *p++ = '*';
133     *p = '\0';
134 
135 #else /* !_UNICODE */
136 
137     /* take directory name... */
138     STRNCPY (dirp->patt, dirname, sizeof(dirp->patt));
139     dirp->patt[MAX_PATH] = '\0';
140 
141     /* ... and append search pattern to it */
142     p = strchr (dirp->patt, '\0');
143     if (dirp->patt < p  &&  *(p-1) != '\\'  &&  *(p-1) != ':') {
144       *p++ = '\\';
145     }
146     *p++ = '*';
147     *p = '\0';
148 
149 #endif /* !_UNICODE */
150 
151     /* open stream and retrieve first file */
152     dirp->search_handle = FindFirstFile (dirp->patt, &dirp->current.data);
153     if (dirp->search_handle == INVALID_HANDLE_VALUE) {
154       /* invalid search pattern? */
155       free (dirp);
156       return NULL;
157     }
158 
159     /* there is an un-processed directory entry in memory now */
160     dirp->cached = 1;
161 
162   }
163   return dirp;
164 }
165 
166 
167 /*
168  * Read a directory entry, and return a pointer to a dirent structure
169  * containing the name of the entry in d_name field.  Individual directory
170  * entries returned by this very function include regular files,
171  * sub-directories, pseudo-directories "." and "..", but also volume labels,
172  * hidden files and system files may be returned.
173  */
174 static struct dirent *
readdir(DIR * dirp)175 readdir(
176     DIR *dirp)
177 {
178   assert (dirp != NULL);
179 
180   if (dirp->search_handle == INVALID_HANDLE_VALUE) {
181     /* directory stream was opened/rewound incorrectly or it ended normally */
182     return NULL;
183   }
184 
185   /* get next directory entry */
186   if (dirp->cached != 0) {
187     /* a valid directory entry already in memory */
188     dirp->cached = 0;
189   } else {
190     /* read next directory entry from disk */
191     if (FindNextFile (dirp->search_handle, &dirp->current.data) == FALSE) {
192       /* the very last file has been processed or an error occured */
193       FindClose (dirp->search_handle);
194       dirp->search_handle = INVALID_HANDLE_VALUE;
195       return NULL;
196     }
197   }
198 
199   /* copy directory entry to d_name */
200 #ifdef _UNICODE
201 
202   /* convert entry name to multibyte */
203   WideCharToMultiByte(
204       CP_ACP,                                  /* code page */
205       0,                                       /* conversion flags */
206       dirp->current.data.cFileName,            /* wc-string to convert */
207       -1,                                      /* length of wc-string */
208       dirp->current.d_name,                    /* mb-string to produce */
209       MAX_PATH,                                /* max length of mb-string */
210       NULL,                                    /* use sys default character */
211       NULL);                                   /* don't care  */
212   dirp->current.d_name[MAX_PATH] = '\0';
213 
214 #else /* !_UNICODE */
215 
216   /* copy as a multibyte character string */
217   STRNCPY (dirp->current.d_name, dirp->current.data.cFileName, sizeof(dirp->current.d_name));
218   dirp->current.d_name[MAX_PATH] = '\0';
219 
220 #endif /* !_UNICODE */
221 
222   return &dirp->current;
223 }
224 
225 
226 /*
227  * Close directory stream opened by opendir() function.  Close of the
228  * directory stream invalidates the DIR structure as well as any previously
229  * read directory entry.
230  */
231 static int
closedir(DIR * dirp)232 closedir(
233     DIR *dirp)
234 {
235   assert (dirp != NULL);
236 
237   /* release search handle */
238   if (dirp->search_handle != INVALID_HANDLE_VALUE) {
239     FindClose (dirp->search_handle);
240     dirp->search_handle = INVALID_HANDLE_VALUE;
241   }
242 
243   /* release directory handle */
244   free (dirp);
245   return 0;
246 }
247 
248 
249 #endif /*DIRENT_H*/
250