1/*
2 * Copyright 2011 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7#include "SkOSFile.h"
8
9SkString SkOSPath::SkPathJoin(const char *rootPath, const char *relativePath) {
10    SkString result(rootPath);
11    if (!result.endsWith(SkPATH_SEPARATOR)) {
12        result.appendUnichar(SkPATH_SEPARATOR);
13    }
14    result.append(relativePath);
15    return result;
16}
17
18SkString SkOSPath::SkBasename(const char* fullPath) {
19    if (!fullPath) {
20        return SkString();
21    }
22    const char* filename = strrchr(fullPath, SkPATH_SEPARATOR);
23    if (NULL == filename) {
24        filename = fullPath;
25    } else {
26        ++filename;
27    }
28    return SkString(filename);
29}
30
31#ifdef SK_BUILD_FOR_WIN
32
33static uint16_t* concat_to_16(const char src[], const char suffix[])
34{
35    size_t  i, len = strlen(src);
36    size_t  len2 = 3 + (suffix ? strlen(suffix) : 0);
37    uint16_t* dst = (uint16_t*)sk_malloc_throw((len + len2) * sizeof(uint16_t));
38
39    for (i = 0; i < len; i++)
40        dst[i] = src[i];
41
42    if (i > 0 && dst[i-1] != '/')
43        dst[i++] = '/';
44    dst[i++] = '*';
45
46    if (suffix)
47    {
48        while (*suffix)
49            dst[i++] = *suffix++;
50    }
51    dst[i] = 0;
52    SkASSERT(i + 1 <= len + len2);
53
54    return dst;
55}
56
57SkUTF16_Str::SkUTF16_Str(const char src[])
58{
59    size_t  len = strlen(src);
60
61    fStr = (uint16_t*)sk_malloc_throw((len + 1) * sizeof(uint16_t));
62    size_t i;
63    for (i = 0; i < len; i++)
64        fStr[i] = src[i];
65    fStr[i] = 0;
66}
67
68////////////////////////////////////////////////////////////////////////////
69
70SkOSFile::Iter::Iter() : fHandle(0), fPath16(NULL)
71{
72}
73
74SkOSFile::Iter::Iter(const char path[], const char suffix[]) : fHandle(0), fPath16(NULL)
75{
76    this->reset(path, suffix);
77}
78
79SkOSFile::Iter::~Iter()
80{
81    sk_free(fPath16);
82    if (fHandle)
83        ::FindClose(fHandle);
84}
85
86void SkOSFile::Iter::reset(const char path[], const char suffix[])
87{
88    if (fHandle)
89    {
90        ::FindClose(fHandle);
91        fHandle = 0;
92    }
93    if (NULL == path)
94        path = "";
95
96    sk_free(fPath16);
97    fPath16 = concat_to_16(path, suffix);
98}
99
100static bool is_magic_dir(const uint16_t dir[])
101{
102    // return true for "." and ".."
103    return dir[0] == '.' && (dir[1] == 0 || dir[1] == '.' && dir[2] == 0);
104}
105
106static bool get_the_file(HANDLE handle, SkString* name, WIN32_FIND_DATAW* dataPtr, bool getDir)
107{
108    WIN32_FIND_DATAW    data;
109
110    if (NULL == dataPtr)
111    {
112        if (::FindNextFileW(handle, &data))
113            dataPtr = &data;
114        else
115            return false;
116    }
117
118    for (;;)
119    {
120        if (getDir)
121        {
122            if ((dataPtr->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && !is_magic_dir((uint16_t*)dataPtr->cFileName))
123                break;
124        }
125        else
126        {
127            if (!(dataPtr->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
128                break;
129        }
130        if (!::FindNextFileW(handle, dataPtr))
131            return false;
132    }
133    // if we get here, we've found a file/dir
134    if (name)
135        name->setUTF16((uint16_t*)dataPtr->cFileName);
136    return true;
137}
138
139bool SkOSFile::Iter::next(SkString* name, bool getDir)
140{
141    WIN32_FIND_DATAW    data;
142    WIN32_FIND_DATAW*   dataPtr = NULL;
143
144    if (fHandle == 0)   // our first time
145    {
146        if (fPath16 == NULL || *fPath16 == 0)    // check for no path
147            return false;
148
149        fHandle = ::FindFirstFileW((LPCWSTR)fPath16, &data);
150        if (fHandle != 0 && fHandle != (HANDLE)~0)
151            dataPtr = &data;
152    }
153    return fHandle != (HANDLE)~0 && get_the_file(fHandle, name, dataPtr, getDir);
154}
155
156#elif defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_IOS)
157
158#if 0
159OSStatus FSPathMakeRef (
160   const UInt8 * path,
161   FSRef * ref,
162   Boolean * isDirectory
163);
164#endif
165
166SkOSFile::Iter::Iter() : fDIR(0)
167{
168}
169
170SkOSFile::Iter::Iter(const char path[], const char suffix[]) : fDIR(0)
171{
172    this->reset(path, suffix);
173}
174
175SkOSFile::Iter::~Iter()
176{
177    if (fDIR)
178        ::closedir(fDIR);
179}
180
181void SkOSFile::Iter::reset(const char path[], const char suffix[])
182{
183    if (fDIR)
184    {
185        ::closedir(fDIR);
186        fDIR = 0;
187    }
188
189    fPath.set(path);
190    if (path)
191    {
192        fDIR = ::opendir(path);
193        fSuffix.set(suffix);
194    }
195    else
196        fSuffix.reset();
197}
198
199// returns true if suffix is empty, or if str ends with suffix
200static bool issuffixfor(const SkString& suffix, const char str[])
201{
202    size_t  suffixLen = suffix.size();
203    size_t  strLen = strlen(str);
204
205    return  strLen >= suffixLen &&
206            memcmp(suffix.c_str(), str + strLen - suffixLen, suffixLen) == 0;
207}
208
209#include <sys/stat.h>
210
211bool SkOSFile::Iter::next(SkString* name, bool getDir)
212{
213    if (fDIR)
214    {
215        dirent* entry;
216
217        while ((entry = ::readdir(fDIR)) != NULL)
218        {
219            struct stat s;
220            SkString    str(fPath);
221
222            if (!str.endsWith("/") && !str.endsWith("\\"))
223                str.append("/");
224            str.append(entry->d_name);
225
226            if (0 == stat(str.c_str(), &s))
227            {
228                if (getDir)
229                {
230                    if (s.st_mode & S_IFDIR)
231                        break;
232                }
233                else
234                {
235                    if (!(s.st_mode & S_IFDIR) && issuffixfor(fSuffix, entry->d_name))
236                        break;
237                }
238            }
239        }
240        if (entry)  // we broke out with a file
241        {
242            if (name)
243                name->set(entry->d_name);
244            return true;
245        }
246    }
247    return false;
248}
249#endif // if one of:SK_BUILD_FOR_MAC, SK_BUILD_FOR_UNIX, SK_BUILD_FOR_ANDROID,SK_BUILD_FOR_IOS
250