Mercurial > foo_out_sdl
comparison 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 |
comparison
equal
deleted
inserted
replaced
| 0:e9bb126753e7 | 1:20d02a178406 |
|---|---|
| 1 #include "StdAfx.h" | |
| 2 #include "create_directory_helper.h" | |
| 3 #include <pfc/pathUtils.h> | |
| 4 #include <SDK/file_lock_manager.h> | |
| 5 | |
| 6 namespace create_directory_helper | |
| 7 { | |
| 8 static void create_path_internal(const char * p_path,t_size p_base,abort_callback & p_abort) { | |
| 9 pfc::string8_fastalloc temp; | |
| 10 auto fs = filesystem::get(p_path); | |
| 11 auto api_lock = file_lock_manager::get(); | |
| 12 for(t_size walk = p_base; p_path[walk]; walk++) { | |
| 13 if (p_path[walk] == '\\') { | |
| 14 temp.set_string(p_path,walk); | |
| 15 // 2024-03 Google Drive bug: | |
| 16 // Creating the same folder concurrently from multiple threads causes erratic behavior | |
| 17 // Thread that got here first behaves OK, others get "already exists" and return, but creating files in the folder fail with "path not found" | |
| 18 // Block other threads trying to do the same until we've finished | |
| 19 const auto lock = api_lock->acquire_write(temp, p_abort); | |
| 20 fs->make_directory(temp, p_abort); | |
| 21 } | |
| 22 } | |
| 23 } | |
| 24 | |
| 25 static bool is_valid_netpath_char(char p_char) { | |
| 26 return pfc::char_is_ascii_alphanumeric(p_char) || p_char == '_' || p_char == '-' || p_char == '.'; | |
| 27 } | |
| 28 | |
| 29 static bool test_localpath(const char * p_path) { | |
| 30 if (pfc::strcmp_partial(p_path,"file://") == 0) p_path += strlen("file://"); | |
| 31 return pfc::char_is_ascii_alpha(p_path[0]) && | |
| 32 p_path[1] == ':' && | |
| 33 p_path[2] == '\\'; | |
| 34 } | |
| 35 static bool test_netpath(const char * p_path) { | |
| 36 if (pfc::strcmp_partial(p_path,"file://") == 0) p_path += strlen("file://"); | |
| 37 if (*p_path != '\\') return false; | |
| 38 p_path++; | |
| 39 if (*p_path != '\\') return false; | |
| 40 p_path++; | |
| 41 if (!is_valid_netpath_char(*p_path)) return false; | |
| 42 p_path++; | |
| 43 while(is_valid_netpath_char(*p_path)) p_path++; | |
| 44 if (*p_path != '\\') return false; | |
| 45 return true; | |
| 46 } | |
| 47 | |
| 48 void create_path(const char * p_path,abort_callback & p_abort) { | |
| 49 if (test_localpath(p_path)) { | |
| 50 t_size walk = 0; | |
| 51 if (pfc::strcmp_partial(p_path,"file://") == 0) walk += strlen("file://"); | |
| 52 create_path_internal(p_path,walk + 3,p_abort); | |
| 53 } else if (test_netpath(p_path)) { | |
| 54 t_size walk = 0; | |
| 55 if (pfc::strcmp_partial(p_path,"file://") == 0) walk += strlen("file://"); | |
| 56 while(p_path[walk] == '\\') walk++; | |
| 57 while(p_path[walk] != 0 && p_path[walk] != '\\') walk++; | |
| 58 while(p_path[walk] == '\\') walk++; | |
| 59 while(p_path[walk] != 0 && p_path[walk] != '\\') walk++; | |
| 60 while(p_path[walk] == '\\') walk++; | |
| 61 create_path_internal(p_path,walk,p_abort); | |
| 62 } else { | |
| 63 pfc::throw_exception_with_message< exception_io > ("Could not create directory structure; unknown path format"); | |
| 64 } | |
| 65 } | |
| 66 | |
| 67 #ifdef _WIN32 | |
| 68 static bool is_bad_dirchar(char c) | |
| 69 { | |
| 70 return c==' ' || c=='.'; | |
| 71 } | |
| 72 #endif | |
| 73 | |
| 74 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) | |
| 75 { | |
| 76 out.reset(); | |
| 77 if (parent && *parent) | |
| 78 { | |
| 79 out = parent; | |
| 80 out.fix_dir_separator('\\'); | |
| 81 } | |
| 82 bool last_char_is_dir_sep = true; | |
| 83 while(*filename) | |
| 84 { | |
| 85 #ifdef WIN32 | |
| 86 if (allow_new_dirs && is_bad_dirchar(*filename)) | |
| 87 { | |
| 88 const char * ptr = filename+1; | |
| 89 while(is_bad_dirchar(*ptr)) ptr++; | |
| 90 if (*ptr!='\\' && *ptr!='/') out.add_string(filename,ptr-filename); | |
| 91 filename = ptr; | |
| 92 if (*filename==0) break; | |
| 93 } | |
| 94 #endif | |
| 95 if (pfc::is_path_bad_char(*filename)) | |
| 96 { | |
| 97 if (allow_new_dirs && (*filename=='\\' || *filename=='/')) | |
| 98 { | |
| 99 if (!last_char_is_dir_sep) | |
| 100 { | |
| 101 if (really_create_dirs) try{filesystem::g_create_directory(out,p_abort);}catch(exception_io_already_exists const &){} | |
| 102 out.add_char('\\'); | |
| 103 last_char_is_dir_sep = true; | |
| 104 } | |
| 105 } | |
| 106 else | |
| 107 out.add_char('_'); | |
| 108 } | |
| 109 else | |
| 110 { | |
| 111 out.add_byte(*filename); | |
| 112 last_char_is_dir_sep = false; | |
| 113 } | |
| 114 filename++; | |
| 115 } | |
| 116 if (out.length()>0 && out[out.length()-1]=='\\') | |
| 117 { | |
| 118 out.add_string("noname"); | |
| 119 } | |
| 120 if (extension && *extension) | |
| 121 { | |
| 122 out.add_char('.'); | |
| 123 out.add_string(extension); | |
| 124 } | |
| 125 } | |
| 126 } | |
| 127 | |
| 128 pfc::string create_directory_helper::sanitize_formatted_path(pfc::stringp formatted, bool allowWC) { | |
| 129 return sanitize_formatted_path_ex(formatted, allowWC, pfc::io::path::charReplaceDefault); | |
| 130 }; | |
| 131 | |
| 132 pfc::string create_directory_helper::sanitize_formatted_path_ex(pfc::stringp formatted, bool allowWC, charReplace_t replace) { | |
| 133 pfc::string out; | |
| 134 t_size curSegBase = 0; | |
| 135 for (t_size walk = 0; ; ++walk) { | |
| 136 const char c = formatted[walk]; | |
| 137 const bool end = (c == 0); | |
| 138 if (end || pfc::io::path::isSeparator(c)) { | |
| 139 if (curSegBase < walk) { | |
| 140 pfc::string seg(formatted + curSegBase, walk - curSegBase); | |
| 141 out = pfc::io::path::combine(out, pfc::io::path::validateFileName(seg, allowWC, end /*preserve ext*/, replace)); | |
| 142 } | |
| 143 if (end) break; | |
| 144 curSegBase = walk + 1; | |
| 145 } | |
| 146 } | |
| 147 return out; | |
| 148 } | |
| 149 | |
| 150 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) { | |
| 151 format_filename_ex(handle, p_hook, spec, suffix, out, pfc::io::path::charReplaceDefault); | |
| 152 } | |
| 153 | |
| 154 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) { | |
| 155 pfc::string_formatter formatted; | |
| 156 titleformat_text_filter_myimpl filter; | |
| 157 filter.m_replace = replace; | |
| 158 handle->format_title(p_hook,formatted,spec,&filter); | |
| 159 formatted << suffix; | |
| 160 out = sanitize_formatted_path_ex(formatted, false, replace).ptr(); | |
| 161 } | |
| 162 void create_directory_helper::format_filename(const metadb_handle_ptr & handle,titleformat_hook * p_hook,titleformat_object::ptr spec,pfc::string_base & out) { | |
| 163 format_filename_ex(handle, p_hook, spec, "", out); | |
| 164 } | |
| 165 void create_directory_helper::format_filename(const metadb_handle_ptr & handle,titleformat_hook * p_hook,const char * spec,pfc::string_base & out) | |
| 166 { | |
| 167 service_ptr_t<titleformat_object> script; | |
| 168 if (titleformat_compiler::get()->compile(script,spec)) { | |
| 169 format_filename(handle, p_hook, script, out); | |
| 170 } else { | |
| 171 out.reset(); | |
| 172 } | |
| 173 } | |
| 174 | |
| 175 static bool substSanity(const char * subst) { | |
| 176 if (subst == nullptr) return false; | |
| 177 for (size_t w = 0; subst[w]; ++w) { | |
| 178 if (pfc::io::path::isSeparator(subst[w])) return false; | |
| 179 } | |
| 180 return true; | |
| 181 } | |
| 182 | |
| 183 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) { | |
| 184 if (p_inputType == titleformat_inputtypes::meta) { | |
| 185 pfc::string_formatter temp; | |
| 186 for(t_size walk = 0; walk < p_dataLength; ++walk) { | |
| 187 char c = p_data[walk]; | |
| 188 if (c == 0) break; | |
| 189 const char * subst = nullptr; | |
| 190 if (pfc::io::path::isSeparator(c)) { | |
| 191 if (m_replace) { | |
| 192 const char * proposed = m_replace(c); | |
| 193 if (substSanity(proposed)) subst = proposed; | |
| 194 } | |
| 195 if (subst == nullptr) subst = "-"; | |
| 196 } | |
| 197 if (subst != nullptr) temp.add_string(subst); | |
| 198 else temp.add_byte(c); | |
| 199 } | |
| 200 p_out.add_string(temp); | |
| 201 } else p_out.add_string(p_data,p_dataLength); | |
| 202 } |
