| 1 | // ================================================================ // |
|---|
| 2 | // // |
|---|
| 3 | // File : AW_file.cxx // |
|---|
| 4 | // Purpose : // |
|---|
| 5 | // // |
|---|
| 6 | // Institute of Microbiology (Technical University Munich) // |
|---|
| 7 | // http://www.arb-home.de/ // |
|---|
| 8 | // // |
|---|
| 9 | // ================================================================ // |
|---|
| 10 | |
|---|
| 11 | #include "aw_file.hxx" |
|---|
| 12 | #include "aw_awar.hxx" |
|---|
| 13 | #include "aw_root.hxx" |
|---|
| 14 | #include "aw_select.hxx" |
|---|
| 15 | #include "aw_msg.hxx" |
|---|
| 16 | #include "aw_question.hxx" |
|---|
| 17 | |
|---|
| 18 | #include <arbdbt.h> |
|---|
| 19 | #include <arb_file.h> |
|---|
| 20 | #include <arb_strbuf.h> |
|---|
| 21 | #include <arb_misc.h> |
|---|
| 22 | #include <arb_str.h> |
|---|
| 23 | #include <arb_strarray.h> |
|---|
| 24 | |
|---|
| 25 | #include <sys/stat.h> |
|---|
| 26 | #include <dirent.h> |
|---|
| 27 | #include <set> |
|---|
| 28 | #include <string> |
|---|
| 29 | |
|---|
| 30 | using std::set; |
|---|
| 31 | using std::string; |
|---|
| 32 | |
|---|
| 33 | #if defined(DEBUG) |
|---|
| 34 | // #define TRACE_FILEBOX |
|---|
| 35 | #endif // DEBUG |
|---|
| 36 | |
|---|
| 37 | static GB_CSTR expand_symbolic_directories(const char *pwd_envar) { |
|---|
| 38 | GB_CSTR res; |
|---|
| 39 | |
|---|
| 40 | if (strcmp(pwd_envar, "PWD") == 0) { |
|---|
| 41 | res = GB_getcwd(); |
|---|
| 42 | } |
|---|
| 43 | else { |
|---|
| 44 | res = NULp; |
|---|
| 45 | } |
|---|
| 46 | |
|---|
| 47 | return res; |
|---|
| 48 | } |
|---|
| 49 | |
|---|
| 50 | |
|---|
| 51 | char *AW_unfold_path(const char *pwd_envar, const char *path) { |
|---|
| 52 | //! create a full path |
|---|
| 53 | gb_getenv_hook oldHook = GB_install_getenv_hook(expand_symbolic_directories); |
|---|
| 54 | char *result = nulldup(GB_unfold_path(pwd_envar, path)); |
|---|
| 55 | GB_install_getenv_hook(oldHook); |
|---|
| 56 | return result; |
|---|
| 57 | } |
|---|
| 58 | |
|---|
| 59 | char *AW_extract_directory(const char *path) { |
|---|
| 60 | const char *lslash = strrchr(path, '/'); |
|---|
| 61 | if (!lslash) return NULp; |
|---|
| 62 | |
|---|
| 63 | char *result = strdup(path); |
|---|
| 64 | result[lslash-path] = 0; |
|---|
| 65 | |
|---|
| 66 | return result; |
|---|
| 67 | } |
|---|
| 68 | |
|---|
| 69 | // ----------------------------- |
|---|
| 70 | // file selection boxes |
|---|
| 71 | |
|---|
| 72 | void AW_create_fileselection_awars(AW_root *awr, const char *awar_base, const char *directories, const char *filter, const char *file_name) { |
|---|
| 73 | int base_len = strlen(awar_base); |
|---|
| 74 | bool has_slash = awar_base[base_len-1] == '/'; |
|---|
| 75 | char *awar_name = new char[base_len+30]; // use private buffer, because caller will most likely use GBS_global_string for arguments |
|---|
| 76 | AW_default default_file = AW_ROOT_DEFAULT; |
|---|
| 77 | |
|---|
| 78 | sprintf(awar_name, "%s%s", awar_base, "/directory"+int(has_slash)); |
|---|
| 79 | AW_awar *awar_dir = awr->awar_string(awar_name, directories, default_file); |
|---|
| 80 | |
|---|
| 81 | sprintf(awar_name, "%s%s", awar_base, "/filter" + int(has_slash)); |
|---|
| 82 | AW_awar *awar_filter = awr->awar_string(awar_name, filter, default_file); |
|---|
| 83 | |
|---|
| 84 | sprintf(awar_name, "%s%s", awar_base, "/file_name"+int(has_slash)); |
|---|
| 85 | AW_awar *awar_filename = awr->awar_string(awar_name, file_name, default_file); |
|---|
| 86 | |
|---|
| 87 | #if defined(ASSERTION_USED) |
|---|
| 88 | bool is_tmp_awar = strncmp(awar_base, "tmp/", 4) == 0 || strncmp(awar_base, "/tmp/", 5) == 0; |
|---|
| 89 | aw_assert(is_tmp_awar); // you need to use a temp awar for file selections |
|---|
| 90 | #endif |
|---|
| 91 | |
|---|
| 92 | awar_dir->write_string(directories); |
|---|
| 93 | awar_filter->write_string(filter); |
|---|
| 94 | awar_filename->write_string(file_name); |
|---|
| 95 | |
|---|
| 96 | // create all (default) directories |
|---|
| 97 | { |
|---|
| 98 | ConstStrArray dirs; |
|---|
| 99 | GBT_split_string(dirs, directories, ":", SPLIT_DROPEMPTY); |
|---|
| 100 | for (unsigned i = 0; i<dirs.size(); ++i) { |
|---|
| 101 | if (!GB_is_directory(dirs[i])) { |
|---|
| 102 | fprintf(stderr, "Creating directory '%s'\n", dirs[i]); |
|---|
| 103 | GB_ERROR error = GB_create_directory(dirs[i]); |
|---|
| 104 | if (error) aw_message(GBS_global_string("Failed to create directory '%s' (Reason: %s)", dirs[i], error)); |
|---|
| 105 | } |
|---|
| 106 | } |
|---|
| 107 | } |
|---|
| 108 | |
|---|
| 109 | delete [] awar_name; |
|---|
| 110 | } |
|---|
| 111 | |
|---|
| 112 | enum DirSortOrder { |
|---|
| 113 | SORT_ALPHA, |
|---|
| 114 | SORT_DATE, |
|---|
| 115 | SORT_SIZE, |
|---|
| 116 | |
|---|
| 117 | DIR_SORT_ORDERS // order count |
|---|
| 118 | }; |
|---|
| 119 | |
|---|
| 120 | class LimitedTime { |
|---|
| 121 | double max_duration; |
|---|
| 122 | time_t start; |
|---|
| 123 | mutable bool aborted; |
|---|
| 124 | |
|---|
| 125 | public: |
|---|
| 126 | LimitedTime(double max_duration_seconds) : max_duration(max_duration_seconds) { reset(); } |
|---|
| 127 | void reset() { |
|---|
| 128 | time(&start); |
|---|
| 129 | aborted = false; |
|---|
| 130 | } |
|---|
| 131 | double allowed_duration() const { return max_duration; } |
|---|
| 132 | bool finished_in_time() const { return !aborted; } |
|---|
| 133 | bool available() const { |
|---|
| 134 | if (!aborted) { |
|---|
| 135 | time_t now; |
|---|
| 136 | time(&now); |
|---|
| 137 | aborted = difftime(now, start) > max_duration; |
|---|
| 138 | } |
|---|
| 139 | return !aborted; |
|---|
| 140 | } |
|---|
| 141 | void increase() { max_duration *= 2.5; } |
|---|
| 142 | }; |
|---|
| 143 | |
|---|
| 144 | class File_selection { // @@@ derive from AW_selection? |
|---|
| 145 | AW_root *awr; |
|---|
| 146 | |
|---|
| 147 | AW_selection_list *filelist; |
|---|
| 148 | |
|---|
| 149 | char *def_name; |
|---|
| 150 | char *def_dir; |
|---|
| 151 | char *def_filter; |
|---|
| 152 | |
|---|
| 153 | char *pwd; |
|---|
| 154 | char *pwdx; // additional directories |
|---|
| 155 | |
|---|
| 156 | DirDisplay dirdisp; |
|---|
| 157 | |
|---|
| 158 | bool leave_wildcards; |
|---|
| 159 | bool filled_by_wildcard; // last fill done with wildcard? |
|---|
| 160 | |
|---|
| 161 | bool show_subdirs; // show or hide subdirs |
|---|
| 162 | bool show_hidden; // show or hide files/directories starting with '.' |
|---|
| 163 | |
|---|
| 164 | DirSortOrder sort_order; |
|---|
| 165 | |
|---|
| 166 | LimitedTime searchTime; |
|---|
| 167 | |
|---|
| 168 | int shown_name_len; |
|---|
| 169 | |
|---|
| 170 | void bind_callbacks(); |
|---|
| 171 | void execute_browser_command(const char *browser_command); |
|---|
| 172 | void fill_recursive(const char *fulldir, int skipleft, const char *mask, bool recurse, bool showdir); |
|---|
| 173 | |
|---|
| 174 | void format_columns(); |
|---|
| 175 | |
|---|
| 176 | public: |
|---|
| 177 | |
|---|
| 178 | File_selection(AW_root *aw_root, const char *awar_prefix, const char *pwd_, DirDisplay disp_dirs, bool allow_wildcards) |
|---|
| 179 | : awr(aw_root), |
|---|
| 180 | filelist(NULp), |
|---|
| 181 | pwd(strdup(pwd_)), |
|---|
| 182 | pwdx(NULp), |
|---|
| 183 | dirdisp(disp_dirs), |
|---|
| 184 | leave_wildcards(allow_wildcards), |
|---|
| 185 | filled_by_wildcard(false), |
|---|
| 186 | show_subdirs(true), |
|---|
| 187 | show_hidden(false), |
|---|
| 188 | sort_order(SORT_ALPHA), |
|---|
| 189 | searchTime(1.3) |
|---|
| 190 | { |
|---|
| 191 | { |
|---|
| 192 | char *multiple_dirs_in_pwd = strchr(pwd, '^'); |
|---|
| 193 | if (multiple_dirs_in_pwd) { |
|---|
| 194 | multiple_dirs_in_pwd[0] = 0; |
|---|
| 195 | pwdx = multiple_dirs_in_pwd+1; |
|---|
| 196 | } |
|---|
| 197 | } |
|---|
| 198 | |
|---|
| 199 | def_name = GBS_string_eval(awar_prefix, "*=*/file_name"); |
|---|
| 200 | def_dir = GBS_string_eval(awar_prefix, "*=*/directory"); |
|---|
| 201 | def_filter = GBS_string_eval(awar_prefix, "*=*/filter"); |
|---|
| 202 | |
|---|
| 203 | aw_assert(!GB_have_error()); |
|---|
| 204 | |
|---|
| 205 | bind_callbacks(); |
|---|
| 206 | } |
|---|
| 207 | |
|---|
| 208 | void create_gui_elements(AW_window *aws, const char *at_prefix) { |
|---|
| 209 | aw_assert(!filelist); |
|---|
| 210 | |
|---|
| 211 | char buffer[1024]; |
|---|
| 212 | sprintf(buffer, "%sfilter", at_prefix); |
|---|
| 213 | if (aws->at_ifdef(buffer)) { |
|---|
| 214 | aws->at(buffer); |
|---|
| 215 | aws->create_input_field(def_filter, 5); |
|---|
| 216 | } |
|---|
| 217 | |
|---|
| 218 | sprintf(buffer, "%sfile_name", at_prefix); |
|---|
| 219 | if (aws->at_ifdef(buffer)) { |
|---|
| 220 | aws->at(buffer); |
|---|
| 221 | aws->create_input_field(def_name, 20); |
|---|
| 222 | } |
|---|
| 223 | |
|---|
| 224 | sprintf(buffer, "%sbox", at_prefix); |
|---|
| 225 | aws->at(buffer); |
|---|
| 226 | filelist = aws->create_selection_list(def_name); |
|---|
| 227 | } |
|---|
| 228 | |
|---|
| 229 | void fill(); |
|---|
| 230 | |
|---|
| 231 | void filename_changed(bool post_filter_change_HACK); |
|---|
| 232 | |
|---|
| 233 | GB_ULONG get_newest_dir_modtime() const { |
|---|
| 234 | ConstStrArray dirs; |
|---|
| 235 | GBT_split_string(dirs, awr->awar(def_dir)->read_char_pntr(), ":", SPLIT_DROPEMPTY); |
|---|
| 236 | unsigned long maxtof = 0; |
|---|
| 237 | for (unsigned i = 0; i<dirs.size(); ++i) { |
|---|
| 238 | unsigned long tof = GB_time_of_file(dirs[i]); |
|---|
| 239 | if (tof>maxtof) maxtof = tof; |
|---|
| 240 | } |
|---|
| 241 | return maxtof; |
|---|
| 242 | } |
|---|
| 243 | |
|---|
| 244 | void trigger_refresh() { awr->awar(def_dir)->touch(); } |
|---|
| 245 | }; |
|---|
| 246 | |
|---|
| 247 | static GB_CSTR get_suffix(GB_CSTR fullpath) { // returns pointer behind '.' of suffix (or NULp if no suffix found) |
|---|
| 248 | GB_CSTR dot = strrchr(fullpath, '.'); |
|---|
| 249 | if (!dot) return NULp; |
|---|
| 250 | |
|---|
| 251 | GB_CSTR lslash = strrchr(fullpath, '/'); |
|---|
| 252 | if (lslash && lslash>dot) return NULp; // no . behind last / |
|---|
| 253 | return dot+1; |
|---|
| 254 | } |
|---|
| 255 | |
|---|
| 256 | |
|---|
| 257 | static char *set_suffix(const char *name, const char *suffix) { |
|---|
| 258 | // returns "name.suffix" (name may contain path information) |
|---|
| 259 | // - eliminates multiple dots |
|---|
| 260 | // - sets name to 'noname' if no name part is given |
|---|
| 261 | |
|---|
| 262 | char *path, *fullname; |
|---|
| 263 | GB_split_full_path(name, &path, &fullname, NULp, NULp); |
|---|
| 264 | |
|---|
| 265 | // remove dots and spaces from suffix: |
|---|
| 266 | while (suffix[0] == '.' || suffix[0] == ' ') ++suffix; |
|---|
| 267 | if (!suffix[0]) suffix = NULp; |
|---|
| 268 | |
|---|
| 269 | GBS_strstruct out(FILENAME_MAX+1); |
|---|
| 270 | if (path) { |
|---|
| 271 | out.cat(path); |
|---|
| 272 | out.put('/'); |
|---|
| 273 | } |
|---|
| 274 | |
|---|
| 275 | if (fullname) out.cat(fullname); |
|---|
| 276 | |
|---|
| 277 | if (GB_is_directory(out.get_data())) { |
|---|
| 278 | // if 'out' contains a directory now, 'name' was lacking a filename |
|---|
| 279 | // (in this case it was only a directory) |
|---|
| 280 | out.cat("/noname"); // invent a name |
|---|
| 281 | } |
|---|
| 282 | |
|---|
| 283 | if (suffix) { |
|---|
| 284 | out.put('.'); |
|---|
| 285 | out.cat(suffix); |
|---|
| 286 | } |
|---|
| 287 | |
|---|
| 288 | free(path); |
|---|
| 289 | free(fullname); |
|---|
| 290 | |
|---|
| 291 | return out.release(); |
|---|
| 292 | } |
|---|
| 293 | |
|---|
| 294 | |
|---|
| 295 | inline const char *valid_path(const char *path) { return path[0] ? path : "."; } |
|---|
| 296 | |
|---|
| 297 | inline bool AW_is_dir(const char *path) { return GB_is_directory(valid_path(path)); } |
|---|
| 298 | inline bool AW_is_file(const char *path) { return GB_is_regularfile(valid_path(path)); } |
|---|
| 299 | inline bool AW_is_link(const char *path) { return GB_is_link(valid_path(path)); } |
|---|
| 300 | |
|---|
| 301 | void File_selection::execute_browser_command(const char *browser_command) { |
|---|
| 302 | if (strcmp(browser_command, "sort") == 0) { |
|---|
| 303 | sort_order = DirSortOrder((sort_order+1)%DIR_SORT_ORDERS); |
|---|
| 304 | } |
|---|
| 305 | else if (strcmp(browser_command, "hide") == 0) { |
|---|
| 306 | show_subdirs = false; |
|---|
| 307 | } |
|---|
| 308 | else if (strcmp(browser_command, "show") == 0) { |
|---|
| 309 | show_subdirs = true; |
|---|
| 310 | } |
|---|
| 311 | else if (strcmp(browser_command, "dot") == 0) { |
|---|
| 312 | show_hidden = !show_hidden; |
|---|
| 313 | } |
|---|
| 314 | else if (strcmp(browser_command, "inctime") == 0) { |
|---|
| 315 | searchTime.increase(); |
|---|
| 316 | } |
|---|
| 317 | else { |
|---|
| 318 | aw_message(GBS_global_string("Unknown browser command '%s'", browser_command)); |
|---|
| 319 | } |
|---|
| 320 | } |
|---|
| 321 | |
|---|
| 322 | inline int entryType(const char *entry) { |
|---|
| 323 | const char *typechar = "DFL"; |
|---|
| 324 | for (int i = 0; typechar[i]; ++i) { |
|---|
| 325 | if (entry[0] == typechar[i]) return i; |
|---|
| 326 | } |
|---|
| 327 | return -1; |
|---|
| 328 | } |
|---|
| 329 | |
|---|
| 330 | void File_selection::format_columns() { |
|---|
| 331 | const int FORMATTED_TYPES = 3; |
|---|
| 332 | |
|---|
| 333 | int maxlen[FORMATTED_TYPES] = { 17, 17, 17 }; |
|---|
| 334 | |
|---|
| 335 | for (int pass = 1; pass<=2; ++pass) { |
|---|
| 336 | AW_selection_list_iterator entry(filelist); |
|---|
| 337 | while (entry) { |
|---|
| 338 | const char *disp = entry.get_displayed(); |
|---|
| 339 | int type = entryType(disp); |
|---|
| 340 | |
|---|
| 341 | if (type>=0) { |
|---|
| 342 | const char *q1 = strchr(disp, '?'); |
|---|
| 343 | if (q1) { |
|---|
| 344 | const char *q2 = strchr(q1+1, '?'); |
|---|
| 345 | if (q2) { |
|---|
| 346 | int len = q2-q1-1; |
|---|
| 347 | if (pass == 1) { |
|---|
| 348 | if (maxlen[type]<len) maxlen[type] = len; |
|---|
| 349 | } |
|---|
| 350 | else { |
|---|
| 351 | GBS_strstruct buf(200); |
|---|
| 352 | buf.ncat(disp, q1-disp); |
|---|
| 353 | buf.ncat(q1+1, len); |
|---|
| 354 | buf.nput(' ', maxlen[type]-len); |
|---|
| 355 | buf.cat(q2+1); |
|---|
| 356 | entry.set_displayed(buf.get_data()); |
|---|
| 357 | } |
|---|
| 358 | } |
|---|
| 359 | } |
|---|
| 360 | } |
|---|
| 361 | ++entry; |
|---|
| 362 | } |
|---|
| 363 | } |
|---|
| 364 | } |
|---|
| 365 | |
|---|
| 366 | void File_selection::fill_recursive(const char *fulldir, int skipleft, const char *mask, bool recurse, bool showdir) { |
|---|
| 367 | DIR *dirp = opendir(fulldir); |
|---|
| 368 | |
|---|
| 369 | #if defined(TRACE_FILEBOX) |
|---|
| 370 | printf("fill_fileselection_recursive for directory '%s'\n", fulldir); |
|---|
| 371 | #endif // TRACE_FILEBOX |
|---|
| 372 | |
|---|
| 373 | if (!dirp) { |
|---|
| 374 | filelist->insert(GBS_global_string("x Your directory path is invalid (%s)", fulldir), "?"); |
|---|
| 375 | return; |
|---|
| 376 | } |
|---|
| 377 | |
|---|
| 378 | struct dirent *dp; |
|---|
| 379 | for (dp = readdir(dirp); dp; dp = readdir(dirp)) { |
|---|
| 380 | const char *entry = dp->d_name; |
|---|
| 381 | char *nontruepath = GBS_global_string_copy("%s/%s", fulldir, entry); |
|---|
| 382 | char *fullname; |
|---|
| 383 | |
|---|
| 384 | if (strlen(fulldir)) fullname = strdup(GB_concat_full_path(fulldir, entry)); |
|---|
| 385 | else fullname = strdup(GB_canonical_path(entry)); |
|---|
| 386 | |
|---|
| 387 | if (AW_is_dir(fullname)) { |
|---|
| 388 | if (!(entry[0] == '.' && (!show_hidden || entry[1] == 0 || (entry[1] == '.' && entry[2] == 0)))) { // skip "." and ".." and dotdirs if requested |
|---|
| 389 | if (showdir) { |
|---|
| 390 | filelist->insert(GBS_global_string("D ?%s? (%s)", entry, fullname), fullname); // '?' used in format_columns() |
|---|
| 391 | } |
|---|
| 392 | if (recurse && !AW_is_link(nontruepath)) { // don't follow links |
|---|
| 393 | if (searchTime.available()) { |
|---|
| 394 | fill_recursive(nontruepath, skipleft, mask, recurse, showdir); |
|---|
| 395 | } |
|---|
| 396 | } |
|---|
| 397 | } |
|---|
| 398 | } |
|---|
| 399 | else { |
|---|
| 400 | if (GBS_string_matches(entry, mask, GB_IGNORE_CASE)) { // entry matches mask |
|---|
| 401 | if ((entry[0] != '.' || show_hidden) && AW_is_file(fullname)) { // regular existing file |
|---|
| 402 | struct stat stt; |
|---|
| 403 | |
|---|
| 404 | stat(fullname, &stt); |
|---|
| 405 | |
|---|
| 406 | char atime[256]; |
|---|
| 407 | struct tm *tms = localtime(&stt.st_mtime); |
|---|
| 408 | strftime(atime, 255, "%Y/%m/%d %k:%M", tms); |
|---|
| 409 | |
|---|
| 410 | char *size = strdup(GBS_readable_size(stt.st_size, "b")); |
|---|
| 411 | char typechar = AW_is_link(nontruepath) ? 'L' : 'F'; |
|---|
| 412 | |
|---|
| 413 | const char *sel_entry = NULp; |
|---|
| 414 | switch (sort_order) { |
|---|
| 415 | case SORT_ALPHA: |
|---|
| 416 | sel_entry = GBS_global_string("%c ?%s? %7s %s", typechar, nontruepath+skipleft, size, atime); // '?' used in format_columns() |
|---|
| 417 | break; |
|---|
| 418 | case SORT_DATE: |
|---|
| 419 | sel_entry = GBS_global_string("%c %s %7s %s", typechar, atime, size, nontruepath+skipleft); |
|---|
| 420 | break; |
|---|
| 421 | case SORT_SIZE: |
|---|
| 422 | sel_entry = GBS_global_string("%c %7s %s %s", typechar, size, atime, nontruepath+skipleft); |
|---|
| 423 | break; |
|---|
| 424 | case DIR_SORT_ORDERS: break; |
|---|
| 425 | } |
|---|
| 426 | |
|---|
| 427 | filelist->insert(sel_entry, nontruepath); |
|---|
| 428 | free(size); |
|---|
| 429 | } |
|---|
| 430 | } |
|---|
| 431 | } |
|---|
| 432 | free(fullname); |
|---|
| 433 | free(nontruepath); |
|---|
| 434 | } |
|---|
| 435 | |
|---|
| 436 | closedir(dirp); |
|---|
| 437 | } |
|---|
| 438 | |
|---|
| 439 | class DuplicateLinkFilter { |
|---|
| 440 | set<string> insertedDirectories; |
|---|
| 441 | |
|---|
| 442 | public: |
|---|
| 443 | DuplicateLinkFilter() {} |
|---|
| 444 | |
|---|
| 445 | bool not_seen_yet(const string& dir) const { return insertedDirectories.find(dir) == insertedDirectories.end(); } |
|---|
| 446 | void register_directory(const string& dir) { |
|---|
| 447 | insertedDirectories.insert(dir); |
|---|
| 448 | } |
|---|
| 449 | }; |
|---|
| 450 | |
|---|
| 451 | |
|---|
| 452 | static void show_soft_link(AW_selection_list *filelist, const char *envar, DuplicateLinkFilter& unDup) { |
|---|
| 453 | // adds a soft link (e.g. ARBMACROHOME or ARB_WORKDIR) into file selection box |
|---|
| 454 | // if content of 'envar' matches 'cwd' nothing is inserted |
|---|
| 455 | |
|---|
| 456 | const char *expanded_dir = expand_symbolic_directories(envar); |
|---|
| 457 | if (!expanded_dir) expanded_dir = GB_getenv(envar); |
|---|
| 458 | |
|---|
| 459 | if (expanded_dir) { |
|---|
| 460 | string edir(expanded_dir); |
|---|
| 461 | |
|---|
| 462 | if (unDup.not_seen_yet(edir)) { |
|---|
| 463 | unDup.register_directory(edir); |
|---|
| 464 | const char *entry = GBS_global_string("$ %-18s(%s)", GBS_global_string("'%s'", envar), expanded_dir); |
|---|
| 465 | filelist->insert(entry, expanded_dir); |
|---|
| 466 | } |
|---|
| 467 | } |
|---|
| 468 | } |
|---|
| 469 | |
|---|
| 470 | inline bool fileOrLink(const char *d) { return d[0] == 'F' || d[0] == 'L'; } |
|---|
| 471 | inline const char *gotounit(const char *d) { |
|---|
| 472 | ++d; |
|---|
| 473 | while (d[0] == ' ') ++d; |
|---|
| 474 | while (d[0] != ' ') ++d; |
|---|
| 475 | while (d[0] == ' ') ++d; |
|---|
| 476 | return d; |
|---|
| 477 | } |
|---|
| 478 | static int cmpBySize(const char *disp1, const char *disp2) { |
|---|
| 479 | if (fileOrLink(disp1) && fileOrLink(disp2)) { |
|---|
| 480 | const char *u1 = gotounit(disp1); |
|---|
| 481 | const char *u2 = gotounit(disp2); |
|---|
| 482 | |
|---|
| 483 | if (u1[0] != u2[0]) { // diff units |
|---|
| 484 | static const char *units = "bkMGTPEZY"; // see also ../CORE/arb_misc.cxx@Tera |
|---|
| 485 | |
|---|
| 486 | const char *p1 = strchr(units, u1[0]); |
|---|
| 487 | const char *p2 = strchr(units, u2[0]); |
|---|
| 488 | if (p1 != p2) { |
|---|
| 489 | return p1-p2; |
|---|
| 490 | } |
|---|
| 491 | } |
|---|
| 492 | } |
|---|
| 493 | return ARB_stricmp(disp1, disp2); |
|---|
| 494 | } |
|---|
| 495 | |
|---|
| 496 | inline bool contains_wildcards(const char *name) { return strpbrk(name, "?*") != NULp; } |
|---|
| 497 | |
|---|
| 498 | void File_selection::fill() { |
|---|
| 499 | AW_root *aw_root = awr; |
|---|
| 500 | filelist->clear(); |
|---|
| 501 | |
|---|
| 502 | char *filter = aw_root->awar(def_filter)->read_string(); |
|---|
| 503 | char *name = aw_root->awar(def_name)->read_string(); |
|---|
| 504 | |
|---|
| 505 | const char *name_only = NULp; |
|---|
| 506 | { |
|---|
| 507 | char *slash = strrchr(name, '/'); |
|---|
| 508 | name_only = slash ? slash+1 : name; |
|---|
| 509 | } |
|---|
| 510 | |
|---|
| 511 | StrArray dirs; |
|---|
| 512 | { |
|---|
| 513 | char *diru = aw_root->awar(def_dir)->read_string(); |
|---|
| 514 | if (dirdisp == MULTI_DIRS) { |
|---|
| 515 | ConstStrArray cdirs; |
|---|
| 516 | GBT_split_string(cdirs, diru, ":", SPLIT_DROPEMPTY); |
|---|
| 517 | for (unsigned i = 0; i<cdirs.size(); ++i) dirs.put(strdup(cdirs[i])); |
|---|
| 518 | } |
|---|
| 519 | else { |
|---|
| 520 | if (name[0] == '/' && AW_is_dir(name)) { |
|---|
| 521 | dirs.put(strdup(name)); |
|---|
| 522 | name_only = ""; |
|---|
| 523 | } |
|---|
| 524 | else { |
|---|
| 525 | char *fulldir = AW_unfold_path(pwd, diru); |
|---|
| 526 | dirs.put(fulldir); |
|---|
| 527 | } |
|---|
| 528 | } |
|---|
| 529 | free(diru); |
|---|
| 530 | } |
|---|
| 531 | |
|---|
| 532 | filled_by_wildcard = contains_wildcards(name_only); |
|---|
| 533 | |
|---|
| 534 | if (dirdisp == ANY_DIR) { |
|---|
| 535 | aw_assert(dirs.size() == 1); |
|---|
| 536 | const char *fulldir = dirs[0]; |
|---|
| 537 | |
|---|
| 538 | DuplicateLinkFilter unDup; |
|---|
| 539 | unDup.register_directory(fulldir); |
|---|
| 540 | |
|---|
| 541 | if (filled_by_wildcard) { |
|---|
| 542 | if (leave_wildcards) { |
|---|
| 543 | filelist->insert(GBS_global_string(" ALL '%s' in '%s'", name_only, fulldir), name); |
|---|
| 544 | } |
|---|
| 545 | else { |
|---|
| 546 | filelist->insert(GBS_global_string(" ALL '%s' in+below '%s'", name_only, fulldir), name); |
|---|
| 547 | } |
|---|
| 548 | } |
|---|
| 549 | else { |
|---|
| 550 | filelist->insert(GBS_global_string(" CONTENTS OF '%s'", fulldir), fulldir); |
|---|
| 551 | if (filter[0]) { |
|---|
| 552 | filelist->insert(GBS_global_string("! Find all (*%s)", filter), "*"); |
|---|
| 553 | } |
|---|
| 554 | } |
|---|
| 555 | |
|---|
| 556 | if (strcmp("/", fulldir)) { |
|---|
| 557 | filelist->insert("! \'PARENT DIR\' (..)", ".."); |
|---|
| 558 | } |
|---|
| 559 | if (show_subdirs) { |
|---|
| 560 | show_soft_link(filelist, pwd, unDup); |
|---|
| 561 | |
|---|
| 562 | if (pwdx) { // additional directories |
|---|
| 563 | char *start = pwdx; |
|---|
| 564 | while (start) { |
|---|
| 565 | char *multiple = strchr(start, '^'); |
|---|
| 566 | if (multiple) { |
|---|
| 567 | multiple[0] = 0; |
|---|
| 568 | show_soft_link(filelist, start, unDup); |
|---|
| 569 | multiple[0] = '^'; |
|---|
| 570 | start = multiple+1; |
|---|
| 571 | } |
|---|
| 572 | else { |
|---|
| 573 | show_soft_link(filelist, start, unDup); |
|---|
| 574 | start = NULp; |
|---|
| 575 | } |
|---|
| 576 | } |
|---|
| 577 | } |
|---|
| 578 | |
|---|
| 579 | show_soft_link(filelist, "HOME", unDup); |
|---|
| 580 | show_soft_link(filelist, "PWD", unDup); |
|---|
| 581 | show_soft_link(filelist, "ARB_WORKDIR", unDup); |
|---|
| 582 | |
|---|
| 583 | filelist->insert("! Sub-directories (shown)", GBS_global_string("%s?hide?", name)); |
|---|
| 584 | } |
|---|
| 585 | else { |
|---|
| 586 | filelist->insert("! Sub-directories (hidden)", GBS_global_string("%s?show?", name)); |
|---|
| 587 | } |
|---|
| 588 | filelist->insert(GBS_global_string("! Hidden (%s)", show_hidden ? "shown" : "not shown"), GBS_global_string("%s?dot?", name)); |
|---|
| 589 | } |
|---|
| 590 | else { |
|---|
| 591 | aw_assert(dirdisp == MULTI_DIRS); |
|---|
| 592 | } |
|---|
| 593 | |
|---|
| 594 | static const char *order_name[DIR_SORT_ORDERS] = { "alpha", "date", "size" }; |
|---|
| 595 | |
|---|
| 596 | filelist->insert(GBS_global_string("! Sort order (%s)", order_name[sort_order]), GBS_global_string("%s?sort?", name)); |
|---|
| 597 | |
|---|
| 598 | bool insert_dirs = dirdisp == ANY_DIR && show_subdirs; |
|---|
| 599 | |
|---|
| 600 | searchTime.reset(); // limits time spent in fill_recursive |
|---|
| 601 | for (unsigned i = 0; i<dirs.size(); ++i) { |
|---|
| 602 | const char *fulldir = dirs[i]; |
|---|
| 603 | if (filled_by_wildcard) { |
|---|
| 604 | if (leave_wildcards) { |
|---|
| 605 | fill_recursive(fulldir, strlen(fulldir)+1, name_only, false, insert_dirs); |
|---|
| 606 | } |
|---|
| 607 | else { |
|---|
| 608 | if (dirdisp == ANY_DIR) { // recursive wildcarded search |
|---|
| 609 | fill_recursive(fulldir, strlen(fulldir)+1, name_only, true, false); |
|---|
| 610 | } |
|---|
| 611 | else { |
|---|
| 612 | char *mask = GBS_global_string_copy("%s*%s", name_only, filter); |
|---|
| 613 | fill_recursive(fulldir, strlen(fulldir)+1, mask, false, false); |
|---|
| 614 | free(mask); |
|---|
| 615 | } |
|---|
| 616 | } |
|---|
| 617 | } |
|---|
| 618 | else { |
|---|
| 619 | char *mask = GBS_global_string_copy("*%s", filter); |
|---|
| 620 | |
|---|
| 621 | fill_recursive(fulldir, strlen(fulldir)+1, mask, false, insert_dirs); |
|---|
| 622 | free(mask); |
|---|
| 623 | } |
|---|
| 624 | } |
|---|
| 625 | |
|---|
| 626 | if (!searchTime.finished_in_time()) { |
|---|
| 627 | filelist->insert(GBS_global_string("! Find aborted (after %.1fs; click to search longer)", searchTime.allowed_duration()), GBS_global_string("%s?inctime?", name)); |
|---|
| 628 | } |
|---|
| 629 | |
|---|
| 630 | if (sort_order == SORT_SIZE) { |
|---|
| 631 | filelist->sortCustom(cmpBySize); |
|---|
| 632 | } |
|---|
| 633 | else { |
|---|
| 634 | filelist->sort(false, false); |
|---|
| 635 | } |
|---|
| 636 | format_columns(); |
|---|
| 637 | filelist->insert_default("", ""); |
|---|
| 638 | filelist->update(); |
|---|
| 639 | |
|---|
| 640 | if (filled_by_wildcard && !leave_wildcards) { // avoid returning wildcarded filename (if !leave_wildcards) |
|---|
| 641 | aw_root->awar(def_name)->write_string(""); |
|---|
| 642 | } |
|---|
| 643 | |
|---|
| 644 | free(name); |
|---|
| 645 | free(filter); |
|---|
| 646 | } |
|---|
| 647 | |
|---|
| 648 | static const char *detectBrowserCommand(const char *fname) { |
|---|
| 649 | // if fname ends with '?word?' |
|---|
| 650 | // -> returns ptr to 'word' |
|---|
| 651 | // NULp otherwise |
|---|
| 652 | |
|---|
| 653 | const char *qm1 = NULp; |
|---|
| 654 | const char *qm2 = NULp; |
|---|
| 655 | const char *qm3 = ARB_strchrnul(fname, '?'); |
|---|
| 656 | |
|---|
| 657 | while (qm3[0]) { |
|---|
| 658 | qm1 = qm2; |
|---|
| 659 | qm2 = qm3; |
|---|
| 660 | qm3 = ARB_strchrnul(qm2+1, '?'); |
|---|
| 661 | } |
|---|
| 662 | |
|---|
| 663 | if (qm1 && qm1[0]) { |
|---|
| 664 | aw_assert(qm2 && qm2[0]); |
|---|
| 665 | if (!qm2[1]) { // 2nd ? at EOS |
|---|
| 666 | int cmdlen = qm2-qm1-1; |
|---|
| 667 | if (cmdlen>=1) return qm1+1; |
|---|
| 668 | } |
|---|
| 669 | } |
|---|
| 670 | return NULp; |
|---|
| 671 | } |
|---|
| 672 | |
|---|
| 673 | void File_selection::filename_changed(bool post_filter_change_HACK) { |
|---|
| 674 | AW_root *aw_root = awr; |
|---|
| 675 | const char *fname = aw_root->awar(def_name)->read_string(); |
|---|
| 676 | |
|---|
| 677 | #if defined(TRACE_FILEBOX) |
|---|
| 678 | printf("fileselection_filename_changed_cb:\n" |
|---|
| 679 | "- fname ='%s'\n", fname); |
|---|
| 680 | #endif // TRACE_FILEBOX |
|---|
| 681 | |
|---|
| 682 | if (fname[0]) { |
|---|
| 683 | const char *browser_command = detectBrowserCommand(fname); |
|---|
| 684 | if (browser_command) { |
|---|
| 685 | char *browser_command_copy = ARB_strndup(browser_command, strlen(browser_command)-1); |
|---|
| 686 | char *fname_no_cmd = ARB_strpartdup(fname, browser_command-2); |
|---|
| 687 | |
|---|
| 688 | aw_root->awar(def_name)->write_string(fname_no_cmd); // re-write w/o browser_command |
|---|
| 689 | execute_browser_command(browser_command_copy); |
|---|
| 690 | trigger_refresh(); |
|---|
| 691 | |
|---|
| 692 | free(fname_no_cmd); |
|---|
| 693 | free(browser_command_copy); |
|---|
| 694 | } |
|---|
| 695 | else if (dirdisp != MULTI_DIRS) { |
|---|
| 696 | char *newName = NULp; |
|---|
| 697 | char *dir = aw_root->awar(def_dir)->read_string(); |
|---|
| 698 | |
|---|
| 699 | #if defined(TRACE_FILEBOX) |
|---|
| 700 | printf("- dir ='%s'\n", dir); |
|---|
| 701 | #endif // TRACE_FILEBOX |
|---|
| 702 | |
|---|
| 703 | if (fname[0] == '/' || fname[0] == '~') { |
|---|
| 704 | newName = strdup(GB_canonical_path(fname)); |
|---|
| 705 | } |
|---|
| 706 | else { |
|---|
| 707 | if (dir[0]) { |
|---|
| 708 | if (dir[0] == '/') { |
|---|
| 709 | newName = strdup(GB_concat_full_path(dir, fname)); |
|---|
| 710 | } |
|---|
| 711 | else { |
|---|
| 712 | char *fulldir = NULp; |
|---|
| 713 | |
|---|
| 714 | if (dir[0] == '.') fulldir = AW_unfold_path(pwd, dir); |
|---|
| 715 | else fulldir = strdup(dir); |
|---|
| 716 | |
|---|
| 717 | newName = strdup(GB_concat_full_path(fulldir, fname)); |
|---|
| 718 | free(fulldir); |
|---|
| 719 | } |
|---|
| 720 | } |
|---|
| 721 | else { |
|---|
| 722 | newName = AW_unfold_path(pwd, fname); |
|---|
| 723 | } |
|---|
| 724 | } |
|---|
| 725 | |
|---|
| 726 | // Allow to select symbolic links for files (i.e. do not automatically switch to link-target; |
|---|
| 727 | // doing so made it impossible to load quicksaves done versus a master DB). |
|---|
| 728 | if (newName && strcmp(fname, newName) != 0) { |
|---|
| 729 | if (!GB_is_directory(fname) && !GB_is_directory(newName)) { |
|---|
| 730 | if (GB_is_link(fname) ) freenull(newName); // do not follow symlink! |
|---|
| 731 | } |
|---|
| 732 | } |
|---|
| 733 | |
|---|
| 734 | if (newName) { |
|---|
| 735 | #if defined(TRACE_FILEBOX) |
|---|
| 736 | printf("- newName ='%s'\n", newName); |
|---|
| 737 | #endif // TRACE_FILEBOX |
|---|
| 738 | |
|---|
| 739 | if (AW_is_dir(newName)) { |
|---|
| 740 | aw_root->awar(def_name)->write_string(""); |
|---|
| 741 | aw_assert(dirdisp != MULTI_DIRS); // overwriting content unwanted if displaying MULTI_DIRS |
|---|
| 742 | aw_root->awar(def_dir)->write_string(newName); |
|---|
| 743 | aw_root->awar(def_name)->write_string(""); |
|---|
| 744 | } |
|---|
| 745 | else { |
|---|
| 746 | char *lslash = strrchr(newName, '/'); |
|---|
| 747 | if (lslash) { |
|---|
| 748 | if (lslash == newName) { // root directory |
|---|
| 749 | aw_assert(dirdisp != MULTI_DIRS); // overwriting content unwanted if displaying MULTI_DIRS |
|---|
| 750 | aw_root->awar(def_dir)->write_string("/"); // write directory part |
|---|
| 751 | } |
|---|
| 752 | else { |
|---|
| 753 | lslash[0] = 0; |
|---|
| 754 | aw_assert(dirdisp != MULTI_DIRS); // overwriting content unwanted if displaying MULTI_DIRS |
|---|
| 755 | aw_root->awar(def_dir)->write_string(newName); // write directory part |
|---|
| 756 | lslash[0] = '/'; |
|---|
| 757 | } |
|---|
| 758 | } |
|---|
| 759 | |
|---|
| 760 | // now check the correct suffix : |
|---|
| 761 | { |
|---|
| 762 | char *filter = aw_root->awar(def_filter)->read_string(); |
|---|
| 763 | if (filter[0]) { |
|---|
| 764 | char *pfilter = strrchr(filter, '.'); |
|---|
| 765 | pfilter = pfilter ? pfilter+1 : filter; |
|---|
| 766 | |
|---|
| 767 | char *suffix = (char*)get_suffix(newName); // cast ok, since get_suffix points into newName |
|---|
| 768 | |
|---|
| 769 | if (!suffix || strcmp(suffix, pfilter) != 0) { |
|---|
| 770 | if (suffix && post_filter_change_HACK) { |
|---|
| 771 | if (suffix[-1] == '.') suffix[-1] = 0; |
|---|
| 772 | } |
|---|
| 773 | freeset(newName, set_suffix(newName, pfilter)); |
|---|
| 774 | } |
|---|
| 775 | } |
|---|
| 776 | free(filter); |
|---|
| 777 | } |
|---|
| 778 | |
|---|
| 779 | if (strcmp(newName, fname) != 0) { |
|---|
| 780 | aw_root->awar(def_name)->write_string(newName); // loops back if changed !!! |
|---|
| 781 | } |
|---|
| 782 | } |
|---|
| 783 | } |
|---|
| 784 | free(dir); |
|---|
| 785 | |
|---|
| 786 | if (strchr(fname, '*')) { // wildcard -> search for suffix |
|---|
| 787 | trigger_refresh(); |
|---|
| 788 | } |
|---|
| 789 | |
|---|
| 790 | free(newName); |
|---|
| 791 | } |
|---|
| 792 | } |
|---|
| 793 | } |
|---|
| 794 | |
|---|
| 795 | static bool avoid_multi_refresh = false; |
|---|
| 796 | |
|---|
| 797 | static void fill_fileselection_cb(AW_root*, File_selection *cbs) { |
|---|
| 798 | if (!avoid_multi_refresh) { |
|---|
| 799 | LocallyModify<bool> flag(avoid_multi_refresh, true); |
|---|
| 800 | cbs->fill(); |
|---|
| 801 | } |
|---|
| 802 | } |
|---|
| 803 | static void fileselection_filename_changed_cb(AW_root*, File_selection *cbs) { |
|---|
| 804 | if (!avoid_multi_refresh) { |
|---|
| 805 | LocallyModify<bool> flag(avoid_multi_refresh, true); |
|---|
| 806 | cbs->filename_changed(false); |
|---|
| 807 | cbs->fill(); |
|---|
| 808 | } |
|---|
| 809 | else { |
|---|
| 810 | cbs->filename_changed(false); |
|---|
| 811 | } |
|---|
| 812 | } |
|---|
| 813 | static void fileselection_filter_changed_cb(AW_root*, File_selection *cbs) { |
|---|
| 814 | if (!avoid_multi_refresh) { |
|---|
| 815 | LocallyModify<bool> flag(avoid_multi_refresh, true); |
|---|
| 816 | cbs->filename_changed(true); |
|---|
| 817 | cbs->fill(); |
|---|
| 818 | } |
|---|
| 819 | else { |
|---|
| 820 | cbs->filename_changed(true); |
|---|
| 821 | } |
|---|
| 822 | } |
|---|
| 823 | |
|---|
| 824 | void File_selection::bind_callbacks() { |
|---|
| 825 | awr->awar(def_name) ->add_callback(makeRootCallback(fileselection_filename_changed_cb, this)); |
|---|
| 826 | awr->awar(def_dir) ->add_callback(makeRootCallback(fill_fileselection_cb, this)); |
|---|
| 827 | awr->awar(def_filter)->add_callback(makeRootCallback(fileselection_filter_changed_cb, this)); |
|---|
| 828 | } |
|---|
| 829 | |
|---|
| 830 | #define SELBOX_AUTOREFRESH_FREQUENCY 3000 // refresh every XXX ms |
|---|
| 831 | |
|---|
| 832 | struct selbox_autorefresh_info { |
|---|
| 833 | unsigned long modtime; |
|---|
| 834 | File_selection *acbs; |
|---|
| 835 | selbox_autorefresh_info *next; |
|---|
| 836 | }; |
|---|
| 837 | static selbox_autorefresh_info *autorefresh_info = NULp; |
|---|
| 838 | |
|---|
| 839 | static unsigned autorefresh_selboxes(AW_root *) { |
|---|
| 840 | selbox_autorefresh_info *check = autorefresh_info; |
|---|
| 841 | |
|---|
| 842 | while (check) { |
|---|
| 843 | GB_ULONG mtime = check->acbs->get_newest_dir_modtime(); |
|---|
| 844 | if (mtime != check->modtime) { |
|---|
| 845 | check->modtime = mtime; |
|---|
| 846 | check->acbs->trigger_refresh(); |
|---|
| 847 | } |
|---|
| 848 | check = check->next; |
|---|
| 849 | } |
|---|
| 850 | |
|---|
| 851 | // refresh again and again and again.. |
|---|
| 852 | return SELBOX_AUTOREFRESH_FREQUENCY; |
|---|
| 853 | } |
|---|
| 854 | |
|---|
| 855 | static void selbox_install_autorefresh(AW_root *aw_root, File_selection *acbs) { |
|---|
| 856 | if (!autorefresh_info) { // when installing first selbox |
|---|
| 857 | aw_root->add_timed_callback(SELBOX_AUTOREFRESH_FREQUENCY, makeTimedCallback(autorefresh_selboxes)); |
|---|
| 858 | // @@@ replace timer callback with AW_add_inotification! |
|---|
| 859 | } |
|---|
| 860 | |
|---|
| 861 | selbox_autorefresh_info *install = new selbox_autorefresh_info; |
|---|
| 862 | |
|---|
| 863 | install->acbs = acbs; |
|---|
| 864 | install->modtime = acbs->get_newest_dir_modtime(); |
|---|
| 865 | |
|---|
| 866 | install->next = autorefresh_info; |
|---|
| 867 | autorefresh_info = install; |
|---|
| 868 | } |
|---|
| 869 | |
|---|
| 870 | void AW_create_fileselection(AW_window *aws, const char *awar_prefix, const char *at_prefix, const char *pwd, DirDisplay disp_dirs, bool allow_wildcards) { |
|---|
| 871 | /*! Create a file selection box, this box needs 3 AWARS: |
|---|
| 872 | * |
|---|
| 873 | * 1. "$awar_prefix/filter" |
|---|
| 874 | * 2. "$awar_prefix/directory" |
|---|
| 875 | * 3. "$awar_prefix/file_name" |
|---|
| 876 | * |
|---|
| 877 | * (Note: The function AW_create_fileselection_awars() can be used to create them) |
|---|
| 878 | * |
|---|
| 879 | * the "$awar_prefix/file_name" contains the full filename |
|---|
| 880 | * Use AW_get_selected_fullname() to read it. |
|---|
| 881 | * |
|---|
| 882 | * The items are placed at |
|---|
| 883 | * |
|---|
| 884 | * 1. "$at_prefix""filter" |
|---|
| 885 | * 2. "$at_prefix""box" |
|---|
| 886 | * 3. "$at_prefix""file_name" |
|---|
| 887 | * |
|---|
| 888 | * if disp_dirs == ANY_DIR, then show directories and files |
|---|
| 889 | * if disp_dirs == MULTI_DIRS, then only show files, but from multiple directories |
|---|
| 890 | * |
|---|
| 891 | * pwd is the name of a 'shell environment variable' which indicates the base directory |
|---|
| 892 | * (e.g. 'PWD' or 'ARBHOME') |
|---|
| 893 | */ |
|---|
| 894 | |
|---|
| 895 | AW_root *aw_root = aws->get_root(); |
|---|
| 896 | File_selection *acbs = new File_selection(aw_root, awar_prefix, pwd, disp_dirs, allow_wildcards); |
|---|
| 897 | |
|---|
| 898 | acbs->create_gui_elements(aws, at_prefix); |
|---|
| 899 | |
|---|
| 900 | fill_fileselection_cb(NULp, acbs); |
|---|
| 901 | fileselection_filename_changed_cb(NULp, acbs); // this fixes the path name |
|---|
| 902 | |
|---|
| 903 | selbox_install_autorefresh(aw_root, acbs); |
|---|
| 904 | } |
|---|
| 905 | |
|---|
| 906 | char *AW_get_selected_fullname(AW_root *awr, const char *awar_prefix) { // @@@ add flag to select whether wildcards are allowed |
|---|
| 907 | char *file = awr->awar(GBS_global_string("%s/file_name", awar_prefix))->read_string(); |
|---|
| 908 | if (file[0] != '/') { |
|---|
| 909 | // if name w/o directory was entered by hand (or by default) then append the directory : |
|---|
| 910 | |
|---|
| 911 | char *awar_dir_name = GBS_global_string_copy("%s/directory", awar_prefix); |
|---|
| 912 | AW_awar *awar_dir = awr->awar_no_error(awar_dir_name); |
|---|
| 913 | |
|---|
| 914 | if (!awar_dir) { |
|---|
| 915 | // file selection box was not active (happens e.g. for print tree) |
|---|
| 916 | awar_dir = awr->awar_string(awar_dir_name, GB_getcwd()); |
|---|
| 917 | } |
|---|
| 918 | |
|---|
| 919 | aw_assert(awar_dir); |
|---|
| 920 | |
|---|
| 921 | char *dir = awar_dir->read_string(); |
|---|
| 922 | if (!dir[0]) { // empty -> fillin current dir |
|---|
| 923 | awar_dir->write_string(GB_getcwd()); |
|---|
| 924 | freeset(dir, awar_dir->read_string()); |
|---|
| 925 | } |
|---|
| 926 | |
|---|
| 927 | char *full = strdup(GB_concat_full_path(dir, file)); |
|---|
| 928 | |
|---|
| 929 | free(dir); |
|---|
| 930 | free(file); |
|---|
| 931 | |
|---|
| 932 | file = full; |
|---|
| 933 | |
|---|
| 934 | free(awar_dir_name); |
|---|
| 935 | } |
|---|
| 936 | |
|---|
| 937 | return file; |
|---|
| 938 | } |
|---|
| 939 | |
|---|
| 940 | void AW_set_selected_fullname(AW_root *awr, const char *awar_prefix, const char *to_fullname) { |
|---|
| 941 | awr->awar(GBS_global_string("%s/file_name", awar_prefix))->write_string(to_fullname); |
|---|
| 942 | } |
|---|
| 943 | |
|---|
| 944 | void AW_refresh_fileselection(AW_root *awr, const char *awar_prefix) { |
|---|
| 945 | // call optionally to force instant refresh |
|---|
| 946 | // (automatic refresh is done every SELBOX_AUTOREFRESH_FREQUENCY) |
|---|
| 947 | |
|---|
| 948 | awr->awar(GBS_global_string("%s/directory", awar_prefix))->touch(); |
|---|
| 949 | } |
|---|
| 950 | |
|---|
| 951 | // -------------------------------------------------------------------------------- |
|---|
| 952 | |
|---|
| 953 | #ifdef UNIT_TESTS |
|---|
| 954 | #include <test_unit.h> |
|---|
| 955 | |
|---|
| 956 | #define TEST_EXPECT_EQUAL_DUPPED(cs1, cs2) \ |
|---|
| 957 | do { \ |
|---|
| 958 | char *s1, *s2; \ |
|---|
| 959 | TEST_EXPECT_EQUAL(s1 = (cs1), s2 = (cs2)); \ |
|---|
| 960 | free(s1); \ |
|---|
| 961 | free(s2); \ |
|---|
| 962 | } while(0) |
|---|
| 963 | |
|---|
| 964 | void TEST_detectBrowserCommand() { |
|---|
| 965 | TEST_EXPECT_EQUAL(detectBrowserCommand("hello"), NULp); |
|---|
| 966 | TEST_EXPECT_EQUAL(detectBrowserCommand("/p/2.f"), NULp); |
|---|
| 967 | TEST_EXPECT_EQUAL(detectBrowserCommand("/p/2.f?cmd?"), "cmd?"); |
|---|
| 968 | TEST_EXPECT_EQUAL(detectBrowserCommand("/p/2.f?cmd?x"), NULp); |
|---|
| 969 | TEST_EXPECT_EQUAL(detectBrowserCommand("/p/2.f?cmd?2nd?"), "2nd?"); |
|---|
| 970 | TEST_EXPECT_EQUAL(detectBrowserCommand("/p/2.f??"), NULp); |
|---|
| 971 | TEST_EXPECT_EQUAL(detectBrowserCommand("/p/2.f???"), NULp); |
|---|
| 972 | TEST_EXPECT_EQUAL(detectBrowserCommand("/p/2.f?cmd"), NULp); |
|---|
| 973 | } |
|---|
| 974 | |
|---|
| 975 | void TEST_path_unfolding() { |
|---|
| 976 | const char *currDir = GB_getcwd(); |
|---|
| 977 | { |
|---|
| 978 | gb_getenv_hook old = GB_install_getenv_hook(expand_symbolic_directories); |
|---|
| 979 | |
|---|
| 980 | TEST_EXPECT_EQUAL(GB_getenv("PWD"), currDir); |
|---|
| 981 | TEST_EXPECT_EQUAL(GB_getenv("ARBHOME"), GB_getenvARBHOME()); |
|---|
| 982 | TEST_EXPECT_NULL(GB_getenv("ARB_NONEXISTING_ENVAR")); |
|---|
| 983 | |
|---|
| 984 | GB_install_getenv_hook(old); |
|---|
| 985 | } |
|---|
| 986 | |
|---|
| 987 | TEST_EXPECT_EQUAL_DUPPED(AW_unfold_path("PWD", "/usr"), strdup("/usr")); |
|---|
| 988 | TEST_EXPECT_EQUAL_DUPPED(AW_unfold_path("PWD", "../tests"), strdup(GB_path_in_ARBHOME("UNIT_TESTER/tests"))); |
|---|
| 989 | TEST_EXPECT_EQUAL_DUPPED(AW_unfold_path("ARB_NONEXISTING_ENVAR", "."), strdup(currDir)); |
|---|
| 990 | } |
|---|
| 991 | TEST_PUBLISH(TEST_path_unfolding); |
|---|
| 992 | |
|---|
| 993 | #endif // UNIT_TESTS |
|---|
| 994 | |
|---|
| 995 | |
|---|