File paths
Overview
The SCR library manipulates file and directory paths. To simplify this
task, it uses the scr_path
data structure. There are a number of
functions to manipulate paths, including combining, slicing,
simplification, computing relative paths, and converting to/from
character strings.
This object stores file paths as a linked list of path components, where a component is a character string separated by ‘/’ symbols. An empty string is a valid component, and they are often found as the first component in an absolute path, as in “/hello/world”, or as the last component in a path ending with ‘/’. Components are indexed starting at 0.
Common functions
This section lists the most common functions used when dealing with
paths. For a full listing, refer to comments in scr_path.h
. The
implementation can be found in scr_path.c
.
Creating and freeing path objects
First, before using a path, one must allocate a path object.
scr_path* path = scr_path_new();
This allocates an empty (or “null”) path having 0 components. One must free the path when done with it.
scr_path_delete(&path);
One may also create a path from a character string.
scr_path* path = scr_path_from_str("/hello/world");
This splits the path into components at ‘/’ characters. In this example, the resulting path would have three components, consisting of the empty string, “hello”, and “world”. One can construct a path from a formatted string.
scr_path* path = scr_path_from_strf("/%s/%s/%d", dir1, dir2, id);
Or to make a full copy of a path as path2.
scr_path* path2 = scr_path_dup(path);
Querying paths and converting them to character string
One can determine the number of components in a path.
int components = scr_path_components(path);
A shortcut is available to identify a “null” path (i.e., a path with 0 components).
int is_null_flag = scr_path_is_null(path);
This function returns 1 if the path has 0 components and 0 otherwise. You can determine whether a path is absolute.
int is_absolute_flag = scr_path_is_absolute(path);
This returns 1 if the path starts with an empty string and 0 otherwise. The character representation of such a path starts with a ‘/’ character or otherwise it is the empty string.
To get the number of characters in a path.
size_t len = scr_path_strlen(path);
This count includes ‘/’ characters, but like the strlen
function, it
excludes the terminating NULL character.
One can convert a path and return it as a newly allocated character string.
char* str = scr_path_strdup(path);
scr_free(&str);
The caller is responsible for freeing the returned string.
Or one can copy the path into a buffer as a character string.
char buf[bufsize];
scr_path_strcpy(buf, bufsize, path);
Combining paths
There are functions to prepend and append entries to a path. To prepend entries of path2 to path1 (does not affect path2).
scr_path_prepend(path1, path2);
Similarly to append path2 to path1.
scr_path_append(path1, path2);
Or one can insert entries of path2 into path1 at an arbitrary location.
scr_path_insert(path1, offset, path2);
Here offset
can be any value in the range \([0, N]\) where
\(N\) is the number of components in path1
. With an offset of 0,
the entries of path2 are inserted before the first component of path1.
With an offset of \(N-1\), path2 in inserted before the last
component of path1. An offset of \(N\) inserts path2 after the last
component of path1.
In addition, one may insert a string into a path using functions ending
with _str
, e.g., scr_path_prepend_str
. One may insert a
formatted string into a path using functions ending with _strf
,
e.g., scr_path_prepend_strf
.
Slicing paths
A number of functions are available to slice paths into smaller pieces. First, one can chop components from the start and end of a path.
scr_path_slice(path, offset, length);
This modifies path
to keep length components starting from the
specified offset. The offset can be negative to count from the back. A
negative length means that all components are taken starting from the
offset to the end of the path.
A shortcut to chop off the last component.
scr_path_dirname(path);
A shortcut that keeps only the last component.
scr_path_basename(path);
The following function cuts a path in two at the specified offset. All components starting at offset are returned as a newly allocated path. The original path is modified to contain the beginning components.
scr_path* path2 = scr_path_cut(path1, offset);
The above functions modify the source path. If one wants to take a piece of a path without modifying the source, you can use the following function. To create a new path which is a substring of a path.
scr_path* path2 = scr_path_sub(path, offset, length);
The offset and length values have the same meaning as in
scr_path_slice
.
Other path manipulation
A common need when dealing with paths is to simplify them to some reduced form. The following function eliminates all “.”, “..”, consecutive ‘/’, and trailing ‘/’ characters.
scr_path_reduce(path);
As an example, the above function converts a path like “/hello/world/../foo/bar/.././” to “/hello/foo”.
Since it is common to start from a string, reduce the path, and convert back to a string, there is a shortcut that allocates a new, reduced path as a string.
char* reduced_str = scr_path_strdup_reduce_str(str);
scr_free(&reduced_str);
The caller is responsible for freeing the returned string.
Another useful function is to compute one path relative to another.
scr_path* path = scr_path_relative(src, dst);
This function computes dst
as a path relative to src
and returns
the result as a newly allocated path object. For example, if src
is
“/hello/world” and dst
is “/hello/foo”, the returned path would be
“../foo”.