Bug Summary

File:io/filesystem/filesystem.cc
Location:line 361, column 3
Description:Branch condition evaluates to a garbage value

Annotated Source Code

1/*
2 * Copyright (C) 2002, 2006, 2008-2010 by the Widelands Development Team
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 */
19// this originally comes from Return to the Shadows (http://www.rtts.org/)
20// files.cc: provides all the OS abstraction to access files
21
22#include "filesystem.h"
23
24#ifdef USE_DATAFILE
25#include "datafile.h"
26#endif
27#include "disk_filesystem.h"
28#include "layered_filesystem.h"
29#include "zip_exceptions.h"
30#include "zip_filesystem.h"
31
32#include <config.h>
33
34#include <cassert>
35#include <cerrno>
36#include <climits>
37#include <cstdio>
38#include <cstdlib>
39#include <cstring>
40#include <string>
41#include <vector>
42#include <list>
43#include <iterator>
44
45#ifdef WIN32
46#include "log.h"
47#include <windows.h>
48#include <io.h>
49#include <direct.h>
50#else
51#include <glob.h>
52#include <sys/types.h>
53#endif
54#include <sys/stat.h>
55#include <unistd.h>
56
57#ifdef WIN32
58#define stat _stat
59#endif
60
61#ifdef _MSC_VER
62#define S_ISDIR(x)((((x)) & 0170000) == (0040000)) ((x&_S_IFDIR)?1:0)
63#define S_ISREG(x)((((x)) & 0170000) == (0100000)) ((x&_S_IFREG)?1:0)
64#define PATH_MAX4096 MAX_PATH
65#endif
66
67FileSystem::FileSystem()
68{
69 m_root = "";
70#ifdef WIN32
71 m_filesep = '\\';
72#else
73 m_filesep = '/';
74#endif
75}
76
77
78/**
79 * \param path A file or directory name
80 * \return True if ref path is absolute and within this FileSystem, false otherwise
81 */
82bool FileSystem::pathIsAbsolute(std::string const & path) const {
83 std::string::size_type const path_size = path .size();
84 std::string::size_type const root_size = m_root.size();
85
86 if (path_size < root_size)
87 return false;
88
89 if (path_size == root_size)
90 return path == m_root;
91
92 if (path.compare(0, m_root.size(), m_root))
93 return false;
94
95#ifdef WIN32
96 if (path.size() >= 3 && path[1] == ':' && path[2] == '\\') //"C:\"
97 {
98 return true;
99 }
100#endif
101 assert(root_size < path_size)((root_size < path_size) ? static_cast<void> (0) : __assert_fail
("root_size < path_size", "/home/arch/widelands/src/io/filesystem/filesystem.cc"
, 101, __PRETTY_FUNCTION__))
; // Otherwise an invalid read happens below.
102 if (path[root_size] != m_filesep)
103 return false;
104
105 return true;
106}
107
108
109/**
110 * Fix a path that might come from another OS.
111 * This function is used to make sure that paths send via network are usable
112 * on locale OS.
113 */
114std::string FileSystem::fixCrossFile(std::string const & path) const {
115 uint32_t path_size = path.size();
116 std::string fixedPath(path);
117 std::string temp;
118 for (uint32_t i = 0; i < path_size; ++i) {
119 temp = path.at(i);
120#ifdef WIN32
121 if (temp == ":")
122 fixedPath.at(i) = '-';
123 else if (temp == "/")
124#else
125 if (temp == "\\")
126#endif
127 fixedPath.at(i) = m_filesep;
128 // As a security measure, eat all:
129 // * tildes
130 // * double dots
131 // * dots with following slash/backslash (but not a single dot - we need it in e.g. "xyz.wmf")
132 // away to avoid misuse of the file transfer function.
133 if (temp == "~")
134 fixedPath.at(i) = '_';
135 if (temp == "." && (i + 1 < path_size)) {
136 std::string temp2;
137 temp2 = path.at(i + 1);
138 if (temp2 == "." || temp2 == "\\" || temp2 == "/")
139 fixedPath.at(i) = '_';
140 }
141 }
142 return fixedPath;
143}
144
145/**
146 * \return The process' current working directory
147 */
148std::string FileSystem::getWorkingDirectory() const {
149 char cwd[PATH_MAX4096 + 1];
150 char * const result = getcwd(cwd, PATH_MAX4096);
151 if (! result)
152 throw File_error("FileSystem::getWorkingDirectory()", "widelands", "can not run getcwd");
153
154 return std::string(cwd);
155}
156
157
158/// \todo Write homedir detection for non-getenv-systems
159std::string FileSystem::GetHomedir()
160{
161 std::string homedir;
162#ifdef WIN32
163 // trying to get it compatible to ALL windows versions...
164 // Could anybody please hit the Megasoft devs for not keeping
165 // their own "standards"?
166#define TRY_USE_AS_HOMEDIR(name) \
167 homedir = getenv(name); \
168 if (homedir.size() and check_writeable_for_data(homedir.c_str())) \
169 return homedir; \
170
171 TRY_USE_AS_HOMEDIR("USERPROFILE");
172 TRY_USE_AS_HOMEDIR("HOMEPATH");
173 TRY_USE_AS_HOMEDIR("HOME");
174 TRY_USE_AS_HOMEDIR("APPDATA");
175
176 log("None of the directories was useable - falling back to \".\"\n");
177#else
178#ifdef HAS_GETENV
179 if (char const * const h = getenv("HOME"))
180 homedir = h;
181#endif
182#endif
183
184 if (homedir.empty()) {
185 printf
186 ("\nWARNING: either we can not detect your home directory "
187 "or you do not have one! Please contact the developers.\n\n");
188
189 //TODO: is it really a good idea to set homedir to "." then ??
190
191 printf("Instead of your home directory, '.' will be used.\n\n");
192 homedir = ".";
193 }
194
195 return homedir;
196}
197
198/**
199 * Split a string into components separated by a certain character.
200 *
201 * \param path The path to parse
202 * \param filesep The file path separator used by the native filesystem
203 * \param components The output iterator to place the path nodes into
204 */
205template<typename Inserter>
206static void FS_Tokenize
207 (std::string const & path, char const filesep, Inserter components)
208{
209 std::string::size_type pos; // start of token
210 std::string::size_type pos2; // next filesep character
211
212 //extract the first path component
213 if (path.find(filesep) == 0) //is this an absolute path?
214 pos = 1;
215 else //relative path
216 pos = 0;
217 pos2 = path.find(filesep, pos);
218 //'current' token is now between pos and pos2
219
220 //split path into it's components
221 while (pos2 != std::string::npos) {
222 if (pos != pos2)
223 {
224 std::string node = path.substr(pos, pos2 - pos);
225 *components++ = node;
226 }
227 pos = pos2 + 1;
228 pos2 = path.find(filesep, pos);
229 }
230
231 //extract the last component (most probably a filename)
232 std::string node = path.substr(pos);
233 if (!node.empty())
234 *components++ = node;
235}
236
237/**
238 * Transform any valid, unique pathname into a well-formed absolute path
239 *
240 * \todo Enable non-Unix paths
241 */
242std::string FileSystem::FS_CanonicalizeName(std::string path) const {
243 std::list<std::string> components;
244 std::list<std::string>::iterator i;
245
246#ifdef WIN32
247 // remove all slashes with backslashes so following can work.
248 for (uint32_t j = 0; j < path.size(); ++j) {
249 if (path[j] == '/')
250 path[j] = '\\';
251 }
252#endif
253
254 FS_Tokenize(path, m_filesep, std::inserter(components, components.begin()));
255
256 //tilde expansion
257 if (!components.empty() && *components.begin() == "~") {
258 components.erase(components.begin());
259 FS_Tokenize
260 (GetHomedir(),
261 m_filesep,
262 std::inserter(components, components.begin()));
263 } else if (!pathIsAbsolute(path))
264 // make relative paths absolute (so that "../../foo" can work)
265 FS_Tokenize
266 (m_root.empty() ? getWorkingDirectory() : m_root, m_filesep,
267 std::inserter(components, components.begin()));
268
269 //clean up the path
270 for (i = components.begin(); i != components.end();) {
271 char const * str = i->c_str();
272 if (*str == '.') {
273 ++str;
274
275 //remove single dot
276 if (*str == '\0') {
277 i = components.erase(i);
278 continue;
279 }
280 //remove double dot and the preceding component (if any)
281 else if (*str == '.' && *(str + 1) == '\0') {
282 if (i != components.begin()) {
283#ifdef WIN32
284 // On windows don't remove driveletter in this error condition
285 if (--i != components.begin())
286 i = components.erase(i);
287 else ++i;
288#else
289 i = components.erase(--i);
290#endif
291 }
292 i = components.erase(i);
293 continue;
294 }
295 }
296
297 ++i;
298 }
299
300 std::string canonpath;
301 canonpath.reserve(path.length());
302#ifndef WIN32
303 for (i = components.begin(); i != components.end(); ++i) {
304 canonpath.push_back('/');
305 canonpath += *i;
306 }
307#else
308 for (i = components.begin(); i != components.end(); ++i) {
309 canonpath += *i;
310 canonpath += '\\';
311 }
312
313 //remove trailing slash
314 if (canonpath.size() > 1) canonpath.erase(canonpath.end() - 1);
315#endif
316
317 return canonpath;
318}
319
320/**
321 * Returns the filename of this path, everything after the last
322 * / or \ (or the whole string)
323 */
324char const * FileSystem::FS_Filename(char const * p) {
325 char const * result = p;
326
327 while (*p != '\0') {
328 if (*p == '/' || *p == '\\')
329 result = p + 1;
330 ++p;
331 }
332
333 return result;
334}
335
336char const * FileSystem::FS_Filename(char const * p, char const * & extension)
337{
338 extension = 0;
339 char const * result = p;
340
341 while (*p != '\0') {
342 if (*p == '/' || *p == '\\') {
343 extension = 0;
344 result = p + 1;
345 } else if (*p == '.')
346 extension = p;
347 ++p;
348 }
349
350
351 if (not extension)
352 extension = p;
353 return result;
354}
355
356std::string FileSystem::FS_FilenameWoExt(char const * const p)
357{
358 char const * extension;
1
Variable 'extension' declared without an initial value
359 std::string fname(p ? FileSystem::FS_Filename(p, extension) : "");
2
Assuming 'p' is null
3
'?' condition is false
360 return
361 extension ? fname.substr(0, fname.length() - strlen(extension)) : fname;
4
Branch condition evaluates to a garbage value
362}
363
364/// Create a filesystem from a zipfile or a real directory
365/// \todo Catch FileType_error in all users
366/// \todo Check for existence before doing anything with the file/dir
367/// \todo Catch FileNotFound_error in all users
368/// \throw FileNotFound_error if root does not exist, is some kind of special
369/// file, loops around (via symlinks) or is too long for the OS/filesystem.
370/// \throw FileAccessDenied_error if the OS denies access (of course ;-)
371/// \throw FileTypeError if root is neither a directory or regular file
372/// \todo throw FileTypeError if root is not a zipfile (exception from
373/// ZipFilesystem)
374FileSystem & FileSystem::Create(std::string const & root)
375throw (FileType_error, FileNotFound_error, FileAccessDenied_error)
376{
377 struct stat statinfo;
378
379 if (stat(root.c_str(), &statinfo) == -1) {
380 if
381 (errno(*__errno_location ()) == EBADF9 ||
382 errno(*__errno_location ()) == ENOENT2 ||
383 errno(*__errno_location ()) == ENOTDIR20 ||
384#ifdef ELOOP40
385 errno(*__errno_location ()) == ELOOP40 || //MinGW does not support ELOOP (yet)
386#endif
387 errno(*__errno_location ()) == ENAMETOOLONG36)
388 {
389 throw FileNotFound_error("FileSystem::Create", root);
390 }
391 if (errno(*__errno_location ()) == EACCES13)
392 throw FileAccessDenied_error("FileSystem::Create", root);
393 }
394
395 if (S_ISDIR(statinfo.st_mode)((((statinfo.st_mode)) & 0170000) == (0040000))) {
396 return *new RealFSImpl(root);
397 }
398 if (S_ISREG(statinfo.st_mode)((((statinfo.st_mode)) & 0170000) == (0100000))) { //TODO: ensure root is a zipfile
399 return *new ZipFilesystem(root);
400 }
401
402 throw FileType_error
403 ("FileSystem::Create", root,
404 "cannot create virtual filesystem from file or directory");
405}
406
407#ifdef WIN32
408/// hack that is unfortunately needed for windows to check whether Widelands
409/// can write in the directory
410bool FileSystem::check_writeable_for_data(char const * const path)
411{
412 RealFSImpl fs(path);
413
414 if (fs.IsDirectory(".widelands"))
415 return true;
416 try {
417 // throws an exception if not writable
418 fs.EnsureDirectoryExists(".widelands");
419 fs.Unlink(".widelands");
420 return true;
421 } catch (...) {
422 log("Directory %s is not writeable - next try\n", path);
423 }
424
425 return false;
426}
427#endif