1/***********************************************************************
2 * Copyright (c) 2009, Secure Endpoints Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 *   notice, this list of conditions and the following disclaimer.
11 *
12 * - Redistributions in binary form must reproduce the above copyright
13 *   notice, this list of conditions and the following disclaimer in
14 *   the documentation and/or other materials provided with the
15 *   distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
22 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28 * OF THE POSSIBILITY OF SUCH DAMAGE.
29 *
30 **********************************************************************/
31
32#include<config.h>
33
34#include <stdlib.h>
35#include <io.h>
36#include <string.h>
37#include <errno.h>
38#include "dirent.h"
39
40#ifndef _WIN32
41#error Only implemented for Win32
42#endif
43
44struct _dirent_dirinfo {
45    int             magic;
46    long            n_entries;
47    long            nc_entries;
48    long            cursor;
49    struct dirent **entries;
50};
51#define DIRINFO_MAGIC 0xf8c0639d
52#define IS_DP(p) ((p) && ((DIR *)(p))->magic == DIRINFO_MAGIC)
53
54#define INITIAL_ENTRIES 16
55
56/**
57 * Create a filespec for use with _findfirst() using a path spec
58 *
59 * If the last component of the path spec contains wildcards, we let
60 * it be.  If the last component doesn't end with a slash, we add one.
61 */
62static const char *
63filespec_from_dir_path(const char * path, char * buffer, size_t cch_buffer)
64{
65    char *comp, *t;
66    size_t pos;
67    int found_sep = 0;
68
69    if (strcpy_s(buffer, cch_buffer, path) != 0)
70        return NULL;
71
72    comp = strrchr(buffer, '\\');
73    if (comp == NULL)
74        comp = buffer;
75    else
76        found_sep = 1;
77
78    t = strrchr(comp, '/');
79    if (t != NULL) {
80        comp = t;
81        found_sep = 1;
82    }
83
84    if (found_sep)
85        comp++;
86
87    pos = strcspn(comp, "*?");
88    if (comp[pos] != '\0')
89        return buffer;
90
91    /* We don't append a slash if pos == 0 because that changes the
92     * meaning:
93     *
94     * "*.*" is all files in the current directory.
95     * "\*.*" is all files in the root directory of the current drive.
96     */
97    if (pos > 0 && comp[pos - 1] != '\\' &&
98        comp[pos - 1] != '/') {
99        strcat_s(comp, cch_buffer - (comp - buffer), "\\");
100    }
101
102    strcat_s(comp, cch_buffer - (comp - buffer), "*.*");
103
104    return buffer;
105}
106
107ROKEN_LIB_FUNCTION DIR * ROKEN_LIB_CALL
108opendir(const char * path)
109{
110    DIR *              dp;
111    struct _finddata_t fd;
112    intptr_t           fd_handle;
113    const char         *filespec;
114    char               path_buffer[1024];
115
116    memset(&fd, 0, sizeof(fd));
117
118    filespec = filespec_from_dir_path(path, path_buffer, sizeof(path_buffer)/sizeof(char));
119    if (filespec == NULL)
120        return NULL;
121
122    fd_handle = _findfirst(filespec, &fd);
123
124    if (fd_handle == -1)
125        return NULL;
126
127    dp = malloc(sizeof(*dp));
128    if (dp == NULL)
129        goto done;
130
131    memset(dp, 0, sizeof(*dp));
132    dp->magic      = DIRINFO_MAGIC;
133    dp->cursor     = 0;
134    dp->n_entries  = 0;
135    dp->nc_entries = INITIAL_ENTRIES;
136    dp->entries    = calloc(dp->nc_entries, sizeof(dp->entries[0]));
137
138    if (dp->entries == NULL) {
139        closedir(dp);
140        dp = NULL;
141        goto done;
142    }
143
144    do {
145        size_t len = strlen(fd.name);
146        struct dirent * e;
147
148        if (dp->n_entries == dp->nc_entries) {
149	    struct dirent ** ne;
150
151            dp->nc_entries *= 2;
152            ne = realloc(dp->entries, sizeof(dp->entries[0]) * dp->nc_entries);
153
154            if (ne == NULL) {
155                closedir(dp);
156                dp = NULL;
157                goto done;
158            }
159
160	    dp->entries = ne;
161        }
162
163        e = malloc(sizeof(*e) + len * sizeof(char));
164        if (e == NULL) {
165            closedir(dp);
166            dp = NULL;
167            goto done;
168        }
169
170        e->d_ino = 0;           /* no inodes :( */
171        strcpy_s(e->d_name, len + 1, fd.name);
172
173        dp->entries[dp->n_entries++] = e;
174
175    } while (_findnext(fd_handle, &fd) == 0);
176
177 done:
178    if (fd_handle != -1)
179        _findclose(fd_handle);
180
181    return dp;
182}
183
184ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
185closedir(DIR * dp)
186{
187    if (!IS_DP(dp))
188        return EINVAL;
189
190    if (dp->entries) {
191        long i;
192
193        for (i=0; i < dp->n_entries; i++) {
194            free(dp->entries[i]);
195        }
196
197        free(dp->entries);
198    }
199
200    free(dp);
201
202    return 0;
203}
204
205ROKEN_LIB_FUNCTION struct dirent * ROKEN_LIB_CALL
206readdir(DIR * dp)
207{
208    if (!IS_DP(dp) ||
209        dp->cursor < 0 ||
210        dp->cursor >= dp->n_entries)
211
212        return NULL;
213
214    return dp->entries[dp->cursor++];
215}
216
217ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
218rewinddir(DIR * dp)
219{
220    if (IS_DP(dp))
221        dp->cursor = 0;
222}
223
224ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
225seekdir(DIR * dp, long offset)
226{
227    if (IS_DP(dp) && offset >= 0 && offset < dp->n_entries)
228        dp->cursor = offset;
229}
230
231ROKEN_LIB_FUNCTION long ROKEN_LIB_CALL
232telldir(DIR * dp)
233{
234    return dp->cursor;
235}
236