1/* 2 * Copyright 2013 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 8#include "SkOSFile.h" 9#include "SkString.h" 10#include "SkTFitsIn.h" 11#include "SkTemplates.h" 12#include "SkTypes.h" 13 14#include <dirent.h> 15#include <stdio.h> 16#include <string.h> 17#include <sys/mman.h> 18#include <sys/stat.h> 19#include <sys/types.h> 20#include <unistd.h> 21 22bool sk_exists(const char *path, SkFILE_Flags flags) { 23 int mode = F_OK; 24 if (flags & kRead_SkFILE_Flag) { 25 mode |= R_OK; 26 } 27 if (flags & kWrite_SkFILE_Flag) { 28 mode |= W_OK; 29 } 30 return (0 == access(path, mode)); 31} 32 33typedef struct { 34 dev_t dev; 35 ino_t ino; 36} SkFILEID; 37 38static bool sk_ino(FILE* a, SkFILEID* id) { 39 int fd = fileno(a); 40 if (fd < 0) { 41 return 0; 42 } 43 struct stat status; 44 if (0 != fstat(fd, &status)) { 45 return 0; 46 } 47 id->dev = status.st_dev; 48 id->ino = status.st_ino; 49 return true; 50} 51 52bool sk_fidentical(FILE* a, FILE* b) { 53 SkFILEID aID, bID; 54 return sk_ino(a, &aID) && sk_ino(b, &bID) 55 && aID.ino == bID.ino 56 && aID.dev == bID.dev; 57} 58 59void sk_fmunmap(const void* addr, size_t length) { 60 munmap(const_cast<void*>(addr), length); 61} 62 63void* sk_fdmmap(int fd, size_t* size) { 64 struct stat status; 65 if (0 != fstat(fd, &status)) { 66 return nullptr; 67 } 68 if (!S_ISREG(status.st_mode)) { 69 return nullptr; 70 } 71 if (!SkTFitsIn<size_t>(status.st_size)) { 72 return nullptr; 73 } 74 size_t fileSize = static_cast<size_t>(status.st_size); 75 76 void* addr = mmap(nullptr, fileSize, PROT_READ, MAP_PRIVATE, fd, 0); 77 if (MAP_FAILED == addr) { 78 return nullptr; 79 } 80 81 *size = fileSize; 82 return addr; 83} 84 85int sk_fileno(FILE* f) { 86 return fileno(f); 87} 88 89void* sk_fmmap(FILE* f, size_t* size) { 90 int fd = sk_fileno(f); 91 if (fd < 0) { 92 return nullptr; 93 } 94 95 return sk_fdmmap(fd, size); 96} 97 98//////////////////////////////////////////////////////////////////////////// 99 100struct SkOSFileIterData { 101 SkOSFileIterData() : fDIR(0) { } 102 DIR* fDIR; 103 SkString fPath, fSuffix; 104}; 105static_assert(sizeof(SkOSFileIterData) <= SkOSFile::Iter::kStorageSize, "not_enough_space"); 106 107SkOSFile::Iter::Iter() { new (fSelf.get()) SkOSFileIterData; } 108 109SkOSFile::Iter::Iter(const char path[], const char suffix[]) { 110 new (fSelf.get()) SkOSFileIterData; 111 this->reset(path, suffix); 112} 113 114SkOSFile::Iter::~Iter() { 115 SkOSFileIterData& self = *static_cast<SkOSFileIterData*>(fSelf.get()); 116 if (self.fDIR) { 117 ::closedir(self.fDIR); 118 } 119 self.~SkOSFileIterData(); 120} 121 122void SkOSFile::Iter::reset(const char path[], const char suffix[]) { 123 SkOSFileIterData& self = *static_cast<SkOSFileIterData*>(fSelf.get()); 124 if (self.fDIR) { 125 ::closedir(self.fDIR); 126 self.fDIR = 0; 127 } 128 129 self.fPath.set(path); 130 if (path) { 131 self.fDIR = ::opendir(path); 132 self.fSuffix.set(suffix); 133 } else { 134 self.fSuffix.reset(); 135 } 136} 137 138// returns true if suffix is empty, or if str ends with suffix 139static bool issuffixfor(const SkString& suffix, const char str[]) { 140 size_t suffixLen = suffix.size(); 141 size_t strLen = strlen(str); 142 143 return strLen >= suffixLen && 144 memcmp(suffix.c_str(), str + strLen - suffixLen, suffixLen) == 0; 145} 146 147bool SkOSFile::Iter::next(SkString* name, bool getDir) { 148 SkOSFileIterData& self = *static_cast<SkOSFileIterData*>(fSelf.get()); 149 if (self.fDIR) { 150 dirent* entry; 151 152 while ((entry = ::readdir(self.fDIR)) != nullptr) { 153 struct stat s; 154 SkString str(self.fPath); 155 156 if (!str.endsWith("/") && !str.endsWith("\\")) { 157 str.append("/"); 158 } 159 str.append(entry->d_name); 160 161 if (0 == stat(str.c_str(), &s)) { 162 if (getDir) { 163 if (s.st_mode & S_IFDIR) { 164 break; 165 } 166 } else { 167 if (!(s.st_mode & S_IFDIR) && issuffixfor(self.fSuffix, entry->d_name)) { 168 break; 169 } 170 } 171 } 172 } 173 if (entry) { // we broke out with a file 174 if (name) { 175 name->set(entry->d_name); 176 } 177 return true; 178 } 179 } 180 return false; 181} 182