From 1167bd97015529b6248b504b8864eb69d7dee89d Mon Sep 17 00:00:00 2001 From: kormax <3392860+kormax@users.noreply.github.com> Date: Tue, 5 May 2026 18:27:19 +0300 Subject: [PATCH] Decompose utils from duox-related code --- client/src/crypto/duoxcrypto.c | 207 ++------------------------------- client/src/crypto/duoxcrypto.h | 1 - client/src/crypto/libpcrypto.c | 89 ++------------ client/src/fileutils.c | 167 +++++++++++++++++++++++++- client/src/fileutils.h | 36 ++++++ client/src/util.c | 72 ++++++++++++ client/src/util.h | 15 +++ 7 files changed, 304 insertions(+), 283 deletions(-) diff --git a/client/src/crypto/duoxcrypto.c b/client/src/crypto/duoxcrypto.c index ae7088131..56a36d25a 100644 --- a/client/src/crypto/duoxcrypto.c +++ b/client/src/crypto/duoxcrypto.c @@ -18,12 +18,9 @@ #include "crypto/duoxcrypto.h" -#include -#include #include #include #include -#include #include #include #include @@ -40,6 +37,7 @@ #include "x509_crt.h" #define DUOX_CERTIFICATE_ANCHOR_INPUT_LEN 8192 +#define DUOX_CERTIFICATE_ANCHOR_SEARCH_DEPTH 4 static int duox_cert_info_from_x509_crt(const mbedtls_x509_crt *cert, duox_cert_info_t *out); @@ -55,189 +53,6 @@ const char *duox_certificate_format_name(duox_certificate_format_t format) { } } -const char *duox_cert_info_format_name(const duox_cert_info_t *cert) { - if (cert == NULL) { - return duox_certificate_format_name(DUOX_CERTIFICATE_FORMAT_UNKNOWN); - } - return duox_certificate_format_name(cert->format); -} - -static void duox_trim_ascii_inplace(char *text) { - if (text == NULL) { - return; - } - - size_t start = 0; - size_t len = strlen(text); - while (start < len && isspace((unsigned char)text[start])) { - start++; - } - while (len > start && isspace((unsigned char)text[len - 1])) { - len--; - } - - if (start > 0) { - memmove(text, text + start, len - start); - } - text[len - start] = '\0'; -} - -static int duox_copy_without_whitespace(const char *src, char *dst, size_t dst_size, size_t *dst_len) { - if (src == NULL || dst == NULL || dst_len == NULL || dst_size == 0) { - return PM3_EINVARG; - } - - size_t out = 0; - for (size_t i = 0; src[i] != '\0'; i++) { - if (isspace((unsigned char)src[i])) { - continue; - } - if ((out + 1) >= dst_size) { - return PM3_EOVFLOW; - } - dst[out++] = src[i]; - } - dst[out] = '\0'; - *dst_len = out; - return PM3_SUCCESS; -} - -static bool duox_path_is_directory(const char *path) { - if (path == NULL) { - return false; - } - struct stat st; - if (stat(path, &st) != 0) { - return false; - } - return S_ISDIR(st.st_mode) != 0; -} - -static bool duox_path_is_regular_file(const char *path) { - if (path == NULL) { - return false; - } - struct stat st; - if (stat(path, &st) != 0) { - return false; - } - return S_ISREG(st.st_mode) != 0; -} - -static const char *duox_path_basename(const char *path) { - if (path == NULL) { - return ""; - } - - const char *base = strrchr(path, '/'); - const char *base_win = strrchr(path, '\\'); - if (base == NULL || (base_win != NULL && base_win > base)) { - base = base_win; - } - return (base == NULL) ? path : (base + 1); -} - -static void duox_path_basename_without_ext(const char *path, char *out, size_t out_len) { - if (out == NULL || out_len == 0) { - return; - } - out[0] = '\0'; - - const char *base = duox_path_basename(path); - if (base[0] == '\0') { - return; - } - - snprintf(out, out_len, "%s", base); - char *dot = strrchr(out, '.'); - if (dot != NULL && dot != out) { - *dot = '\0'; - } -} - -static int duox_qsort_path_cmp(const void *a, const void *b) { - const char *pa = (const char *)a; - const char *pb = (const char *)b; - return strcmp(pa, pb); -} - -static int duox_collect_certificate_anchor_paths_recursive(const char *dirpath, - char paths[][DUOX_CERTIFICATE_ANCHOR_PATH_LEN], - size_t max_paths, size_t *count) { - if (dirpath == NULL || paths == NULL || count == NULL) { - return PM3_EINVARG; - } - - DIR *dir = opendir(dirpath); - if (dir == NULL) { - return PM3_EFILE; - } - - struct dirent *entry = NULL; - while ((entry = readdir(dir)) != NULL) { - if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0 || entry->d_name[0] == '.') { - continue; - } - - char fullpath[DUOX_CERTIFICATE_ANCHOR_PATH_LEN] = {0}; - if (snprintf(fullpath, sizeof(fullpath), "%s/%s", dirpath, entry->d_name) >= (int)sizeof(fullpath)) { - continue; - } - - if (duox_path_is_directory(fullpath)) { - int res = duox_collect_certificate_anchor_paths_recursive(fullpath, paths, max_paths, count); - if (res != PM3_SUCCESS) { - closedir(dir); - return res; - } - continue; - } - - if (!duox_path_is_regular_file(fullpath)) { - continue; - } - if (*count >= max_paths) { - closedir(dir); - return PM3_EOVFLOW; - } - - snprintf(paths[*count], DUOX_CERTIFICATE_ANCHOR_PATH_LEN, "%s", fullpath); - (*count)++; - } - - closedir(dir); - return PM3_SUCCESS; -} - -static int duox_collect_certificate_anchor_paths(const char *anchor_store_dir, - char paths[][DUOX_CERTIFICATE_ANCHOR_PATH_LEN], - size_t max_paths, size_t *count) { - if (anchor_store_dir == NULL || anchor_store_dir[0] == '\0' || paths == NULL || count == NULL) { - return PM3_EINVARG; - } - - char *rootdir = NULL; - int res = searchFile(&rootdir, RESOURCES_SUBDIR, anchor_store_dir, "", true); - if (res != PM3_SUCCESS) { - return res; - } - - if (!duox_path_is_directory(rootdir)) { - free(rootdir); - return PM3_EFILE; - } - - *count = 0; - res = duox_collect_certificate_anchor_paths_recursive(rootdir, paths, max_paths, count); - free(rootdir); - if (res != PM3_SUCCESS) { - return res; - } - - qsort(paths, *count, sizeof(paths[0]), duox_qsort_path_cmp); - return PM3_SUCCESS; -} - int duox_certificate_anchor_public_key(const duox_certificate_anchor_t *anchor, mbedtls_ecp_group_id *curveid, const uint8_t **pubkey, size_t *pubkey_len) { @@ -299,7 +114,7 @@ static int duox_load_x509_certificate_input(const char *input, mbedtls_x509_crt return PM3_EOVFLOW; } memcpy(normalized, input, input_len + 1); - duox_trim_ascii_inplace(normalized); + str_trim_ascii_inplace(normalized); if (normalized[0] == '\0') { return PM3_EINVARG; } @@ -319,7 +134,7 @@ static int duox_load_x509_certificate_input(const char *input, mbedtls_x509_crt char compact[DUOX_CERTIFICATE_ANCHOR_INPUT_LEN] = {0}; size_t compact_len = 0; - if (duox_copy_without_whitespace(normalized, compact, sizeof(compact), &compact_len) != PM3_SUCCESS || compact_len == 0) { + if (str_copy_without_whitespace(normalized, compact, sizeof(compact), &compact_len) != PM3_SUCCESS || compact_len == 0) { return PM3_EINVARG; } @@ -425,7 +240,7 @@ static int duox_load_certificate_anchor_from_file_path(const char *filepath, duo } char filename_anchor_name[DUOX_CERTIFICATE_ANCHOR_NAME_LEN] = {0}; - duox_path_basename_without_ext(filepath, filename_anchor_name, sizeof(filename_anchor_name)); + path_basename_without_ext(filepath, filename_anchor_name, sizeof(filename_anchor_name)); if (filename_anchor_name[0] == '\0') { return PM3_EINVARG; } @@ -446,14 +261,15 @@ static int duox_load_named_certificate_anchor_from_store(const char *token, cons char paths[DUOX_CERTIFICATE_ANCHOR_MAX_PATHS][DUOX_CERTIFICATE_ANCHOR_PATH_LEN] = {{0}}; size_t path_count = 0; - int res = duox_collect_certificate_anchor_paths(anchor_store_dir, paths, ARRAYLEN(paths), &path_count); + int res = collect_resource_file_paths(anchor_store_dir, (char *)paths, sizeof(paths[0]), ARRAYLEN(paths), &path_count, + false, DUOX_CERTIFICATE_ANCHOR_SEARCH_DEPTH); if (res != PM3_SUCCESS) { return res; } for (size_t i = 0; i < path_count; i++) { char filename_anchor_name[DUOX_CERTIFICATE_ANCHOR_NAME_LEN] = {0}; - duox_path_basename_without_ext(paths[i], filename_anchor_name, sizeof(filename_anchor_name)); + path_basename_without_ext(paths[i], filename_anchor_name, sizeof(filename_anchor_name)); if (filename_anchor_name[0] == '\0') { continue; } @@ -466,7 +282,7 @@ static int duox_load_named_certificate_anchor_from_store(const char *token, cons const char *matched_path = NULL; for (size_t i = 0; i < path_count; i++) { char filename_anchor_name[DUOX_CERTIFICATE_ANCHOR_NAME_LEN] = {0}; - duox_path_basename_without_ext(paths[i], filename_anchor_name, sizeof(filename_anchor_name)); + path_basename_without_ext(paths[i], filename_anchor_name, sizeof(filename_anchor_name)); if (filename_anchor_name[0] == '\0' || !str_startswith_case_insensitive(filename_anchor_name, token)) { continue; } @@ -491,7 +307,7 @@ int duox_load_certificate_anchor_from_input(const char *input, const char *ancho char normalized[DUOX_CERTIFICATE_ANCHOR_INPUT_LEN] = {0}; snprintf(normalized, sizeof(normalized), "%s", input); - duox_trim_ascii_inplace(normalized); + str_trim_ascii_inplace(normalized); if (normalized[0] == '\0') { return PM3_EINVARG; } @@ -499,7 +315,7 @@ int duox_load_certificate_anchor_from_input(const char *input, const char *ancho char *resolved_path = NULL; if (searchFile(&resolved_path, RESOURCES_SUBDIR, normalized, "", true) == PM3_SUCCESS) { int res = PM3_EINVARG; - if (duox_path_is_regular_file(resolved_path)) { + if (path_is_regular_file(resolved_path)) { res = duox_load_certificate_anchor_from_file_path(resolved_path, anchor); } free(resolved_path); @@ -532,7 +348,8 @@ int duox_load_certificate_anchors_from_store(const char *anchor_store_dir, char paths[DUOX_CERTIFICATE_ANCHOR_MAX_PATHS][DUOX_CERTIFICATE_ANCHOR_PATH_LEN] = {{0}}; size_t path_count = 0; - int res = duox_collect_certificate_anchor_paths(anchor_store_dir, paths, ARRAYLEN(paths), &path_count); + int res = collect_resource_file_paths(anchor_store_dir, (char *)paths, sizeof(paths[0]), ARRAYLEN(paths), &path_count, + false, DUOX_CERTIFICATE_ANCHOR_SEARCH_DEPTH); if (res != PM3_SUCCESS) { return res; } diff --git a/client/src/crypto/duoxcrypto.h b/client/src/crypto/duoxcrypto.h index 4b492c142..76b9a9f32 100644 --- a/client/src/crypto/duoxcrypto.h +++ b/client/src/crypto/duoxcrypto.h @@ -76,7 +76,6 @@ typedef struct { } duox_certificate_anchor_t; const char *duox_certificate_format_name(duox_certificate_format_t format); -const char *duox_cert_info_format_name(const duox_cert_info_t *cert); int duox_certificate_anchor_public_key(const duox_certificate_anchor_t *anchor, mbedtls_ecp_group_id *curveid, const uint8_t **pubkey, size_t *pubkey_len); diff --git a/client/src/crypto/libpcrypto.c b/client/src/crypto/libpcrypto.c index 7844a41c9..6bb87072c 100644 --- a/client/src/crypto/libpcrypto.c +++ b/client/src/crypto/libpcrypto.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -106,78 +105,6 @@ int pcrypto_rng_fill_oneshot(uint8_t *out, size_t out_len, const char *personali return res; } -static void pcrypto_trim_ascii_inplace(char *text) { - if (text == NULL) { - return; - } - - size_t start = 0; - size_t len = strlen(text); - while (start < len && isspace((unsigned char)text[start])) { - start++; - } - while (len > start && isspace((unsigned char)text[len - 1])) { - len--; - } - - if (start > 0) { - memmove(text, text + start, len - start); - } - text[len - start] = '\0'; -} - -static void pcrypto_unescape_newlines_inplace(char *text) { - if (text == NULL) { - return; - } - - size_t read_pos = 0; - size_t write_pos = 0; - size_t len = strlen(text); - while (read_pos < len) { - if (text[read_pos] == '\\' && (read_pos + 1) < len) { - char esc = text[read_pos + 1]; - if (esc == 'n') { - text[write_pos++] = '\n'; - read_pos += 2; - continue; - } - if (esc == 'r') { - text[write_pos++] = '\r'; - read_pos += 2; - continue; - } - if (esc == 't') { - text[write_pos++] = '\t'; - read_pos += 2; - continue; - } - } - text[write_pos++] = text[read_pos++]; - } - text[write_pos] = '\0'; -} - -static int pcrypto_copy_without_whitespace(const char *src, char *dst, size_t dst_size, size_t *dst_len) { - if (src == NULL || dst == NULL || dst_len == NULL || dst_size == 0) { - return PM3_EINVARG; - } - - size_t out = 0; - for (size_t i = 0; src[i] != '\0'; i++) { - if (isspace((unsigned char)src[i])) { - continue; - } - if ((out + 1) >= dst_size) { - return PM3_EOVFLOW; - } - dst[out++] = src[i]; - } - dst[out] = '\0'; - *dst_len = out; - return PM3_SUCCESS; -} - static int pcrypto_extract_priv_scalar_from_pk(const mbedtls_pk_context *pkctx, mbedtls_ecp_group_id curveid, uint8_t *out_priv, size_t out_priv_len) { @@ -265,7 +192,7 @@ static int pcrypto_parse_ec_private_base64(const char *input, char compact[PCRYPTO_MAX_KEY_INPUT] = {0}; size_t compact_len = 0; - int res = pcrypto_copy_without_whitespace(input, compact, sizeof(compact), &compact_len); + int res = str_copy_without_whitespace(input, compact, sizeof(compact), &compact_len); if (res != PM3_SUCCESS || compact_len == 0) { return PM3_EINVARG; } @@ -399,7 +326,7 @@ static int pcrypto_parse_ec_private_text(const char *input, bool allow_file_path return PM3_EOVFLOW; } memcpy(normalized, input, input_len + 1); - pcrypto_trim_ascii_inplace(normalized); + str_trim_ascii_inplace(normalized); if (normalized[0] == '\0') { return PM3_EINVARG; @@ -416,13 +343,13 @@ static int pcrypto_parse_ec_private_text(const char *input, bool allow_file_path // Only unescape after path resolution fails, to avoid mutating valid paths // (for example Windows paths containing '\t', '\n' or '\r'). - pcrypto_unescape_newlines_inplace(normalized); + str_unescape_newlines_inplace(normalized); uint8_t decoded[PCRYPTO_MAX_KEY_INPUT] = {0}; int decoded_len = -1; char compact[PCRYPTO_MAX_KEY_INPUT] = {0}; size_t compact_len = 0; - if (pcrypto_copy_without_whitespace(normalized, compact, sizeof(compact), &compact_len) == PM3_SUCCESS && + if (str_copy_without_whitespace(normalized, compact, sizeof(compact), &compact_len) == PM3_SUCCESS && compact_len > 0) { decoded_len = hex_to_bytes(compact, decoded, sizeof(decoded)); } @@ -740,7 +667,7 @@ static int pcrypto_parse_ec_public_base64(const char *input, char compact[PCRYPTO_MAX_KEY_INPUT] = {0}; size_t compact_len = 0; - int res = pcrypto_copy_without_whitespace(input, compact, sizeof(compact), &compact_len); + int res = str_copy_without_whitespace(input, compact, sizeof(compact), &compact_len); if (res != PM3_SUCCESS || compact_len == 0) { return PM3_EINVARG; } @@ -777,7 +704,7 @@ static int pcrypto_parse_ec_public_text(const char *input, bool allow_file_path, return PM3_EOVFLOW; } memcpy(normalized, input, input_len + 1); - pcrypto_trim_ascii_inplace(normalized); + str_trim_ascii_inplace(normalized); if (normalized[0] == '\0') { return PM3_EINVARG; @@ -793,14 +720,14 @@ static int pcrypto_parse_ec_public_text(const char *input, bool allow_file_path, } // Only unescape after path resolution fails - pcrypto_unescape_newlines_inplace(normalized); + str_unescape_newlines_inplace(normalized); // Try as hex string uint8_t decoded[PCRYPTO_MAX_KEY_INPUT] = {0}; int decoded_len = -1; char compact[PCRYPTO_MAX_KEY_INPUT] = {0}; size_t compact_len = 0; - if (pcrypto_copy_without_whitespace(normalized, compact, sizeof(compact), &compact_len) == PM3_SUCCESS && + if (str_copy_without_whitespace(normalized, compact, sizeof(compact), &compact_len) == PM3_SUCCESS && compact_len > 0) { decoded_len = hex_to_bytes(compact, decoded, sizeof(decoded)); } diff --git a/client/src/fileutils.c b/client/src/fileutils.c index c5a3b8dfa..1101370e6 100644 --- a/client/src/fileutils.c +++ b/client/src/fileutils.c @@ -127,20 +127,176 @@ int fileExists(const char *filename) { * @param filename * @return */ -static bool is_directory(const char *filename) { +bool path_is_directory(const char *path) { + if (path == NULL) { + return false; + } #ifdef _WIN32 struct _stat st; - if (_stat(filename, &st) == -1) + if (_stat(path, &st) == -1) return false; #else struct stat st; // stat(filename, &st); - if (lstat(filename, &st) == -1) + if (lstat(path, &st) == -1) return false; #endif return S_ISDIR(st.st_mode) != 0; } +bool path_is_regular_file(const char *path) { + if (path == NULL) { + return false; + } +#ifdef _WIN32 + struct _stat st; + if (_stat(path, &st) == -1) + return false; +#else + struct stat st; + if (stat(path, &st) == -1) + return false; +#endif + return S_ISREG(st.st_mode) != 0; +} + +const char *path_basename(const char *path) { + if (path == NULL) { + return ""; + } + + const char *base = strrchr(path, '/'); + const char *base_win = strrchr(path, '\\'); + if (base == NULL || (base_win != NULL && base_win > base)) { + base = base_win; + } + return (base == NULL) ? path : (base + 1); +} + +void path_basename_without_ext(const char *path, char *out, size_t out_len) { + if (out == NULL || out_len == 0) { + return; + } + out[0] = '\0'; + + const char *base = path_basename(path); + if (base[0] == '\0') { + return; + } + + snprintf(out, out_len, "%s", base); + char *dot = strrchr(out, '.'); + if (dot != NULL && dot != out) { + *dot = '\0'; + } +} + +static int qsort_path_cmp(const void *a, const void *b) { + const char *pa = (const char *)a; + const char *pb = (const char *)b; + return strcmp(pa, pb); +} + +static char *path_list_slot(char *paths, size_t path_len, size_t index) { + return paths + (index * path_len); +} + +int collect_file_paths_recursive(const char *dirpath, char *paths, size_t path_len, + size_t max_paths, size_t *count, bool include_hidden, size_t max_depth) { + if (dirpath == NULL || paths == NULL || path_len == 0 || count == NULL) { + return PM3_EINVARG; + } + + DIR *dir = opendir(dirpath); + if (dir == NULL) { + return PM3_EFILE; + } + + struct dirent *entry = NULL; + while ((entry = readdir(dir)) != NULL) { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0 || + (include_hidden == false && entry->d_name[0] == '.')) { + continue; + } + + char *fullpath = calloc(path_len, sizeof(char)); + if (fullpath == NULL) { + closedir(dir); + return PM3_EMALLOC; + } + const char *sep = ""; + size_t dir_len = strlen(dirpath); + if (dir_len > 0 && dirpath[dir_len - 1] != '/' && dirpath[dir_len - 1] != '\\') { + sep = PATHSEP; + } + if (snprintf(fullpath, path_len, "%s%s%s", dirpath, sep, entry->d_name) >= (int)path_len) { + free(fullpath); + continue; + } + + if (path_is_directory(fullpath)) { + if (max_depth == 0) { + free(fullpath); + continue; + } + int res = collect_file_paths_recursive(fullpath, paths, path_len, max_paths, count, include_hidden, max_depth - 1); + free(fullpath); + if (res != PM3_SUCCESS) { + closedir(dir); + return res; + } + continue; + } + + if (!path_is_regular_file(fullpath)) { + free(fullpath); + continue; + } + if (*count >= max_paths) { + free(fullpath); + closedir(dir); + return PM3_EOVFLOW; + } + if (snprintf(path_list_slot(paths, path_len, *count), path_len, "%s", fullpath) >= (int)path_len) { + free(fullpath); + continue; + } + free(fullpath); + (*count)++; + } + + closedir(dir); + return PM3_SUCCESS; +} + +int collect_resource_file_paths(const char *resource_dir, char *paths, size_t path_len, + size_t max_paths, size_t *count, bool include_hidden, size_t max_depth) { + if (resource_dir == NULL || resource_dir[0] == '\0' || paths == NULL || path_len == 0 || count == NULL) { + return PM3_EINVARG; + } + + char *rootdir = NULL; + int res = searchFile(&rootdir, RESOURCES_SUBDIR, resource_dir, "", true); + if (res != PM3_SUCCESS) { + return res; + } + + if (!path_is_directory(rootdir)) { + free(rootdir); + return PM3_EFILE; + } + + *count = 0; + res = collect_file_paths_recursive(rootdir, paths, path_len, max_paths, count, include_hidden, max_depth); + free(rootdir); + if (res != PM3_SUCCESS) { + return res; + } + + qsort(paths, *count, path_len, qsort_path_cmp); + return PM3_SUCCESS; +} + bool setDefaultPath(savePaths_t pathIndex, const char *path) { if (pathIndex < spItemCount) { @@ -2884,7 +3040,7 @@ static int filelist(const char *path, const char *ext, uint8_t last, bool tentat tmp_fullpath[1023] = 0x00; strncat(tmp_fullpath, namelist[i]->d_name, strlen(tmp_fullpath) - 1); - if (is_directory(tmp_fullpath)) { + if (path_is_directory(tmp_fullpath)) { char newpath[1024]; if (strcmp(namelist[i]->d_name, ".") == 0 || strcmp(namelist[i]->d_name, "..") == 0) @@ -3144,7 +3300,7 @@ int searchFile(char **foundpath, const char *pm3dir, const char *searchname, con return PM3_EINVARG; } - if (is_directory(searchname)) { + if (path_is_directory(searchname)) { return PM3_EINVARG; } @@ -3397,4 +3553,3 @@ int pm3_save_fm11rf08s_nonces(const char *fn, iso14a_fm11rf08s_nonces_with_data_ } return PM3_SUCCESS; } - diff --git a/client/src/fileutils.h b/client/src/fileutils.h index ef1642a3f..0642cf478 100644 --- a/client/src/fileutils.h +++ b/client/src/fileutils.h @@ -114,6 +114,42 @@ typedef enum { int fileExists(const char *filename); +/** + * @brief Check whether a path exists and is a directory. + */ +bool path_is_directory(const char *path); + +/** + * @brief Check whether a path exists and is a regular file. + */ +bool path_is_regular_file(const char *path); + +/** + * @brief Return the final path component, or an empty string for NULL input. + */ +const char *path_basename(const char *path); + +/** + * @brief Copy the final path component without its last file extension. + */ +void path_basename_without_ext(const char *path, char *out, size_t out_len); + +/** + * @brief Recursively collect regular file paths under a directory into fixed-size slots. + * + * A max_depth of 0 scans only dirpath and does not descend into subdirectories. + */ +int collect_file_paths_recursive(const char *dirpath, char *paths, size_t path_len, + size_t max_paths, size_t *count, bool include_hidden, size_t max_depth); + +/** + * @brief Resolve a resources subdirectory and recursively collect regular file paths from it. + * + * A max_depth of 0 scans only the resolved resource_dir and does not descend into subdirectories. + */ +int collect_resource_file_paths(const char *resource_dir, char *paths, size_t path_len, + size_t max_paths, size_t *count, bool include_hidden, size_t max_depth); + // set a path in the path list g_session.defaultPaths bool setDefaultPath(savePaths_t pathIndex, const char *path); diff --git a/client/src/util.c b/client/src/util.c index 68e9ec401..8f4b5589a 100644 --- a/client/src/util.c +++ b/client/src/util.c @@ -1944,3 +1944,75 @@ size_t unduplicate(uint8_t *d, size_t n, const uint8_t item_n) { return write_index; } + +void str_trim_ascii_inplace(char *s) { + if (s == NULL) { + return; + } + + size_t start = 0; + size_t len = strlen(s); + while (start < len && isspace((unsigned char)s[start])) { + start++; + } + while (len > start && isspace((unsigned char)s[len - 1])) { + len--; + } + + if (start > 0) { + memmove(s, s + start, len - start); + } + s[len - start] = '\0'; +} + +void str_unescape_newlines_inplace(char *s) { + if (s == NULL) { + return; + } + + size_t read_pos = 0; + size_t write_pos = 0; + size_t len = strlen(s); + while (read_pos < len) { + if (s[read_pos] == '\\' && (read_pos + 1) < len) { + char esc = s[read_pos + 1]; + if (esc == 'n') { + s[write_pos++] = '\n'; + read_pos += 2; + continue; + } + if (esc == 'r') { + s[write_pos++] = '\r'; + read_pos += 2; + continue; + } + if (esc == 't') { + s[write_pos++] = '\t'; + read_pos += 2; + continue; + } + } + s[write_pos++] = s[read_pos++]; + } + s[write_pos] = '\0'; +} + +int str_copy_without_whitespace(const char *src, char *dst, size_t dst_size, size_t *dst_len) { + if (src == NULL || dst == NULL || dst_len == NULL || dst_size == 0) { + return PM3_EINVARG; + } + + size_t out = 0; + for (size_t i = 0; src[i] != '\0'; i++) { + if (isspace((unsigned char)src[i])) { + continue; + } + if ((out + 1) >= dst_size) { + return PM3_EOVFLOW; + } + dst[out++] = src[i]; + } + dst[out] = '\0'; + *dst_len = out; + return PM3_SUCCESS; +} diff --git a/client/src/util.h b/client/src/util.h index 1ba9f8763..75cd7c9b3 100644 --- a/client/src/util.h +++ b/client/src/util.h @@ -215,4 +215,19 @@ uint8_t get_highest_frequency(const uint8_t *d, uint8_t n); size_t unduplicate(uint8_t *d, size_t n, const uint8_t item_n); +/** + * @brief Trim leading and trailing ASCII whitespace from a mutable string. + */ +void str_trim_ascii_inplace(char *s); + +/** + * @brief Replace escaped \n, \r, and \t sequences with their control characters in-place. + */ +void str_unescape_newlines_inplace(char *s); + +/** + * @brief Copy a string while dropping all ASCII whitespace characters. + */ +int str_copy_without_whitespace(const char *src, char *dst, size_t dst_size, size_t *dst_len); + #endif