Skip to content

Commit

Permalink
Introduce cpj_path_join_and_normalize
Browse files Browse the repository at this point in the history
Make sure the test case more consistence with nodejs
  • Loading branch information
lygstate committed Dec 16, 2024
1 parent 4f3c9d2 commit cbde67c
Show file tree
Hide file tree
Showing 6 changed files with 954 additions and 990 deletions.
3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.9.2)
cmake_minimum_required(VERSION 3.10)

# set project name
project(cpj
Expand Down Expand Up @@ -175,6 +175,7 @@ if(ENABLE_TESTS)
create_test(DEFAULT normalize mixed)
create_test(DEFAULT normalize overlap)
create_test(DEFAULT normalize empty)
create_test(DEFAULT normalize zero_length)
create_test(DEFAULT normalize only_separators)
create_test(DEFAULT normalize back_after_root)
create_test(DEFAULT normalize forward_slashes)
Expand Down
190 changes: 152 additions & 38 deletions include/cpj.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

#if defined(_WIN32) || defined(__CYGWIN__)
#define CPJ_EXPORT __declspec(dllexport)
Expand Down Expand Up @@ -34,17 +35,25 @@ extern "C"

typedef char cpj_char_t;
typedef size_t cpj_size_t;
#define CPJ_SIZE_MAX SIZE_MAX

/**
* Description of a JerryScript string for arguments passing
*/
typedef struct
{
const cpj_char_t
*ptr; /**< pointer to the zero-terminated ASCII/UTF-8/CESU-8 string */
/**< pointer to the zero-terminated ASCII/UTF-8/CESU-8 string */
const cpj_char_t *ptr;
cpj_size_t size; /**< size of the string, excluding '\0' terminator */
} cpj_string_t;

typedef struct
{
cpj_size_t segment_count_base;
cpj_size_t segment_count_other;
cpj_size_t equal_segment;
} cpj_path_intersection_t;

/**
* A segment represents a single component of a path. For instance, on linux a
* path might look like this "/var/log/", which consists of two segments "var"
Expand All @@ -56,7 +65,7 @@ struct cpj_segment
const cpj_char_t *segments;
const cpj_char_t *begin;
const cpj_char_t *end;
size_t size;
cpj_size_t size;
};

/**
Expand Down Expand Up @@ -96,6 +105,69 @@ typedef enum
#define CPJ_ZSTR_ARG(str) \
((CPJ_ZSTR_LITERAL(str))), ((cpj_size_t)(sizeof(str) - 1))

/**
* @brief Checks whether the submitted pointer points to a separator.
*
* This function simply checks whether the submitted pointer points to a
* separator, which has to be null-terminated (but not necessarily after the
* separator). The function will return true if it is a separator, or false
* otherwise.
*
* @param path_style Style depending on the operating system. So this should
* detect whether we should use windows or unix paths.
* @param ch A character
* @return Returns true if it is a separator, or false otherwise.
*/
CPJ_PUBLIC bool
cpj_path_is_separator(cpj_path_style_t style, const cpj_char_t ch);

/**
* Join and normalize the `path_list_p`.
*
* @note
* - If is_resolve is true. The given sequence of paths is processed from right
* to left, with each subsequent path prepended until an absolute path is
* constructed. For instance, given the sequence of path segments: /foo, /bar,
* baz, calling path.resolve('/foo', '/bar', 'baz') would return /bar/baz
* because 'baz' is not an absolute path but
* '/bar' + '/' + 'baz' is.
* - If is_resolve is false. All paths are joined
*
* @return The size of the joined path, excluding the '\0' teminiator
*/
CPJ_PUBLIC
cpj_size_t cpj_path_join_and_normalize(
cpj_path_style_t path_style, /**< The style of the path list */
bool is_resolve, /**< Join path in resolve mode */
bool remove_trailing_slash, /**< If remove the trailing slash symbol */
const cpj_string_t *path_list_p, /**< Path list */
cpj_size_t path_list_count, /**< Path list count */
cpj_char_t *buffer_p, /**< The buffer to storaged the joined path */
cpj_size_t buffer_size /**< The size of the buffer_p */
);

CPJ_PUBLIC
cpj_path_intersection_t cpj_path_get_intersection_segments(
cpj_path_style_t path_style, const cpj_string_t *path_base,
const cpj_string_t *path_other, cpj_size_t path_count
);

CPJ_PUBLIC
cpj_size_t cpj_path_relative_to(
cpj_path_style_t path_style, const cpj_string_t *cwd_directory,
const cpj_string_t *path_directory, const cpj_string_t *path,
cpj_char_t *buffer, cpj_size_t buffer_size
);

cpj_size_t cpj_strlen(const cpj_char_t *str);

static inline cpj_string_t
cpj_string_create(const cpj_char_t *ptr, cpj_size_t size)
{
cpj_string_t str = {ptr, size};
return str;
}

/**
* @brief Generates an absolute path based on a base.
*
Expand All @@ -116,10 +188,25 @@ typedef enum
* @param buffer_size The size of the result buffer.
* @return Returns the total amount of characters of the new absolute path.
*/
CPJ_PUBLIC cpj_size_t cpj_path_get_absolute(
static inline cpj_size_t cpj_path_get_absolute(
cpj_path_style_t path_style, const cpj_char_t *base, const cpj_char_t *path,
cpj_char_t *buffer, cpj_size_t buffer_size
);
)
{
cpj_string_t paths[3];

// The basename should be an absolute path if the caller is using the API
// correctly. However, he might not and in that case we will append a fake
// root at the beginning.
paths[0] = cpj_string_create(CPJ_ZSTR_ARG("/"));
paths[1] = cpj_string_create(base, cpj_strlen(base));
paths[2] = cpj_string_create(path, cpj_strlen(path));

// Finally join everything together and normalize it.
return cpj_path_join_and_normalize(
path_style, true, true, paths, 3, buffer, buffer_size
);
}

/**
* @brief Generates a relative path based on a base.
Expand Down Expand Up @@ -166,18 +253,31 @@ CPJ_PUBLIC cpj_size_t cpj_path_get_relative(
* @param buffer_size The size of the result buffer.
* @return Returns the total amount of characters of the full, combined path.
*/
CPJ_PUBLIC cpj_size_t cpj_path_join(
static inline cpj_size_t cpj_path_join(
cpj_path_style_t path_style, const cpj_char_t *path_a,
const cpj_char_t *path_b, cpj_char_t *buffer, cpj_size_t buffer_size
);
)
{
cpj_string_t paths[2];

// This is simple. We will just create an array with the two paths which we
// wish to join.
paths[0] = cpj_string_create(path_a, cpj_strlen(path_a));
paths[1] = cpj_string_create(path_b, cpj_strlen(path_b));

// And then call the join and normalize function which will do the hard work
// for us.
return cpj_path_join_and_normalize(
path_style, false, true, paths, 2, buffer, buffer_size
);
}

/**
* @brief Joins multiple paths together.
* @brief Joins two paths together.
*
* This function generates a new path by joining multiple paths together. It
* This function generates a new path by combining the two submitted paths. It
* will remove double separators, and unlike cpj_path_get_absolute it permits
* the use of multiple relative paths to combine. The last path of the
* submitted string array must be set to NULL. The result will be written to a
* the use of two relative paths to combine. The result will be written to a
* buffer, which might be truncated if the buffer is not large enough to hold
* the full path. However, the truncated result will always be
* null-terminated. The returned value is the amount of characters which the
Expand All @@ -186,15 +286,26 @@ CPJ_PUBLIC cpj_size_t cpj_path_join(
*
* @param path_style Style depending on the operating system. So this should
* detect whether we should use windows or unix paths.
* @param paths An array of paths which will be joined.
* @param path_a The first path which comes first.
* @param path_b The second path which comes after the first.
* @param buffer The buffer where the result will be written to.
* @param buffer_size The size of the result buffer.
* @return Returns the total amount of characters of the full, combined path.
*/
CPJ_PUBLIC cpj_size_t cpj_path_join_multiple(
cpj_path_style_t path_style, const cpj_char_t **paths, cpj_char_t *buffer,
cpj_size_t buffer_size
);
static inline cpj_size_t cpj_path_join_module(
cpj_path_style_t path_style, const cpj_char_t *path_a,
const cpj_char_t *path_b, cpj_char_t *buffer, cpj_size_t buffer_size
)
{
cpj_string_t path_list[2] = {
{(const cpj_char_t *)path_a, (cpj_size_t)cpj_strlen(path_a)},
{(const cpj_char_t *)path_b, (cpj_size_t)cpj_strlen(path_b)},
};
return cpj_path_join_and_normalize(
path_style, true, true, path_list, sizeof(path_list) / sizeof(path_list[0]),
buffer, buffer_size
);
}

/**
* @brief Determines the root of a path.
Expand Down Expand Up @@ -246,8 +357,13 @@ CPJ_PUBLIC cpj_size_t cpj_path_change_root(
* @param path The path which will be checked.
* @return Returns true if the path is absolute or false otherwise.
*/
CPJ_PUBLIC bool
cpj_path_is_absolute(cpj_path_style_t path_style, const cpj_char_t *path);
static inline bool
cpj_path_is_absolute(cpj_path_style_t path_style, const cpj_char_t *path)
{
cpj_size_t length = cpj_path_get_root(path_style, path);
return length > 0 ? cpj_path_is_separator(path_style, path[length - 1])
: false;
}

/**
* @brief Determine whether the path is relative or not.
Expand All @@ -260,8 +376,11 @@ cpj_path_is_absolute(cpj_path_style_t path_style, const cpj_char_t *path);
* @param path The path which will be checked.
* @return Returns true if the path is relative or false otherwise.
*/
CPJ_PUBLIC bool
cpj_path_is_relative(cpj_path_style_t path_style, const cpj_char_t *path);
static inline bool
cpj_path_is_relative(cpj_path_style_t path_style, const cpj_char_t *path)
{
return !cpj_path_is_absolute(path_style, path);
}

/**
* @brief Gets the basename of a file path.
Expand Down Expand Up @@ -416,10 +535,21 @@ CPJ_PUBLIC cpj_size_t cpj_path_change_extension(
* @return The size which the complete normalized path has if it was not
* truncated.
*/
CPJ_PUBLIC cpj_size_t cpj_path_normalize(
static inline cpj_size_t cpj_path_normalize(
cpj_path_style_t path_style, const cpj_char_t *path, cpj_char_t *buffer,
cpj_size_t buffer_size
);
)
{
cpj_string_t paths[1];

// Now we initialize the paths which we will normalize. Since this function
// only supports submitting a single path, we will only add that one.
paths[0] = cpj_string_create(path, cpj_strlen(path));

return cpj_path_join_and_normalize(
path_style, false, true, paths, 1, buffer, buffer_size
);
}

/**
* @brief Finds common portions in two paths.
Expand Down Expand Up @@ -548,22 +678,6 @@ CPJ_PUBLIC cpj_size_t cpj_path_change_segment(
const cpj_char_t *value, cpj_char_t *buffer, cpj_size_t buffer_size
);

/**
* @brief Checks whether the submitted pointer points to a separator.
*
* This function simply checks whether the submitted pointer points to a
* separator, which has to be null-terminated (but not necessarily after the
* separator). The function will return true if it is a separator, or false
* otherwise.
*
* @param path_style Style depending on the operating system. So this should
* detect whether we should use windows or unix paths.
* @param str A pointer to a string.
* @return Returns true if it is a separator, or false otherwise.
*/
CPJ_PUBLIC bool
cpj_path_is_separator(cpj_path_style_t path_style, const cpj_char_t *str);

/**
* @brief Guesses the path style.
*
Expand Down
Loading

0 comments on commit cbde67c

Please sign in to comment.