1
1
forked from 0ad/0ad

#merge (2)

path_util: add cppdoc
string_s: fix self test
wsock: fix initialization (no longer requires NLSO ctor, which doesn't
work)

This was SVN commit r4010.
This commit is contained in:
janwas 2006-06-22 18:49:23 +00:00
parent ee4c7965dd
commit aeac009990
6 changed files with 215 additions and 139 deletions

View File

@ -502,9 +502,6 @@ extern const wchar_t* debug_dump_stack(wchar_t* buf, size_t max_chars, uint skip
**/
extern void debug_puts(const char* text);
/// abstraction of all STL iterators used by debug_stl.
typedef const u8* (*DebugIterator)(void* internal, size_t el_size);
/**
* return address of the Nth function on the call stack.
*

View File

@ -337,7 +337,9 @@ ERR(105, INFO_ALL_COMPLETE, "5 (not an error)")
ERR(106, INFO_ALREADY_EXISTS, "6 (not an error)")
ERR(-100000, ERR_LOGIC, "Logic error in code")
ERR(-100060, ERR_TIMED_OUT, "Timed out")
ERR(-100001, ERR_TIMED_OUT, "Timed out")
ERR(-100002, ERR_STRING_NOT_TERMINATED, "Invalid string (no 0 terminator found in buffer)")
// these are for cases where we just want a distinct value to display and
// a symbolic name + string would be overkill (e.g. the various

View File

@ -35,103 +35,163 @@
#include "posix.h" // PATH_MAX
// if path is invalid (see source for criteria), return a
// descriptive error code, otherwise INFO_OK.
/**
* check if path is valid. (see source for criteria)
*
* @return LibError (ERR_PATH_* or INFO_OK)
**/
extern LibError path_validate(const char* path);
/**
* return appropriate code if path is invalid, otherwise continue.
**/
#define CHECK_PATH(path) RETURN_ERR(path_validate(path))
// if name is invalid, (see source for criteria), return a
// descriptive error code, otherwise INFO_OK.
/**
* check if name is valid. (see source for criteria)
*
* @return LibError (ERR_PATH_* or INFO_OK)
**/
extern LibError path_component_validate(const char* name);
// is s2 a subpath of s1, or vice versa?
// (equal counts as subpath)
/**
* is s2 a subpath of s1, or vice versa? (equal counts as subpath)
*
* @param s1, s2 comparand strings
* @return bool
**/
extern bool path_is_subpath(const char* s1, const char* s2);
// if path is invalid, return a descriptive error code, otherwise INFO_OK.
extern LibError path_validate(const char* path);
// if name is invalid, return a descriptive error code, otherwise INFO_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).
/**
* copy path strings (provided for convenience).
*
* @param dst destination; must be at least as large as source buffer,
* and should hold PATH_MAX chars.
* @param src source; should not exceed PATH_MAX chars
**/
extern void path_copy(char* dst, const char* src);
/**
* flags controlling path_append behavior
**/
enum PathAppendFlags
{
// make sure <dst> ends up with a trailing slash. this is useful for
// VFS directory paths, which have that requirement.
/**
* make sure <dst> ends up with a trailing slash. this is useful for
* VFS directory paths, which have that requirement.
**/
PATH_APPEND_SLASH = 1
};
// 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.
/**
* append one path onto another, adding directory separator if necessary.
*
* @param dst destination into which combined path is written;
* must hold at least PATH_MAX chars.
* @param path1, path2 strings: empty, filenames, or full paths.
* total resulting string must not exceed PATH_MAX chars.
* @param flags see PathAppendFlags.
* @return LibError
**/
extern LibError path_append(char* dst, const char* path1, const char* path2,
uint flags = 0);
// strip <remove> from the start of <src>, prepend <replace>,
// and write to <dst>.
// returns ERR_FAIL (without warning!) if the beginning of <src> doesn't
// match <remove>.
/**
* at the start of a path, replace the given substring with another.
*
* @param dst destination; must hold at least PATH_MAX chars.
* @param src source string.
* @param remove substring to remove; must match (case-sensitive) the
* start of src.
* @param replace string to prepend to output after stripping <remove>.
* @return LibError; ERR_FAIL (without warning!) if <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).
/**
* get the name component of a path.
*
* skips over all characters up to the last dir separator, if any.
* @param path input path.
* @return pointer to name component within <path>.
**/
extern const char* path_name_only(const char* path);
// return last component within path. this is similar to path_name_only,
// but correctly handles VFS paths, which must end with '/'.
// (path_name_only would return "")
/**
* get the last component of a path.
*
* this is similar to path_name_only, but correctly handles VFS paths,
* which must end with '/'. (path_name_only would return "")
* @param path input path.
* @return pointer to last component within <path>.
**/
extern const char* path_last_component(const char* path);
// if <path> contains a name component, it is stripped away.
/**
* strip away the name component in a path.
*
* @param path input and output; chopped by inserting '\0'.
**/
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.
/**
* retrieve only the directory path portion of a path.
*
* @param path source path.
* @param dir output directory path ("" if root dir,
* otherwise it ends with '/').
* note: implementation via path_copy and path_strip_fn.
**/
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".
/**
* get filename's extension.
*
* @return pointer to extension within <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);
// called for each component in a path string, indicating if it's
// a directory (i.e. <component> is followed by a slash in the original
// path).
// if path is empty (i.e. ""), this is not called.
//
// component: 0-terminated name of the component (does not include any
// trailing slash!)
// ctx: context parameter that was passed to path_foreach_component.
// return: INFO_CB_CONTINUE to continue operation normally; anything else
// will cause path_foreach_component to abort immediately and return that.
// no need to 'abort' (e.g. return INFO_OK) after a filename is encountered -
// that's taken care of automatically.
//
// rationale:
// - we indicate if it's a directory via bool. this isn't as nice as a
// flag or enum, but vfs_tree already has TNodeType and we don't want
// to expose that or create a new one.
/**
* callback for each component in a path string.
*
* if path is empty (i.e. ""), this is not called.
*
* @param component: 0-terminated name of the component (does not
* include any trailing slash!)
* @param is_dir indicates if it's a directory (i.e. <component> is
* followed by a slash in the original path).
* rationale: a bool isn't as nice as a flag or enum, but vfs_tree already
* has TNodeType and we don't want to expose that or create a new one.
* @param ctx: context parameter that was passed to path_foreach_component.
* @return LibError; INFO_CB_CONTINUE to continue operation normally;
* anything else will cause path_foreach_component to abort immediately and
* return that. no need to 'abort' (e.g. return INFO_OK) after a filename is
* encountered - that's taken care of automatically.
**/
typedef LibError (*PathComponentCb)(const char* component, bool is_dir, void* ctx);
// call <cb> with <ctx> for each component in <path>.
/**
* call <cb> with <ctx> for each component in <path>.
* @return LibError
**/
extern LibError path_foreach_component(const char* path, PathComponentCb cb, void* ctx);
//-----------------------------------------------------------------------------
// 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.
/**
* 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;
@ -139,16 +199,22 @@ struct PathPackage
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.
/**
* 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.
* @return LibError
**/
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.
/**
* 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.
* @return LibError
**/
extern LibError path_package_append_file(PathPackage* pp, const char* path);
#endif // #ifndef PATH_UTIL_H__

View File

@ -59,10 +59,10 @@
// return <retval> and raise an assertion if <condition> doesn't hold.
// usable as a statement.
#define ENFORCE(condition, retval) STMT(\
#define ENFORCE(condition, err_to_warn, retval) STMT(\
if(!(condition)) \
{ \
debug_assert(condition); \
DEBUG_WARN_ERR(err_to_warn); \
return retval; \
} \
)
@ -113,10 +113,10 @@ int tncpy_s(tchar* dst, size_t max_dst_chars, const tchar* src, size_t max_src_c
// the MS implementation returns EINVAL and allows dst = 0 if
// max_dst_chars = max_src_chars = 0. no mention of this in
// 3.6.2.1.1, so don't emulate that behavior.
ENFORCE(dst != 0, EINVAL);
ENFORCE(max_dst_chars != 0, ERANGE);
ENFORCE(dst != 0, ERR_INVALID_PARAM, EINVAL);
ENFORCE(max_dst_chars != 0, ERR_INVALID_PARAM, ERANGE);
*dst = '\0'; // in case src ENFORCE is triggered
ENFORCE(src != 0, EINVAL);
ENFORCE(src != 0, ERR_INVALID_PARAM, EINVAL);
WARN_IF_PTR_LEN(max_dst_chars);
WARN_IF_PTR_LEN(max_src_chars);
@ -135,11 +135,11 @@ int tncpy_s(tchar* dst, size_t max_dst_chars, const tchar* src, size_t max_src_c
}
// which limit did we hit?
// .. dest, and last character wasn't null: overflow.
// .. dst, and last character wasn't null: overflow.
if(max_dst_chars <= max_src_chars)
{
*dst = '\0';
ENFORCE(0 && "Buffer too small", ERANGE);
ENFORCE(0, ERR_BUF_SIZE, ERANGE);
}
// .. source: success, but still need to null-terminate the destination.
*p = '\0';
@ -163,8 +163,8 @@ int tcpy_s(tchar* dst, size_t max_dst_chars, const tchar* src)
// 0 is returned to indicate success and that <dst> is null-terminated.
int tncat_s(tchar* dst, size_t max_dst_chars, const tchar* src, size_t max_src_chars)
{
ENFORCE(dst != 0, EINVAL);
ENFORCE(max_dst_chars != 0, ERANGE);
ENFORCE(dst != 0, ERR_INVALID_PARAM, EINVAL);
ENFORCE(max_dst_chars != 0, ERR_INVALID_PARAM, ERANGE);
// src is checked in tncpy_s
// WARN_IF_PTR_LEN not necessary: both max_dst_chars and max_src_chars
@ -174,7 +174,7 @@ int tncat_s(tchar* dst, size_t max_dst_chars, const tchar* src, size_t max_src_c
if(dst_len == max_dst_chars)
{
*dst = '\0';
ENFORCE(0 && "Destination string not null-terminated", ERANGE);
ENFORCE(0, ERR_STRING_NOT_TERMINATED, ERANGE);
}
tchar* const end = dst+dst_len;

View File

@ -34,6 +34,8 @@
#endif
#pragma data_seg(WIN_CALLBACK_PRE_MAIN(k))
WIN_REGISTER_FUNC(wsock_init);
#pragma data_seg(WIN_CALLBACK_POST_ATEXIT(b))
WIN_REGISTER_FUNC(wsock_shutdown);
#pragma data_seg()
@ -51,7 +53,7 @@ static int dll_refs;
// called from delay loader the first time a wsock function is called
// (shortly before the actual wsock function is called).
static LibError wsock_init()
static LibError wsock_actual_init()
{
hWs2_32Dll = LoadLibrary("ws2_32.dll");
@ -66,9 +68,14 @@ static LibError wsock_init()
return INFO_OK;
}
WDLL_LOAD_NOTIFY("ws2_32", wsock_init);
// called via module init mechanism. triggers wsock_actual_init when
// someone first calls a wsock function.
static LibError wsock_init()
{
WDLL_LOAD_NOTIFY("ws2_32", wsock_actual_init);
return INFO_OK;
}
static LibError wsock_shutdown()
{

View File

@ -29,7 +29,7 @@ class TestString_s : public CxxTest::TestSuite
char no_null[7];
static void TEST_LEN(const char* string, size_t limit, size_t expected)
static void TEST_LEN(const char* string, size_t limit,
{
TS_ASSERT_EQUALS(strnlen((string), (limit)), (expected));
}
@ -37,13 +37,13 @@ class TestString_s : public CxxTest::TestSuite
static void TEST_CPY(char* dst, size_t dst_max, const char* src,
int expected_ret, const char* expected_dst)
{
int ret = strcpy_s((dst), dst_max, (src));
int ret = strcpy_s(dst, dst_max, src);
TS_ASSERT_EQUALS(ret, expected_ret);
if(dst != 0)
TS_ASSERT(!strcmp(dst, expected_dst));
}
static void TEST_CPY2(char* dst, const char* src,
static void TEST_CPY2(char* dst, size_t max_dst_chars, const char* src,
int expected_ret, const char* expected_dst)
{
int ret = strcpy_s((dst), ARRAY_SIZE(dst), (src));
@ -52,40 +52,39 @@ class TestString_s : public CxxTest::TestSuite
TS_ASSERT(!strcmp(dst, expected_dst));
}
static void TEST_NCPY(char* dst, const char* src, size_t max_src_chars,
static void TEST_NCPY(char* dst, size_t max_dst_chars, const char* src, size_t max_src_chars,
int expected_ret, const char* expected_dst)
{
int ret = strncpy_s((dst), ARRAY_SIZE(dst), (src), (max_src_chars));
int ret = strncpy_s(dst, max_dst_chars, src, max_src_chars);
TS_ASSERT_EQUALS(ret, expected_ret);
if(dst != 0)
TS_ASSERT(!strcmp(dst, expected_dst));
}
static void TEST_CAT(char* dst, size_t dst_max, const char* src,
int expected_ret, const char* expected_dst)
static void TEST_CAT(char* dst, size_t max_dst_chars, const char* src,
int expected_ret, const char expected_dst)
{
int ret = strcat_s((dst), dst_max, (src));
int ret = strcat_s(dst, max_dst_chars, src);
TS_ASSERT_EQUALS(ret, expected_ret);
if(dst != 0)
TS_ASSERT(!strcmp(dst, expected_dst));
}
static void TEST_CAT2(char* dst, const char* dst_val, const char* src,
int expected_ret, const char* expected_dst)
static void TEST_CAT2(char* dst, size_t max_dst_chars, const char* src,
const char* dst_val, int expected_ret, const char* expected_dst)
{
strcpy(dst, dst_val);
int ret = strcat_s((dst), ARRAY_SIZE(dst), (src));
int ret = strcat_s(dst, max_dst_chars, src);
TS_ASSERT_EQUALS(ret, expected_ret);
if(dst != 0)
TS_ASSERT(!strcmp(dst, expected_dst));
}
static void TEST_NCAT(char* dst, const char* dst_val,
const char* src, size_t max_src_chars,
int expected_ret, const char* expected_dst)
static void TEST_NCAT(char* dst, size_t max_dst_chars, const char* src, size_t max_src_chars,
const char* dst_val, int expected_ret, const char* expected_dst)
{
strcpy(dst, dst_val);
int ret = strncat_s((dst), ARRAY_SIZE(dst), (src), (max_src_chars));
int ret = strncat_s(dst, max_dst_chars, src, (max_src_chars));
TS_ASSERT_EQUALS(ret, expected_ret);
if(dst != 0)
TS_ASSERT(!strcmp(dst, expected_dst));
@ -108,19 +107,24 @@ public:
void test_param_validation()
{
#if !HAVE_STRING_S
expect(ERR_INVALID_PARAM);
TEST_CPY(0 ,0,0 , EINVAL,""); // all invalid
expect(ERR_INVALID_PARAM);
TEST_CPY(0 ,0,s1, EINVAL,""); // dst = 0, max = 0
expect(ERR_INVALID_PARAM);
TEST_CPY(0 ,1,s1, EINVAL,""); // dst = 0, max > 0
expect(ERR_INVALID_PARAM);
TEST_CPY(d1,1,0 , EINVAL,""); // src = 0
expect(ERR_INVALID_PARAM);
TEST_CPY(d1,0,s1, ERANGE,""); // max_dst_chars = 0
TEST_CPY2(d1 ,s1, ERANGE,"");
TEST_CPY2(d1 ,s5, ERANGE,"");
TEST_CPY2(d5 ,s5, ERANGE,"");
TEST_CPY2(d1,1, s1, ERANGE,"");
TEST_CPY2(d1,1, s5, ERANGE,"");
TEST_CPY2(d5,5, s5, ERANGE,"");
TEST_NCPY(d1 ,s1,1, ERANGE,"");
TEST_NCPY(d1 ,s5,1, ERANGE,"");
TEST_NCPY(d5 ,s5,5, ERANGE,"");
TEST_NCPY(d1,1 ,s1,1, ERANGE,"");
TEST_NCPY(d1,1 ,s5,1, ERANGE,"");
TEST_NCPY(d5,5 ,s5,5, ERANGE,"");
TEST_CAT(0 ,0,0 , EINVAL,""); // all invalid
TEST_CAT(0 ,0,s1, EINVAL,""); // dst = 0, max = 0
@ -129,17 +133,17 @@ public:
TEST_CAT(d1,0,s1, ERANGE,""); // max_dst_chars = 0
TEST_CAT(no_null,5,s1, ERANGE,""); // dst not terminated
TEST_CAT2(d1 ,"" ,s1, ERANGE,"");
TEST_CAT2(d1 ,"" ,s5, ERANGE,"");
TEST_CAT2(d10,"" ,s10, ERANGE,""); // empty, total overflow
TEST_CAT2(d10,"12345",s5 , ERANGE,""); // not empty, overflow
TEST_CAT2(d10,"12345",s10, ERANGE,""); // not empty, total overflow
TEST_CAT2(d1,1, s1, "",ERANGE,"");
TEST_CAT2(d1,1, s5, "",ERANGE,"");
TEST_CAT2(d10,10, s10, "",ERANGE,""); // empty, total overflow
TEST_CAT2(d10,10, s5, "12345",ERANGE,""); // not empty, overflow
TEST_CAT2(d10,10, s10, "12345",ERANGE,""); // not empty, total overflow
TEST_NCAT(d1 ,"" ,s1,1, ERANGE,"");
TEST_NCAT(d1 ,"" ,s5,5, ERANGE,"");
TEST_NCAT(d10,"" ,s10,10, ERANGE,""); // empty, total overflow
TEST_NCAT(d10,"12345",s5 ,5 , ERANGE,""); // not empty, overflow
TEST_NCAT(d10,"12345",s10,10, ERANGE,""); // not empty, total overflow
TEST_NCAT(d1,1, s1,1, "",ERANGE,"");
TEST_NCAT(d1,1, s5,5, "",ERANGE,"");
TEST_NCAT(d10,10, s10,10, "",ERANGE,""); // empty, total overflow
TEST_NCAT(d10,10, s5,5, "12345",ERANGE,""); // not empty, overflow
TEST_NCAT(d10,10, s10,10, "12345",ERANGE,""); // not empty, total overflow
#endif
}
@ -163,41 +167,41 @@ public:
void test_copy()
{
TEST_CPY2(d2 ,s1, 0,"a");
TEST_CPY2(d6 ,s5, 0,"abcde");
TEST_CPY2(d11,s5, 0,"abcde");
TEST_CPY2(d2,2 ,s1, 0,"a");
TEST_CPY2(d6,6 ,s5, 0,"abcde");
TEST_CPY2(d11,11, s5, 0,"abcde");
TEST_NCPY(d2 ,s1,1, 0,"a");
TEST_NCPY(d6 ,s5,5, 0,"abcde");
TEST_NCPY(d11,s5,5, 0,"abcde");
TEST_NCPY(d2,2 ,s1,1, 0,"a");
TEST_NCPY(d6,6 ,s5,5, 0,"abcde");
TEST_NCPY(d11,11, s5,5, 0,"abcde");
strcpy(d5, "----");
TEST_NCPY(d5,s5,0 , 0,""); // specified behavior! see 3.6.2.1.1 #4
TEST_NCPY(d5,s5,1 , 0,"a");
TEST_NCPY(d5,s5,4 , 0,"abcd");
TEST_NCPY(d6,s5,5 , 0,"abcde");
TEST_NCPY(d6,s5,10, 0,"abcde");
TEST_NCPY(d5,5, s5,0 , 0,""); // specified behavior! see 3.6.2.1.1 #4
TEST_NCPY(d5,5, s5,1 , 0,"a");
TEST_NCPY(d5,5, s5,4 , 0,"abcd");
TEST_NCPY(d6,6, s5,5 , 0,"abcde");
TEST_NCPY(d6,6, s5,10, 0,"abcde");
}
void test_concatenate()
{
TEST_CAT2(d3 ,"1",s1, 0,"1a");
TEST_CAT2(d5 ,"1",s1, 0,"1a");
TEST_CAT2(d6 ,"" ,s5, 0,"abcde");
TEST_CAT2(d10,"" ,s5, 0,"abcde");
TEST_CAT2(d10,"1234" ,s5 , 0,"1234abcde");
TEST_CAT2(d3,3, s1, ,"1",0,"1a");
TEST_CAT2(d5,5, s1, "1",0,"1a");
TEST_CAT2(d6,6, s5, "",0,"abcde");
TEST_CAT2(d10,10, s5, "",0,"abcde");
TEST_CAT2(d10,10, s5, "1234",0,"1234abcde");
TEST_NCAT(d3 ,"1",s1,1, 0,"1a");
TEST_NCAT(d5 ,"1",s1,1, 0,"1a");
TEST_NCAT(d6 ,"" ,s5,5, 0,"abcde");
TEST_NCAT(d10,"" ,s5,5, 0,"abcde");
TEST_NCAT(d10,"1234" ,s5 ,5 , 0,"1234abcde");
TEST_NCAT(d3,3, s1,1, "1",0,"1a");
TEST_NCAT(d5,5, s1,1, "1",0,"1a");
TEST_NCAT(d6,6, s5,5, "",0,"abcde");
TEST_NCAT(d10,10, s5,5, "",0,"abcde");
TEST_NCAT(d10,10, s5,5, "1234",0,"1234abcde");
TEST_NCAT(d5,"----",s5,0 , 0,"----");
TEST_NCAT(d5,"",s5,1 , 0,"a");
TEST_NCAT(d5,"",s5,4 , 0,"abcd");
TEST_NCAT(d5,"12",s5,2 , 0,"12ab");
TEST_NCAT(d6,"",s5,10, 0,"abcde");
TEST_NCAT(d5,5, s5,0, "----",0,"----");
TEST_NCAT(d5,5, s5,1, "",0,"a");
TEST_NCAT(d5,5, s5,4, "",0,"abcd");
TEST_NCAT(d5,5, s5,2, "12",0,"12ab");
TEST_NCAT(d6,6, s5,10, "",0,"abcde");
}
};