Skip to content

Commit b4d6730

Browse files
authored
Reimplement and bugfix environment_variable_path_create, portability_path_get_name, portability_path_get_name_canonical (#580)
- Get rid of some buffer overruns - make more functions return more sensible things on NULL arguments - properly DOCUMENT each function - Add const type qualifiers where applicable - Replace some string functions and loops with memcpy
1 parent f343311 commit b4d6730

File tree

5 files changed

+213
-143
lines changed

5 files changed

+213
-143
lines changed

source/environment/include/environment/environment_variable_path.h

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,31 @@ extern "C" {
5252

5353
/* -- Methods -- */
5454

55-
ENVIRONMENT_API char *environment_variable_path_create(const char *name, const char *default_path, size_t default_path_size, size_t *env_size);
55+
/**
56+
* @brief
57+
* If the value of `name` exists as an environment variable, return a live string of its value, otherwise return a live value of `default_path` or "/".
58+
*
59+
* `name` should not be `NULL`.
60+
*
61+
* If `default_path` is not `NULL`, `default_path_size` must be set to <= the length (including 0-terminator) of the `default_path` string.
62+
*
63+
* If `env_size` is not `NULL`, the length (including 0-terminator) of the returned string will be set to it
64+
* @param[in] name
65+
* The environment variable name to look up.
66+
*
67+
* @param[in] default_path
68+
* If the environment variable value is not found, the value to return instead.
69+
*
70+
* @param[in] default_path_size
71+
* The length (including 0-terminator) of `default_path` in chars.
72+
*
73+
* @param[out] env_size
74+
* Pointer to a size_t to write the length of the returned string to (optional).
75+
*
76+
* @return
77+
* The allocated string containing the environment variable value or the default or "/".
78+
*/
79+
ENVIRONMENT_API char *environment_variable_path_create(const char *const name, const char *const default_path, const size_t default_path_size, size_t *const env_size);
5680

5781
ENVIRONMENT_API void environment_variable_path_destroy(char *variable_path);
5882

source/environment/source/environment_variable_path.c

Lines changed: 20 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -43,61 +43,32 @@
4343

4444
/* -- Methods -- */
4545

46-
char *environment_variable_path_create(const char *name, const char *default_path, size_t default_path_size, size_t *env_size)
46+
char *environment_variable_path_create(const char *const name, const char *const default_path, const size_t default_path_size, size_t *const env_size)
4747
{
48-
const char *path_ptr = getenv(name);
49-
char *path;
50-
size_t length, size, last, end;
51-
52-
if (path_ptr == NULL)
53-
{
54-
if (default_path == NULL)
55-
{
56-
static const char empty_path[] = "";
57-
58-
default_path = empty_path;
59-
default_path_size = sizeof(empty_path);
60-
}
61-
62-
path_ptr = default_path;
63-
length = default_path_size - 1;
64-
}
65-
else
66-
{
67-
length = strlen(path_ptr);
68-
}
69-
70-
last = length - 1;
71-
72-
if (ENVIRONMENT_VARIABLE_PATH_SEPARATOR(path_ptr[last]))
48+
const char *value = getenv(name);
49+
size_t size;
50+
if (value)
51+
size = strlen(value) + 1;
52+
else if (default_path)
7353
{
74-
end = length;
75-
size = length + 1;
54+
value = default_path;
55+
size = default_path_size;
7656
}
7757
else
7858
{
79-
last = length;
80-
end = length + 1;
81-
size = length + 2;
59+
value = ENVIRONMENT_VARIABLE_PATH_SEPARATOR_STR;
60+
size = sizeof(ENVIRONMENT_VARIABLE_PATH_SEPARATOR_STR);
8261
}
83-
84-
path = malloc(sizeof(char) * size);
85-
86-
if (path == NULL)
87-
{
88-
return NULL;
89-
}
90-
91-
strncpy(path, path_ptr, length);
92-
93-
path[last] = ENVIRONMENT_VARIABLE_PATH_SEPARATOR_C;
94-
path[end] = '\0';
95-
96-
if (env_size != NULL)
97-
{
98-
*env_size = size;
99-
}
100-
62+
size_t alloc_size = size;
63+
if (size > 1)
64+
alloc_size += !ENVIRONMENT_VARIABLE_PATH_SEPARATOR(value[size - 2]);
65+
char *const path = malloc(sizeof(char) * alloc_size);
66+
memcpy(path, value, sizeof(char) * size);
67+
if (size > 1)
68+
path[alloc_size - 2] = ENVIRONMENT_VARIABLE_PATH_SEPARATOR_C;
69+
path[alloc_size - 1] = '\0';
70+
if (env_size)
71+
*env_size = alloc_size;
10172
return path;
10273
}
10374

source/portability/include/portability/portability_path.h

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,55 @@ extern "C" {
109109

110110
/* -- Methods -- */
111111

112-
PORTABILITY_API size_t portability_path_get_name(const char *path, size_t path_size, char *name, size_t name_size);
112+
/**
113+
* @brief
114+
* Get the file name portion out of a path and strip away the file extension if any.
115+
*
116+
* If `path` is NULL this will return an empty string.
117+
*
118+
* If `name` is NULL or `name_size is 0 this will return the size it requires in order to write `name`.
119+
*
120+
* If `path` or `name` are not NULL, then `path_size` or `name_size`, respectively, must be set to <= the length (including 0-terminator) of the memory regions pointed to by `path` and `name`.
121+
* @param[in] path
122+
* The full path to extract the name from.
123+
* @param[in] path_size
124+
* The length (including 0-terminator) of `path` in chars.
125+
* @param[out] name
126+
* The memory location to write the extracted name to. If `NULL` the size required will be returned instead of the size written.
127+
* @param[in] name_size
128+
* The size of the memory location pointed to by `name`.
129+
* @return
130+
* The size of the name.
131+
*/
132+
PORTABILITY_API size_t portability_path_get_name(const char *const path, const size_t path_size, char *const name, const size_t name_size);
113133

114-
PORTABILITY_API size_t portability_path_get_name_canonical(const char *path, size_t path_size, char *name, size_t name_size);
134+
/**
135+
* @brief
136+
* Get the file name portion out of a path and strip away any amount of file extensions.
137+
*
138+
* When called with `"/foo/bar.baz.qux"`:
139+
*
140+
* - `portability_path_get_name` will produce the string `"bar.baz"`
141+
*
142+
* - `portability_path_get_name_canonical` will produce the string `"bar"`
143+
*
144+
* If `path` is NULL this will return an empty string.
145+
*
146+
* If `name` is NULL or `name_size is 0 this will return the size it requires in order to write `name`.
147+
*
148+
* If `path` or `name` are not NULL, then `path_size` or `name_size`, respectively, must be set to <= the length (including 0-terminator) of the memory regions pointed to by `path` and `name`.
149+
* @param[in] path
150+
* The full path to extract the name from.
151+
* @param[in] path_size
152+
* The length (including 0-terminator) of `path` in chars.
153+
* @param[out] name
154+
* The memory location to write the extracted name to. If `NULL` the size required will be returned instead of the size written.
155+
* @param[in] name_size
156+
* The size of the memory location pointed to by `name`.
157+
* @return
158+
* The size of the name.
159+
*/
160+
PORTABILITY_API size_t portability_path_get_name_canonical(const char *const path, const size_t path_size, char *const name, const size_t name_size);
115161

116162
PORTABILITY_API size_t portability_path_get_fullname(const char *path, size_t path_size, char *name, size_t name_size);
117163

source/portability/source/portability_path.c

Lines changed: 65 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -25,107 +25,81 @@
2525
/* Define separator checking for any platform */
2626
#define PORTABILITY_PATH_SEPARATOR_ALL(chr) (chr == '\\' || chr == '/')
2727

28-
size_t portability_path_get_name(const char *path, size_t path_size, char *name, size_t name_size)
28+
static size_t basename_offset(const char *const path, const size_t path_size)
2929
{
30-
size_t i, count, last;
31-
32-
if (path == NULL || name == NULL)
33-
{
34-
return 0;
35-
}
36-
37-
for (i = 0, count = 0, last = 0; path[i] != '\0' && i < path_size && count < name_size; ++i)
38-
{
39-
name[count++] = path[i];
40-
41-
if (PORTABILITY_PATH_SEPARATOR(path[i]))
42-
{
43-
count = 0;
44-
}
45-
else if (path[i] == '.')
46-
{
47-
if (i > 0 && path[i - 1] == '.')
48-
{
49-
last = 0;
50-
count = 0;
51-
}
52-
else
53-
{
54-
if (count > 0)
55-
{
56-
last = count - 1;
57-
}
58-
else
59-
{
60-
last = 0;
61-
}
62-
}
63-
}
64-
}
30+
size_t offset = path_size;
31+
for (; offset != 0; offset--)
32+
if (PORTABILITY_PATH_SEPARATOR(path[offset - 1]))
33+
break;
34+
return offset;
35+
}
6536

66-
if ((last == 0 && count > 1) || last > count)
37+
size_t portability_path_get_name(const char *const path, const size_t path_size, char *const name, const size_t name_size)
38+
{
39+
if (path == NULL)
6740
{
68-
last = count;
41+
if (name == NULL || name_size == 0)
42+
return 0;
43+
name[0] = '\0';
44+
return 1;
6945
}
70-
71-
name[last] = '\0';
72-
73-
return last + 1;
46+
// find rightmost path separator
47+
const size_t name_start = basename_offset(path, path_size);
48+
// Find rightmost dot
49+
size_t rightmost_dot = path_size;
50+
for (; rightmost_dot != name_start; rightmost_dot--)
51+
if (path[rightmost_dot - 1] == '.')
52+
break;
53+
// No dots found, or name starts with dot and is non-empty, use whole name
54+
if (rightmost_dot == name_start || (rightmost_dot == name_start + 1 && rightmost_dot != path_size - 1))
55+
rightmost_dot = path_size - 1;
56+
// remove all consecutive dots at the end
57+
while (rightmost_dot != name_start && path[rightmost_dot - 1] == '.')
58+
rightmost_dot--;
59+
const size_t length = rightmost_dot - name_start;
60+
const size_t size = length + 1;
61+
// Return required size
62+
if (name == NULL || size > name_size)
63+
return size;
64+
if (length)
65+
memcpy(name, path + name_start, length);
66+
name[length] = '\0';
67+
return size;
7468
}
7569

76-
size_t portability_path_get_name_canonical(const char *path, size_t path_size, char *name, size_t name_size)
70+
size_t portability_path_get_name_canonical(const char *const path, const size_t path_size, char *const name, const size_t name_size)
7771
{
78-
if (path == NULL || name == NULL)
72+
if (path == NULL)
7973
{
80-
return 0;
74+
if (name == NULL || name_size == 0)
75+
return 0;
76+
name[0] = '\0';
77+
return 1;
8178
}
82-
83-
size_t i, count, last;
84-
85-
for (i = 0, count = 0, last = 0; path[i] != '\0' && i < path_size && count < name_size; ++i)
86-
{
87-
name[count++] = path[i];
88-
89-
if (PORTABILITY_PATH_SEPARATOR(path[i]))
90-
{
91-
count = 0;
92-
}
93-
else if (path[i] == '.')
94-
{
95-
if (i > 0 && path[i - 1] == '.')
96-
{
97-
last = 0;
98-
count = 0;
99-
}
100-
else
101-
{
102-
if (count > 0)
103-
{
104-
last = count - 1;
105-
}
106-
else
107-
{
108-
last = 0;
109-
}
110-
111-
/* This function is the same as portability_path_get_name but
112-
returns the name of the file without any extension, for example:
113-
- portability_path_get_name of libnode.so.72 is libnode.so
114-
- portability_path_get_name_canonical of libnode.so.72 is libnode
115-
*/
79+
// find rightmost path separator
80+
const size_t name_start = basename_offset(path, path_size);
81+
// find leftmost dot
82+
size_t leftmost_dot = name_start;
83+
for (; leftmost_dot < path_size; leftmost_dot++)
84+
if (path[leftmost_dot] == '.')
85+
break;
86+
// No dots found, use whole name
87+
if (leftmost_dot == path_size)
88+
leftmost_dot--;
89+
// name starts with dot, use the following dot instead
90+
if (leftmost_dot == name_start)
91+
for (leftmost_dot = name_start + 1; leftmost_dot < path_size; leftmost_dot++)
92+
if (path[leftmost_dot] == '.')
11693
break;
117-
}
118-
}
119-
}
120-
121-
if (last == 0 && count > 1)
122-
{
123-
last = count;
124-
}
125-
126-
name[last] = '\0';
127-
128-
return last + 1;
94+
const size_t length = leftmost_dot - name_start;
95+
const size_t size = length + 1;
96+
// Return required size
97+
if (name == NULL || size > name_size)
98+
return size;
99+
if (length)
100+
memcpy(name, path + name_start, length);
101+
name[length] = '\0';
102+
return size;
129103
}
130104

131105
size_t portability_path_get_fullname(const char *path, size_t path_size, char *name, size_t name_size)

0 commit comments

Comments
 (0)