# refactor VFS path-related functions; split into separate files
- replace all hardcoded strrchr functions (used to get extension / file name only) with path helper functions (found a few bugs in the process) - split VFS-independent path helper functions into lib/path_util (allows including by other files without pulling in entire VFS) - renamed pp_* functions path_package_* - split remaining path helper functions into lib/res/file/path - vfs: split should-reload logic out of vfs_reload_changed_files - lib: add comments to rand/xrand This was SVN commit r3796.
This commit is contained in:
parent
b5d9da29c1
commit
01700f0e9f
@ -116,22 +116,14 @@ void CTextureManager::LoadTextures(CTerrainPropertiesPtr props, const char* dir)
|
||||
// we can't use FindFile's filter param because new texture formats
|
||||
// may later be added and that interface doesn't support specifying
|
||||
// multiple extensions.
|
||||
const char* ext = strrchr(texture_name, '.');
|
||||
if(!ext
|
||||
|| !stricmp(ext, ".xml")
|
||||
|| !stricmp(ext, ".xmb")
|
||||
|| !stricmp(ext, ".dtd")
|
||||
|| !stricmp(ext, ".jbf") // PSP browser files. (This list is getting quite long and ugly...)
|
||||
)
|
||||
continue;
|
||||
// Also allow storage of other temporary files in the texture directories
|
||||
if(ext[strlen(ext)-1] == '~')
|
||||
if(!tex_is_known_extension(texture_name))
|
||||
continue;
|
||||
|
||||
// build name of associated xml file (i.e. replace extension)
|
||||
char xml_name[PATH_MAX+5]; // add room for .XML
|
||||
strcpy_s(xml_name, PATH_MAX, texture_name);
|
||||
strcpy(xml_name + (ext-texture_name), ".xml"); // safe
|
||||
const char* ext = path_extension(texture_name);
|
||||
SAFE_STRCPY(xml_name + (ext-texture_name), "xml");
|
||||
|
||||
CTerrainPropertiesPtr myprops;
|
||||
// Has XML file -> attempt to load properties
|
||||
|
@ -26,9 +26,11 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "sysdep/gfx.h"
|
||||
#include "res/graphics/ogl_tex.h"
|
||||
#include "res/file/file.h"
|
||||
#include "res/file/vfs.h"
|
||||
#include "lib/res/graphics/ogl_tex.h"
|
||||
#include "lib/res/file/file.h"
|
||||
#include "lib/res/file/vfs.h"
|
||||
#include "lib/res/graphics/ogl_tex.h"
|
||||
#include "lib/path_util.h"
|
||||
|
||||
#include "app_hooks.h"
|
||||
|
||||
@ -56,10 +58,9 @@ static const char* get_log_dir()
|
||||
ONCE(\
|
||||
char N_exe_name[PATH_MAX];\
|
||||
(void)sys_get_executable_name(N_exe_name, ARRAY_SIZE(N_exe_name));\
|
||||
/* strip app name (we only need path) */\
|
||||
char* slash = strrchr(N_exe_name, DIR_SEP);\
|
||||
if(slash) *slash = '\0';\
|
||||
(void)vfs_path_append(N_log_dir, N_exe_name, "../logs/");
|
||||
/* strip app name (we only want its path) */\
|
||||
path_strip_fn(N_exe_name);\
|
||||
(void)path_append(N_log_dir, N_exe_name, "../logs/");
|
||||
);
|
||||
return N_log_dir;
|
||||
}
|
||||
|
@ -26,14 +26,15 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "lib.h"
|
||||
#include "debug.h"
|
||||
#include "debug_stl.h"
|
||||
#include "posix.h"
|
||||
// some functions here are called from within mmgr; disable its hooks
|
||||
// so that our allocations don't cause infinite recursion.
|
||||
#include "nommgr.h"
|
||||
#include "self_test.h"
|
||||
#include "app_hooks.h"
|
||||
#include "lib/path_util.h"
|
||||
#include "debug_stl.h"
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
// needed when writing crashlog
|
||||
@ -216,7 +217,7 @@ void debug_wprintf(const wchar_t* fmt, ...)
|
||||
LibError debug_write_crashlog(const wchar_t* text)
|
||||
{
|
||||
// note: we go through some gyrations here (strcpy+strcat) to avoid
|
||||
// dependency on file code (vfs_path_append).
|
||||
// dependency on file code (path_append).
|
||||
char N_path[PATH_MAX];
|
||||
strcpy_s(N_path, ARRAY_SIZE(N_path), ah_get_log_dir());
|
||||
strcat_s(N_path, ARRAY_SIZE(N_path), "crashlog.txt");
|
||||
@ -300,12 +301,9 @@ static const char* symbol_string_build(void* symbol, const char* name, const cha
|
||||
if(file && line)
|
||||
{
|
||||
// strip path from filename (long and irrelevant)
|
||||
const char* filename_only = file;
|
||||
const char* slash = strrchr(file, DIR_SEP);
|
||||
if(slash)
|
||||
filename_only = slash+1;
|
||||
const char* fn_only = path_name_only(file);
|
||||
|
||||
len = snprintf(string, STRING_MAX-1, "%s:%05d ", filename_only, line);
|
||||
len = snprintf(string, STRING_MAX-1, "%s:%05d ", fn_only, line);
|
||||
}
|
||||
// only address is known
|
||||
else
|
||||
@ -471,9 +469,8 @@ ErrorReaction debug_display_error(const wchar_t* description,
|
||||
description = ah_translate(description);
|
||||
|
||||
// display in output window; double-click will navigate to error location.
|
||||
const char* slash = strrchr(file, DIR_SEP);
|
||||
const char* filename = slash? slash+1 : file;
|
||||
debug_wprintf(L"%hs(%d): %ls\n", filename, line, description);
|
||||
const char* fn_only = path_name_only(file);
|
||||
debug_wprintf(L"%hs(%d): %ls\n", fn_only, line, description);
|
||||
|
||||
// allocate memory for the stack trace. this needs to be quite large,
|
||||
// so preallocating is undesirable. it must work even if the heap is
|
||||
@ -559,13 +556,12 @@ ErrorReaction debug_assert_failed(const char* expr,
|
||||
|
||||
// __FILE__ evaluates to the full path (albeit without drive letter)
|
||||
// which is rather long. we only display the base name for clarity.
|
||||
const char* slash = strrchr(file, DIR_SEP);
|
||||
const char* base_name = slash? slash+1 : file;
|
||||
const char* fn_only = path_name_only(file);
|
||||
|
||||
uint skip = 1; void* context = 0;
|
||||
wchar_t buf[400];
|
||||
swprintf(buf, ARRAY_SIZE(buf), L"Assertion failed at %hs:%d (%hs): \"%hs\"", base_name, line, func, expr);
|
||||
return debug_display_error(buf, DE_ALLOW_SUPPRESS|DE_MANUAL_BREAK, skip,context, base_name,line);
|
||||
swprintf(buf, ARRAY_SIZE(buf), L"Assertion failed at %hs:%d (%hs): \"%hs\"", fn_only, line, func, expr);
|
||||
return debug_display_error(buf, DE_ALLOW_SUPPRESS|DE_MANUAL_BREAK, skip,context, fn_only,line);
|
||||
}
|
||||
|
||||
|
||||
@ -583,14 +579,13 @@ ErrorReaction debug_warn_err(LibError err,
|
||||
|
||||
// __FILE__ evaluates to the full path (albeit without drive letter)
|
||||
// which is rather long. we only display the base name for clarity.
|
||||
const char* slash = strrchr(file, DIR_SEP);
|
||||
const char* base_name = slash? slash+1 : file;
|
||||
const char* fn_only = path_name_only(file);
|
||||
|
||||
uint skip = 1; void* context = 0;
|
||||
wchar_t buf[400];
|
||||
char err_buf[200]; error_description_r(err, err_buf, ARRAY_SIZE(err_buf));
|
||||
swprintf(buf, ARRAY_SIZE(buf), L"Function call failed at %hs:%d (%hs): return value was %d (%hs)", base_name, line, func, err, err_buf);
|
||||
return debug_display_error(buf, DE_ALLOW_SUPPRESS|DE_MANUAL_BREAK, skip,context, base_name,line);
|
||||
swprintf(buf, ARRAY_SIZE(buf), L"Function call failed at %hs:%d (%hs): return value was %d (%hs)", fn_only, line, func, err, err_buf);
|
||||
return debug_display_error(buf, DE_ALLOW_SUPPRESS|DE_MANUAL_BREAK, skip,context, fn_only,line);
|
||||
}
|
||||
|
||||
|
||||
|
@ -526,18 +526,21 @@ int match_wildcardw(const wchar_t* s, const wchar_t* w)
|
||||
// avoids several common pitfalls; see discussion at
|
||||
// http://www.azillionmonkeys.com/qed/random.html
|
||||
|
||||
// Silly heuristic: glibc has RAND_MAX of 2^31 - 1 while MS CRT has 2^15-1 or something...
|
||||
// rand() is poorly implemented (e.g. in VC7) and only returns < 16 bits;
|
||||
// double that amount by concatenating 2 random numbers.
|
||||
// this is not to fix poor rand() randomness - the number returned will be
|
||||
// folded down to a much smaller interval anyway. instead, a larger XRAND_MAX
|
||||
// decreases the probability of having to repeat the loop.
|
||||
#if RAND_MAX < 65536
|
||||
static const uint XRAND_MAX = (RAND_MAX+1)*(RAND_MAX+1) - 1;
|
||||
|
||||
uint fullrand()
|
||||
static uint xrand()
|
||||
{
|
||||
return rand()*(RAND_MAX+1) + rand();
|
||||
}
|
||||
// rand() is already ok; no need to do anything.
|
||||
#else
|
||||
static const uint XRAND_MAX = RAND_MAX;
|
||||
|
||||
uint fullrand()
|
||||
static uint xrand()
|
||||
{
|
||||
return rand();
|
||||
}
|
||||
@ -556,9 +559,12 @@ uint rand(uint min_inclusive, uint max_exclusive)
|
||||
const uint inv_range = XRAND_MAX / range;
|
||||
|
||||
// generate random number in [0, range)
|
||||
// idea: avoid skewed distributions when <range> doesn't evenly divide
|
||||
// XRAND_MAX by simply discarding values in the "remainder".
|
||||
// not expected to run often since XRAND_MAX is large.
|
||||
uint x;
|
||||
do
|
||||
x = fullrand();
|
||||
x = xrand();
|
||||
while(x >= range * inv_range);
|
||||
x /= inv_range;
|
||||
|
||||
|
@ -413,7 +413,7 @@ ERR(-100262, ERR_MEM_OVERWRITTEN, "Wrote to memory outside valid allocation")
|
||||
|
||||
// file + vfs
|
||||
// .. path
|
||||
ERR(-100300, ERR_PATH_LENGTH, "Path exceeds VFS_MAX_PATH characters")
|
||||
ERR(-100300, ERR_PATH_LENGTH, "Path exceeds PATH_MAX characters")
|
||||
ERR(-100301, ERR_PATH_EMPTY, "Path is an empty string")
|
||||
ERR(-100302, ERR_PATH_NOT_RELATIVE, "Path is not relative")
|
||||
ERR(-100303, ERR_PATH_NON_PORTABLE, "Path contains OS-specific dir separator")
|
||||
|
299
source/lib/path_util.cpp
Normal file
299
source/lib/path_util.cpp
Normal file
@ -0,0 +1,299 @@
|
||||
/**
|
||||
* =========================================================================
|
||||
* File : path_util.cpp
|
||||
* Project : 0 A.D.
|
||||
* Description : helper functions for path strings.
|
||||
*
|
||||
* @author Jan.Wassenberg@stud.uni-karlsruhe.de
|
||||
* =========================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2004-2006 Jan Wassenberg
|
||||
*
|
||||
* Redistribution and/or modification are also permitted under the
|
||||
* terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation (version 2 or later, at your option).
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
#include "precompiled.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "lib.h"
|
||||
#include "posix.h"
|
||||
#include "path_util.h"
|
||||
|
||||
|
||||
static inline bool is_dir_sep(char c)
|
||||
{
|
||||
if(c == '/' || c == DIR_SEP)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// is s2 a subpath of s1, or vice versa?
|
||||
bool path_is_subpath(const char* s1, const char* s2)
|
||||
{
|
||||
// make sure s1 is the shorter string
|
||||
if(strlen(s1) > strlen(s2))
|
||||
std::swap(s1, s2);
|
||||
|
||||
int c1 = 0, last_c1, c2;
|
||||
for(;;)
|
||||
{
|
||||
last_c1 = c1;
|
||||
c1 = *s1++, c2 = *s2++;
|
||||
|
||||
// end of s1 reached:
|
||||
if(c1 == '\0')
|
||||
{
|
||||
// s1 matched s2 up until:
|
||||
if((c2 == '\0') || // its end (i.e. they're equal length) OR
|
||||
is_dir_sep(c2) || // start of next component OR
|
||||
is_dir_sep(last_c1)) // ", but both have a trailing slash
|
||||
// => is subpath
|
||||
return true;
|
||||
}
|
||||
|
||||
// mismatch => is not subpath
|
||||
if(c1 != c2)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// if path is invalid, return a descriptive error code, otherwise ERR_OK.
|
||||
LibError path_validate(const char* path)
|
||||
{
|
||||
// disallow "/", because it would create a second 'root' (with name = "").
|
||||
// root dir is "".
|
||||
if(path[0] == '/')
|
||||
return ERR_PATH_NOT_RELATIVE;
|
||||
|
||||
// scan each char in path string; count length.
|
||||
int c = 0; // current char; used for .. detection
|
||||
size_t path_len = 0;
|
||||
for(;;)
|
||||
{
|
||||
const int last_c = c;
|
||||
c = path[path_len++];
|
||||
|
||||
// whole path is too long
|
||||
if(path_len >= PATH_MAX)
|
||||
return ERR_PATH_LENGTH;
|
||||
|
||||
// disallow:
|
||||
// - ".." (prevent going above the VFS root dir)
|
||||
// - "./" (security hole when mounting and not supported on Windows).
|
||||
// allow "/.", because CVS backup files include it.
|
||||
if(last_c == '.' && (c == '.' || c == '/'))
|
||||
return ERR_PATH_NON_CANONICAL;
|
||||
|
||||
// disallow OS-specific dir separators
|
||||
if(c == '\\' || c == ':')
|
||||
return ERR_PATH_NON_PORTABLE;
|
||||
|
||||
// end of string, no errors encountered
|
||||
if(c == '\0')
|
||||
break;
|
||||
}
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
// if name is invalid, return a descriptive error code, otherwise ERR_OK.
|
||||
// (name is a path component, i.e. that between directory separators)
|
||||
LibError path_component_validate(const char* name)
|
||||
{
|
||||
// disallow empty strings
|
||||
if(*name == '\0')
|
||||
return ERR_PATH_EMPTY;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
const int c = *name++;
|
||||
|
||||
// disallow *any* dir separators (regardless of which
|
||||
// platform we're on).
|
||||
if(c == '\\' || c == ':' || c == '/')
|
||||
return ERR_PATH_COMPONENT_SEPARATOR;
|
||||
|
||||
// end of string, no errors encountered
|
||||
if(c == '\0')
|
||||
break;
|
||||
}
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
// copy path strings (provided for convenience).
|
||||
void path_copy(char* dst, const char* src)
|
||||
{
|
||||
strcpy_s(dst, PATH_MAX, src);
|
||||
}
|
||||
|
||||
|
||||
// combine <path1> and <path2> into one path, and write to <dst>.
|
||||
// if necessary, a directory separator is added between the paths.
|
||||
// each may be empty, filenames, or full paths.
|
||||
// total path length (including '\0') must not exceed PATH_MAX.
|
||||
LibError path_append(char* dst, const char* path1, const char* path2)
|
||||
{
|
||||
const size_t len1 = strlen(path1);
|
||||
const size_t len2 = strlen(path2);
|
||||
size_t total_len = len1 + len2 + 1; // includes '\0'
|
||||
|
||||
// check if we need to add '/' between path1 and path2
|
||||
// note: the second can't start with '/' (not allowed by path_validate)
|
||||
bool need_separator = false;
|
||||
if(len1 != 0 && !is_dir_sep(path1[len1-1]))
|
||||
{
|
||||
total_len++; // for '/'
|
||||
need_separator = true;
|
||||
}
|
||||
|
||||
if(total_len+1 > PATH_MAX)
|
||||
WARN_RETURN(ERR_PATH_LENGTH);
|
||||
|
||||
strcpy(dst, path1); // safe
|
||||
dst += len1;
|
||||
if(need_separator)
|
||||
*dst++ = '/';
|
||||
strcpy(dst, path2); // safe
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
// strip <remove> from the start of <src>, prepend <replace>,
|
||||
// and write to <dst>.
|
||||
// returns ERR_FAIL if the beginning of <src> doesn't match <remove>.
|
||||
LibError path_replace(char* dst, const char* src, const char* remove, const char* replace)
|
||||
{
|
||||
// remove doesn't match start of <src>
|
||||
const size_t remove_len = strlen(remove);
|
||||
if(strncmp(src, remove, remove_len) != 0)
|
||||
WARN_RETURN(ERR_FAIL);
|
||||
|
||||
// get rid of trailing / in src (must not be included in remove)
|
||||
const char* start = src+remove_len;
|
||||
if(is_dir_sep(*start))
|
||||
start++;
|
||||
|
||||
// prepend replace.
|
||||
RETURN_ERR(path_append(dst, replace, start));
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// split paths into specific parts
|
||||
|
||||
// return pointer to the name component within path (i.e. skips over all
|
||||
// characters up to the last dir separator, if any).
|
||||
const char* path_name_only(const char* path)
|
||||
{
|
||||
// first try: look for portable '/'
|
||||
const char* slash = strrchr(path, '/');
|
||||
// not present
|
||||
if(!slash)
|
||||
{
|
||||
// now look for platform-specific DIR_SEP
|
||||
slash = strrchr(path, DIR_SEP);
|
||||
// neither present, it's a filename only
|
||||
if(!slash)
|
||||
return path;
|
||||
}
|
||||
|
||||
const char* name = slash+1;
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
// if <path> contains a name component, it is stripped away.
|
||||
void path_strip_fn(char* path)
|
||||
{
|
||||
char* name = (char*)path_name_only(path);
|
||||
*name = '\0'; // cut off string here
|
||||
}
|
||||
|
||||
|
||||
// fill <dir> with the directory path portion of <path>
|
||||
// ("" if root dir, otherwise ending with '/').
|
||||
// note: copies to <dir> and proceeds to path_strip_fn it.
|
||||
void path_dir_only(const char* path, char* dir)
|
||||
{
|
||||
path_copy(dir, path);
|
||||
path_strip_fn(dir);
|
||||
}
|
||||
|
||||
|
||||
// return extension of <fn>, or "" if there is none.
|
||||
// NOTE: does not include the period; e.g. "a.bmp" yields "bmp".
|
||||
const char* path_extension(const char* fn)
|
||||
{
|
||||
const char* dot = strrchr(fn, '.');
|
||||
if(!dot)
|
||||
return "";
|
||||
const char* ext = dot+1;
|
||||
return ext;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// convenience "class" that simplifies successively appending a filename to
|
||||
// its parent directory. this avoids needing to allocate memory and calling
|
||||
// strlen/strcat. used by wdll_ver and dir_next_ent.
|
||||
// we want to maintain C compatibility, so this isn't a C++ class.
|
||||
|
||||
// write the given directory path into our buffer and set end/chars_left
|
||||
// accordingly. <dir> need not but can end with a directory separator.
|
||||
//
|
||||
// note: <dir> and the filename set via path_package_append_file are separated by
|
||||
// '/'. this is to allow use on portable paths; the function otherwise
|
||||
// does not care if paths are relative/portable/absolute.
|
||||
LibError path_package_set_dir(PathPackage* pp, const char* dir)
|
||||
{
|
||||
// -1 allows for trailing DIR_SEP that will be added if not
|
||||
// already present.
|
||||
if(strcpy_s(pp->path, ARRAY_SIZE(pp->path)-1, dir) != 0)
|
||||
WARN_RETURN(ERR_PATH_LENGTH);
|
||||
size_t len = strlen(pp->path);
|
||||
// add directory separator if not already present
|
||||
// .. but only check this if dir != "" (=> len-1 is safe)
|
||||
if(len != 0)
|
||||
{
|
||||
char* last_char = pp->path+len-1;
|
||||
if(!is_dir_sep(*last_char))
|
||||
{
|
||||
*(last_char+1) = '/';
|
||||
// note: need to 0-terminate because pp.path is uninitialized
|
||||
// and we overwrite strcpy_s's terminator above.
|
||||
*(last_char+2) = '\0';
|
||||
// only bump by 1 - filename must overwrite '\0'.
|
||||
len++;
|
||||
}
|
||||
}
|
||||
|
||||
pp->end = pp->path+len;
|
||||
pp->chars_left = ARRAY_SIZE(pp->path)-len;
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
// append the given filename to the directory established by the last
|
||||
// path_package_set_dir on this package. the whole path is accessible at pp->path.
|
||||
LibError path_package_append_file(PathPackage* pp, const char* path)
|
||||
{
|
||||
CHECK_ERR(strcpy_s(pp->end, pp->chars_left, path));
|
||||
return ERR_OK;
|
||||
}
|
105
source/lib/path_util.h
Normal file
105
source/lib/path_util.h
Normal file
@ -0,0 +1,105 @@
|
||||
/**
|
||||
* =========================================================================
|
||||
* File : path_util.h
|
||||
* Project : 0 A.D.
|
||||
* Description : helper functions for path strings.
|
||||
*
|
||||
* @author Jan.Wassenberg@stud.uni-karlsruhe.de
|
||||
* =========================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2004-2006 Jan Wassenberg
|
||||
*
|
||||
* Redistribution and/or modification are also permitted under the
|
||||
* terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation (version 2 or later, at your option).
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
// notes:
|
||||
// - this module is split out of lib/res/file so that it can be used from
|
||||
// other code without pulling in the entire file manager.
|
||||
// - there is no restriction on buffer lengths except the underlying OS.
|
||||
// input buffers must not exceed PATH_MAX chars, while outputs
|
||||
// must hold at least that much.
|
||||
// - unless otherwise mentioned, all functions are intended to work with
|
||||
// native and portable and VFS paths.
|
||||
// when reading, both '/' and DIR_SEP are accepted; '/' is written.
|
||||
|
||||
#ifndef PATH_UTIL_H__
|
||||
#define PATH_UTIL_H__
|
||||
|
||||
#include "posix.h" // PATH_MAX
|
||||
|
||||
// is s2 a subpath of s1, or vice versa?
|
||||
extern bool path_is_subpath(const char* s1, const char* s2);
|
||||
|
||||
// if path is invalid, return a descriptive error code, otherwise ERR_OK.
|
||||
extern LibError path_validate(const char* path);
|
||||
|
||||
// if name is invalid, return a descriptive error code, otherwise ERR_OK.
|
||||
// (name is a path component, i.e. that between directory separators)
|
||||
extern LibError path_component_validate(const char* name);
|
||||
|
||||
// copy path strings (provided for convenience).
|
||||
extern void path_copy(char* dst, const char* src);
|
||||
|
||||
// combine <path1> and <path2> into one path, and write to <dst>.
|
||||
// if necessary, a directory separator is added between the paths.
|
||||
// each may be empty, filenames, or full paths.
|
||||
// total path length (including '\0') must not exceed PATH_MAX.
|
||||
extern LibError path_append(char* dst, const char* path1, const char* path2);
|
||||
|
||||
// strip <remove> from the start of <src>, prepend <replace>,
|
||||
// and write to <dst>.
|
||||
// returns ERR_FAIL if the beginning of <src> doesn't match <remove>.
|
||||
extern LibError path_replace(char* dst, const char* src, const char* remove, const char* replace);
|
||||
|
||||
// return pointer to the name component within path (i.e. skips over all
|
||||
// characters up to the last dir separator, if any).
|
||||
extern const char* path_name_only(const char* path);
|
||||
|
||||
// if <path> contains a name component, it is stripped away.
|
||||
extern void path_strip_fn(char* path);
|
||||
|
||||
// fill <dir> with the directory path portion of <path>
|
||||
// ("" if root dir, otherwise ending with '/').
|
||||
// note: copies to <dir> and proceeds to path_strip_fn it.
|
||||
extern void path_dir_only(const char* path, char* dir);
|
||||
|
||||
// return extension of <fn>, or "" if there is none.
|
||||
// NOTE: does not include the period; e.g. "a.bmp" yields "bmp".
|
||||
extern const char* path_extension(const char* fn);
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// convenience "class" that simplifies successively appending a filename to
|
||||
// its parent directory. this avoids needing to allocate memory and calling
|
||||
// strlen/strcat. used by wdll_ver and dir_next_ent.
|
||||
// we want to maintain C compatibility, so this isn't a C++ class.
|
||||
|
||||
struct PathPackage
|
||||
{
|
||||
char* end;
|
||||
size_t chars_left;
|
||||
char path[PATH_MAX];
|
||||
};
|
||||
|
||||
// write the given directory path into our buffer and set end/chars_left
|
||||
// accordingly. <dir> need not but can end with a directory separator.
|
||||
//
|
||||
// note: <dir> and the filename set via path_package_append_file are separated by
|
||||
// '/'. this is to allow use on portable paths; the function otherwise
|
||||
// does not care if paths are relative/portable/absolute.
|
||||
extern LibError path_package_set_dir(PathPackage* pp, const char* dir);
|
||||
|
||||
// append the given filename to the directory established by the last
|
||||
// path_package_set_dir on this package. the whole path is accessible at pp->path.
|
||||
extern LibError path_package_append_file(PathPackage* pp, const char* path);
|
||||
|
||||
#endif // #ifndef PATH_UTIL_H__
|
@ -712,10 +712,7 @@ LibError afile_unmap(File* f)
|
||||
|
||||
static inline bool file_type_is_uncompressible(const char* fn)
|
||||
{
|
||||
const char* ext = strrchr(fn, '.');
|
||||
// no extension? bail; assume compressible
|
||||
if(!ext)
|
||||
return true;
|
||||
const char* ext = path_extension(fn);
|
||||
|
||||
// this is a selection of file types that are certainly not
|
||||
// further compressible. we need not include every type under the sun -
|
||||
|
@ -54,239 +54,6 @@
|
||||
|
||||
|
||||
|
||||
// convenience "class" that simplifies successively appending a filename to
|
||||
// its parent directory. this avoids needing to allocate memory and calling
|
||||
// strlen/strcat. used by wdll_ver and dir_next_ent.
|
||||
// we want to maintain C compatibility, so this isn't a C++ class.
|
||||
|
||||
// write the given directory path into our buffer and set end/chars_left
|
||||
// accordingly. <dir> need not but can end with a directory separator.
|
||||
//
|
||||
// note: <dir> and the filename set via pp_append_file are separated by
|
||||
// '/'. this is to allow use on portable paths; the function otherwise
|
||||
// does not care if paths are relative/portable/absolute.
|
||||
LibError pp_set_dir(PathPackage* pp, const char* dir)
|
||||
{
|
||||
// -1 allows for trailing DIR_SEP that will be added if not
|
||||
// already present.
|
||||
if(strcpy_s(pp->path, ARRAY_SIZE(pp->path)-1, dir) != 0)
|
||||
WARN_RETURN(ERR_PATH_LENGTH);
|
||||
size_t len = strlen(pp->path);
|
||||
// add directory separator if not already present
|
||||
// .. but only check this if dir != "" (=> len-1 is safe)
|
||||
if(len != 0)
|
||||
{
|
||||
char* last_char = pp->path+len-1;
|
||||
// note: must handle both portable and native separators -
|
||||
// <dir> may be either.
|
||||
if(*last_char != '/' && *last_char != DIR_SEP)
|
||||
{
|
||||
*(last_char+1) = '/';
|
||||
// note: need to 0-terminate because pp.path is uninitialized
|
||||
// and we overwrite strcpy_s's terminator above.
|
||||
*(last_char+2) = '\0';
|
||||
// only bump by 1 - filename must overwrite '\0'.
|
||||
len++;
|
||||
}
|
||||
}
|
||||
|
||||
pp->end = pp->path+len;
|
||||
pp->chars_left = ARRAY_SIZE(pp->path)-len;
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
// append the given filename to the directory established by the last
|
||||
// pp_set_dir on this package. the whole path is accessible at pp->path.
|
||||
LibError pp_append_file(PathPackage* pp, const char* fn)
|
||||
{
|
||||
CHECK_ERR(strcpy_s(pp->end, pp->chars_left, fn));
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
// is s2 a subpath of s1, or vice versa? used by VFS and wdir_watch.
|
||||
// works for portable and native paths.
|
||||
bool file_is_subpath(const char* s1, const char* s2)
|
||||
{
|
||||
// make sure s1 is the shorter string
|
||||
if(strlen(s1) > strlen(s2))
|
||||
std::swap(s1, s2);
|
||||
|
||||
int c1 = 0, last_c1, c2;
|
||||
for(;;)
|
||||
{
|
||||
last_c1 = c1;
|
||||
c1 = *s1++, c2 = *s2++;
|
||||
|
||||
// end of s1 reached:
|
||||
if(c1 == '\0')
|
||||
{
|
||||
// s1 matched s2 up until:
|
||||
if((c2 == '\0') || // its end (i.e. they're equal length)
|
||||
(c2 == '/' || c2 == DIR_SEP) || // start of next component
|
||||
(last_c1 == '/' || last_c1 == DIR_SEP)) // ", but both have a trailing slash
|
||||
// => is subpath
|
||||
return true;
|
||||
}
|
||||
|
||||
// mismatch => is not subpath
|
||||
if(c1 != c2)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
enum Conversion
|
||||
{
|
||||
TO_NATIVE,
|
||||
TO_PORTABLE
|
||||
};
|
||||
|
||||
static LibError convert_path(char* dst, const char* src, Conversion conv = TO_NATIVE)
|
||||
{
|
||||
// DIR_SEP is assumed to be a single character!
|
||||
|
||||
const char* s = src;
|
||||
char* d = dst;
|
||||
|
||||
char from = DIR_SEP, to = '/';
|
||||
if(conv == TO_NATIVE)
|
||||
from = '/', to = DIR_SEP;
|
||||
|
||||
size_t len = 0;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
len++;
|
||||
if(len >= PATH_MAX)
|
||||
WARN_RETURN(ERR_PATH_LENGTH);
|
||||
|
||||
char c = *s++;
|
||||
|
||||
if(c == from)
|
||||
c = to;
|
||||
|
||||
*d++ = c;
|
||||
|
||||
// end of string - done
|
||||
if(c == '\0')
|
||||
return ERR_OK;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// set by file_set_root_dir
|
||||
static char n_root_dir[PATH_MAX];
|
||||
static size_t n_root_dir_len;
|
||||
|
||||
|
||||
// return the native equivalent of the given relative portable path
|
||||
// (i.e. convert all '/' to the platform's directory separator)
|
||||
// makes sure length < PATH_MAX.
|
||||
LibError file_make_native_path(const char* path, char* n_path)
|
||||
{
|
||||
return convert_path(n_path, path, TO_NATIVE);
|
||||
}
|
||||
|
||||
// return the portable equivalent of the given relative native path
|
||||
// (i.e. convert the platform's directory separators to '/')
|
||||
// makes sure length < PATH_MAX.
|
||||
LibError file_make_portable_path(const char* n_path, char* path)
|
||||
{
|
||||
return convert_path(path, n_path, TO_PORTABLE);
|
||||
}
|
||||
|
||||
|
||||
// return the native equivalent of the given portable path
|
||||
// (i.e. convert all '/' to the platform's directory separator).
|
||||
// also prepends current directory => n_full_path is absolute.
|
||||
// makes sure length < PATH_MAX.
|
||||
LibError file_make_full_native_path(const char* path, char* n_full_path)
|
||||
{
|
||||
debug_assert(path != n_full_path); // doesn't work in-place
|
||||
|
||||
strcpy_s(n_full_path, PATH_MAX, n_root_dir);
|
||||
return convert_path(n_full_path+n_root_dir_len, path, TO_NATIVE);
|
||||
}
|
||||
|
||||
// return the portable equivalent of the given relative native path
|
||||
// (i.e. convert the platform's directory separators to '/')
|
||||
// n_full_path is absolute; if it doesn't match the current dir, fail.
|
||||
// (note: portable paths are always relative to the file root dir).
|
||||
// makes sure length < PATH_MAX.
|
||||
LibError file_make_full_portable_path(const char* n_full_path, char* path)
|
||||
{
|
||||
debug_assert(path != n_full_path); // doesn't work in-place
|
||||
|
||||
if(strncmp(n_full_path, n_root_dir, n_root_dir_len) != 0)
|
||||
WARN_RETURN(ERR_PATH_NOT_FOUND);
|
||||
return convert_path(path, n_full_path+n_root_dir_len, TO_PORTABLE);
|
||||
}
|
||||
|
||||
|
||||
// establish the root directory from <rel_path>, which is treated as
|
||||
// relative to the executable's directory (determined via argv[0]).
|
||||
// all relative file paths passed to this module will be based from
|
||||
// this root dir.
|
||||
//
|
||||
// example: executable in "$install_dir/system"; desired root dir is
|
||||
// "$install_dir/data" => rel_path = "../data".
|
||||
//
|
||||
// argv[0] is necessary because the current directory is unknown at startup
|
||||
// (e.g. it isn't set when invoked via batch file), and this is the
|
||||
// easiest portable way to find our install directory.
|
||||
//
|
||||
// can only be called once, by design (see below). rel_path is trusted.
|
||||
LibError file_set_root_dir(const char* argv0, const char* rel_path)
|
||||
{
|
||||
// security check: only allow attempting to chdir once, so that malicious
|
||||
// code cannot circumvent the VFS checks that disallow access to anything
|
||||
// above the current directory (set here).
|
||||
// this routine is called early at startup, so any subsequent attempts
|
||||
// are likely bogus.
|
||||
static bool already_attempted;
|
||||
if(already_attempted)
|
||||
WARN_RETURN(ERR_ROOT_DIR_ALREADY_SET);
|
||||
already_attempted = true;
|
||||
|
||||
// get full path to executable
|
||||
char n_path[PATH_MAX];
|
||||
// .. first try safe, but system-dependent version
|
||||
if(sys_get_executable_name(n_path, PATH_MAX) < 0)
|
||||
{
|
||||
// .. failed; use argv[0]
|
||||
if(!realpath(argv0, n_path))
|
||||
return LibError_from_errno();
|
||||
}
|
||||
|
||||
// make sure it's valid
|
||||
if(access(n_path, X_OK) < 0)
|
||||
return LibError_from_errno();
|
||||
|
||||
// strip executable name, append rel_path, convert to native
|
||||
char* slash = strrchr(n_path, DIR_SEP);
|
||||
// .. safely handle n_path not containing DIR_SEP (not expected)
|
||||
if(!slash) slash = n_path-1;
|
||||
RETURN_ERR(file_make_native_path(rel_path, slash+1));
|
||||
|
||||
// get actual root dir - previous n_path may include ".."
|
||||
// (slight optimization, speeds up path lookup)
|
||||
if(!realpath(n_path, n_root_dir))
|
||||
return LibError_from_errno();
|
||||
// .. append DIR_SEP to simplify code that uses n_root_dir
|
||||
// (note: already 0-terminated, since it's static)
|
||||
n_root_dir_len = strlen(n_root_dir)+1; // +1 for trailing DIR_SEP
|
||||
n_root_dir[n_root_dir_len-1] = DIR_SEP;
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// layer on top of POSIX opendir/readdir/closedir that handles
|
||||
// portable -> native path conversion, ignores non-file/directory entries,
|
||||
// and additionally returns the file status (size and mtime).
|
||||
@ -332,7 +99,7 @@ LibError dir_open(const char* P_path, DirIterator* d_)
|
||||
if(!d->os_dir)
|
||||
return LibError_from_errno();
|
||||
|
||||
RETURN_ERR(pp_set_dir(&d->pp, n_path));
|
||||
RETURN_ERR(path_package_set_dir(&d->pp, n_path));
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
@ -357,7 +124,7 @@ get_another_entry:
|
||||
|
||||
// copy os_ent.name[]; we need it for stat() #if !OS_WIN and
|
||||
// return it as ent.name (since os_ent.name[] is volatile).
|
||||
pp_append_file(&d->pp, os_ent->d_name);
|
||||
path_package_append_file(&d->pp, os_ent->d_name);
|
||||
const char* name = d->pp.end;
|
||||
|
||||
// get file information (mode, size, mtime)
|
||||
@ -368,7 +135,7 @@ get_another_entry:
|
||||
CHECK_ERR(readdir_stat_np(d->os_dir, &s));
|
||||
#else
|
||||
// .. call regular stat().
|
||||
// we need the full pathname for this. don't use vfs_path_append because
|
||||
// we need the full pathname for this. don't use path_append because
|
||||
// it would unnecessarily call strlen.
|
||||
|
||||
CHECK_ERR(stat(d->pp.path, &s));
|
||||
@ -404,90 +171,8 @@ LibError dir_close(DirIterator* d_)
|
||||
}
|
||||
|
||||
|
||||
static bool dirent_less(const DirEnt& d1, const DirEnt& d2)
|
||||
{
|
||||
return strcmp(d1.name, d2.name) < 0;
|
||||
}
|
||||
|
||||
|
||||
// enumerate all directory entries in <P_path>; add to container and
|
||||
// then sort it by filename.
|
||||
LibError file_get_sorted_dirents(const char* P_path, DirEnts& dirents)
|
||||
{
|
||||
DirIterator d;
|
||||
RETURN_ERR(dir_open(P_path, &d));
|
||||
|
||||
dirents.reserve(50); // preallocate for efficiency
|
||||
|
||||
DirEnt ent;
|
||||
for(;;)
|
||||
{
|
||||
LibError ret = dir_next_ent(&d, &ent);
|
||||
if(ret == ERR_DIR_END)
|
||||
break;
|
||||
RETURN_ERR(ret);
|
||||
|
||||
ent.name = file_make_unique_fn_copy(ent.name);
|
||||
dirents.push_back(ent);
|
||||
}
|
||||
|
||||
std::sort(dirents.begin(), dirents.end(), dirent_less);
|
||||
|
||||
(void)dir_close(&d);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
// call <cb> for each file and subdirectory in <dir> (alphabetical order),
|
||||
// passing the entry name (not full path!), stat info, and <user>.
|
||||
//
|
||||
// first builds a list of entries (sorted) and remembers if an error occurred.
|
||||
// if <cb> returns non-zero, abort immediately and return that; otherwise,
|
||||
// return first error encountered while listing files, or 0 on success.
|
||||
//
|
||||
// rationale:
|
||||
// this makes file_enum and zip_enum slightly incompatible, since zip_enum
|
||||
// returns the full path. that's necessary because VFS zip_cb
|
||||
// has no other way of determining what VFS dir a Zip file is in,
|
||||
// since zip_enum enumerates all files in the archive (not only those
|
||||
// in a given dir). no big deal though, since add_ent has to
|
||||
// special-case Zip files anyway.
|
||||
// the advantage here is simplicity, and sparing callbacks the trouble
|
||||
// of converting from/to native path (we just give 'em the dirent name).
|
||||
LibError file_enum(const char* P_path, const FileCB cb, const uintptr_t user)
|
||||
{
|
||||
LibError stat_err = ERR_OK; // first error encountered by stat()
|
||||
LibError cb_err = ERR_OK; // first error returned by cb
|
||||
|
||||
DirEnts dirents;
|
||||
RETURN_ERR(file_get_sorted_dirents(P_path, dirents));
|
||||
|
||||
// call back for each entry (now sorted);
|
||||
// first, expand each DirEnt to full struct stat (we store as such to
|
||||
// reduce memory use and therefore speed up sorting)
|
||||
struct stat s;
|
||||
memset(&s, 0, sizeof(s));
|
||||
// .. not needed for plain files (OS opens them; memento doesn't help)
|
||||
const uintptr_t memento = 0;
|
||||
for(DirEntCIt it = dirents.begin(); it != dirents.end(); ++it)
|
||||
{
|
||||
const DirEnt& dirent = *it;
|
||||
s.st_mode = (dirent.size == -1)? S_IFDIR : S_IFREG;
|
||||
s.st_size = dirent.size;
|
||||
s.st_mtime = dirent.mtime;
|
||||
LibError ret = cb(dirent.name, &s, memento, user);
|
||||
if(ret != INFO_CB_CONTINUE)
|
||||
{
|
||||
cb_err = ret; // first error (since we now abort)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(cb_err != ERR_OK)
|
||||
return cb_err;
|
||||
return stat_err;
|
||||
}
|
||||
|
||||
|
||||
// get file information. output param is zeroed on error.
|
||||
static LibError file_stat_impl(const char* fn, struct stat* s, bool warn_if_failed = true)
|
||||
@ -556,6 +241,8 @@ LibError file_delete(const char* fn)
|
||||
// and the Handle approach doesn't guard against some idiot calling
|
||||
// close(our_fd_value) directly, either.
|
||||
|
||||
cassert(sizeof(PosixFile) < FILE_OPAQUE_SIZE);
|
||||
|
||||
|
||||
LibError file_validate(const File* f)
|
||||
{
|
||||
@ -573,87 +260,6 @@ LibError file_validate(const File* f)
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
// rationale: we want a constant-time IsAtomFn(string pointer) lookup:
|
||||
// this avoids any overhead of calling file_make_unique_fn_copy on
|
||||
// already-atomized strings. that requires allocating from one contiguous
|
||||
// arena, which is also more memory-efficient than the heap (no headers).
|
||||
static Pool atom_pool;
|
||||
|
||||
// allocate a copy of P_fn in our string pool. strings are equal iff
|
||||
// their addresses are equal, thus allowing fast comparison.
|
||||
//
|
||||
// if the (generous) filename storage is full, 0 is returned.
|
||||
// this is not ever expected to happen; callers need not check the
|
||||
// return value because a warning is raised anyway.
|
||||
const char* file_make_unique_fn_copy(const char* P_fn)
|
||||
{
|
||||
// early out: if already an atom, return immediately.
|
||||
if(pool_contains(&atom_pool, (void*)P_fn))
|
||||
return P_fn;
|
||||
|
||||
const size_t fn_len = strlen(P_fn);
|
||||
const char* unique_fn;
|
||||
|
||||
// check if already allocated; return existing copy if so.
|
||||
//
|
||||
// rationale: the entire storage could be done via container,
|
||||
// rather than simply using it as a lookup mapping.
|
||||
// however, DynHashTbl together with Pool (see above) is more efficient.
|
||||
typedef DynHashTbl<const char*, const char*> AtomMap;
|
||||
static AtomMap atom_map;
|
||||
unique_fn = atom_map.find(P_fn);
|
||||
if(unique_fn)
|
||||
return unique_fn;
|
||||
|
||||
unique_fn = (const char*)pool_alloc(&atom_pool, fn_len+1);
|
||||
if(!unique_fn)
|
||||
{
|
||||
DEBUG_WARN_ERR(ERR_NO_MEM);
|
||||
return 0;
|
||||
}
|
||||
memcpy2((void*)unique_fn, P_fn, fn_len);
|
||||
((char*)unique_fn)[fn_len] = '\0';
|
||||
|
||||
atom_map.insert(unique_fn, unique_fn);
|
||||
|
||||
stats_unique_name(fn_len);
|
||||
return unique_fn;
|
||||
}
|
||||
|
||||
|
||||
const char* file_get_random_name()
|
||||
{
|
||||
// there had better be names in atom_pool, else this will fail.
|
||||
debug_assert(atom_pool.da.pos != 0);
|
||||
|
||||
again:
|
||||
const size_t start_ofs = (size_t)rand(0, (uint)atom_pool.da.pos-1);
|
||||
|
||||
// scan ahead to next string boundary
|
||||
const char* start = (const char*)atom_pool.da.base+start_ofs;
|
||||
const char* next_0 = strchr(start, '\0')+1;
|
||||
// .. at end of storage: restart
|
||||
if((u8*)next_0 >= atom_pool.da.base+atom_pool.da.pos)
|
||||
goto again;
|
||||
// .. skip all '\0' (may be several due to pool alignment)
|
||||
const char* next_name = next_0;
|
||||
while(*next_name == '\0') next_name++;
|
||||
|
||||
return next_name;
|
||||
}
|
||||
|
||||
|
||||
static inline void atom_init()
|
||||
{
|
||||
pool_create(&atom_pool, 8*MiB, POOL_VARIABLE_ALLOCS);
|
||||
}
|
||||
|
||||
static inline void atom_shutdown()
|
||||
{
|
||||
(void)pool_destroy(&atom_pool);
|
||||
}
|
||||
|
||||
|
||||
|
||||
LibError file_open(const char* P_fn, uint flags, File* f)
|
||||
{
|
||||
@ -853,7 +459,7 @@ LibError file_unmap(File* f)
|
||||
|
||||
LibError file_init()
|
||||
{
|
||||
atom_init();
|
||||
path_init();
|
||||
file_cache_init();
|
||||
file_io_init();
|
||||
|
||||
@ -866,7 +472,7 @@ LibError file_init()
|
||||
LibError file_shutdown()
|
||||
{
|
||||
stats_dump();
|
||||
atom_shutdown();
|
||||
path_shutdown();
|
||||
file_io_shutdown();
|
||||
return ERR_OK;
|
||||
}
|
||||
|
@ -28,35 +28,6 @@
|
||||
|
||||
extern LibError file_init();
|
||||
|
||||
// convenience "class" that simplifies successively appending a filename to
|
||||
// its parent directory. this avoids needing to allocate memory and calling
|
||||
// strlen/strcat. used by wdll_ver and dir_next_ent.
|
||||
// we want to maintain C compatibility, so this isn't a C++ class.
|
||||
|
||||
struct PathPackage
|
||||
{
|
||||
char* end;
|
||||
size_t chars_left;
|
||||
char path[PATH_MAX];
|
||||
};
|
||||
|
||||
// write the given directory path into our buffer and set end/chars_left
|
||||
// accordingly. <dir> need not but can end with a directory separator.
|
||||
//
|
||||
// note: <dir> and the filename set via pp_append_file are separated by
|
||||
// '/'. this is to allow use on portable paths; the function otherwise
|
||||
// does not care if paths are relative/portable/absolute.
|
||||
extern LibError pp_set_dir(PathPackage* pp, const char* dir);
|
||||
|
||||
// append the given filename to the directory established by the last
|
||||
// pp_set_dir on this package. the whole path is accessible at pp->path.
|
||||
extern LibError pp_append_file(PathPackage* pp, const char* file);
|
||||
|
||||
|
||||
// is s2 a subpath of s1, or vice versa? used by VFS and wdir_watch.
|
||||
// works for portable and native paths.
|
||||
extern bool file_is_subpath(const char* s1, const char* s2);
|
||||
|
||||
|
||||
//
|
||||
// path conversion functions (native <--> portable),
|
||||
|
@ -20,6 +20,9 @@
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
#include "lib/path_util.h"
|
||||
#include "path.h"
|
||||
|
||||
#include "file.h"
|
||||
#include "file_cache.h"
|
||||
#include "file_io.h"
|
||||
@ -31,7 +34,6 @@
|
||||
#include "archive.h"
|
||||
|
||||
#include "vfs.h"
|
||||
#include "vfs_path.h"
|
||||
#include "vfs_mount.h"
|
||||
#include "vfs_tree.h"
|
||||
#include "vfs_redirector.h"
|
||||
@ -69,4 +71,3 @@ struct PosixFile
|
||||
void* mapping;
|
||||
uint map_refs;
|
||||
};
|
||||
cassert(sizeof(PosixFile) < FILE_OPAQUE_SIZE);
|
||||
|
274
source/lib/res/file/path.cpp
Normal file
274
source/lib/res/file/path.cpp
Normal file
@ -0,0 +1,274 @@
|
||||
/**
|
||||
* =========================================================================
|
||||
* File : path.cpp
|
||||
* Project : 0 A.D.
|
||||
* Description : helper functions for VFS paths.
|
||||
*
|
||||
* @author Jan.Wassenberg@stud.uni-karlsruhe.de
|
||||
* =========================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2004-2006 Jan Wassenberg
|
||||
*
|
||||
* Redistribution and/or modification are also permitted under the
|
||||
* terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation (version 2 or later, at your option).
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
#include "precompiled.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "lib.h"
|
||||
#include "adts.h"
|
||||
#include "file_internal.h"
|
||||
#include "allocators.h"
|
||||
|
||||
|
||||
// path types:
|
||||
// p_*: posix (e.g. mount object name or for open())
|
||||
// v_*: vfs (e.g. mount point)
|
||||
// fn : filename only (e.g. from readdir)
|
||||
// dir_name: directory only, no path (e.g. subdir name)
|
||||
//
|
||||
// all paths must be relative (no leading '/'); components are separated
|
||||
// by '/'; no ':', '\\', "." or ".." allowed; root dir is "".
|
||||
//
|
||||
// grammar:
|
||||
// path ::= dir*file?
|
||||
// dir ::= name/
|
||||
// file ::= name
|
||||
// name ::= [^/]
|
||||
|
||||
|
||||
enum Conversion
|
||||
{
|
||||
TO_NATIVE,
|
||||
TO_PORTABLE
|
||||
};
|
||||
|
||||
static LibError convert_path(char* dst, const char* src, Conversion conv = TO_NATIVE)
|
||||
{
|
||||
// DIR_SEP is assumed to be a single character!
|
||||
|
||||
const char* s = src;
|
||||
char* d = dst;
|
||||
|
||||
char from = DIR_SEP, to = '/';
|
||||
if(conv == TO_NATIVE)
|
||||
from = '/', to = DIR_SEP;
|
||||
|
||||
size_t len = 0;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
len++;
|
||||
if(len >= PATH_MAX)
|
||||
WARN_RETURN(ERR_PATH_LENGTH);
|
||||
|
||||
char c = *s++;
|
||||
|
||||
if(c == from)
|
||||
c = to;
|
||||
|
||||
*d++ = c;
|
||||
|
||||
// end of string - done
|
||||
if(c == '\0')
|
||||
return ERR_OK;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// set by file_set_root_dir
|
||||
static char n_root_dir[PATH_MAX];
|
||||
static size_t n_root_dir_len;
|
||||
|
||||
|
||||
// return the native equivalent of the given relative portable path
|
||||
// (i.e. convert all '/' to the platform's directory separator)
|
||||
// makes sure length < PATH_MAX.
|
||||
LibError file_make_native_path(const char* path, char* n_path)
|
||||
{
|
||||
return convert_path(n_path, path, TO_NATIVE);
|
||||
}
|
||||
|
||||
// return the portable equivalent of the given relative native path
|
||||
// (i.e. convert the platform's directory separators to '/')
|
||||
// makes sure length < PATH_MAX.
|
||||
LibError file_make_portable_path(const char* n_path, char* path)
|
||||
{
|
||||
return convert_path(path, n_path, TO_PORTABLE);
|
||||
}
|
||||
|
||||
|
||||
// return the native equivalent of the given portable path
|
||||
// (i.e. convert all '/' to the platform's directory separator).
|
||||
// also prepends current directory => n_full_path is absolute.
|
||||
// makes sure length < PATH_MAX.
|
||||
LibError file_make_full_native_path(const char* path, char* n_full_path)
|
||||
{
|
||||
debug_assert(path != n_full_path); // doesn't work in-place
|
||||
|
||||
strcpy_s(n_full_path, PATH_MAX, n_root_dir);
|
||||
return convert_path(n_full_path+n_root_dir_len, path, TO_NATIVE);
|
||||
}
|
||||
|
||||
// return the portable equivalent of the given relative native path
|
||||
// (i.e. convert the platform's directory separators to '/')
|
||||
// n_full_path is absolute; if it doesn't match the current dir, fail.
|
||||
// (note: portable paths are always relative to the file root dir).
|
||||
// makes sure length < PATH_MAX.
|
||||
LibError file_make_full_portable_path(const char* n_full_path, char* path)
|
||||
{
|
||||
debug_assert(path != n_full_path); // doesn't work in-place
|
||||
|
||||
if(strncmp(n_full_path, n_root_dir, n_root_dir_len) != 0)
|
||||
WARN_RETURN(ERR_PATH_NOT_FOUND);
|
||||
return convert_path(path, n_full_path+n_root_dir_len, TO_PORTABLE);
|
||||
}
|
||||
|
||||
|
||||
// establish the root directory from <rel_path>, which is treated as
|
||||
// relative to the executable's directory (determined via argv[0]).
|
||||
// all relative file paths passed to this module will be based from
|
||||
// this root dir.
|
||||
//
|
||||
// example: executable in "$install_dir/system"; desired root dir is
|
||||
// "$install_dir/data" => rel_path = "../data".
|
||||
//
|
||||
// argv[0] is necessary because the current directory is unknown at startup
|
||||
// (e.g. it isn't set when invoked via batch file), and this is the
|
||||
// easiest portable way to find our install directory.
|
||||
//
|
||||
// can only be called once, by design (see below). rel_path is trusted.
|
||||
LibError file_set_root_dir(const char* argv0, const char* rel_path)
|
||||
{
|
||||
// security check: only allow attempting to chdir once, so that malicious
|
||||
// code cannot circumvent the VFS checks that disallow access to anything
|
||||
// above the current directory (set here).
|
||||
// this routine is called early at startup, so any subsequent attempts
|
||||
// are likely bogus.
|
||||
static bool already_attempted;
|
||||
if(already_attempted)
|
||||
WARN_RETURN(ERR_ROOT_DIR_ALREADY_SET);
|
||||
already_attempted = true;
|
||||
|
||||
// get full path to executable
|
||||
char n_path[PATH_MAX];
|
||||
// .. first try safe, but system-dependent version
|
||||
if(sys_get_executable_name(n_path, PATH_MAX) < 0)
|
||||
{
|
||||
// .. failed; use argv[0]
|
||||
if(!realpath(argv0, n_path))
|
||||
return LibError_from_errno();
|
||||
}
|
||||
|
||||
// make sure it's valid
|
||||
if(access(n_path, X_OK) < 0)
|
||||
return LibError_from_errno();
|
||||
|
||||
// strip executable name, append rel_path, convert to native
|
||||
char* start_of_fn = (char*)path_name_only(n_path);
|
||||
RETURN_ERR(file_make_native_path(rel_path, start_of_fn));
|
||||
|
||||
// get actual root dir - previous n_path may include ".."
|
||||
// (slight optimization, speeds up path lookup)
|
||||
if(!realpath(n_path, n_root_dir))
|
||||
return LibError_from_errno();
|
||||
// .. append DIR_SEP to simplify code that uses n_root_dir
|
||||
// (note: already 0-terminated, since it's static)
|
||||
n_root_dir_len = strlen(n_root_dir)+1; // +1 for trailing DIR_SEP
|
||||
n_root_dir[n_root_dir_len-1] = DIR_SEP;
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// storage for path strings
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// rationale: we want a constant-time IsAtomFn(string pointer) lookup:
|
||||
// this avoids any overhead of calling file_make_unique_fn_copy on
|
||||
// already-atomized strings. that requires allocating from one contiguous
|
||||
// arena, which is also more memory-efficient than the heap (no headers).
|
||||
static Pool atom_pool;
|
||||
|
||||
// allocate a copy of P_fn in our string pool. strings are equal iff
|
||||
// their addresses are equal, thus allowing fast comparison.
|
||||
//
|
||||
// if the (generous) filename storage is full, 0 is returned.
|
||||
// this is not ever expected to happen; callers need not check the
|
||||
// return value because a warning is raised anyway.
|
||||
const char* file_make_unique_fn_copy(const char* P_fn)
|
||||
{
|
||||
// early out: if already an atom, return immediately.
|
||||
if(pool_contains(&atom_pool, (void*)P_fn))
|
||||
return P_fn;
|
||||
|
||||
const size_t fn_len = strlen(P_fn);
|
||||
const char* unique_fn;
|
||||
|
||||
// check if already allocated; return existing copy if so.
|
||||
//
|
||||
// rationale: the entire storage could be done via container,
|
||||
// rather than simply using it as a lookup mapping.
|
||||
// however, DynHashTbl together with Pool (see above) is more efficient.
|
||||
typedef DynHashTbl<const char*, const char*> AtomMap;
|
||||
static AtomMap atom_map;
|
||||
unique_fn = atom_map.find(P_fn);
|
||||
if(unique_fn)
|
||||
return unique_fn;
|
||||
|
||||
unique_fn = (const char*)pool_alloc(&atom_pool, fn_len+1);
|
||||
if(!unique_fn)
|
||||
{
|
||||
DEBUG_WARN_ERR(ERR_NO_MEM);
|
||||
return 0;
|
||||
}
|
||||
memcpy2((void*)unique_fn, P_fn, fn_len);
|
||||
((char*)unique_fn)[fn_len] = '\0';
|
||||
|
||||
atom_map.insert(unique_fn, unique_fn);
|
||||
|
||||
stats_unique_name(fn_len);
|
||||
return unique_fn;
|
||||
}
|
||||
|
||||
|
||||
void path_init()
|
||||
{
|
||||
pool_create(&atom_pool, 8*MiB, POOL_VARIABLE_ALLOCS);
|
||||
}
|
||||
|
||||
void path_shutdown()
|
||||
{
|
||||
(void)pool_destroy(&atom_pool);
|
||||
}
|
||||
|
||||
|
||||
const char* file_get_random_name()
|
||||
{
|
||||
// there had better be names in atom_pool, else this will fail.
|
||||
debug_assert(atom_pool.da.pos != 0);
|
||||
|
||||
again:
|
||||
const size_t start_ofs = (size_t)rand(0, (uint)atom_pool.da.pos-1);
|
||||
|
||||
// scan ahead to next string boundary
|
||||
const char* start = (const char*)atom_pool.da.base+start_ofs;
|
||||
const char* next_0 = strchr(start, '\0')+1;
|
||||
// .. at end of storage: restart
|
||||
if((u8*)next_0 >= atom_pool.da.base+atom_pool.da.pos)
|
||||
goto again;
|
||||
// .. skip all '\0' (may be several due to pool alignment)
|
||||
const char* next_name = next_0;
|
||||
while(*next_name == '\0') next_name++;
|
||||
|
||||
return next_name;
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* =========================================================================
|
||||
* File : vfs_path.h
|
||||
* File : path.h
|
||||
* Project : 0 A.D.
|
||||
* Description : helper functions for VFS paths.
|
||||
*
|
||||
@ -9,7 +9,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2004-2005 Jan Wassenberg
|
||||
* Copyright (c) 2004-2006 Jan Wassenberg
|
||||
*
|
||||
* Redistribution and/or modification are also permitted under the
|
||||
* terms of the GNU General Public License as published by the
|
||||
@ -50,13 +50,16 @@ extern void path_dir_only(const char* V_src_fn, char* V_dir_only);
|
||||
// return pointer to the name component within V_src_fn
|
||||
extern const char* path_name_only(const char* V_src_fn);
|
||||
|
||||
extern const char* path_extension(const char* fn);
|
||||
|
||||
extern void path_strip_fn(char* fn);
|
||||
|
||||
struct NextNumberedFilenameInfo
|
||||
{
|
||||
int next_num;
|
||||
};
|
||||
|
||||
// fill V_next_fn (which must be big enough for VFS_MAX_PATH chars) with
|
||||
// fill V_next_fn (which must be big enough for PATH_MAX chars) with
|
||||
// the next numbered filename according to the pattern defined by V_fn_fmt.
|
||||
// <nfi> must be initially zeroed (e.g. by defining as static) and passed
|
||||
// each time.
|
||||
@ -70,5 +73,7 @@ struct NextNumberedFilenameInfo
|
||||
extern void next_numbered_filename(const char* V_fn_fmt,
|
||||
NextNumberedFilenameInfo* nfi, char* V_next_fn, bool use_vfs = true);
|
||||
|
||||
extern void path_init();
|
||||
extern void path_shutdown();
|
||||
|
||||
#endif // #ifndef VFS_PATH_H__
|
@ -123,8 +123,8 @@ static LibError VDir_reload(VDir* vd, const char* path, Handle UNUSED(hvd))
|
||||
{
|
||||
// add required trailing slash if not already present to make
|
||||
// caller's life easier.
|
||||
char V_path_slash[VFS_MAX_PATH];
|
||||
RETURN_ERR(vfs_path_append(V_path_slash, path, ""));
|
||||
char V_path_slash[PATH_MAX];
|
||||
RETURN_ERR(path_append(V_path_slash, path, ""));
|
||||
|
||||
RETURN_ERR(tree_dir_open(V_path_slash, &vd->it));
|
||||
vd->it_valid = 1;
|
||||
@ -250,7 +250,7 @@ LibError vfs_dir_enum(const char* start_path, uint flags, const char* user_filte
|
||||
debug_assert((flags & ~(VFS_DIR_RECURSIVE)) == 0);
|
||||
const bool recursive = (flags & VFS_DIR_RECURSIVE) != 0;
|
||||
|
||||
char filter_buf[VFS_MAX_PATH];
|
||||
char filter_buf[PATH_MAX];
|
||||
const char* filter = user_filter;
|
||||
bool user_filter_wants_dirs = true;
|
||||
if(user_filter)
|
||||
@ -281,7 +281,7 @@ LibError vfs_dir_enum(const char* start_path, uint flags, const char* user_filte
|
||||
// note: can't refer to the queue contents - those are invalidated
|
||||
// as soon as a directory is pushed onto it.
|
||||
PathPackage pp;
|
||||
(void)pp_set_dir(&pp, dir_queue.front());
|
||||
(void)path_package_set_dir(&pp, dir_queue.front());
|
||||
dir_queue.pop();
|
||||
|
||||
Handle hdir = vfs_dir_open(pp.path);
|
||||
@ -296,7 +296,7 @@ LibError vfs_dir_enum(const char* start_path, uint flags, const char* user_filte
|
||||
while(vfs_dir_next_ent(hdir, &ent, filter) == 0)
|
||||
{
|
||||
// build complete path (DirEnt only stores entry name)
|
||||
(void)pp_append_file(&pp, ent.name);
|
||||
(void)path_package_append_file(&pp, ent.name);
|
||||
const char* atom_path = file_make_unique_fn_copy(pp.path);
|
||||
|
||||
if(DIRENT_IS_DIR(&ent))
|
||||
@ -772,17 +772,57 @@ LibError vfs_reload(const char* fn)
|
||||
return reload_without_rebuild(fn);
|
||||
}
|
||||
|
||||
// array of reloads requested this frame (see 'do we really need to
|
||||
// reload' below). go through gyrations to avoid heap allocs.
|
||||
const size_t MAX_RELOADS_PER_FRAME = 12;
|
||||
typedef char Path[PATH_MAX];
|
||||
typedef Path PathList[MAX_RELOADS_PER_FRAME];
|
||||
|
||||
// do we really need to reload? try to avoid the considerable cost of
|
||||
// rebuilding VFS and scanning all Handles.
|
||||
static bool can_ignore_reload(const char* V_path, PathList pending_reloads, uint num_pending)
|
||||
{
|
||||
// note: be careful to avoid 'race conditions' depending on the
|
||||
// timeframe in which notifications reach us.
|
||||
// example: editor deletes a.tga; we are notified; reload is
|
||||
// triggered but fails since the file isn't found; further
|
||||
// notifications (e.g. renamed a.tmp to a.tga) come within x [ms] and
|
||||
// are ignored due to a time limit.
|
||||
// therefore, we can only check for multiple reload requests a frame;
|
||||
// to that purpose, an array is built and duplicates ignored.
|
||||
const char* ext = path_extension(V_path);
|
||||
// .. directory change notification; ignore because we get
|
||||
// per-file notifications anyway. (note: assume no extension =>
|
||||
// it's a directory).
|
||||
if(ext[0] == '\0')
|
||||
return true;
|
||||
// .. compiled XML files the engine writes out by the hundreds;
|
||||
// skipping them is a big performance gain.
|
||||
if(!stricmp(ext, "xmb"))
|
||||
return true;
|
||||
// .. temp files, usually created when an editor saves a file
|
||||
// (delete, create temp, rename temp); no need to reload those.
|
||||
if(!stricmp(ext, "tmp"))
|
||||
return true;
|
||||
// .. more than one notification for a file; only reload once.
|
||||
// note: this doesn't suffer from the 'reloaded too early'
|
||||
// problem described above; if there's more than one
|
||||
// request in the array, the file has since been written.
|
||||
for(uint i = 0; i < num_pending; i++)
|
||||
{
|
||||
if(!strcmp(pending_reloads[i], V_path))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// get directory change notifications, and reload all affected files.
|
||||
// must be called regularly (e.g. once a frame). this is much simpler
|
||||
// than asynchronous notifications: everything would need to be thread-safe.
|
||||
LibError vfs_reload_changed_files()
|
||||
{
|
||||
// array of reloads requested this frame (see 'do we really need to
|
||||
// reload' below). go through gyrations to avoid heap allocs.
|
||||
const size_t MAX_RELOADS_PER_FRAME = 12;
|
||||
typedef char Path[VFS_MAX_PATH];
|
||||
typedef Path PathList[MAX_RELOADS_PER_FRAME];
|
||||
PathList pending_reloads;
|
||||
|
||||
uint num_pending = 0;
|
||||
@ -803,38 +843,8 @@ LibError vfs_reload_changed_files()
|
||||
char* V_path = pending_reloads[num_pending];
|
||||
CHECK_ERR(mount_make_vfs_path(P_path, V_path));
|
||||
|
||||
// do we really need to reload? try to avoid the considerable cost of
|
||||
// rebuilding VFS and scanning all Handles.
|
||||
//
|
||||
// note: be careful to avoid 'race conditions' depending on the
|
||||
// timeframe in which notifications reach us.
|
||||
// example: editor deletes a.tga; we are notified; reload is
|
||||
// triggered but fails since the file isn't found; further
|
||||
// notifications (e.g. renamed a.tmp to a.tga) come within x [ms] and
|
||||
// are ignored due to a time limit.
|
||||
// therefore, we can only check for multiple reload requests a frame;
|
||||
// to that purpose, an array is built and duplicates ignored.
|
||||
const char* ext = strrchr(V_path, '.');
|
||||
// .. directory change notification; ignore because we get
|
||||
// per-file notifications anyway. (note: assume no extension =>
|
||||
// it's a directory). this also protects the strcmp calls below.
|
||||
if(!ext)
|
||||
if(can_ignore_reload(V_path, pending_reloads, num_pending))
|
||||
continue;
|
||||
// .. compiled XML files the engine writes out by the hundreds;
|
||||
// skipping them is a big performance gain.
|
||||
if(!stricmp(ext, ".xmb"))
|
||||
continue;
|
||||
// .. temp files, usually created when an editor saves a file
|
||||
// (delete, create temp, rename temp); no need to reload those.
|
||||
if(!stricmp(ext, ".tmp"))
|
||||
continue;
|
||||
// .. more than one notification for a file; only reload once.
|
||||
// note: this doesn't suffer from the 'reloaded too early'
|
||||
// problem described above; if there's more than one
|
||||
// request in the array, the file has since been written.
|
||||
for(uint i = 0; i < num_pending; i++)
|
||||
if(!strcmp(pending_reloads[i], V_path))
|
||||
continue;
|
||||
|
||||
// path has already been written to pending_reloads,
|
||||
// so just mark it valid.
|
||||
|
@ -227,19 +227,21 @@ extern void vfs_display(void);
|
||||
// paths
|
||||
//
|
||||
|
||||
// the VFS doesn't require this length restriction - VFS internal storage
|
||||
// is not fixed-length. the purpose here is to give an indication of how
|
||||
// large fixed-size user buffers should be. length includes trailing '\0'.
|
||||
#define VFS_MAX_PATH 256
|
||||
// note: the VFS doesn't specify any path length restriction -
|
||||
// internal filename storage is not fixed-length.
|
||||
// for an an indication of how large fixed-size user buffers should be,
|
||||
// use PATH_MAX.
|
||||
|
||||
// convenience function
|
||||
extern void vfs_path_copy(char* dst, const char* src);
|
||||
extern void path_copy(char* dst, const char* src);
|
||||
|
||||
// combine <path1> and <path2> into one path, and write to <dst>.
|
||||
// if necessary, a directory separator is added between the paths.
|
||||
// each may be empty, filenames, or full paths.
|
||||
// total path length (including '\0') must not exceed VFS_MAX_PATH.
|
||||
extern LibError vfs_path_append(char* dst, const char* path1, const char* path2);
|
||||
// total path length (including '\0') must not exceed PATH_MAX.
|
||||
extern LibError path_append(char* dst, const char* path1, const char* path2);
|
||||
|
||||
extern const char* path_extension(const char* fn);
|
||||
|
||||
// VFS paths are of the form: "(dir/)*file?"
|
||||
// in English: '/' as path separator; trailing '/' required for dir names;
|
||||
|
@ -227,7 +227,7 @@ static LibError afile_cb(const char* atom_fn, const struct stat* s, uintptr_t me
|
||||
CHECK_PATH(atom_fn);
|
||||
|
||||
const char* name = path_name_only(atom_fn);
|
||||
char path[VFS_MAX_PATH];
|
||||
char path[PATH_MAX];
|
||||
path_dir_only(atom_fn, path);
|
||||
const char* atom_path = file_make_unique_fn_copy(path);
|
||||
|
||||
@ -288,7 +288,7 @@ static LibError enqueue_archive(const char* name, const char* P_archive_dir, Arc
|
||||
// this doesn't (need to) work for subdirectories of the mounted td!
|
||||
// we can't use mount_get_path because we don't have the VFS path.
|
||||
char P_path[PATH_MAX];
|
||||
RETURN_ERR(vfs_path_append(P_path, P_archive_dir, name));
|
||||
RETURN_ERR(path_append(P_path, P_archive_dir, name));
|
||||
|
||||
// just open the Zip file and see if it's valid. we don't bother
|
||||
// checking the extension because archives won't necessarily be
|
||||
@ -374,7 +374,7 @@ static LibError enqueue_dir(TDir* parent_td, const char* name,
|
||||
|
||||
// prepend parent path to get complete pathname.
|
||||
char P_path[PATH_MAX];
|
||||
CHECK_ERR(vfs_path_append(P_path, P_parent_path, name));
|
||||
CHECK_ERR(path_append(P_path, P_parent_path, name));
|
||||
|
||||
// create subdirectory..
|
||||
TDir* td;
|
||||
@ -429,7 +429,7 @@ static LibError add_ent(TDir* td, DirEnt* ent, const char* P_parent_path, const
|
||||
{
|
||||
// prepend parent path to get complete pathname.
|
||||
char V_path[PATH_MAX];
|
||||
CHECK_ERR(vfs_path_append(V_path, tfile_get_atom_fn((TFile*)td), name));
|
||||
CHECK_ERR(path_append(V_path, tfile_get_atom_fn((TFile*)td), name));
|
||||
const char* atom_fn = file_make_unique_fn_copy(V_path);
|
||||
|
||||
vfs_opt_notify_loose_file(atom_fn);
|
||||
@ -639,7 +639,7 @@ LibError vfs_mount(const char* V_mount_point, const char* P_real_path, uint flag
|
||||
// no matter if it's an archive - still shouldn't be a "subpath".
|
||||
for(MountIt it = mounts.begin(); it != mounts.end(); ++it)
|
||||
{
|
||||
if(file_is_subpath(P_real_path, it->P_name.c_str()))
|
||||
if(path_is_subpath(P_real_path, it->P_name.c_str()))
|
||||
WARN_RETURN(ERR_ALREADY_MOUNTED);
|
||||
}
|
||||
|
||||
|
@ -601,8 +601,7 @@ class IsArchive
|
||||
public:
|
||||
IsArchive(const char* archive_fn)
|
||||
{
|
||||
archive_ext = strrchr(archive_fn, '.');
|
||||
if(!archive_ext) archive_ext = ""; // for safe comparison
|
||||
archive_ext = path_extension(archive_fn);
|
||||
}
|
||||
|
||||
bool operator()(DirEnt& ent) const
|
||||
@ -612,8 +611,7 @@ public:
|
||||
return true;
|
||||
|
||||
// remove if not same extension
|
||||
const char* ext = strrchr(ent.name, '.');
|
||||
if(!ext) ext = ""; // for safe comparison
|
||||
const char* ext = path_extension(ent.name);
|
||||
if(stricmp(archive_ext, ext) != 0)
|
||||
return true;
|
||||
|
||||
@ -633,7 +631,7 @@ static LibError vfs_opt_init(const char* trace_filename, const char* archive_fn_
|
||||
// note: this is needed by should_rebuild_main_archive and later in
|
||||
// vfs_opt_continue; must be done here instead of inside the former
|
||||
// because that is not called when force_build == true.
|
||||
char dir[VFS_MAX_PATH];
|
||||
char dir[PATH_MAX];
|
||||
path_dir_only(archive_fn_fmt, dir);
|
||||
RETURN_ERR(file_get_sorted_dirents(dir, existing_archives));
|
||||
DirEntIt new_end = std::remove_if(existing_archives.begin(), existing_archives.end(), IsArchive(archive_fn));
|
||||
@ -682,13 +680,13 @@ static int vfs_opt_continue()
|
||||
// delete old archives
|
||||
PathPackage pp; // need path to each existing_archive, not only name
|
||||
{
|
||||
char archive_dir[VFS_MAX_PATH];
|
||||
char archive_dir[PATH_MAX];
|
||||
path_dir_only(archive_fn, archive_dir);
|
||||
(void)pp_set_dir(&pp, archive_dir);
|
||||
(void)path_package_set_dir(&pp, archive_dir);
|
||||
}
|
||||
for(DirEntCIt it = existing_archives.begin(); it != existing_archives.end(); ++it)
|
||||
{
|
||||
(void)pp_append_file(&pp, it->name);
|
||||
(void)path_package_append_file(&pp, it->name);
|
||||
(void)file_delete(pp.path);
|
||||
}
|
||||
|
||||
@ -731,7 +729,7 @@ static LibError build_mini_archive(const char* mini_archive_fn_fmt)
|
||||
V_fns[loose_files.size()] = 0; // terminator
|
||||
|
||||
// get new unused mini archive name at P_dst_path
|
||||
char mini_archive_fn[VFS_MAX_PATH];
|
||||
char mini_archive_fn[PATH_MAX];
|
||||
static NextNumberedFilenameInfo nfi;
|
||||
bool use_vfs = false; // can't use VFS for archive files
|
||||
next_numbered_filename(mini_archive_fn_fmt, &nfi, mini_archive_fn, use_vfs);
|
||||
|
@ -1,258 +0,0 @@
|
||||
/**
|
||||
* =========================================================================
|
||||
* File : vfs_path.cpp
|
||||
* Project : 0 A.D.
|
||||
* Description : helper functions for VFS paths.
|
||||
*
|
||||
* @author Jan.Wassenberg@stud.uni-karlsruhe.de
|
||||
* =========================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2004-2005 Jan Wassenberg
|
||||
*
|
||||
* Redistribution and/or modification are also permitted under the
|
||||
* terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation (version 2 or later, at your option).
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
#include "precompiled.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "lib.h"
|
||||
#include "file_internal.h"
|
||||
|
||||
|
||||
// path types:
|
||||
// p_*: posix (e.g. mount object name or for open())
|
||||
// v_*: vfs (e.g. mount point)
|
||||
// fn : filename only (e.g. from readdir)
|
||||
// dir_name: directory only, no path (e.g. subdir name)
|
||||
//
|
||||
// all paths must be relative (no leading '/'); components are separated
|
||||
// by '/'; no ':', '\\', "." or ".." allowed; root dir is "".
|
||||
//
|
||||
// grammar:
|
||||
// path ::= dir*file?
|
||||
// dir ::= name/
|
||||
// file ::= name
|
||||
// name ::= [^/]
|
||||
|
||||
|
||||
// if path is invalid, return a descriptive error code, otherwise ERR_OK.
|
||||
LibError path_validate(const char* path)
|
||||
{
|
||||
// disallow "/", because it would create a second 'root' (with name = "").
|
||||
// root dir is "".
|
||||
if(path[0] == '/')
|
||||
return ERR_PATH_NOT_RELATIVE;
|
||||
|
||||
// scan each char in path string; count length.
|
||||
int c = 0; // current char; used for .. detection
|
||||
size_t path_len = 0;
|
||||
for(;;)
|
||||
{
|
||||
const int last_c = c;
|
||||
c = path[path_len++];
|
||||
|
||||
// whole path is too long
|
||||
if(path_len >= VFS_MAX_PATH)
|
||||
return ERR_PATH_LENGTH;
|
||||
|
||||
// disallow:
|
||||
// - ".." (prevent going above the VFS root dir)
|
||||
// - "./" (security hole when mounting and not supported on Windows).
|
||||
// allow "/.", because CVS backup files include it.
|
||||
if(last_c == '.' && (c == '.' || c == '/'))
|
||||
return ERR_PATH_NON_CANONICAL;
|
||||
|
||||
// disallow OS-specific dir separators
|
||||
if(c == '\\' || c == ':')
|
||||
return ERR_PATH_NON_PORTABLE;
|
||||
|
||||
// end of string, no errors encountered
|
||||
if(c == '\0')
|
||||
break;
|
||||
}
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
// if name is invalid, return a descriptive error code, otherwise ERR_OK.
|
||||
LibError path_component_validate(const char* name)
|
||||
{
|
||||
// disallow empty strings
|
||||
if(*name == '\0')
|
||||
return ERR_PATH_EMPTY;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
const int c = *name++;
|
||||
|
||||
// disallow dir separators
|
||||
if(c == '\\' || c == ':' || c == '/')
|
||||
return ERR_PATH_COMPONENT_SEPARATOR;
|
||||
|
||||
// end of string, no errors encountered
|
||||
if(c == '\0')
|
||||
break;
|
||||
}
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
// convenience function
|
||||
void vfs_path_copy(char* dst, const char* src)
|
||||
{
|
||||
strcpy_s(dst, VFS_MAX_PATH, src);
|
||||
}
|
||||
|
||||
|
||||
// combine <path1> and <path2> into one path, and write to <dst>.
|
||||
// if necessary, a directory separator is added between the paths.
|
||||
// each may be empty, filenames, or full paths.
|
||||
// total path length (including '\0') must not exceed VFS_MAX_PATH.
|
||||
LibError vfs_path_append(char* dst, const char* path1, const char* path2)
|
||||
{
|
||||
const size_t len1 = strlen(path1);
|
||||
const size_t len2 = strlen(path2);
|
||||
size_t total_len = len1 + len2 + 1; // includes '\0'
|
||||
|
||||
// check if we need to add '/' between path1 and path2
|
||||
// note: the second can't start with '/' (not allowed by path_validate)
|
||||
bool need_separator = false;
|
||||
if(len1 != 0 && path1[len1-1] != '/')
|
||||
{
|
||||
total_len++; // for '/'
|
||||
need_separator = true;
|
||||
}
|
||||
|
||||
if(total_len+1 > VFS_MAX_PATH)
|
||||
WARN_RETURN(ERR_PATH_LENGTH);
|
||||
|
||||
strcpy(dst, path1); // safe
|
||||
dst += len1;
|
||||
if(need_separator)
|
||||
*dst++ = '/';
|
||||
strcpy(dst, path2); // safe
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
// strip <remove> from the start of <src>, prepend <replace>,
|
||||
// and write to <dst>.
|
||||
// used when converting VFS <--> real paths.
|
||||
LibError path_replace(char* dst, const char* src, const char* remove, const char* replace)
|
||||
{
|
||||
// remove doesn't match start of <src>
|
||||
const size_t remove_len = strlen(remove);
|
||||
if(strncmp(src, remove, remove_len) != 0)
|
||||
WARN_RETURN(ERR_FAIL);
|
||||
|
||||
// get rid of trailing / in src (must not be included in remove)
|
||||
const char* start = src+remove_len;
|
||||
if(*start == '/' || *start == DIR_SEP)
|
||||
start++;
|
||||
|
||||
// prepend replace.
|
||||
CHECK_ERR(vfs_path_append(dst, replace, start));
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
// fill V_dir_only with the path portion of V_src_fn
|
||||
// ("" if root dir, otherwise ending with /)
|
||||
void path_dir_only(const char* V_src_fn, char* V_dir_only)
|
||||
{
|
||||
vfs_path_copy(V_dir_only, V_src_fn);
|
||||
char* slash = strrchr(V_dir_only, '/');
|
||||
// was filename only; directory = "" (empty string)
|
||||
if(!slash)
|
||||
V_dir_only[0] = '\0';
|
||||
// normal directory+filename: cut off after last slash
|
||||
else
|
||||
*(slash+1) = '\0';
|
||||
}
|
||||
|
||||
|
||||
// return pointer to the name component within V_src_fn
|
||||
const char* path_name_only(const char* V_src_fn)
|
||||
{
|
||||
const char* slash = strrchr(V_src_fn, '/');
|
||||
return slash? slash+1 : V_src_fn;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// fill V_next_fn (which must be big enough for VFS_MAX_PATH chars) with
|
||||
// the next numbered filename according to the pattern defined by V_fn_fmt.
|
||||
// <nfi> must be initially zeroed (e.g. by defining as static) and passed
|
||||
// each time.
|
||||
// if <use_vfs> (default), the paths are treated as VFS paths; otherwise,
|
||||
// file.cpp's functions are used. this is necessary because one of
|
||||
// our callers needs a filename for VFS archive files.
|
||||
//
|
||||
// this function is useful when creating new files which are not to
|
||||
// overwrite the previous ones, e.g. screenshots.
|
||||
// example for V_fn_fmt: "screenshots/screenshot%04d.png".
|
||||
void next_numbered_filename(const char* fn_fmt,
|
||||
NextNumberedFilenameInfo* nfi, char* next_fn, bool use_vfs)
|
||||
{
|
||||
// (first call only:) scan directory and set next_num according to
|
||||
// highest matching filename found. this avoids filling "holes" in
|
||||
// the number series due to deleted files, which could be confusing.
|
||||
// example: add 1st and 2nd; [exit] delete 1st; [restart]
|
||||
// add 3rd -> without this measure it would get number 1, not 3.
|
||||
if(nfi->next_num == 0)
|
||||
{
|
||||
char dir[VFS_MAX_PATH];
|
||||
path_dir_only(fn_fmt, dir);
|
||||
const char* name_fmt = path_name_only(fn_fmt);
|
||||
|
||||
int max_num = -1; int num;
|
||||
DirEnt ent;
|
||||
|
||||
if(use_vfs)
|
||||
{
|
||||
Handle hd = vfs_dir_open(dir);
|
||||
if(hd > 0)
|
||||
{
|
||||
while(vfs_dir_next_ent(hd, &ent, 0) == ERR_OK)
|
||||
if(!DIRENT_IS_DIR(&ent) && sscanf(ent.name, name_fmt, &num) == 1)
|
||||
max_num = MAX(num, max_num);
|
||||
(void)vfs_dir_close(hd);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DirIterator it;
|
||||
if(dir_open(dir, &it) == ERR_OK)
|
||||
{
|
||||
while(dir_next_ent(&it, &ent) == ERR_OK)
|
||||
if(!DIRENT_IS_DIR(&ent) && sscanf(ent.name, name_fmt, &num) == 1)
|
||||
max_num = MAX(num, max_num);
|
||||
(void)dir_close(&it);
|
||||
}
|
||||
}
|
||||
|
||||
nfi->next_num = max_num+1;
|
||||
}
|
||||
|
||||
bool (*exists)(const char* fn) = use_vfs? vfs_exists : file_exists;
|
||||
|
||||
// now increment number until that file doesn't yet exist.
|
||||
// this is fairly slow, but typically only happens once due
|
||||
// to scan loop above. (we still need to provide for looping since
|
||||
// someone may have added files in the meantime)
|
||||
// binary search isn't expected to improve things.
|
||||
do
|
||||
snprintf(next_fn, VFS_MAX_PATH, fn_fmt, nfi->next_num++);
|
||||
while(exists(next_fn));
|
||||
}
|
@ -213,8 +213,8 @@ public:
|
||||
|
||||
LibError add(const char* name_tmp, TNodeType type, TNode** pnode)
|
||||
{
|
||||
char V_new_path_tmp[VFS_MAX_PATH];
|
||||
vfs_path_append(V_new_path_tmp, V_path, name_tmp);
|
||||
char V_new_path_tmp[PATH_MAX];
|
||||
path_append(V_new_path_tmp, V_path, name_tmp);
|
||||
const char* V_new_path = file_make_unique_fn_copy(V_new_path_tmp);
|
||||
const char* name = path_name_only(V_new_path);
|
||||
|
||||
@ -377,7 +377,7 @@ static LibError lookup(TDir* td, const char* path, uint flags, TNode** pnode)
|
||||
|
||||
// copy into (writeable) buffer so we can 'tokenize' path components
|
||||
// by replacing '/' with '\0'.
|
||||
char V_path[VFS_MAX_PATH];
|
||||
char V_path[PATH_MAX];
|
||||
strcpy_s(V_path, sizeof(V_path), path);
|
||||
char* cur_component = V_path;
|
||||
|
||||
|
@ -191,7 +191,7 @@ static void Cursor_dtor(Cursor* c)
|
||||
|
||||
static LibError Cursor_reload(Cursor* c, const char* name, Handle)
|
||||
{
|
||||
char filename[VFS_MAX_PATH];
|
||||
char filename[PATH_MAX];
|
||||
|
||||
// read pixel offset of the cursor's hotspot [the bit of it that's
|
||||
// drawn at (g_mouse_x,g_mouse_y)] from file.
|
||||
|
@ -27,6 +27,7 @@
|
||||
|
||||
#include "tex_codec.h"
|
||||
#include "tex.h"
|
||||
#include "lib/path_util.h"
|
||||
|
||||
static const TexCodecVTbl* codecs;
|
||||
|
||||
@ -54,11 +55,7 @@ int tex_codec_register(TexCodecVTbl* c)
|
||||
// tex_is_known_extension.
|
||||
LibError tex_codec_for_filename(const char* fn, const TexCodecVTbl** c)
|
||||
{
|
||||
const char* ext = strrchr(fn, '.');
|
||||
if(!ext)
|
||||
return ERR_UNKNOWN_FORMAT;
|
||||
ext++; // skip '.'
|
||||
|
||||
const char* ext = path_extension(fn);
|
||||
for(*c = codecs; *c; *c = (*c)->next)
|
||||
{
|
||||
// we found it
|
||||
|
@ -50,7 +50,7 @@
|
||||
# include "sysdep/win/win_internal.h"
|
||||
#endif
|
||||
|
||||
#include "../res.h"
|
||||
#include "lib/res/res.h"
|
||||
#include "snd_mgr.h"
|
||||
#include "lib/timer.h"
|
||||
#include "app_hooks.h"
|
||||
@ -929,9 +929,9 @@ static LibError SndData_reload(SndData* sd, const char* fn, Handle hsd)
|
||||
}
|
||||
file_type;
|
||||
|
||||
const char* ext = strrchr(fn, '.');
|
||||
const char* ext = path_extension(fn);
|
||||
// .. OGG (data will be passed directly to OpenAL)
|
||||
if(ext && !stricmp(ext, ".ogg"))
|
||||
if(!stricmp(ext, "ogg"))
|
||||
{
|
||||
#ifdef OGG_HACK
|
||||
#else
|
||||
@ -950,7 +950,7 @@ static LibError SndData_reload(SndData* sd, const char* fn, Handle hsd)
|
||||
file_type = FT_OGG;
|
||||
}
|
||||
// .. WAV
|
||||
else if(ext && !stricmp(ext, ".wav"))
|
||||
else if(!stricmp(ext, "wav"))
|
||||
file_type = FT_WAV;
|
||||
// .. unknown extension
|
||||
else
|
||||
@ -1375,7 +1375,7 @@ static LibError VSrc_reload(VSrc* vs, const char* fn, Handle hvs)
|
||||
CHECK_ERR(err);
|
||||
|
||||
//
|
||||
// if extension is .txt, fn is a definition file containing the
|
||||
// if extension is "txt", fn is a definition file containing the
|
||||
// sound file name and its gain; otherwise, read directly from fn
|
||||
// and assume default gain (1.0).
|
||||
//
|
||||
@ -1385,8 +1385,8 @@ static LibError VSrc_reload(VSrc* vs, const char* fn, Handle hvs)
|
||||
// extracted from stringstream;
|
||||
// declare here so that it doesn't go out of scope below.
|
||||
|
||||
const char* ext = strchr(fn, '.');
|
||||
if(ext && !stricmp(ext, ".txt"))
|
||||
const char* ext = path_extension(fn);
|
||||
if(!stricmp(ext, "txt"))
|
||||
{
|
||||
FileIOBuf buf; size_t size;
|
||||
RETURN_ERR(vfs_load(fn, buf, size));
|
||||
|
@ -144,7 +144,7 @@ extern LibError snd_set_master_gain(float gain);
|
||||
|
||||
// open and return a handle to a sound instance.
|
||||
//
|
||||
// if <snd_fn> is a text file (extension ".txt"), it is assumed
|
||||
// if <snd_fn> is a text file (extension "txt"), it is assumed
|
||||
// to be a definition file containing the sound file name and
|
||||
// its gain (0.0 .. 1.0).
|
||||
// otherwise, <snd_fn> is taken to be the sound file name and
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "win_internal.h"
|
||||
#include "lib/path_util.h"
|
||||
#include "dll_ver.h"
|
||||
|
||||
#if MSC_VERSION
|
||||
@ -108,8 +109,8 @@ LibError dll_list_add(const char* name)
|
||||
// ".sys" extension, so always appending ".dll" is incorrect.
|
||||
char buf[MAX_PATH];
|
||||
const char* dll_name = name;
|
||||
const char* ext = strrchr(name, '.');
|
||||
if(!ext)
|
||||
const char* ext = path_extension(name);
|
||||
if(ext[0] == '\0') // no extension
|
||||
{
|
||||
snprintf(buf, ARRAY_SIZE(buf), "%s.dll", name);
|
||||
dll_name = buf;
|
||||
@ -128,8 +129,7 @@ LibError dll_list_add(const char* name)
|
||||
dll_list_pos += sprintf(dll_list_pos, ", ");
|
||||
|
||||
// extract filename.
|
||||
const char* slash = strrchr(dll_name, '\\');
|
||||
const char* dll_fn = slash? slash+1 : dll_name;
|
||||
const char* dll_fn = path_name_only(dll_name);
|
||||
|
||||
int len = snprintf(dll_list_pos, max_chars_to_write, "%s (%s)", dll_fn, dll_ver);
|
||||
// success
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include "wdbg.h"
|
||||
#include "debug_stl.h"
|
||||
#include "app_hooks.h"
|
||||
#include "lib/path_util.h"
|
||||
#if CPU_IA32
|
||||
# include "lib/sysdep/ia32.h"
|
||||
#endif
|
||||
@ -207,11 +208,7 @@ LibError debug_resolve_symbol(void* ptr_of_interest, char* sym_name, char* file,
|
||||
// this loses information, but that isn't expected to be a
|
||||
// problem and is balanced by not having to do this from every
|
||||
// call site (full path is too long to display nicely).
|
||||
const char* base_name = line_info.FileName;
|
||||
const char* slash = strrchr(base_name, DIR_SEP);
|
||||
if(slash)
|
||||
base_name = slash+1;
|
||||
|
||||
const char* base_name = path_name_only(line_info.FileName);
|
||||
snprintf(file, DBG_FILE_LEN, "%s", base_name);
|
||||
successes++;
|
||||
}
|
||||
@ -1918,7 +1915,7 @@ void wdbg_write_minidump(EXCEPTION_POINTERS* exception_pointers)
|
||||
lock();
|
||||
|
||||
// note: we go through some gyrations here (strcpy+strcat) to avoid
|
||||
// dependency on file code (vfs_path_append).
|
||||
// dependency on file code (path_append).
|
||||
char N_path[PATH_MAX];
|
||||
strcpy_s(N_path, ARRAY_SIZE(N_path), ah_get_log_dir());
|
||||
strcat_s(N_path, ARRAY_SIZE(N_path), "crashlog.dmp");
|
||||
|
@ -22,16 +22,15 @@
|
||||
|
||||
#include "precompiled.h"
|
||||
|
||||
#include "lib.h"
|
||||
#include "win_internal.h"
|
||||
#include "lib/res/file/file.h" // file_is_subpath
|
||||
|
||||
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <list>
|
||||
|
||||
#include "lib.h"
|
||||
#include "lib/path_util.h"
|
||||
#include "win_internal.h"
|
||||
#include "lib/res/file/file.h" // path_is_subpath
|
||||
|
||||
|
||||
#pragma data_seg(WIN_CALLBACK_POST_ATEXIT(x))
|
||||
WIN_REGISTER_FUNC(wdir_watch_shutdown);
|
||||
@ -204,7 +203,7 @@ LibError dir_add_watch(const char* dir, intptr_t* _reqnum)
|
||||
if(!w)
|
||||
continue;
|
||||
const char* old_dir = w->dir_name.c_str();
|
||||
if(file_is_subpath(dir, old_dir))
|
||||
if(path_is_subpath(dir, old_dir))
|
||||
{
|
||||
reqnum = w->reqnum;
|
||||
w->refs++;
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <stdlib.h> // __argc
|
||||
|
||||
#include "win_internal.h"
|
||||
#include "lib/path_util.h"
|
||||
|
||||
#if MSC_VERSION >= 1400
|
||||
#include <process.h> // __security_init_cookie
|
||||
@ -318,11 +319,7 @@ static inline void pre_libc_init()
|
||||
GetSystemDirectory(win_sys_dir, sizeof(win_sys_dir));
|
||||
|
||||
if(GetModuleFileName(GetModuleHandle(0), win_exe_dir, MAX_PATH) != 0)
|
||||
{
|
||||
char* slash = strrchr(win_exe_dir, '\\');
|
||||
if(slash)
|
||||
*slash = '\0';
|
||||
}
|
||||
path_strip_fn(win_exe_dir);
|
||||
|
||||
// HACK: make sure a reference to user32 is held, even if someone
|
||||
// decides to delay-load it. this fixes bug #66, which was the
|
||||
|
@ -26,9 +26,10 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "lib.h"
|
||||
#include "posix.h"
|
||||
#include "win_internal.h"
|
||||
#include "allocators.h"
|
||||
#include "lib/path_util.h"
|
||||
#include "posix.h"
|
||||
|
||||
|
||||
// cast intptr_t to HANDLE; centralized for easier changing, e.g. avoiding
|
||||
@ -793,11 +794,12 @@ void* dlopen(const char* so_name, int flags)
|
||||
|
||||
// if present, strip .so extension; add .dll extension
|
||||
char dll_name[MAX_PATH];
|
||||
strcpy_s(dll_name, ARRAY_SIZE(dll_name)-4, so_name);
|
||||
char* ext = strrchr(dll_name, '.');
|
||||
if(!ext)
|
||||
ext = dll_name + strlen(dll_name);
|
||||
strcpy(ext, ".dll"); // safe
|
||||
strcpy_s(dll_name, ARRAY_SIZE(dll_name)-5, so_name);
|
||||
char* ext = (char*)path_extension(dll_name);
|
||||
if(ext[0] == '\0') // no extension
|
||||
strcat(dll_name, ".dll"); // safe
|
||||
else // need to replace extension
|
||||
SAFE_STRCPY(ext, "dll");
|
||||
|
||||
HMODULE hModule = LoadLibrary(dll_name);
|
||||
if(!hModule)
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "lib/path_util.h"
|
||||
#include "lib/sysdep/snd.h"
|
||||
#include "dll_ver.h" // dll_list_*
|
||||
#include "win_internal.h"
|
||||
@ -99,7 +100,7 @@ static LibError add_if_oal_dll(const DirEnt* ent, PathPackage* pp, StringSet* dl
|
||||
if(!ret.second) // insert failed - element already there
|
||||
return ERR_OK;
|
||||
|
||||
RETURN_ERR(pp_append_file(pp, fn));
|
||||
RETURN_ERR(path_package_append_file(pp, fn));
|
||||
return dll_list_add(pp->path);
|
||||
}
|
||||
|
||||
@ -113,7 +114,7 @@ static LibError add_if_oal_dll(const DirEnt* ent, PathPackage* pp, StringSet* dl
|
||||
static LibError add_oal_dlls_in_dir(const char* dir, StringSet* dlls)
|
||||
{
|
||||
PathPackage pp;
|
||||
RETURN_ERR(pp_set_dir(&pp, dir));
|
||||
RETURN_ERR(path_package_set_dir(&pp, dir));
|
||||
|
||||
DirIterator d;
|
||||
RETURN_ERR(dir_open(dir, &d));
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "CLogger.h"
|
||||
#include "ConfigDB.h"
|
||||
#include "lib.h"
|
||||
#include "lib/path_util.h"
|
||||
#include "lib/res/file/file.h"
|
||||
|
||||
#include <time.h>
|
||||
@ -37,12 +38,12 @@ CLogger::CLogger()
|
||||
char N_path[PATH_MAX];
|
||||
(void)file_make_full_native_path("../logs", N_path);
|
||||
PathPackage pp;
|
||||
(void)pp_set_dir(&pp, N_path);
|
||||
(void)pp_append_file(&pp, "mainlog.html");
|
||||
(void)path_package_set_dir(&pp, N_path);
|
||||
(void)path_package_append_file(&pp, "mainlog.html");
|
||||
m_MainLog.open (pp.path, ofstream::out | ofstream::trunc);
|
||||
(void)pp_append_file(&pp, "interestinglog.html");
|
||||
(void)path_package_append_file(&pp, "interestinglog.html");
|
||||
m_InterestingLog.open(pp.path, ofstream::out | ofstream::trunc);
|
||||
(void)pp_append_file(&pp, "memorylog.html");
|
||||
(void)path_package_append_file(&pp, "memorylog.html");
|
||||
m_MemoryLog.open (pp.path, ofstream::out | ofstream::trunc);
|
||||
|
||||
//Write Headers for the HTML documents
|
||||
|
@ -330,8 +330,8 @@ bool CConfigDB::WriteFile(EConfigNamespace ns, bool useVFS, CStr path)
|
||||
{
|
||||
debug_assert(ns >= 0 && ns < CFG_LAST);
|
||||
|
||||
char realpath[VFS_MAX_PATH];
|
||||
char nativepath[VFS_MAX_PATH];
|
||||
char realpath[PATH_MAX];
|
||||
char nativepath[PATH_MAX];
|
||||
const char *filepath=path.c_str();
|
||||
int err;
|
||||
FILE *fp;
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "precompiled.h"
|
||||
|
||||
#include "FileUnpacker.h"
|
||||
#include "lib/path_util.h"
|
||||
#include "lib/res/res.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -44,8 +45,8 @@ void CFileUnpacker::Read(const char* filename,const char magicstr[4])
|
||||
// somewhat of a hack: if loading a map (.PMP), tell the file manager
|
||||
// that the buffer will be kept in memory longer (avoids warning).
|
||||
uint flags = 0;
|
||||
const char* ext = strrchr(filename, '.');
|
||||
if(ext && !stricmp(ext, ".pmp"))
|
||||
const char* ext = path_extension(filename);
|
||||
if(!stricmp(ext, "pmp"))
|
||||
flags |= FILE_LONG_LIVED;
|
||||
|
||||
// load the whole thing into memory
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "Profile.h"
|
||||
#include "Renderer.h"
|
||||
#include "lib/res/graphics/unifont.h"
|
||||
#include "lib/path_util.h"
|
||||
#include "lib/res/file/file.h"
|
||||
#include "Hotkey.h"
|
||||
#include "ps/CLogger.h"
|
||||
@ -418,8 +419,8 @@ void CProfileViewer::SaveToFile()
|
||||
char N_path[PATH_MAX];
|
||||
(void)file_make_full_native_path("../logs", N_path);
|
||||
PathPackage pp;
|
||||
(void)pp_set_dir(&pp, N_path);
|
||||
(void)pp_append_file(&pp, "profile.txt");
|
||||
(void)path_package_set_dir(&pp, N_path);
|
||||
(void)path_package_append_file(&pp, "profile.txt");
|
||||
|
||||
// Open the file. (It will be closed when the CProfileViewer
|
||||
// destructor is called.)
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include "precompiled.h"
|
||||
|
||||
#include "lib/res/file/path.h"
|
||||
#include "lib/res/file/vfs.h"
|
||||
#include "lib/res/file/vfs_path.h"
|
||||
#include "lib/ogl.h"
|
||||
#include "lib/timer.h"
|
||||
#include "lib/sysdep/gfx.h"
|
||||
@ -170,9 +170,9 @@ void WriteScreenshot(const char* extension)
|
||||
// get next available numbered filename
|
||||
// .. bake extension into format string.
|
||||
// note: %04d -> always 4 digits, so sorting by filename works correctly.
|
||||
char file_format_string[VFS_MAX_PATH];
|
||||
char file_format_string[PATH_MAX];
|
||||
snprintf(file_format_string, PATH_MAX, "screenshots/screenshot%%04d.%s", extension);
|
||||
char fn[VFS_MAX_PATH];
|
||||
char fn[PATH_MAX];
|
||||
next_numbered_filename(file_format_string, &screenshot_nfi, fn);
|
||||
const char* atom_fn = file_make_unique_fn_copy(fn);
|
||||
|
||||
@ -209,9 +209,9 @@ void WriteBigScreenshot(const char* extension, int tiles)
|
||||
// get next available numbered filename
|
||||
// .. bake extension into format string.
|
||||
// note: %04d -> always 4 digits, so sorting by filename works correctly.
|
||||
char file_format_string[VFS_MAX_PATH];
|
||||
char file_format_string[PATH_MAX];
|
||||
snprintf(file_format_string, PATH_MAX, "screenshots/screenshot%%04d.%s", extension);
|
||||
char fn[VFS_MAX_PATH];
|
||||
char fn[PATH_MAX];
|
||||
next_numbered_filename(file_format_string, &screenshot_nfi, fn);
|
||||
const char* atom_fn = file_make_unique_fn_copy(fn);
|
||||
|
||||
|
@ -106,7 +106,7 @@ InputSource *CVFSEntityResolver::resolveEntity(const XMLCh *const UNUSED(publicI
|
||||
char *path=XMLString::transcode(systemId);
|
||||
char *orgpath=path;
|
||||
|
||||
char abspath[VFS_MAX_PATH];
|
||||
char abspath[PATH_MAX];
|
||||
const char *end=strchr(m_DocName, '\0');
|
||||
|
||||
if (IS_PATH_SEP(*path))
|
||||
@ -140,9 +140,9 @@ InputSource *CVFSEntityResolver::resolveEntity(const XMLCh *const UNUSED(publicI
|
||||
const ptrdiff_t prefixlen=end-m_DocName;
|
||||
|
||||
memcpy2(abspath, m_DocName, prefixlen);
|
||||
strncpy(abspath+prefixlen, path, VFS_MAX_PATH-prefixlen);
|
||||
strncpy(abspath+prefixlen, path, PATH_MAX-prefixlen);
|
||||
// strncpy might not have terminated, if path was too long
|
||||
abspath[VFS_MAX_PATH-1]=0;
|
||||
abspath[PATH_MAX-1]=0;
|
||||
|
||||
path=abspath;
|
||||
}
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "ModelDef.h"
|
||||
|
||||
#include "ogl.h"
|
||||
#include "lib/path_util.h"
|
||||
#include "lib/res/res.h"
|
||||
#include "lib/res/file/file.h"
|
||||
#include "lib/res/graphics/tex.h"
|
||||
@ -1245,7 +1246,7 @@ int CRenderer::LoadAlphaMaps()
|
||||
//
|
||||
Handle textures[NumAlphaMaps] = {0};
|
||||
PathPackage pp;
|
||||
(void)pp_set_dir(&pp, "art/textures/terrain/alphamaps/special");
|
||||
(void)path_package_set_dir(&pp, "art/textures/terrain/alphamaps/special");
|
||||
const char* fnames[NumAlphaMaps] = {
|
||||
"blendcircle.dds",
|
||||
"blendlshape.dds",
|
||||
@ -1268,7 +1269,7 @@ int CRenderer::LoadAlphaMaps()
|
||||
uint bpp = 0;
|
||||
for(uint i=0;i<NumAlphaMaps;i++)
|
||||
{
|
||||
(void)pp_append_file(&pp, fnames[i]);
|
||||
(void)path_package_append_file(&pp, fnames[i]);
|
||||
// note: these individual textures can be discarded afterwards;
|
||||
// we cache the composite.
|
||||
textures[i] = ogl_tex_load(pp.path, RES_NO_CACHE);
|
||||
|
@ -72,7 +72,7 @@ int WaterManager::LoadWaterTextures()
|
||||
|
||||
while (cur_loading_water_tex < num_textures)
|
||||
{
|
||||
char waterName[VFS_MAX_PATH];
|
||||
char waterName[PATH_MAX];
|
||||
// TODO: add a member variable and setter for this. (can't make this
|
||||
// a parameter because this function is called via delay-load code)
|
||||
static const char* const water_type = "animation2";
|
||||
|
Loading…
Reference in New Issue
Block a user