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