Mercurial > foo_out_sdl
diff foosdk/sdk/foobar2000/helpers/create_directory_helper.cpp @ 1:20d02a178406 default tip
*: check in everything else
yay
| author | Paper <paper@tflc.us> |
|---|---|
| date | Mon, 05 Jan 2026 02:15:46 -0500 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/foosdk/sdk/foobar2000/helpers/create_directory_helper.cpp Mon Jan 05 02:15:46 2026 -0500 @@ -0,0 +1,202 @@ +#include "StdAfx.h" +#include "create_directory_helper.h" +#include <pfc/pathUtils.h> +#include <SDK/file_lock_manager.h> + +namespace create_directory_helper +{ + static void create_path_internal(const char * p_path,t_size p_base,abort_callback & p_abort) { + pfc::string8_fastalloc temp; + auto fs = filesystem::get(p_path); + auto api_lock = file_lock_manager::get(); + for(t_size walk = p_base; p_path[walk]; walk++) { + if (p_path[walk] == '\\') { + temp.set_string(p_path,walk); + // 2024-03 Google Drive bug: + // Creating the same folder concurrently from multiple threads causes erratic behavior + // Thread that got here first behaves OK, others get "already exists" and return, but creating files in the folder fail with "path not found" + // Block other threads trying to do the same until we've finished + const auto lock = api_lock->acquire_write(temp, p_abort); + fs->make_directory(temp, p_abort); + } + } + } + + static bool is_valid_netpath_char(char p_char) { + return pfc::char_is_ascii_alphanumeric(p_char) || p_char == '_' || p_char == '-' || p_char == '.'; + } + + static bool test_localpath(const char * p_path) { + if (pfc::strcmp_partial(p_path,"file://") == 0) p_path += strlen("file://"); + return pfc::char_is_ascii_alpha(p_path[0]) && + p_path[1] == ':' && + p_path[2] == '\\'; + } + static bool test_netpath(const char * p_path) { + if (pfc::strcmp_partial(p_path,"file://") == 0) p_path += strlen("file://"); + if (*p_path != '\\') return false; + p_path++; + if (*p_path != '\\') return false; + p_path++; + if (!is_valid_netpath_char(*p_path)) return false; + p_path++; + while(is_valid_netpath_char(*p_path)) p_path++; + if (*p_path != '\\') return false; + return true; + } + + void create_path(const char * p_path,abort_callback & p_abort) { + if (test_localpath(p_path)) { + t_size walk = 0; + if (pfc::strcmp_partial(p_path,"file://") == 0) walk += strlen("file://"); + create_path_internal(p_path,walk + 3,p_abort); + } else if (test_netpath(p_path)) { + t_size walk = 0; + if (pfc::strcmp_partial(p_path,"file://") == 0) walk += strlen("file://"); + while(p_path[walk] == '\\') walk++; + while(p_path[walk] != 0 && p_path[walk] != '\\') walk++; + while(p_path[walk] == '\\') walk++; + while(p_path[walk] != 0 && p_path[walk] != '\\') walk++; + while(p_path[walk] == '\\') walk++; + create_path_internal(p_path,walk,p_abort); + } else { + pfc::throw_exception_with_message< exception_io > ("Could not create directory structure; unknown path format"); + } + } + +#ifdef _WIN32 + static bool is_bad_dirchar(char c) + { + return c==' ' || c=='.'; + } +#endif + + void make_path(const char * parent,const char * filename,const char * extension,bool allow_new_dirs,pfc::string8 & out,bool really_create_dirs,abort_callback & p_abort) + { + out.reset(); + if (parent && *parent) + { + out = parent; + out.fix_dir_separator('\\'); + } + bool last_char_is_dir_sep = true; + while(*filename) + { +#ifdef WIN32 + if (allow_new_dirs && is_bad_dirchar(*filename)) + { + const char * ptr = filename+1; + while(is_bad_dirchar(*ptr)) ptr++; + if (*ptr!='\\' && *ptr!='/') out.add_string(filename,ptr-filename); + filename = ptr; + if (*filename==0) break; + } +#endif + if (pfc::is_path_bad_char(*filename)) + { + if (allow_new_dirs && (*filename=='\\' || *filename=='/')) + { + if (!last_char_is_dir_sep) + { + if (really_create_dirs) try{filesystem::g_create_directory(out,p_abort);}catch(exception_io_already_exists const &){} + out.add_char('\\'); + last_char_is_dir_sep = true; + } + } + else + out.add_char('_'); + } + else + { + out.add_byte(*filename); + last_char_is_dir_sep = false; + } + filename++; + } + if (out.length()>0 && out[out.length()-1]=='\\') + { + out.add_string("noname"); + } + if (extension && *extension) + { + out.add_char('.'); + out.add_string(extension); + } + } +} + +pfc::string create_directory_helper::sanitize_formatted_path(pfc::stringp formatted, bool allowWC) { + return sanitize_formatted_path_ex(formatted, allowWC, pfc::io::path::charReplaceDefault); +}; + +pfc::string create_directory_helper::sanitize_formatted_path_ex(pfc::stringp formatted, bool allowWC, charReplace_t replace) { + pfc::string out; + t_size curSegBase = 0; + for (t_size walk = 0; ; ++walk) { + const char c = formatted[walk]; + const bool end = (c == 0); + if (end || pfc::io::path::isSeparator(c)) { + if (curSegBase < walk) { + pfc::string seg(formatted + curSegBase, walk - curSegBase); + out = pfc::io::path::combine(out, pfc::io::path::validateFileName(seg, allowWC, end /*preserve ext*/, replace)); + } + if (end) break; + curSegBase = walk + 1; + } + } + return out; +} + +void create_directory_helper::format_filename_ex(const metadb_handle_ptr & handle, titleformat_hook * p_hook, titleformat_object::ptr spec, const char * suffix, pfc::string_base & out) { + format_filename_ex(handle, p_hook, spec, suffix, out, pfc::io::path::charReplaceDefault); +} + +void create_directory_helper::format_filename_ex(const metadb_handle_ptr & handle,titleformat_hook * p_hook,titleformat_object::ptr spec,const char * suffix, pfc::string_base & out, charReplace_t replace) { + pfc::string_formatter formatted; + titleformat_text_filter_myimpl filter; + filter.m_replace = replace; + handle->format_title(p_hook,formatted,spec,&filter); + formatted << suffix; + out = sanitize_formatted_path_ex(formatted, false, replace).ptr(); +} +void create_directory_helper::format_filename(const metadb_handle_ptr & handle,titleformat_hook * p_hook,titleformat_object::ptr spec,pfc::string_base & out) { + format_filename_ex(handle, p_hook, spec, "", out); +} +void create_directory_helper::format_filename(const metadb_handle_ptr & handle,titleformat_hook * p_hook,const char * spec,pfc::string_base & out) +{ + service_ptr_t<titleformat_object> script; + if (titleformat_compiler::get()->compile(script,spec)) { + format_filename(handle, p_hook, script, out); + } else { + out.reset(); + } +} + +static bool substSanity(const char * subst) { + if (subst == nullptr) return false; + for (size_t w = 0; subst[w]; ++w) { + if (pfc::io::path::isSeparator(subst[w])) return false; + } + return true; +} + +void create_directory_helper::titleformat_text_filter_myimpl::write(const GUID & p_inputType,pfc::string_receiver & p_out,const char * p_data,t_size p_dataLength) { + if (p_inputType == titleformat_inputtypes::meta) { + pfc::string_formatter temp; + for(t_size walk = 0; walk < p_dataLength; ++walk) { + char c = p_data[walk]; + if (c == 0) break; + const char * subst = nullptr; + if (pfc::io::path::isSeparator(c)) { + if (m_replace) { + const char * proposed = m_replace(c); + if (substSanity(proposed)) subst = proposed; + } + if (subst == nullptr) subst = "-"; + } + if (subst != nullptr) temp.add_string(subst); + else temp.add_byte(c); + } + p_out.add_string(temp); + } else p_out.add_string(p_data,p_dataLength); +}
