1 | //============================================================================ |
2 | // |
3 | // SSSS tt lll lll |
4 | // SS SS tt ll ll |
5 | // SS tttttt eeee ll ll aaaa |
6 | // SSSS tt ee ee ll ll aa |
7 | // SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" |
8 | // SS SS tt ee ll ll aa aa |
9 | // SSSS ttt eeeee llll llll aaaaa |
10 | // |
11 | // Copyright (c) 1995-2019 by Bradford W. Mott, Stephen Anthony |
12 | // and the Stella Team |
13 | // |
14 | // See the file "License.txt" for information on usage and redistribution of |
15 | // this file, and for a DISCLAIMER OF ALL WARRANTIES. |
16 | //============================================================================ |
17 | |
18 | #if defined(RETRON77) |
19 | #define ROOT_DIR "/mnt/games/" |
20 | #else |
21 | #define ROOT_DIR "/" |
22 | #endif |
23 | |
24 | #include "FSNodePOSIX.hxx" |
25 | |
26 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
27 | void FilesystemNodePOSIX::setFlags() |
28 | { |
29 | struct stat st; |
30 | |
31 | _isValid = (0 == stat(_path.c_str(), &st)); |
32 | if(_isValid) |
33 | { |
34 | _isDirectory = S_ISDIR(st.st_mode); |
35 | _isFile = S_ISREG(st.st_mode); |
36 | |
37 | // Add a trailing slash, if necessary |
38 | if (_isDirectory && _path.length() > 0 && _path[_path.length()-1] != '/') |
39 | _path += '/'; |
40 | } |
41 | else |
42 | _isDirectory = _isFile = false; |
43 | } |
44 | |
45 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
46 | FilesystemNodePOSIX::FilesystemNodePOSIX() |
47 | : _path(ROOT_DIR), |
48 | _displayName(_path), |
49 | _isValid(true), |
50 | _isFile(false), |
51 | _isDirectory(true) |
52 | { |
53 | } |
54 | |
55 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
56 | FilesystemNodePOSIX::FilesystemNodePOSIX(const string& path, bool verify) |
57 | : _isValid(true), |
58 | _isFile(false), |
59 | _isDirectory(true) |
60 | { |
61 | // Default to home directory |
62 | _path = path.length() > 0 ? path : "~" ; |
63 | |
64 | // Expand '~' to the HOME environment variable |
65 | if(_path[0] == '~') |
66 | { |
67 | const char* home = getenv("HOME" ); |
68 | if(home != nullptr) |
69 | _path.replace(0, 1, home); |
70 | } |
71 | // Get absolute path (only used for relative directories) |
72 | else if(_path[0] == '.') |
73 | { |
74 | char buf[MAXPATHLEN]; |
75 | if(realpath(_path.c_str(), buf)) |
76 | _path = buf; |
77 | } |
78 | |
79 | _displayName = lastPathComponent(_path); |
80 | |
81 | if(verify) |
82 | setFlags(); |
83 | } |
84 | |
85 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
86 | string FilesystemNodePOSIX::getShortPath() const |
87 | { |
88 | // If the path starts with the home directory, replace it with '~' |
89 | const char* home = getenv("HOME" ); |
90 | if(home != nullptr && BSPF::startsWithIgnoreCase(_path, home)) |
91 | { |
92 | string path = "~" ; |
93 | const char* offset = _path.c_str() + strlen(home); |
94 | if(*offset != '/') path += "/" ; |
95 | path += offset; |
96 | return path; |
97 | } |
98 | return _path; |
99 | } |
100 | |
101 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
102 | bool FilesystemNodePOSIX::hasParent() const |
103 | { |
104 | return _path != "" && _path != ROOT_DIR; |
105 | } |
106 | |
107 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
108 | bool FilesystemNodePOSIX::getChildren(AbstractFSList& myList, ListMode mode) const |
109 | { |
110 | assert(_isDirectory); |
111 | |
112 | DIR* dirp = opendir(_path.c_str()); |
113 | if (dirp == nullptr) |
114 | return false; |
115 | |
116 | // Loop over dir entries using readdir |
117 | struct dirent* dp; |
118 | while ((dp = readdir(dirp)) != nullptr) |
119 | { |
120 | // Ignore all hidden files |
121 | if (dp->d_name[0] == '.') |
122 | continue; |
123 | |
124 | string newPath(_path); |
125 | if (newPath.length() > 0 && newPath[newPath.length()-1] != '/') |
126 | newPath += '/'; |
127 | newPath += dp->d_name; |
128 | |
129 | FilesystemNodePOSIX entry(newPath, false); |
130 | |
131 | #if defined(SYSTEM_NOT_SUPPORTING_D_TYPE) |
132 | /* TODO: d_type is not part of POSIX, so it might not be supported |
133 | * on some of our targets. For those systems where it isn't supported, |
134 | * add this #elif case, which tries to use stat() instead. |
135 | * |
136 | * The d_type method is used to avoid costly recurrent stat() calls in big |
137 | * directories. |
138 | */ |
139 | entry.setFlags(); |
140 | #else |
141 | if (dp->d_type == DT_UNKNOWN) |
142 | { |
143 | // Fall back to stat() |
144 | entry.setFlags(); |
145 | } |
146 | else |
147 | { |
148 | if (dp->d_type == DT_LNK) |
149 | { |
150 | struct stat st; |
151 | if (stat(entry._path.c_str(), &st) == 0) |
152 | { |
153 | entry._isDirectory = S_ISDIR(st.st_mode); |
154 | entry._isFile = S_ISREG(st.st_mode); |
155 | } |
156 | else |
157 | entry._isDirectory = entry._isFile = false; |
158 | } |
159 | else |
160 | { |
161 | entry._isDirectory = (dp->d_type == DT_DIR); |
162 | entry._isFile = (dp->d_type == DT_REG); |
163 | } |
164 | |
165 | if (entry._isDirectory) |
166 | entry._path += "/" ; |
167 | |
168 | entry._isValid = entry._isDirectory || entry._isFile; |
169 | } |
170 | #endif |
171 | |
172 | // Skip files that are invalid for some reason (e.g. because we couldn't |
173 | // properly stat them). |
174 | if (!entry._isValid) |
175 | continue; |
176 | |
177 | // Honor the chosen mode |
178 | if ((mode == FilesystemNode::ListMode::FilesOnly && !entry._isFile) || |
179 | (mode == FilesystemNode::ListMode::DirectoriesOnly && !entry._isDirectory)) |
180 | continue; |
181 | |
182 | myList.emplace_back(make_shared<FilesystemNodePOSIX>(entry)); |
183 | } |
184 | closedir(dirp); |
185 | |
186 | return true; |
187 | } |
188 | |
189 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
190 | bool FilesystemNodePOSIX::makeDir() |
191 | { |
192 | if(mkdir(_path.c_str(), 0777) == 0) |
193 | { |
194 | // Get absolute path |
195 | char buf[MAXPATHLEN]; |
196 | if(realpath(_path.c_str(), buf)) |
197 | _path = buf; |
198 | |
199 | _displayName = lastPathComponent(_path); |
200 | setFlags(); |
201 | |
202 | // Add a trailing slash, if necessary |
203 | if (_path.length() > 0 && _path[_path.length()-1] != '/') |
204 | _path += '/'; |
205 | |
206 | return true; |
207 | } |
208 | else |
209 | return false; |
210 | } |
211 | |
212 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
213 | bool FilesystemNodePOSIX::rename(const string& newfile) |
214 | { |
215 | if(std::rename(_path.c_str(), newfile.c_str()) == 0) |
216 | { |
217 | _path = newfile; |
218 | |
219 | // Get absolute path |
220 | char buf[MAXPATHLEN]; |
221 | if(realpath(_path.c_str(), buf)) |
222 | _path = buf; |
223 | |
224 | _displayName = lastPathComponent(_path); |
225 | setFlags(); |
226 | |
227 | // Add a trailing slash, if necessary |
228 | if (_isDirectory && _path.length() > 0 && _path[_path.length()-1] != '/') |
229 | _path += '/'; |
230 | |
231 | return true; |
232 | } |
233 | else |
234 | return false; |
235 | } |
236 | |
237 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
238 | AbstractFSNodePtr FilesystemNodePOSIX::getParent() const |
239 | { |
240 | if (_path == ROOT_DIR) |
241 | return nullptr; |
242 | |
243 | const char* start = _path.c_str(); |
244 | const char* end = lastPathComponent(_path); |
245 | |
246 | return make_unique<FilesystemNodePOSIX>(string(start, size_t(end - start))); |
247 | } |
248 | |