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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
27void 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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
46FilesystemNodePOSIX::FilesystemNodePOSIX()
47 : _path(ROOT_DIR),
48 _displayName(_path),
49 _isValid(true),
50 _isFile(false),
51 _isDirectory(true)
52{
53}
54
55// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
56FilesystemNodePOSIX::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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
86string 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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
102bool FilesystemNodePOSIX::hasParent() const
103{
104 return _path != "" && _path != ROOT_DIR;
105}
106
107// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
108bool 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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
190bool 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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
213bool 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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
238AbstractFSNodePtr 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