diff options
Diffstat (limited to 'source/blender/blenlib/intern/path_util.c')
-rw-r--r-- | source/blender/blenlib/intern/path_util.c | 792 |
1 files changed, 508 insertions, 284 deletions
diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c index 444daf8817c..f9cf913157b 100644 --- a/source/blender/blenlib/intern/path_util.c +++ b/source/blender/blenlib/intern/path_util.c @@ -41,13 +41,13 @@ #include "DNA_listBase.h" +#include "BLI_utildefines.h" #include "BLI_fileops.h" #include "BLI_path_util.h" #include "BLI_string.h" #include "BLI_string_utf8.h" -#include "BLI_utildefines.h" -#include "BKE_blender.h" /* BLENDER_VERSION */ +#include "../blenkernel/BKE_blender.h" /* BLENDER_VERSION, bad level include (no function call) */ #include "GHOST_Path-api.h" @@ -79,43 +79,51 @@ /* local */ #define UNIQUE_NAME_MAX 128 -static char bprogname[FILE_MAX]; /* path to program executable */ -static char bprogdir[FILE_MAX]; /* path in which executable is located */ +static char bprogname[FILE_MAX]; /* full path to program executable */ +static char bprogdir[FILE_MAX]; /* full path to directory in which executable is located */ static char btempdir[FILE_MAX]; /* temporary directory */ -static int add_win32_extension(char *name); -static char *blender_version_decimal(const int ver); - /* implementation */ +/** + * Looks for a sequence of decimal digits in string, preceding any filename extension, + * returning the integer value if found, or 0 if not. + * + * \param string String to scan. + * \param head Optional area to return copy of part of string prior to digits, or before dot if no digits. + * \param tail Optional area to return copy of part of string following digits, or from dot if no digits. + * \param numlen Optional to return number of digits found. + */ int BLI_stringdec(const char *string, char *head, char *tail, unsigned short *numlen) { - unsigned short len, len2, lenlslash = 0, nums = 0, nume = 0; - short i, found = 0; - char *lslash = BLI_last_slash(string); - len2 = len = strlen(string); - if (lslash) - lenlslash = (int)(lslash - string); + unsigned short nums = 0, nume = 0; + short i; + bool found_digit = false; + const char * const lslash = BLI_last_slash(string); + const unsigned short string_len = strlen(string); + const unsigned short lslash_len = lslash != NULL ? (int)(lslash - string) : 0; + unsigned short name_end = string_len; - while (len > lenlslash && string[--len] != '.') {} - if (len == lenlslash && string[len] != '.') len = len2; + while (name_end > lslash_len && string[--name_end] != '.') {} /* name ends at dot if present */ + if (name_end == lslash_len && string[name_end] != '.') name_end = string_len; - for (i = len - 1; i >= lenlslash; i--) { + for (i = name_end - 1; i >= lslash_len; i--) { if (isdigit(string[i])) { - if (found) { + if (found_digit) { nums = i; } else { nume = i; nums = i; - found = 1; + found_digit = true; } } else { - if (found) break; + if (found_digit) break; } } - if (found) { + + if (found_digit) { if (tail) strcpy(tail, &string[nume + 1]); if (head) { strcpy(head, string); @@ -124,56 +132,72 @@ int BLI_stringdec(const char *string, char *head, char *tail, unsigned short *nu if (numlen) *numlen = nume - nums + 1; return ((int)atoi(&(string[nums]))); } - if (tail) strcpy(tail, string + len); - if (head) { - strncpy(head, string, len); - head[len] = '\0'; + else { + if (tail) strcpy(tail, string + name_end); + if (head) { + BLI_strncpy(head, string, name_end); + } + if (numlen) *numlen = 0; + return 0; } - if (numlen) *numlen = 0; - return 0; } +/** + * Returns in area pointed to by string a string of the form "<head><pic><tail>", where pic + * is formatted as numlen digits with leading zeroes. + */ void BLI_stringenc(char *string, const char *head, const char *tail, unsigned short numlen, int pic) { - char fmtstr[16] = ""; - if (pic < 0) pic = 0; - sprintf(fmtstr, "%%s%%.%dd%%s", numlen); - sprintf(string, fmtstr, head, pic, tail); + sprintf(string, "%s%.*d%s", head, numlen, MAX2(0, pic), tail); } -/* Foo.001 -> "Foo", 1 - * Returns the length of "Foo" */ +/** + * Looks for a numeric suffix preceded by delim character on the end of + * name, puts preceding part into *left and value of suffix into *nr. + * Returns the length of *left. + * + * Foo.001 -> "Foo", 1 + * Returning the length of "Foo" + * + * \param left Where to return copy of part preceding delim + * \param nr Where to return value of numeric suffix + * \param name String to split + * \param delim Delimiter character + * \return Length of \a left + */ int BLI_split_name_num(char *left, int *nr, const char *name, const char delim) { - int a; + const int name_len = strlen(name); *nr = 0; - a = strlen(name); - memcpy(left, name, (a + 1) * sizeof(char)); - - if (a > 1 && name[a - 1] == delim) return a; - - while (a--) { - if (name[a] == delim) { - left[a] = 0; - *nr = atol(name + a + 1); - /* casting down to an int, can overflow for large numbers */ - if (*nr < 0) - *nr = 0; - return a; - } - if (isdigit(name[a]) == 0) break; - - left[a] = 0; + memcpy(left, name, (name_len + 1) * sizeof(char)); + + /* name doesn't end with a delimiter "foo." */ + if ((name_len > 1 && name[name_len - 1] == delim) == 0) { + int a = name_len; + while (a--) { + if (name[a] == delim) { + left[a] = '\0'; /* truncate left part here */ + *nr = atol(name + a + 1); + /* casting down to an int, can overflow for large numbers */ + if (*nr < 0) + *nr = 0; + return a; + } + else if (isdigit(name[a]) == 0) { + /* non-numeric suffix - give up */ + break; + } + } } - for (a = 0; name[a]; a++) - left[a] = name[a]; - - return a; + return name_len; } +/** + * Looks for a string of digits within name (using BLI_stringdec) and adjusts it by add. + */ void BLI_newname(char *name, int add) { char head[UNIQUE_NAME_MAX], tail[UNIQUE_NAME_MAX]; @@ -196,9 +220,20 @@ void BLI_newname(char *name, int add) BLI_stringenc(name, head, tail, digits, pic); } - - -int BLI_uniquename_cb(int (*unique_check)(void *, const char *), void *arg, const char defname[], char delim, char *name, short name_len) +/** + * Ensures name is unique (according to criteria specified by caller in unique_check callback), + * incrementing its numeric suffix as necessary. Returns true if name had to be adjusted. + * + * \param unique_check Return true if name is not unique + * \param arg Additional arg to unique_check--meaning is up to caller + * \param defname To initialize name if latter is empty + * \param delim Delimits numeric suffix in name + * \param name Name to be ensured unique + * \param name_len Maximum length of name area + * \return true if there if the name was changed + */ +bool BLI_uniquename_cb(bool (*unique_check)(void * arg, const char *name), + void *arg, const char *defname, char delim, char *name, short name_len) { if (name[0] == '\0') { BLI_strncpy(name, defname, name_len); @@ -211,7 +246,7 @@ int BLI_uniquename_cb(int (*unique_check)(void *, const char *), void *arg, cons int number; int len = BLI_split_name_num(left, &number, name, delim); do { - int numlen = BLI_snprintf(numstr, sizeof(numstr), "%c%03d", delim, ++number); + const int numlen = BLI_snprintf(numstr, sizeof(numstr), "%c%03d", delim, ++number); /* highly unlikely the string only has enough room for the number * but support anyway */ @@ -229,10 +264,10 @@ int BLI_uniquename_cb(int (*unique_check)(void *, const char *), void *arg, cons BLI_strncpy(name, tempname, name_len); - return 1; + return true; } - return 0; + return false; } /* little helper macro for BLI_uniquename */ @@ -250,28 +285,39 @@ int BLI_uniquename_cb(int (*unique_check)(void *, const char *), void *arg, cons * defname: the name that should be used by default if none is specified already * delim: the character which acts as a delimiter between parts of the name */ -static int uniquename_find_dupe(ListBase *list, void *vlink, const char *name, short name_offs) +static bool uniquename_find_dupe(ListBase *list, void *vlink, const char *name, short name_offs) { Link *link; for (link = list->first; link; link = link->next) { if (link != vlink) { if (!strcmp(GIVE_STRADDR(link, name_offs), name)) { - return 1; + return true; } } } - return 0; + return false; } -static int uniquename_unique_check(void *arg, const char *name) +static bool uniquename_unique_check(void *arg, const char *name) { struct {ListBase *lb; void *vlink; short name_offs; } *data = arg; return uniquename_find_dupe(data->lb, data->vlink, name, data->name_offs); } -void BLI_uniquename(ListBase *list, void *vlink, const char defname[], char delim, short name_offs, short name_len) +/** + * Ensures that the specified block has a unique name within the containing list, + * incrementing its numeric suffix as necessary. + * + * \param list List containing the block + * \param vlink The block to check the name for + * \param defname To initialize block name if latter is empty + * \param delim Delimits numeric suffix in name + * \param name_offs Offset of name within block structure + * \param name_len Maximum length of name area + */ +void BLI_uniquename(ListBase *list, void *vlink, const char *defname, char delim, short name_offs, short name_len) { struct {ListBase *lb; void *vlink; short name_offs; } data; data.lb = list; @@ -299,19 +345,19 @@ void BLI_uniquename(ListBase *list, void *vlink, const char defname[], char deli * If relbase is NULL then its ignored */ -void BLI_cleanup_path(const char *relabase, char *dir) +void BLI_cleanup_path(const char *relabase, char *path) { ptrdiff_t a; char *start, *eind; if (relabase) { - BLI_path_abs(dir, relabase); + BLI_path_abs(path, relabase); } else { - if (dir[0] == '/' && dir[1] == '/') { - if (dir[2] == '\0') { + if (path[0] == '/' && path[1] == '/') { + if (path[2] == '\0') { return; /* path is "//" - cant clean it */ } - dir = dir + 2; /* skip the first // */ + path = path + 2; /* leave the initial "//" untouched */ } } @@ -327,70 +373,72 @@ void BLI_cleanup_path(const char *relabase, char *dir) /* Note, this should really be moved to the file selector, * since this function is used in many areas */ - if (strcmp(dir, ".") == 0) { /* happens for example in FILE_MAIN */ - get_default_root(dir); + if (strcmp(path, ".") == 0) { /* happens for example in FILE_MAIN */ + get_default_root(path); return; } - while ( (start = strstr(dir, "\\..\\")) ) { + while ( (start = strstr(path, "\\..\\")) ) { eind = start + strlen("\\..\\") - 1; - a = start - dir - 1; + a = start - path - 1; while (a > 0) { - if (dir[a] == '\\') break; + if (path[a] == '\\') break; a--; } if (a < 0) { break; } else { - memmove(dir + a, eind, strlen(eind) + 1); + memmove(path + a, eind, strlen(eind) + 1); } } - while ( (start = strstr(dir, "\\.\\")) ) { + while ( (start = strstr(path, "\\.\\")) ) { eind = start + strlen("\\.\\") - 1; memmove(start, eind, strlen(eind) + 1); } - while ( (start = strstr(dir, "\\\\")) ) { + while ( (start = strstr(path, "\\\\")) ) { eind = start + strlen("\\\\") - 1; memmove(start, eind, strlen(eind) + 1); } #else - if (dir[0] == '.') { /* happens, for example in FILE_MAIN */ - dir[0] = '/'; - dir[1] = 0; + if (path[0] == '.') { /* happens, for example in FILE_MAIN */ + path[0] = '/'; + path[1] = 0; return; } - /* support for odd paths: eg /../home/me --> /home/me - * this is a valid path in blender but we cant handle this the usual way below - * simply strip this prefix then evaluate the path as usual. pythons os.path.normpath() does this */ - while ((strncmp(dir, "/../", 4) == 0)) { - memmove(dir, dir + 4, strlen(dir + 4) + 1); - } - - while ( (start = strstr(dir, "/../")) ) { - eind = start + (4 - 1) /* strlen("/../") - 1 */; - a = start - dir - 1; - while (a > 0) { - if (dir[a] == '/') break; - a--; - } - if (a < 0) { - break; + while ( (start = strstr(path, "/../")) ) { + a = start - path - 1; + if (a > 0) { + /* <prefix>/<parent>/../<postfix> => <prefix>/<postfix> */ + eind = start + (4 - 1) /* strlen("/../") - 1 */; /* strip "/.." and keep last "/" */ + while (a > 0 && path[a] != '/') { /* find start of <parent> */ + a--; + } + memmove(path + a, eind, strlen(eind) + 1); } else { - memmove(dir + a, eind, strlen(eind) + 1); + /* support for odd paths: eg /../home/me --> /home/me + * this is a valid path in blender but we cant handle this the usual way below + * simply strip this prefix then evaluate the path as usual. + * pythons os.path.normpath() does this */ + + /* Note: previous version of following call used an offset of 3 instead of 4, + * which meant that the "/../home/me" example actually became "home/me". + * Using offset of 3 gives behaviour consistent with the abovementioned + * Python routine. */ + memmove(path, path + 3, strlen(path + 3) + 1); } } - while ( (start = strstr(dir, "/./")) ) { + while ( (start = strstr(path, "/./")) ) { eind = start + (3 - 1) /* strlen("/./") - 1 */; memmove(start, eind, strlen(eind) + 1); } - while ( (start = strstr(dir, "//")) ) { + while ( (start = strstr(path, "//")) ) { eind = start + (2 - 1) /* strlen("//") - 1 */; memmove(start, eind, strlen(eind) + 1); } @@ -404,20 +452,28 @@ void BLI_cleanup_dir(const char *relabase, char *dir) } -void BLI_cleanup_file(const char *relabase, char *dir) +void BLI_cleanup_file(const char *relabase, char *path) { - BLI_cleanup_path(relabase, dir); - BLI_del_slash(dir); + BLI_cleanup_path(relabase, path); + BLI_del_slash(path); } -int BLI_path_is_rel(const char *path) +/** + * Does path begin with the special "//" prefix that Blender uses to indicate + * a path relative to the .blend file. + */ +bool BLI_path_is_rel(const char *path) { return path[0] == '/' && path[1] == '/'; } +/** + * Replaces *file with a relative version (prefixed by "//") such that BLI_path_abs, given + * the same *relfile, will convert it back to its original value. + */ void BLI_path_rel(char *file, const char *relfile) { - char *lslash; + const char *lslash; char temp[FILE_MAX]; char res[FILE_MAX]; @@ -519,7 +575,11 @@ void BLI_path_rel(char *file, const char *relfile) } } -int BLI_has_parent(char *path) +/** + * Cleans path and makes sure it ends with a slash. + * \return true if \a path has more than one other path separator in it. + */ +bool BLI_has_parent(char *path) { int len; int slashes = 0; @@ -534,25 +594,34 @@ int BLI_has_parent(char *path) return slashes > 1; } -int BLI_parent_dir(char *path) +/** + * Replaces path with the path of its parent directory, returning true if + * it was able to find a parent directory within the pathname. + */ +bool BLI_parent_dir(char *path) { static char parent_dir[] = {'.', '.', SEP, '\0'}; /* "../" or "..\\" */ char tmp[FILE_MAX + 4]; BLI_strncpy(tmp, path, sizeof(tmp) - 4); BLI_add_slash(tmp); strcat(tmp, parent_dir); - BLI_cleanup_dir(NULL, tmp); + BLI_cleanup_dir(NULL, tmp); /* does all the work of normalizing the path for us */ if (!BLI_testextensie(tmp, parent_dir)) { BLI_strncpy(path, tmp, sizeof(tmp)); - return 1; + return true; } else { - return 0; + return false; } } -static int stringframe_chars(char *path, int *char_start, int *char_end) +/** + * Looks for a sequence of "#" characters in the last slash-separated component of *path, + * returning the indexes of the first and one past the last character in the sequence in + * *char_start and *char_end respectively. Returns true if such a sequence was found. + */ +static bool stringframe_chars(const char *path, int *char_start, int *char_end) { int ch_sta, ch_end, i; /* Insert current frame: file### -> file001 */ @@ -576,18 +645,22 @@ static int stringframe_chars(char *path, int *char_start, int *char_end) if (ch_end) { *char_start = ch_sta; *char_end = ch_end; - return 1; + return true; } else { *char_start = -1; *char_end = -1; - return 0; + return false; } } +/** + * Ensure *path contains at least one "#" character in its last slash-separated + * component, appending one digits long if not. + */ static void ensure_digits(char *path, int digits) { - char *file = BLI_last_slash(path); + char *file = (char *)BLI_last_slash(path); if (file == NULL) file = path; @@ -602,7 +675,11 @@ static void ensure_digits(char *path, int digits) } } -int BLI_path_frame(char *path, int frame, int digits) +/** + * Replaces "#" character sequence in last slash-separated component of *path + * with frame as decimal integer, with leading zeroes as necessary, to make digits digits. + */ +bool BLI_path_frame(char *path, int frame, int digits) { int ch_sta, ch_end; @@ -613,12 +690,17 @@ int BLI_path_frame(char *path, int frame, int digits) char tmp[FILE_MAX]; sprintf(tmp, "%.*s%.*d%s", ch_sta, path, ch_end - ch_sta, frame, path + ch_end); strcpy(path, tmp); - return 1; + return true; } - return 0; + return false; } -int BLI_path_frame_range(char *path, int sta, int end, int digits) +/** + * Replaces "#" character sequence in last slash-separated component of *path + * with sta and end as decimal integers, with leading zeroes as necessary, to make digits + * digits each, with a hyphen in-between. + */ +bool BLI_path_frame_range(char *path, int sta, int end, int digits) { int ch_sta, ch_end; @@ -631,14 +713,19 @@ int BLI_path_frame_range(char *path, int sta, int end, int digits) "%.*s%.*d-%.*d%s", ch_sta, path, ch_end - ch_sta, sta, ch_end - ch_sta, end, path + ch_end); BLI_strncpy(path, tmp, FILE_MAX); - return 1; + return true; } - return 0; + return false; } -int BLI_path_abs(char *path, const char *basepath) +/** + * If path begins with "//", strips that and replaces it with basepath directory. Also converts + * a drive-letter prefix to something more sensible if this is a non-drive-letter-based system. + * Returns true if "//" prefix expansion was done. + */ +bool BLI_path_abs(char *path, const char *basepath) { - int wasrelative = BLI_path_is_rel(path); + const bool wasrelative = BLI_path_is_rel(path); char tmp[FILE_MAX]; char base[FILE_MAX]; #ifdef WIN32 @@ -696,21 +783,23 @@ int BLI_path_abs(char *path, const char *basepath) /* Paths starting with // will get the blend file as their base, * this isn't standard in any os but is used in blender all over the place */ if (wasrelative) { - char *lslash = BLI_last_slash(base); + const char * const lslash = BLI_last_slash(base); if (lslash) { - int baselen = (int) (lslash - base) + 1; + const int baselen = (int) (lslash - base) + 1; /* length up to and including last "/" */ /* use path for temp storage here, we copy back over it right away */ - BLI_strncpy(path, tmp + 2, FILE_MAX); + BLI_strncpy(path, tmp + 2, FILE_MAX); /* strip "//" */ - memcpy(tmp, base, baselen); - BLI_strncpy(tmp + baselen, path, sizeof(tmp) - baselen); - BLI_strncpy(path, tmp, FILE_MAX); + memcpy(tmp, base, baselen); /* prefix with base up to last "/" */ + BLI_strncpy(tmp + baselen, path, sizeof(tmp) - baselen); /* append path after "//" */ + BLI_strncpy(path, tmp, FILE_MAX); /* return as result */ } else { + /* base doesn't seem to be a directory--ignore it and just strip "//" prefix on path */ BLI_strncpy(path, tmp + 2, FILE_MAX); } } else { + /* base ignored */ BLI_strncpy(path, tmp, FILE_MAX); } @@ -730,24 +819,27 @@ int BLI_path_abs(char *path, const char *basepath) } -/* - * Should only be done with command line paths. - * this is NOT something blenders internal paths support like the // prefix +/** + * Expands path relative to the current working directory, if it was relative. + * Returns true if such expansion was done. + * + * \note Should only be done with command line paths. + * this is _not_ something blenders internal paths support like the "//" prefix */ -int BLI_path_cwd(char *path) +bool BLI_path_cwd(char *path) { - int wasrelative = 1; - int filelen = strlen(path); + bool wasrelative = true; + const int filelen = strlen(path); #ifdef WIN32 if (filelen >= 3 && path[1] == ':' && (path[2] == '\\' || path[2] == '/')) - wasrelative = 0; + wasrelative = false; #else if (filelen >= 2 && path[0] == '/') - wasrelative = 0; + wasrelative = false; #endif - if (wasrelative == 1) { + if (wasrelative) { char cwd[FILE_MAX] = ""; BLI_current_working_dir(cwd, sizeof(cwd)); /* in case the full path to the blend isn't used */ @@ -772,22 +864,9 @@ int BLI_path_cwd(char *path) return wasrelative; } - -/* 'di's filename component is moved into 'fi', di is made a dir path */ -void BLI_splitdirstring(char *di, char *fi) -{ - char *lslash = BLI_last_slash(di); - - if (lslash) { - BLI_strncpy(fi, lslash + 1, FILE_MAXFILE); - *(lslash + 1) = 0; - } - else { - BLI_strncpy(fi, di, FILE_MAXFILE); - di[0] = 0; - } -} - +/** + * Copies into *last the part of *dir following the second-last slash. + */ void BLI_getlastdir(const char *dir, char *last, const size_t maxlen) { const char *s = dir; @@ -814,7 +893,7 @@ void BLI_getlastdir(const char *dir, char *last, const size_t maxlen) const char *BLI_getDefaultDocumentFolder(void) { #ifndef WIN32 - const char *xdg_documents_dir = getenv("XDG_DOCUMENTS_DIR"); + const char * const xdg_documents_dir = getenv("XDG_DOCUMENTS_DIR"); if (xdg_documents_dir) return xdg_documents_dir; @@ -848,8 +927,9 @@ const char *BLI_getDefaultDocumentFolder(void) /* ************************************************************* */ /* ************************************************************* */ -// #define PATH_DEBUG2 +// #define PATH_DEBUG +/* returns a formatted representation of the specified version number. Non-reentrant! */ static char *blender_version_decimal(const int ver) { static char version_str[5]; @@ -857,7 +937,11 @@ static char *blender_version_decimal(const int ver) return version_str; } -static int test_path(char *targetpath, const char *path_base, const char *path_sep, const char *folder_name) +/** + * Concatenates path_base, (optional) path_sep and (optional) folder_name into targetpath, + * returning true if result points to a directory. + */ +static bool test_path(char *targetpath, const char *path_base, const char *path_sep, const char *folder_name) { char tmppath[FILE_MAX]; @@ -869,43 +953,65 @@ static int test_path(char *targetpath, const char *path_base, const char *path_s BLI_make_file_string("/", targetpath, tmppath, folder_name); else BLI_strncpy(targetpath, tmppath, sizeof(tmppath)); + /* FIXME: why is "//" on front of tmppath expanded to "/" (by BLI_join_dirfile) + * if folder_name is specified but not otherwise? */ if (BLI_is_dir(targetpath)) { -#ifdef PATH_DEBUG2 - printf("\tpath found: %s\n", targetpath); +#ifdef PATH_DEBUG + printf("\t%s found: %s\n", __func__, targetpath); #endif - return 1; + return true; } else { -#ifdef PATH_DEBUG2 - printf("\tpath missing: %s\n", targetpath); +#ifdef PATH_DEBUG + printf("\t%s missing: %s\n", __func__, targetpath); #endif //targetpath[0] = '\0'; - return 0; + return false; } } -static int test_env_path(char *path, const char *envvar) +/** + * Puts the value of the specified environment variable into *path if it exists + * and points at a directory. Returns true if this was done. + */ +static bool test_env_path(char *path, const char *envvar) { const char *env = envvar ? getenv(envvar) : NULL; - if (!env) return 0; + if (!env) return false; if (BLI_is_dir(env)) { BLI_strncpy(path, env, FILE_MAX); - return 1; +#ifdef PATH_DEBUG + printf("\t%s env %s found: %s\n", __func__, envvar, env); +#endif + return true; } else { path[0] = '\0'; - return 0; +#ifdef PATH_DEBUG + printf("\t%s env %s missing: %s\n", __func__, envvar, env); +#endif + return false; } } -static int get_path_local(char *targetpath, const char *folder_name, const char *subfolder_name, const int ver) +/** + * Constructs in \a targetpath the name of a directory relative to a version-specific + * subdirectory in the parent directory of the Blender executable. + * + * \param targetpath String to return path + * \param folder_name Optional folder name within version-specific directory + * \param subfolder_name Optional subfolder name within folder_name + * \param ver To construct name of version-specific directory within bprogdir + * \return true if such a directory exists. + */ +static bool get_path_local(char *targetpath, const char *folder_name, const char *subfolder_name, const int ver) { char relfolder[FILE_MAX]; -#ifdef PATH_DEBUG2 - printf("get_path_local...\n"); +#ifdef PATH_DEBUG + printf("%s...\n", __func__); #endif if (folder_name) { @@ -921,22 +1027,34 @@ static int get_path_local(char *targetpath, const char *folder_name, const char } /* try EXECUTABLE_DIR/2.5x/folder_name - new default directory for local blender installed files */ - if (test_path(targetpath, bprogdir, blender_version_decimal(ver), relfolder)) - return 1; - - return 0; + return test_path(targetpath, bprogdir, blender_version_decimal(ver), relfolder); } -static int is_portable_install(void) +/** + * Is this an install with user files kept together with the Blender executable and its + * installation files. + */ +static bool is_portable_install(void) { - /* detect portable install by the existance of config folder */ + /* detect portable install by the existence of config folder */ const int ver = BLENDER_VERSION; char path[FILE_MAX]; return get_path_local(path, "config", NULL, ver); } -static int get_path_user(char *targetpath, const char *folder_name, const char *subfolder_name, const char *envvar, const int ver) +/** + * Returns the path of a folder within the user-files area. + * + * + * \param targetpath String to return path + * \param folder_name default name of folder within user area + * \param subfolder_name optional name of subfolder within folder + * \param envvar name of environment variable which, if defined, overrides folder_name + * \param ver Blender version, used to construct a subdirectory name + * \return true if it was able to construct such a path. + */ +static bool get_path_user(char *targetpath, const char *folder_name, const char *subfolder_name, const char *envvar, const int ver) { char user_path[FILE_MAX]; const char *user_base_path; @@ -953,7 +1071,7 @@ static int get_path_user(char *targetpath, const char *folder_name, const char * } else { BLI_strncpy(targetpath, user_path, FILE_MAX); - return 1; + return true; } } @@ -962,30 +1080,34 @@ static int get_path_user(char *targetpath, const char *folder_name, const char * BLI_strncpy(user_path, user_base_path, FILE_MAX); if (!user_path[0]) - return 0; + return false; -#ifdef PATH_DEBUG2 - printf("get_path_user: %s\n", user_path); +#ifdef PATH_DEBUG + printf("%s: %s\n", __func__, user_path); #endif if (subfolder_name) { - /* try $HOME/folder_name/subfolder_name */ return test_path(targetpath, user_path, folder_name, subfolder_name); } else { - /* try $HOME/folder_name */ return test_path(targetpath, user_path, NULL, folder_name); } } -static int get_path_system(char *targetpath, const char *folder_name, const char *subfolder_name, const char *envvar, const int ver) +/** + * Returns the path of a folder within the Blender installation directory. + * + * \param targetpath String to return path + * \param folder_name default name of folder within installation area + * \param subfolder_name optional name of subfolder within folder + * \param envvar name of environment variable which, if defined, overrides folder_name + * \param ver Blender version, used to construct a subdirectory name + * \return true if it was able to construct such a path. + */ +static bool get_path_system(char *targetpath, const char *folder_name, const char *subfolder_name, const char *envvar, const int ver) { char system_path[FILE_MAX]; const char *system_base_path; - - - /* first allow developer only overrides to the system path - * these are only used when running blender from source */ char cwd[FILE_MAX]; char relfolder[FILE_MAX]; @@ -1001,16 +1123,20 @@ static int get_path_system(char *targetpath, const char *folder_name, const char relfolder[0] = '\0'; } + /* first allow developer only overrides to the system path + * these are only used when running blender from source */ + /* try CWD/release/folder_name */ if (BLI_current_working_dir(cwd, sizeof(cwd))) { if (test_path(targetpath, cwd, "release", relfolder)) { - return 1; + return true; } } /* try EXECUTABLE_DIR/release/folder_name */ if (test_path(targetpath, bprogdir, "release", relfolder)) - return 1; + return true; + /* end developer overrides */ @@ -1023,7 +1149,7 @@ static int get_path_system(char *targetpath, const char *folder_name, const char } else { BLI_strncpy(targetpath, system_path, FILE_MAX); - return 1; + return true; } } @@ -1032,10 +1158,10 @@ static int get_path_system(char *targetpath, const char *folder_name, const char BLI_strncpy(system_path, system_base_path, FILE_MAX); if (!system_path[0]) - return 0; + return false; -#ifdef PATH_DEBUG2 - printf("get_path_system: %s\n", system_path); +#ifdef PATH_DEBUG + printf("%s: %s\n", __func__, system_path); #endif if (subfolder_name) { @@ -1050,7 +1176,7 @@ static int get_path_system(char *targetpath, const char *folder_name, const char /* get a folder out of the 'folder_id' presets for paths */ /* returns the path if found, NULL string if not */ -char *BLI_get_folder(int folder_id, const char *subfolder) +const char *BLI_get_folder(int folder_id, const char *subfolder) { const int ver = BLENDER_VERSION; static char path[FILE_MAX] = ""; @@ -1097,7 +1223,10 @@ char *BLI_get_folder(int folder_id, const char *subfolder) return path; } -char *BLI_get_user_folder_notest(int folder_id, const char *subfolder) +/** + * Returns the path to a folder in the user area without checking that it actually exists first. + */ +const char *BLI_get_user_folder_notest(int folder_id, const char *subfolder) { const int ver = BLENDER_VERSION; static char path[FILE_MAX] = ""; @@ -1122,9 +1251,12 @@ char *BLI_get_user_folder_notest(int folder_id, const char *subfolder) return path; } -char *BLI_get_folder_create(int folder_id, const char *subfolder) +/** + * Returns the path to a folder in the user area, creating it if it doesn't exist. + */ +const char *BLI_get_folder_create(int folder_id, const char *subfolder) { - char *path; + const char *path; /* only for user folders */ if (!ELEM4(folder_id, BLENDER_USER_DATAFILES, BLENDER_USER_CONFIG, BLENDER_USER_SCRIPTS, BLENDER_USER_AUTOSAVE)) @@ -1140,10 +1272,14 @@ char *BLI_get_folder_create(int folder_id, const char *subfolder) return path; } -char *BLI_get_folder_version(const int id, const int ver, const int do_check) +/** + * Returns the path of the top-level version-specific local, user or system directory. + * If do_check, then the result will be NULL if the directory doesn't exist. + */ +const char *BLI_get_folder_version(const int id, const int ver, const bool do_check) { static char path[FILE_MAX] = ""; - int ok; + bool ok; switch (id) { case BLENDER_RESOURCE_PATH_USER: ok = get_path_user(path, NULL, NULL, NULL, ver); @@ -1156,11 +1292,11 @@ char *BLI_get_folder_version(const int id, const int ver, const int do_check) break; default: path[0] = '\0'; /* in case do_check is false */ - ok = FALSE; + ok = false; BLI_assert(!"incorrect ID"); } - if ((ok == FALSE) && do_check) { + if (!ok && do_check) { return NULL; } @@ -1174,9 +1310,12 @@ char *BLI_get_folder_version(const int id, const int ver, const int do_check) #ifdef PATH_DEBUG -#undef PATH_DEBUG +# undef PATH_DEBUG #endif +/** + * Sets the specified environment variable to the specified value. + */ void BLI_setenv(const char *env, const char *val) { /* free windows */ @@ -1202,6 +1341,8 @@ void BLI_setenv(const char *env, const char *val) /** * Only set an env var if already not there. * Like Unix setenv(env, val, 0); + * + * (not used anywhere). */ void BLI_setenv_if_new(const char *env, const char *val) { @@ -1210,10 +1351,11 @@ void BLI_setenv_if_new(const char *env, const char *val) } +/** + * Changes to the path separators to the native ones for this OS. + */ void BLI_clean(char *path) { - if (path == NULL) return; - #ifdef WIN32 if (path && BLI_strnlen(path, 3) > 2) { BLI_char_switch(path + 2, '/', '\\'); @@ -1223,15 +1365,26 @@ void BLI_clean(char *path) #endif } +/** + * Change every \a from in \a string into \a to. The + * result will be in \a string + * + * \param string The string to work on + * \param from The character to replace + * \param to The character to replace with + */ void BLI_char_switch(char *string, char from, char to) { - if (string == NULL) return; while (*string != 0) { if (*string == from) *string = to; string++; } } +/** + * Strips off nonexistent subdirectories from the end of *dir, leaving the path of + * the lowest-level directory that does exist. + */ void BLI_make_exist(char *dir) { int a; @@ -1240,7 +1393,7 @@ void BLI_make_exist(char *dir) a = strlen(dir); - while (BLI_is_dir(dir) == 0) { + while (!BLI_is_dir(dir)) { a--; while (dir[a] != SEP) { a--; @@ -1260,21 +1413,28 @@ void BLI_make_exist(char *dir) } } +/** + * Ensures that the parent directory of *name exists. + */ void BLI_make_existing_file(const char *name) { - char di[FILE_MAX], fi[FILE_MAXFILE]; + char di[FILE_MAX]; + BLI_split_dir_part(name, di, sizeof(di)); - BLI_strncpy(di, name, sizeof(di)); - BLI_splitdirstring(di, fi); - - /* test exist */ - if (BLI_exists(di) == 0) { - BLI_dir_create_recursive(di); - } + /* make if if the dir doesn't exist */ + BLI_dir_create_recursive(di); } - -void BLI_make_file_string(const char *relabase, char *string, const char *dir, const char *file) +/** + * Returns in *string the concatenation of *dir and *file (also with *relabase on the + * front if specified and *dir begins with "//"). Normalizes all occurrences of path + * separators, including ensuring there is exactly one between the copies of *dir and *file, + * and between the copies of *relabase and *dir. + * + * \param relabase Optional prefix to substitute for "//" on front of *dir + * \param string Area to return result + */ +void BLI_make_file_string(const char *relabase, char *string, const char *dir, const char *file) { int sl; @@ -1307,7 +1467,7 @@ void BLI_make_file_string(const char *relabase, char *string, const char *dir, /* Get the file name, chop everything past the last slash (ie. the filename) */ strcpy(string, relabase); - lslash = BLI_last_slash(string); + lslash = (char *)BLI_last_slash(string); if (lslash) *(lslash + 1) = 0; dir += 2; /* Skip over the relative reference */ @@ -1356,43 +1516,34 @@ void BLI_make_file_string(const char *relabase, char *string, const char *dir, BLI_clean(string); } -int BLI_testextensie(const char *str, const char *ext) +/* does str end with ext. */ +bool BLI_testextensie(const char *str, const char *ext) { - short a, b; - int retval; - - a = strlen(str); - b = strlen(ext); - - if (a == 0 || b == 0 || b >= a) { - retval = 0; - } - else if (BLI_strcasecmp(ext, str + a - b)) { - retval = 0; - } - else { - retval = 1; - } - - return (retval); + const size_t a = strlen(str); + const size_t b = strlen(ext); + return !(a == 0 || b == 0 || b >= a) && (BLI_strcasecmp(ext, str + a - b) == 0); } -int BLI_testextensie_array(const char *str, const char **ext_array) +/* does str end with any of the suffixes in *ext_array. */ +bool BLI_testextensie_array(const char *str, const char **ext_array) { int i = 0; while (ext_array[i]) { if (BLI_testextensie(str, ext_array[i])) { - return 1; + return true; } i++; } - return 0; + return false; } -/* semicolon separated wildcards, eg: - * '*.zip;*.py;*.exe' */ -int BLI_testextensie_glob(const char *str, const char *ext_fnmatch) +/** + * Semicolon separated wildcards, eg: + * '*.zip;*.py;*.exe' + * does str match any of the semicolon-separated glob patterns in fnmatch. + */ +bool BLI_testextensie_glob(const char *str, const char *ext_fnmatch) { const char *ext_step = ext_fnmatch; char pattern[16]; @@ -1411,19 +1562,23 @@ int BLI_testextensie_glob(const char *str, const char *ext_fnmatch) BLI_strncpy(pattern, ext_step, len_ext); if (fnmatch(pattern, str, FNM_CASEFOLD) == 0) { - return 1; + return true; } ext_step += len_ext; } - return 0; + return false; } -int BLI_replace_extension(char *path, size_t maxlen, const char *ext) +/** + * Removes any existing extension on the end of \a path and appends \a ext. + * \return false if there was no room. + */ +bool BLI_replace_extension(char *path, size_t maxlen, const char *ext) { - size_t path_len = strlen(path); - size_t ext_len = strlen(ext); + const size_t path_len = strlen(path); + const size_t ext_len = strlen(ext); ssize_t a; for (a = path_len - 1; a >= 0; a--) { @@ -1437,24 +1592,26 @@ int BLI_replace_extension(char *path, size_t maxlen, const char *ext) } if (a + ext_len >= maxlen) - return 0; + return false; memcpy(path + a, ext, ext_len + 1); - return 1; + return true; } -/* strip's trailing '.'s and adds the extension only when needed */ -int BLI_ensure_extension(char *path, size_t maxlen, const char *ext) +/** + * Strip's trailing '.'s and adds the extension only when needed + */ +bool BLI_ensure_extension(char *path, size_t maxlen, const char *ext) { - size_t path_len = strlen(path); - size_t ext_len = strlen(ext); + const size_t path_len = strlen(path); + const size_t ext_len = strlen(ext); ssize_t a; - /* first check the extension is alread there */ + /* first check the extension is already there */ if ( (ext_len <= path_len) && (strcmp(path + (path_len - ext_len), ext) == 0)) { - return 1; + return true; } for (a = path_len - 1; a >= 0; a--) { @@ -1468,10 +1625,10 @@ int BLI_ensure_extension(char *path, size_t maxlen, const char *ext) a++; if (a + ext_len >= maxlen) - return 0; + return false; memcpy(path + a, ext, ext_len + 1); - return 1; + return true; } /* Converts "/foo/bar.txt" to "/foo/" and "bar.txt" @@ -1482,8 +1639,8 @@ int BLI_ensure_extension(char *path, size_t maxlen, const char *ext) * */ void BLI_split_dirfile(const char *string, char *dir, char *file, const size_t dirlen, const size_t filelen) { - char *lslash_str = BLI_last_slash(string); - size_t lslash = lslash_str ? (size_t)(lslash_str - string) + 1 : 0; + const char *lslash_str = BLI_last_slash(string); + const size_t lslash = lslash_str ? (size_t)(lslash_str - string) + 1 : 0; if (dir) { if (lslash) { @@ -1499,17 +1656,26 @@ void BLI_split_dirfile(const char *string, char *dir, char *file, const size_t d } } +/** + * Copies the parent directory part of string into *dir, max length dirlen. + */ void BLI_split_dir_part(const char *string, char *dir, const size_t dirlen) { BLI_split_dirfile(string, dir, NULL, dirlen, 0); } +/** + * Copies the leaf filename part of string into *file, max length filelen. + */ void BLI_split_file_part(const char *string, char *file, const size_t filelen) { BLI_split_dirfile(string, NULL, file, 0, filelen); } -/* simple appending of filename to dir, does not check for valid path! */ +/** + * Simple appending of filename to dir, does not check for valid path! + * Puts result into *dst, which may be same area as *dir. + */ void BLI_join_dirfile(char *dst, const size_t maxlen, const char *dir, const char *file) { size_t dirlen = BLI_strnlen(dir, maxlen); @@ -1546,10 +1712,15 @@ void BLI_join_dirfile(char *dst, const size_t maxlen, const char *dir, const cha BLI_strncpy(dst + dirlen, file, maxlen - dirlen); } -/* like pythons os.path.basename( ) */ -char *BLI_path_basename(char *path) +/** + * like pythons os.path.basename() + * + * \return The pointer into \a path string immediately after last slash, + * or start of \a path if none found. + */ +const char *BLI_path_basename(const char *path) { - char *filename = BLI_last_slash(path); + const char * const filename = BLI_last_slash(path); return filename ? filename + 1 : path; } @@ -1588,11 +1759,29 @@ char *BLI_path_basename(char *path) * this function returns wrong results! * XXX: test on empty base_dir and return an error ? */ -int BLI_rebase_path(char *abs, size_t abs_len, char *rel, size_t rel_len, const char *base_dir, const char *src_dir, const char *dest_dir) + +/** + * + * \param abs Optional string to return new full path + * \param abs_len Size of *abs string + * \param rel Optional area to return new path relative to parent directory of .blend file + * (only meaningful if item is in a subdirectory thereof) + * \param rel_len Size of *rel area + * \param base_dir Path of .blend file + * \param src_dir Original path of item (any initial "//" will be expanded to + * parent directory of .blend file) + * \param dest_dir New directory into which item will be moved + * \return bli_rebase_state + * + * \note Not actually used anywhere! + */ +int BLI_rebase_path(char *abs, size_t abs_len, + char *rel, size_t rel_len, + const char *base_dir, const char *src_dir, const char *dest_dir) { - char path[FILE_MAX]; - char dir[FILE_MAX]; - char base[FILE_MAX]; + char path[FILE_MAX]; /* original full path of item */ + char dir[FILE_MAX]; /* directory part of src_dir */ + char base[FILE_MAX]; /* basename part of src_dir */ char blend_dir[FILE_MAX]; /* directory, where current .blend file resides */ char dest_path[FILE_MAX]; char rel_dir[FILE_MAX]; @@ -1622,23 +1811,25 @@ int BLI_rebase_path(char *abs, size_t abs_len, char *rel, size_t rel_len, const rel_dir[0] = 0; /* if image is "below" current .blend file directory */ - if (!strncmp(path, blend_dir, len)) { + if (!BLI_path_ncmp(path, blend_dir, len)) { - /* if image is _in_ current .blend file directory */ if (BLI_path_cmp(dir, blend_dir) == 0) { + /* image is directly in .blend file parent directory => put directly in dest_dir */ BLI_join_dirfile(dest_path, sizeof(dest_path), dest_dir, base); } - /* "below" */ else { + /* "below" (in subdirectory of .blend file parent directory) => put in same relative directory structure in dest_dir */ /* rel = image_path_dir - blend_dir */ BLI_strncpy(rel_dir, dir + len, sizeof(rel_dir)); - + /* subdirectories relative to blend_dir */ BLI_join_dirfile(dest_path, sizeof(dest_path), dest_dir, rel_dir); + /* same subdirectories relative to dest_dir */ BLI_join_dirfile(dest_path, sizeof(dest_path), dest_path, base); + /* keeping original item basename */ } } - /* image is out of current directory */ + /* image is out of current directory -- just put straight in dest_dir */ else { BLI_join_dirfile(dest_path, sizeof(dest_path), dest_dir, base); } @@ -1648,7 +1839,7 @@ int BLI_rebase_path(char *abs, size_t abs_len, char *rel, size_t rel_len, const if (rel) { strncat(rel, rel_dir, rel_len); - strncat(rel, base, rel_len); + strncat(rel, base, rel_len); /* FIXME: could overflow rel area! */ } /* return 2 if (src == dest) */ @@ -1660,12 +1851,13 @@ int BLI_rebase_path(char *abs, size_t abs_len, char *rel, size_t rel_len, const return BLI_REBASE_OK; } -char *BLI_first_slash(char *string) +/** + * Returns pointer to the leftmost path separator in string. Not actually used anywhere. + */ +const char *BLI_first_slash(const char *string) { - char *ffslash, *fbslash; - - ffslash = strchr(string, '/'); - fbslash = strchr(string, '\\'); + char * const ffslash = strchr(string, '/'); + char * const fbslash = strchr(string, '\\'); if (!ffslash) return fbslash; else if (!fbslash) return ffslash; @@ -1674,12 +1866,13 @@ char *BLI_first_slash(char *string) else return fbslash; } -char *BLI_last_slash(const char *string) +/** + * Returns pointer to the rightmost path separator in string. + */ +const char *BLI_last_slash(const char *string) { - char *lfslash, *lbslash; - - lfslash = strrchr(string, '/'); - lbslash = strrchr(string, '\\'); + const char * const lfslash = strrchr(string, '/'); + const char * const lbslash = strrchr(string, '\\'); if (!lfslash) return lbslash; else if (!lbslash) return lfslash; @@ -1688,7 +1881,10 @@ char *BLI_last_slash(const char *string) else return lfslash; } -/* adds a slash if there isn't one there already */ +/** + * Appends a slash to string if there isn't one there already. + * Returns the new length of the string. + */ int BLI_add_slash(char *string) { int len = strlen(string); @@ -1700,7 +1896,9 @@ int BLI_add_slash(char *string) return len; } -/* removes a slash if there is one */ +/** + * Removes the last slash and everything after it to the end of string, if there is one. + */ void BLI_del_slash(char *string) { int len = strlen(string); @@ -1715,6 +1913,11 @@ void BLI_del_slash(char *string) } } +/** + * Tries appending each of the semicolon-separated extensions in the PATHEXT + * environment variable (Windows-only) onto *name in turn until such a file is found. + * Returns success/failure. + */ static int add_win32_extension(char *name) { int retval = 0; @@ -1864,11 +2067,17 @@ void BLI_init_program_path(const char *argv0) BLI_split_dir_part(bprogname, bprogdir, sizeof(bprogdir)); } +/** + * Path to executable + */ const char *BLI_program_path(void) { return bprogname; } +/** + * Path to directory of executable + */ const char *BLI_program_dir(void) { return bprogdir; @@ -1931,16 +2140,26 @@ static void BLI_where_is_temp(char *fullname, const size_t maxlen, char *userdir } } +/** + * Sets btempdir to userdir if specified and is a valid directory, otherwise + * chooses a suitable OS-specific temporary directory. + */ void BLI_init_temporary_dir(char *userdir) { BLI_where_is_temp(btempdir, FILE_MAX, userdir); } +/** + * Path to temporary directory (with trailing slash) + */ const char *BLI_temporary_dir(void) { return btempdir; } +/** + * Path to the system temporary directory (with trailing slash) + */ void BLI_system_temporary_dir(char *dir) { BLI_where_is_temp(dir, FILE_MAX, NULL); @@ -1948,6 +2167,10 @@ void BLI_system_temporary_dir(char *dir) #ifdef WITH_ICONV +/** + * Converts a string encoded in the charset named by *code to UTF-8. + * Opens a new iconv context each time it is run, which is probably not the + * most efficient. */ void BLI_string_to_utf8(char *original, char *utf_8, const char *code) { size_t inbytesleft = strlen(original); @@ -1968,6 +2191,7 @@ void BLI_string_to_utf8(char *original, char *utf_8, const char *code) rv = iconv(cd, &original, &inbytesleft, &utf_8, &outbytesleft); if (rv == (size_t) -1) { printf("iconv Error\n"); + iconv_close(cd); return; } *utf_8 = '\0'; |