1/* Copyright (C) 2007-2008 The Android Open Source Project
2**
3** This software is licensed under the terms of the GNU General Public
4** License version 2, as published by the Free Software Foundation, and
5** may be copied, distributed, and modified under those terms.
6**
7** This program is distributed in the hope that it will be useful,
8** but WITHOUT ANY WARRANTY; without even the implied warranty of
9** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10** GNU General Public License for more details.
11*/
12#include "android/utils/dirscanner.h"
13#include "android/utils/bufprint.h"
14#include "android/utils/system.h"
15#include "android/utils/path.h"
16#include <stddef.h>
17
18#define  DIRSCANNER_BASE     \
19    char  root[PATH_MAX];    \
20    int   rootLen;           \
21    char  full[PATH_MAX];    \
22
23
24#if _WIN32
25
26#include <io.h>
27
28struct DirScanner {
29    DIRSCANNER_BASE
30    intptr_t            findIndex1;
31    struct _finddata_t  findData;
32};
33
34/* note: findIndex1 contains the find index + 1
35 *       so a value of 0 means 'invalid'
36 */
37
38static int
39_dirScannerInit( DirScanner*  s )
40{
41    char*  p   = s->root + s->rootLen;
42    char*  end = s->root + sizeof s->root;
43    int    ret;
44
45    /* create file spec by appending \* to root */
46    p = bufprint(p, end, "\\*");
47    if (p >= end)
48        return -1;
49
50    ret = _findfirst(s->root, &s->findData);
51
52    s->findIndex1 = ret+1;
53    return ret;
54}
55
56static void
57_dirScanner_done( DirScanner*  s )
58{
59    if (s->findIndex1 > 0) {
60        _findclose(s->findIndex1-1);
61        s->findIndex1 = 0;
62    }
63}
64
65const char*
66dirScanner_next( DirScanner*  s )
67{
68    char*  ret = NULL;
69
70    if (!s || s->findIndex1 <= 0)
71        return NULL;
72
73    while (ret == NULL) {
74        ret = s->findData.name;
75
76        /* ignore special directories */
77        if (!strcmp(ret, ".") || !strcmp(ret, "..")) {
78            ret = NULL;
79        }
80        /* find next one */
81        if (_findnext(s->findIndex1-1, &s->findData) < 0) {
82            _dirScanner_done(s);
83            break;
84        }
85    }
86    return ret;
87}
88
89#else /* !_WIN32 */
90
91#include <dirent.h>
92struct DirScanner {
93    DIRSCANNER_BASE
94    DIR*            dir;
95    struct dirent*  entry;
96};
97
98static int
99_dirScannerInit( DirScanner*  s )
100{
101    s->dir = opendir(s->root);
102
103    if (s->dir == NULL)
104        return -1;
105
106    s->entry = NULL;
107    return 0;
108}
109
110static void
111_dirScanner_done( DirScanner*  s )
112{
113    if (s->dir) {
114        closedir(s->dir);
115        s->dir = NULL;
116    }
117}
118
119const char*
120dirScanner_next( DirScanner*  s )
121{
122    char*  ret = NULL;
123
124    if (!s || s->dir == NULL)
125        return NULL;
126
127    for (;;)
128    {
129        /* read new entry if needed */
130        s->entry = readdir(s->dir);
131        if (s->entry == NULL) {
132            _dirScanner_done(s);
133            break;
134        }
135
136        /* ignore special directories */
137        ret = s->entry->d_name;
138
139        if (!strcmp(ret,".") || !strcmp(ret,"..")) {
140            ret = NULL;
141            continue;
142        }
143        break;
144    }
145    return ret;
146}
147
148#endif /* !_WIN32 */
149
150DirScanner*
151dirScanner_new ( const char*  rootPath )
152{
153    DirScanner*  s   = android_alloc0(sizeof *s);
154    char*        p   = s->root;
155    char*        end = p + sizeof s->root;
156
157    p = bufprint(p, end, "%s", rootPath);
158    if (p >= end)
159        goto FAIL;
160
161    s->rootLen = (p - s->root);
162
163    if (_dirScannerInit(s) < 0)
164        goto FAIL;
165
166    return s;
167
168FAIL:
169    dirScanner_free(s);
170    return NULL;
171}
172
173
174void
175dirScanner_free( DirScanner*  s )
176{
177    if (!s)
178        return;
179
180    _dirScanner_done(s);
181    AFREE(s);
182}
183
184
185const char*
186dirScanner_nextFull( DirScanner*  s )
187{
188    const char*  name = dirScanner_next(s);
189    char*        p;
190    char*        end;
191
192    if (name == NULL)
193        return NULL;
194
195    p   = s->full;
196    end = p + sizeof s->full;
197
198    p = bufprint(p, end, "%.*s/%s", s->rootLen, s->root, name);
199    if (p >= end) {
200        /* ignore if the full name is too long */
201        return dirScanner_nextFull(s);
202    }
203    return s->full;
204}
205