1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2013 Google Inc.
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include "include/core/SkString.h"
9cb93a386Sopenharmony_ci#include "include/core/SkTypes.h"
10cb93a386Sopenharmony_ci#include "include/private/SkTFitsIn.h"
11cb93a386Sopenharmony_ci#include "include/private/SkTemplates.h"
12cb93a386Sopenharmony_ci#include "src/core/SkOSFile.h"
13cb93a386Sopenharmony_ci
14cb93a386Sopenharmony_ci#include <dirent.h>
15cb93a386Sopenharmony_ci#include <new>
16cb93a386Sopenharmony_ci#include <stdio.h>
17cb93a386Sopenharmony_ci#include <string.h>
18cb93a386Sopenharmony_ci#include <sys/mman.h>
19cb93a386Sopenharmony_ci#include <sys/stat.h>
20cb93a386Sopenharmony_ci#include <sys/types.h>
21cb93a386Sopenharmony_ci#include <unistd.h>
22cb93a386Sopenharmony_ci
23cb93a386Sopenharmony_ci#ifdef SK_BUILD_FOR_IOS
24cb93a386Sopenharmony_ci#include "src/ports/SkOSFile_ios.h"
25cb93a386Sopenharmony_ci#endif
26cb93a386Sopenharmony_ci
27cb93a386Sopenharmony_cibool sk_exists(const char *path, SkFILE_Flags flags) {
28cb93a386Sopenharmony_ci    int mode = F_OK;
29cb93a386Sopenharmony_ci    if (flags & kRead_SkFILE_Flag) {
30cb93a386Sopenharmony_ci        mode |= R_OK;
31cb93a386Sopenharmony_ci    }
32cb93a386Sopenharmony_ci    if (flags & kWrite_SkFILE_Flag) {
33cb93a386Sopenharmony_ci        mode |= W_OK;
34cb93a386Sopenharmony_ci    }
35cb93a386Sopenharmony_ci#ifdef SK_BUILD_FOR_IOS
36cb93a386Sopenharmony_ci    // if the default path fails, check the bundle (but only if read-only)
37cb93a386Sopenharmony_ci    if (0 == access(path, mode)) {
38cb93a386Sopenharmony_ci        return true;
39cb93a386Sopenharmony_ci    } else {
40cb93a386Sopenharmony_ci        return (kRead_SkFILE_Flag == flags && ios_get_path_in_bundle(path, nullptr));
41cb93a386Sopenharmony_ci    }
42cb93a386Sopenharmony_ci#else
43cb93a386Sopenharmony_ci    return (0 == access(path, mode));
44cb93a386Sopenharmony_ci#endif
45cb93a386Sopenharmony_ci}
46cb93a386Sopenharmony_ci
47cb93a386Sopenharmony_citypedef struct {
48cb93a386Sopenharmony_ci    dev_t dev;
49cb93a386Sopenharmony_ci    ino_t ino;
50cb93a386Sopenharmony_ci} SkFILEID;
51cb93a386Sopenharmony_ci
52cb93a386Sopenharmony_cistatic bool sk_ino(FILE* a, SkFILEID* id) {
53cb93a386Sopenharmony_ci    int fd = fileno(a);
54cb93a386Sopenharmony_ci    if (fd < 0) {
55cb93a386Sopenharmony_ci        return 0;
56cb93a386Sopenharmony_ci    }
57cb93a386Sopenharmony_ci    struct stat status;
58cb93a386Sopenharmony_ci    if (0 != fstat(fd, &status)) {
59cb93a386Sopenharmony_ci        return 0;
60cb93a386Sopenharmony_ci    }
61cb93a386Sopenharmony_ci    id->dev = status.st_dev;
62cb93a386Sopenharmony_ci    id->ino = status.st_ino;
63cb93a386Sopenharmony_ci    return true;
64cb93a386Sopenharmony_ci}
65cb93a386Sopenharmony_ci
66cb93a386Sopenharmony_cibool sk_fidentical(FILE* a, FILE* b) {
67cb93a386Sopenharmony_ci    SkFILEID aID, bID;
68cb93a386Sopenharmony_ci    return sk_ino(a, &aID) && sk_ino(b, &bID)
69cb93a386Sopenharmony_ci           && aID.ino == bID.ino
70cb93a386Sopenharmony_ci           && aID.dev == bID.dev;
71cb93a386Sopenharmony_ci}
72cb93a386Sopenharmony_ci
73cb93a386Sopenharmony_civoid sk_fmunmap(const void* addr, size_t length) {
74cb93a386Sopenharmony_ci    munmap(const_cast<void*>(addr), length);
75cb93a386Sopenharmony_ci}
76cb93a386Sopenharmony_ci
77cb93a386Sopenharmony_civoid* sk_fdmmap(int fd, size_t* size) {
78cb93a386Sopenharmony_ci    struct stat status;
79cb93a386Sopenharmony_ci    if (0 != fstat(fd, &status)) {
80cb93a386Sopenharmony_ci        return nullptr;
81cb93a386Sopenharmony_ci    }
82cb93a386Sopenharmony_ci    if (!S_ISREG(status.st_mode)) {
83cb93a386Sopenharmony_ci        return nullptr;
84cb93a386Sopenharmony_ci    }
85cb93a386Sopenharmony_ci    if (!SkTFitsIn<size_t>(status.st_size)) {
86cb93a386Sopenharmony_ci        return nullptr;
87cb93a386Sopenharmony_ci    }
88cb93a386Sopenharmony_ci    size_t fileSize = static_cast<size_t>(status.st_size);
89cb93a386Sopenharmony_ci
90cb93a386Sopenharmony_ci    void* addr = mmap(nullptr, fileSize, PROT_READ, MAP_PRIVATE, fd, 0);
91cb93a386Sopenharmony_ci    if (MAP_FAILED == addr) {
92cb93a386Sopenharmony_ci        return nullptr;
93cb93a386Sopenharmony_ci    }
94cb93a386Sopenharmony_ci
95cb93a386Sopenharmony_ci    *size = fileSize;
96cb93a386Sopenharmony_ci    return addr;
97cb93a386Sopenharmony_ci}
98cb93a386Sopenharmony_ci
99cb93a386Sopenharmony_ciint sk_fileno(FILE* f) {
100cb93a386Sopenharmony_ci    return fileno(f);
101cb93a386Sopenharmony_ci}
102cb93a386Sopenharmony_ci
103cb93a386Sopenharmony_civoid* sk_fmmap(FILE* f, size_t* size) {
104cb93a386Sopenharmony_ci    int fd = sk_fileno(f);
105cb93a386Sopenharmony_ci    if (fd < 0) {
106cb93a386Sopenharmony_ci        return nullptr;
107cb93a386Sopenharmony_ci    }
108cb93a386Sopenharmony_ci
109cb93a386Sopenharmony_ci    return sk_fdmmap(fd, size);
110cb93a386Sopenharmony_ci}
111cb93a386Sopenharmony_ci
112cb93a386Sopenharmony_cisize_t sk_qread(FILE* file, void* buffer, size_t count, size_t offset) {
113cb93a386Sopenharmony_ci    int fd = sk_fileno(file);
114cb93a386Sopenharmony_ci    if (fd < 0) {
115cb93a386Sopenharmony_ci        return SIZE_MAX;
116cb93a386Sopenharmony_ci    }
117cb93a386Sopenharmony_ci    ssize_t bytesRead = pread(fd, buffer, count, offset);
118cb93a386Sopenharmony_ci    if (bytesRead < 0) {
119cb93a386Sopenharmony_ci        return SIZE_MAX;
120cb93a386Sopenharmony_ci    }
121cb93a386Sopenharmony_ci    return bytesRead;
122cb93a386Sopenharmony_ci}
123cb93a386Sopenharmony_ci
124cb93a386Sopenharmony_ci////////////////////////////////////////////////////////////////////////////
125cb93a386Sopenharmony_ci
126cb93a386Sopenharmony_cistruct SkOSFileIterData {
127cb93a386Sopenharmony_ci    SkOSFileIterData() : fDIR(nullptr) { }
128cb93a386Sopenharmony_ci    DIR* fDIR;
129cb93a386Sopenharmony_ci    SkString fPath, fSuffix;
130cb93a386Sopenharmony_ci};
131cb93a386Sopenharmony_cistatic_assert(sizeof(SkOSFileIterData) <= SkOSFile::Iter::kStorageSize, "not_enough_space");
132cb93a386Sopenharmony_ci
133cb93a386Sopenharmony_ciSkOSFile::Iter::Iter() { new (fSelf) SkOSFileIterData; }
134cb93a386Sopenharmony_ci
135cb93a386Sopenharmony_ciSkOSFile::Iter::Iter(const char path[], const char suffix[]) {
136cb93a386Sopenharmony_ci    new (fSelf) SkOSFileIterData;
137cb93a386Sopenharmony_ci    this->reset(path, suffix);
138cb93a386Sopenharmony_ci}
139cb93a386Sopenharmony_ci
140cb93a386Sopenharmony_ciSkOSFile::Iter::~Iter() {
141cb93a386Sopenharmony_ci    SkOSFileIterData& self = *reinterpret_cast<SkOSFileIterData*>(fSelf);
142cb93a386Sopenharmony_ci    if (self.fDIR) {
143cb93a386Sopenharmony_ci        ::closedir(self.fDIR);
144cb93a386Sopenharmony_ci    }
145cb93a386Sopenharmony_ci    self.~SkOSFileIterData();
146cb93a386Sopenharmony_ci}
147cb93a386Sopenharmony_ci
148cb93a386Sopenharmony_civoid SkOSFile::Iter::reset(const char path[], const char suffix[]) {
149cb93a386Sopenharmony_ci    SkOSFileIterData& self = *reinterpret_cast<SkOSFileIterData*>(fSelf);
150cb93a386Sopenharmony_ci    if (self.fDIR) {
151cb93a386Sopenharmony_ci        ::closedir(self.fDIR);
152cb93a386Sopenharmony_ci        self.fDIR = nullptr;
153cb93a386Sopenharmony_ci    }
154cb93a386Sopenharmony_ci    self.fPath.set(path);
155cb93a386Sopenharmony_ci
156cb93a386Sopenharmony_ci    if (path) {
157cb93a386Sopenharmony_ci        self.fDIR = ::opendir(path);
158cb93a386Sopenharmony_ci#ifdef SK_BUILD_FOR_IOS
159cb93a386Sopenharmony_ci        // check bundle for directory
160cb93a386Sopenharmony_ci        if (!self.fDIR && ios_get_path_in_bundle(path, &self.fPath)) {
161cb93a386Sopenharmony_ci            self.fDIR = ::opendir(self.fPath.c_str());
162cb93a386Sopenharmony_ci        }
163cb93a386Sopenharmony_ci#endif
164cb93a386Sopenharmony_ci        self.fSuffix.set(suffix);
165cb93a386Sopenharmony_ci    } else {
166cb93a386Sopenharmony_ci        self.fSuffix.reset();
167cb93a386Sopenharmony_ci    }
168cb93a386Sopenharmony_ci}
169cb93a386Sopenharmony_ci
170cb93a386Sopenharmony_ci// returns true if suffix is empty, or if str ends with suffix
171cb93a386Sopenharmony_cistatic bool issuffixfor(const SkString& suffix, const char str[]) {
172cb93a386Sopenharmony_ci    size_t  suffixLen = suffix.size();
173cb93a386Sopenharmony_ci    size_t  strLen = strlen(str);
174cb93a386Sopenharmony_ci
175cb93a386Sopenharmony_ci    return  strLen >= suffixLen &&
176cb93a386Sopenharmony_ci            memcmp(suffix.c_str(), str + strLen - suffixLen, suffixLen) == 0;
177cb93a386Sopenharmony_ci}
178cb93a386Sopenharmony_ci
179cb93a386Sopenharmony_cibool SkOSFile::Iter::next(SkString* name, bool getDir) {
180cb93a386Sopenharmony_ci    SkOSFileIterData& self = *reinterpret_cast<SkOSFileIterData*>(fSelf);
181cb93a386Sopenharmony_ci    if (self.fDIR) {
182cb93a386Sopenharmony_ci        dirent* entry;
183cb93a386Sopenharmony_ci
184cb93a386Sopenharmony_ci        while ((entry = ::readdir(self.fDIR)) != nullptr) {
185cb93a386Sopenharmony_ci            struct stat s;
186cb93a386Sopenharmony_ci            SkString str(self.fPath);
187cb93a386Sopenharmony_ci
188cb93a386Sopenharmony_ci            if (!str.endsWith("/") && !str.endsWith("\\")) {
189cb93a386Sopenharmony_ci                str.append("/");
190cb93a386Sopenharmony_ci            }
191cb93a386Sopenharmony_ci            str.append(entry->d_name);
192cb93a386Sopenharmony_ci
193cb93a386Sopenharmony_ci            if (0 == stat(str.c_str(), &s)) {
194cb93a386Sopenharmony_ci                if (getDir) {
195cb93a386Sopenharmony_ci                    if (s.st_mode & S_IFDIR) {
196cb93a386Sopenharmony_ci                        break;
197cb93a386Sopenharmony_ci                    }
198cb93a386Sopenharmony_ci                } else {
199cb93a386Sopenharmony_ci                    if (!(s.st_mode & S_IFDIR) && issuffixfor(self.fSuffix, entry->d_name)) {
200cb93a386Sopenharmony_ci                        break;
201cb93a386Sopenharmony_ci                    }
202cb93a386Sopenharmony_ci                }
203cb93a386Sopenharmony_ci            }
204cb93a386Sopenharmony_ci        }
205cb93a386Sopenharmony_ci        if (entry) { // we broke out with a file
206cb93a386Sopenharmony_ci            if (name) {
207cb93a386Sopenharmony_ci                name->set(entry->d_name);
208cb93a386Sopenharmony_ci            }
209cb93a386Sopenharmony_ci            return true;
210cb93a386Sopenharmony_ci        }
211cb93a386Sopenharmony_ci    }
212cb93a386Sopenharmony_ci    return false;
213cb93a386Sopenharmony_ci}
214