50c38a1c14c1c76dbab0a38c5edf09205c9c5fa3
[goodguy/history.git] / cinelerra-5.1 / guicast / filesystem.C
1 /*
2  * CINELERRA
3  * Copyright (C) 2010 Adam Williams <broadcast at earthling dot net>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  */
20
21 #include "bcsignals.h"
22 #include "cstrdup.h"
23 #include <dirent.h>
24 #include <errno.h>
25 #include <pwd.h>
26 #include <stddef.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <sys/stat.h>
31 #include <sys/types.h>
32 #include <time.h>
33 #include <unistd.h>
34 #include <utime.h>
35
36
37 #include "filesystem.h"
38
39 FileItem::FileItem()
40 {
41         path = 0;
42         name = 0;
43         reset();
44 }
45
46 FileItem::FileItem(char *path, char *name, int is_dir,
47         int64_t size, int month, int day, int year,
48         int64_t calendar_time, int item_no)
49 {
50         this->path = new char[strlen(path)];
51         this->name = new char[strlen(name)];
52         if(this->path) strcpy(this->path, path);
53         if(this->name) strcpy(this->name, name);
54         this->is_dir = is_dir;
55         this->size = size;
56         this->month = month;
57         this->day = day;
58         this->year = year;
59         this->calendar_time = calendar_time;
60         this->item_no = item_no;
61 }
62
63 FileItem::~FileItem()
64 {
65         reset();
66 }
67
68 int FileItem::reset()
69 {
70         if(this->path) delete [] this->path;
71         if(this->name) delete [] this->name;
72         path = 0;
73         name = 0;
74         is_dir = 0;
75         size = 0;
76         month = 0;
77         day = 0;
78         year = 0;
79         calendar_time = 0;
80         item_no = -1;
81         return 0;
82 }
83
84 int FileItem::set_path(char *path)
85 {
86         if(this->path) delete [] this->path;
87         this->path = new char[strlen(path) + 1];
88         strcpy(this->path, path);
89         return 0;
90 }
91
92 int FileItem::set_name(char *name)
93 {
94         if(this->name) delete [] this->name;
95         this->name = new char[strlen(name) + 1];
96         strcpy(this->name, name);
97         return 0;
98 }
99
100 const char* FileItem::get_path()
101 {
102         return path;
103 }
104
105 const char* FileItem::get_name()
106 {
107         return name;
108 }
109
110 int FileItem::get_is_dir()
111 {
112         return is_dir;
113 }
114
115
116
117 FileSystem::FileSystem()
118 {
119         reset_parameters();
120         (void)getcwd(current_dir, BCTEXTLEN);
121 }
122
123 FileSystem::~FileSystem()
124 {
125         delete_directory();
126 }
127
128 int FileSystem::reset_parameters()
129 {
130         show_all_files = 0;
131         want_directory = 0;
132         strcpy(filter, "");
133         strcpy(current_dir, "");
134         sort_order = SORT_ASCENDING;
135         sort_field = SORT_PATH;
136         return 0;
137 }
138
139 int FileSystem::delete_directory()
140 {
141         dir_list.remove_all_objects();
142         return 0;
143 }
144
145 int FileSystem::set_sort_order(int value)
146 {
147         this->sort_order = value;
148         return 0;
149 }
150
151 int FileSystem::set_sort_field(int field)
152 {
153         this->sort_field = field;
154         return 0;
155 }
156
157 // filename.with.dots.extension
158 //   becomes
159 // extension.dots.with.filename
160
161 int FileSystem::dot_reverse_filename(char *out, const char *in)
162 {
163         int i, i2, j=0, lastdot;
164         lastdot = strlen(in);
165         for ( i=strlen(in); --i >= 0; ) {
166                 if (in[i] == '.') {
167                         i2 = i+1;
168                                 while (i2 < lastdot)
169                                         out[j++] = in[i2++];
170                         out[j++] = in[i];
171                         lastdot = i;
172                 }
173         }
174         if (in[++i] != '.') {
175                 while (i < lastdot) out[j++] = in[i++];
176         }
177         out[j++] = '\0';
178         return 0;
179 }
180
181 int FileSystem::path_ascending(const void *ptr1, const void *ptr2)
182 {
183         FileItem *item1 = *(FileItem**)ptr1;
184         FileItem *item2 = *(FileItem**)ptr2;
185 //printf("path_ascending %p %p\n", ptr1, ptr2);
186         int ret = strcasecmp(item1->name, item2->name);
187         if( ret != 0 ) return ret;
188         return item1->item_no - item2->item_no;
189 }
190
191 int FileSystem::path_descending(const void *ptr1, const void *ptr2)
192 {
193         FileItem *item1 = *(FileItem**)ptr1;
194         FileItem *item2 = *(FileItem**)ptr2;
195         int ret = strcasecmp(item2->name, item1->name);
196         if( ret != 0 ) return ret;
197         return item2->item_no - item1->item_no;
198 }
199
200
201 int FileSystem::size_ascending(const void *ptr1, const void *ptr2)
202 {
203         FileItem *item1 = *(FileItem**)ptr1;
204         FileItem *item2 = *(FileItem**)ptr2;
205         return item1->size == item2->size ?
206                 item1->item_no - item2->item_no :
207                 item1->size > item2->size;
208 }
209
210 int FileSystem::size_descending(const void *ptr1, const void *ptr2)
211 {
212         FileItem *item1 = *(FileItem**)ptr1;
213         FileItem *item2 = *(FileItem**)ptr2;
214         return item2->size == item1->size ?
215                 item2->item_no - item1->item_no :
216                 item2->size > item1->size;
217 }
218
219
220 int FileSystem::date_ascending(const void *ptr1, const void *ptr2)
221 {
222         FileItem *item1 = *(FileItem**)ptr1;
223         FileItem *item2 = *(FileItem**)ptr2;
224         return item1->calendar_time == item2->calendar_time ?
225                 item1->item_no - item2->item_no :
226                 item1->calendar_time > item2->calendar_time;
227 }
228
229 int FileSystem::date_descending(const void *ptr1, const void *ptr2)
230 {
231         FileItem *item1 = *(FileItem**)ptr1;
232         FileItem *item2 = *(FileItem**)ptr2;
233         return item2->calendar_time == item1->calendar_time ?
234                 item2->item_no - item1->item_no :
235                 item2->calendar_time > item1->calendar_time;
236 }
237
238 int FileSystem::ext_ascending(const void *ptr1, const void *ptr2)
239 {
240         FileItem *item1 = *(FileItem**)ptr1;
241         FileItem *item2 = *(FileItem**)ptr2;
242         char *ext1 = strrchr(item1->name,'.');
243         if( !ext1 ) ext1 = item1->name;
244         char *ext2 = strrchr(item2->name,'.');
245         if( !ext2 ) ext2 = item2->name;
246         int ret = strcasecmp(ext1, ext2);
247         if( ret ) return ret;
248         if( item1->item_no >= 0 && item2->item_no >= 0 )
249                 return item1->item_no - item2->item_no;
250         char dotreversedname1[BCTEXTLEN], dotreversedname2[BCTEXTLEN];
251         dot_reverse_filename(dotreversedname1,item1->name);
252         dot_reverse_filename(dotreversedname2,item2->name);
253         return strcasecmp(dotreversedname1, dotreversedname2);
254 }
255
256 int FileSystem::ext_descending(const void *ptr1, const void *ptr2)
257 {
258         FileItem *item1 = *(FileItem**)ptr1;
259         FileItem *item2 = *(FileItem**)ptr2;
260         char *ext1 = strrchr(item1->name,'.');
261         if( !ext1 ) ext1 = item1->name;
262         char *ext2 = strrchr(item2->name,'.');
263         if( !ext2 ) ext2 = item2->name;
264         int ret = strcasecmp(ext2, ext1);
265         if( ret ) return ret;
266         if( item2->item_no >= 0 && item1->item_no >= 0 )
267                 return item2->item_no - item1->item_no;
268         char dotreversedname1[BCTEXTLEN], dotreversedname2[BCTEXTLEN];
269         dot_reverse_filename(dotreversedname1,item1->name);
270         dot_reverse_filename(dotreversedname2,item2->name);
271         return strcasecmp(dotreversedname2, dotreversedname1);
272 }
273
274 int FileSystem::sort_table(ArrayList<FileItem*> *dir_list)
275 {
276         if(!dir_list || !dir_list->size()) return 0;
277         static int (*cmpr[][2])(const void *ptr1, const void *ptr2) = {
278                 { &path_ascending, &path_descending },
279                 { &size_ascending, &size_descending },
280                 { &date_ascending, &date_descending },
281                 { &ext_ascending,  &ext_descending  },
282         };
283
284         qsort(dir_list->values,
285                 dir_list->size(), sizeof(FileItem*),
286                 cmpr[sort_field][sort_order]);
287
288         return 0;
289 }
290
291 int FileSystem::combine(ArrayList<FileItem*> *dir_list, ArrayList<FileItem*> *file_list)
292 {
293         int i;
294
295         sort_table(dir_list);
296         for(i = 0; i < dir_list->total; i++)
297         {
298                 this->dir_list.append(dir_list->values[i]);
299         }
300
301         sort_table(file_list);
302         for(i = 0; i < file_list->total; i++)
303         {
304                 this->dir_list.append(file_list->values[i]);
305         }
306         return 0;
307 }
308
309 void FileSystem::alphabetize()
310 {
311         sort_table(&dir_list);
312 }
313
314 int FileSystem::is_root_dir(char *path)
315 {
316         if(!strcmp(current_dir, "/")) return 1;
317         return 0;
318 }
319
320 int FileSystem::test_filter(FileItem *file)
321 {
322         char *filter1 = 0, *filter2 = filter, *subfilter1, *subfilter2;
323         int result = 0;
324         int done = 0, token_done;
325         int token_number = 0;
326
327 // Don't filter directories
328         if(file->is_dir) return 0;
329
330 // Empty filename string
331         if(!file->name) return 1;
332
333         do
334         {
335 // Get next token
336                 filter1 = strchr(filter2, '[');
337                 string[0] = 0;
338
339 // Get next filter
340                 if(filter1)
341                 {
342                         filter1++;
343                         filter2 = strchr(filter1, ']');
344
345                         if(filter2)
346                         {
347                                 int i;
348                                 for(i = 0; filter1 + i < filter2; i++)
349                                         string[i] = filter1[i];
350                                 string[i] = 0;
351                         }
352                         else
353                         {
354                                 strcpy(string, filter1);
355                                 done = 1;
356                         }
357                 }
358                 else
359                 {
360                         if(!token_number)
361                                 strcpy(string, filter);
362                         else
363                                 done = 1;
364                 }
365
366 // Process the token
367                 if(string[0] != 0)
368                 {
369                         char *path = file->name;
370                         subfilter1 = string;
371                         token_done = 0;
372                         result = 0;
373
374                         do
375                         {
376                                 string2[0] = 0;
377                                 subfilter2 = strchr(subfilter1, '*');
378
379                                 if(subfilter2)
380                                 {
381                                         int i;
382                                         for(i = 0; subfilter1 + i < subfilter2; i++)
383                                                 string2[i] = subfilter1[i];
384
385                                         string2[i] = 0;
386                                 }
387                                 else
388                                 {
389                                         strcpy(string2, subfilter1);
390                                         token_done = 1;
391                                 }
392
393                                 if(string2[0] != 0)
394                                 {
395 // Subfilter must exist at some later point in the string
396                                         if(subfilter1 > string)
397                                         {
398                                                 if(!strstr(path, string2))
399                                                 {
400                                                         result = 1;
401                                                         token_done = 1;
402                                                 }
403                                                 else
404                                                 path = strstr(path, string2) + strlen(string2);
405                                         }
406                                         else
407 // Subfilter must exist at this point in the string
408                                         {
409                                                 if(strncmp(path, string2, strlen(string2)))
410 //                                              if(strncasecmp(path, string2, strlen(string2)))
411                                                 {
412                                                         result = 1;
413                                                         token_done = 1;
414                                                 }
415                                                 else
416                                                 path += strlen(string2);
417                                         }
418
419 // String must terminate after subfilter
420                                         if(!subfilter2)
421                                         {
422                                                 if(*path != 0)
423                                                 {
424                                                         result = 1;
425                                                         token_done = 1;
426                                                 }
427                                         }
428                                 }
429                                 subfilter1 = subfilter2 + 1;
430 // Let pass if no subfilter
431                         }while(!token_done && !result);
432                 }
433                 token_number++;
434         }while(!done && result);
435
436         return result;
437 }
438
439
440 int FileSystem::scan_directory(const char *new_dir)
441 {
442         if( new_dir != 0 )
443                 strcpy(current_dir, new_dir);
444         DIR *dirstream = opendir(current_dir);
445         if( !dirstream ) return 1;          // failed to open directory
446
447         struct dirent64 *new_filename;
448         while( (new_filename = readdir64(dirstream)) != 0 ) {
449                 FileItem *new_file = 0;
450                 int include_this = 1;
451
452 // File is directory heirarchy
453                 if(!strcmp(new_filename->d_name, ".") ||
454                         !strcmp(new_filename->d_name, "..")) include_this = 0;
455
456 // File is hidden and we don't want all files
457                 if(include_this &&
458                         !show_all_files &&
459                         new_filename->d_name[0] == '.') include_this = 0;
460
461 // file not hidden
462                 if(include_this)
463                 {
464                         new_file = new FileItem;
465                         char full_path[BCTEXTLEN], name_only[BCTEXTLEN];
466                         sprintf(full_path, "%s", current_dir);
467                         if(!is_root_dir(current_dir)) strcat(full_path, "/");
468                         strcat(full_path, new_filename->d_name);
469                         strcpy(name_only, new_filename->d_name);
470                         new_file->set_path(full_path);
471                         new_file->set_name(name_only);
472
473 // Get information about the file.
474                         struct stat ostat;
475                         if(!stat(full_path, &ostat))
476                         {
477                                 new_file->size = ostat.st_size;
478                                 struct tm *mod_time = localtime(&(ostat.st_mtime));
479                                 new_file->month = mod_time->tm_mon + 1;
480                                 new_file->day = mod_time->tm_mday;
481                                 new_file->year = mod_time->tm_year + 1900;
482                                 new_file->calendar_time = ostat.st_mtime;
483
484                                 if(S_ISDIR(ostat.st_mode))
485                                 {
486                                         strcat(name_only, "/"); // is a directory
487                                         new_file->is_dir = 1;
488                                 }
489
490 // File is excluded from filter
491                                 if(include_this && test_filter(new_file)) include_this = 0;
492 //printf("FileSystem::update 3 %d %d\n", include_this, test_filter(new_file));
493
494 // File is not a directory and we just want directories
495                                 if(include_this && want_directory && !new_file->is_dir) include_this = 0;
496                         }
497                         else
498                         {
499                                 printf("FileSystem::update %s %s\n", full_path, strerror(errno));
500                                 include_this = 0;
501                         }
502
503 // add to list
504                         if(include_this)
505                                 dir_list.append(new_file);
506                         else
507                                 delete new_file;
508                 }
509         }
510         return 0;
511 }
512
513 int FileSystem::update(const char *new_dir)
514 {
515         delete_directory();
516         int result = scan_directory(new_dir);
517 // combine the directories and files in the master list
518         return !result ? update_sort() : result;
519 }
520
521 int FileSystem::update_sort()
522 {
523         ArrayList<FileItem*> directories, files;
524         for( int i=0; i< dir_list.size(); ++i ) {
525                 FileItem *item = dir_list[i];
526                 item->item_no = i;
527                 (item->is_dir ? &directories : &files)->append(item);
528         }
529         dir_list.remove_all();
530 // combine the directories and files in the master list
531         combine(&directories, &files);
532         return 0;
533 // success
534 }
535
536
537 int FileSystem::set_filter(const char *new_filter)
538 {
539         strcpy(filter, new_filter);
540         return 0;
541 }
542
543 int FileSystem::set_show_all()
544 {
545         show_all_files = 1;
546         return 0;
547 }
548
549 int FileSystem::set_want_directory()
550 {
551         want_directory = 1;
552         return 0;
553 }
554
555 int FileSystem::is_dir(const char *path)      // return 0 if the text is a directory
556 {
557         if(!strlen(path)) return 0;
558
559         char new_dir[BCTEXTLEN];
560         struct stat ostat;    // entire name is a directory
561
562         strcpy(new_dir, path);
563         complete_path(new_dir);
564         if(!stat(new_dir, &ostat) && S_ISDIR(ostat.st_mode))
565                 return 1;
566         else
567                 return 0;
568 }
569
570 int FileSystem::create_dir(const char *new_dir_)
571 {
572         char new_dir[BCTEXTLEN];
573         strcpy(new_dir, new_dir_);
574         complete_path(new_dir);
575
576         mkdir(new_dir, S_IREAD | S_IWRITE | S_IEXEC);
577         return 0;
578 }
579
580 int FileSystem::parse_tildas(char *new_dir)
581 {
582         if(new_dir[0] == 0) return 1;
583
584 // Our home directory
585         if(new_dir[0] == '~')
586         {
587
588                 if(new_dir[1] == '/' || new_dir[1] == 0)
589                 {
590 // user's home directory
591                         char *home;
592                         char string[BCTEXTLEN];
593                         home = getenv("HOME");
594
595 // print starting after tilda
596                         if(home) sprintf(string, "%s%s", home, &new_dir[1]);
597                         strcpy(new_dir, string);
598                         return 0;
599                 }
600                 else
601 // Another user's home directory
602                 {
603                         char string[BCTEXTLEN], new_user[BCTEXTLEN];
604                         struct passwd *pw;
605                         int i, j;
606
607                         for(i = 1, j = 0; new_dir[i] != 0 && new_dir[i] != '/'; i++, j++)
608                         {                // copy user name
609                                 new_user[j] = new_dir[i];
610                         }
611                         new_user[j] = 0;
612
613                         setpwent();
614                         while( (pw = getpwent()) != 0 )
615                         {
616 // get info for user
617                                 if(!strcmp(pw->pw_name, new_user))
618                                 {
619 // print starting after tilda
620                                 sprintf(string, "%s%s", pw->pw_dir, &new_dir[i]);
621                                 strcpy(new_dir, string);
622                                 break;
623                         }
624                         }
625                         endpwent();
626                         return 0;
627                 }
628         }
629         return 0;
630 }
631
632 int FileSystem::parse_directories(char *new_dir)
633 {
634 //printf("FileSystem::parse_directories 1 %s\n", new_dir);
635         if(new_dir[0] != '/')
636         {
637 // extend path completely
638                 char string[BCTEXTLEN];
639 //printf("FileSystem::parse_directories 2 %s\n", current_dir);
640                 if(!strlen(current_dir))
641                 {
642 // no current directory
643                         strcpy(string, new_dir);
644                 }
645                 else
646                 if(!is_root_dir(current_dir))
647                 {
648 // current directory is not root
649                         if(current_dir[strlen(current_dir) - 1] == '/')
650 // current_dir already has ending /
651                         sprintf(string, "%s%s", current_dir, new_dir);
652                         else
653 // need ending /
654                         sprintf(string, "%s/%s", current_dir, new_dir);
655                 }
656                 else
657                         sprintf(string, "%s%s", current_dir, new_dir);
658
659 //printf("FileSystem::parse_directories 3 %s %s\n", new_dir, string);
660                 strcpy(new_dir, string);
661 //printf("FileSystem::parse_directories 4\n");
662         }
663         return 0;
664 }
665
666 int FileSystem::parse_dots(char *new_dir)
667 {
668 // recursively remove ..s
669         int changed = 1;
670         while(changed)
671         {
672                 int i, j, len;
673                 len = strlen(new_dir);
674                 changed = 0;
675                 for(i = 0, j = 1; !changed && j < len; i++, j++)
676                 {
677 // Got 2 dots
678                         if(new_dir[i] == '.' && new_dir[j] == '.')
679                         {
680 // Ignore if character after .. doesn't qualify
681                                 if(j + 1 < len &&
682                                         new_dir[j + 1] != ' ' &&
683                                         new_dir[j + 1] != '/')
684                                         continue;
685
686 // Ignore if character before .. doesn't qualify
687                                 if(i > 0 &&
688                                         new_dir[i - 1] != '/') continue;
689
690                                 changed = 1;
691                                 while(new_dir[i] != '/' && i > 0)
692                                 {
693 // look for first / before ..
694                                         i--;
695                                 }
696
697 // find / before this /
698                                 if(i > 0) i--;
699                                 while(new_dir[i] != '/' && i > 0)
700                                 {
701 // look for first / before first / before ..
702                                         i--;
703                                 }
704
705 // i now equals /first filename before ..
706 // look for first / after ..
707                                 while(new_dir[j] != '/' && j < len)
708                                 {
709                                         j++;
710                                 }
711
712 // j now equals /first filename after ..
713                                 while(j < len)
714                                 {
715                                         new_dir[i++] = new_dir[j++];
716                                 }
717
718                                 new_dir[i] = 0;
719 // default to root directory
720                                 if((new_dir[0]) == 0) sprintf(new_dir, "/");
721                                 break;
722                         }
723                 }
724         }
725         return 0;
726 }
727
728 int FileSystem::complete_path(char *filename)
729 {
730 //printf("FileSystem::complete_path 1\n");
731         if(!strlen(filename)) return 1;
732 //printf("FileSystem::complete_path 1\n");
733         parse_tildas(filename);
734 //printf("FileSystem::complete_path 1\n");
735         parse_directories(filename);
736 //printf("FileSystem::complete_path 1\n");
737         parse_dots(filename);
738 // don't add end slash since this requires checking if dir
739 //printf("FileSystem::complete_path 2\n");
740         return 0;
741 }
742
743 int FileSystem::extract_dir(char *out, const char *in)
744 {
745         strcpy(out, in);
746         if(!is_dir(in))
747         {
748 // complete string is not directory
749                 int i;
750
751                 complete_path(out);
752
753                 for(i = strlen(out); i > 0 && out[i - 1] != '/'; i--)
754                 {
755                         ;
756                 }
757                 if(i >= 0) out[i] = 0;
758         }
759         return 0;
760 }
761
762 int FileSystem::extract_name(char *out, const char *in, int test_dir)
763 {
764         int i;
765
766         if(test_dir && is_dir(in))
767                 out[0] = 0; // complete string is directory
768         else
769         {
770                 for(i = strlen(in)-1; i > 0 && in[i] != '/'; i--)
771                 {
772                         ;
773                 }
774                 if(in[i] == '/') i++;
775                 strcpy(out, &in[i]);
776         }
777         return 0;
778 }
779
780 int FileSystem::join_names(char *out, const char *dir_in, const char *name_in)
781 {
782         strcpy(out, dir_in);
783         int len = strlen(out);
784         int result = 0;
785
786         while(!result)
787                 if(len == 0 || out[len] != 0) result = 1; else len--;
788
789         if(len != 0)
790         {
791                 if(out[len] != '/') strcat(out, "/");
792         }
793
794         strcat(out, name_in);
795         return 0;
796 }
797
798 int64_t FileSystem::get_date(const char *filename)
799 {
800         struct stat file_status;
801         bzero(&file_status, sizeof(struct stat));
802         int result = stat(filename, &file_status);
803         return !result ? file_status.st_mtime : -1;
804 }
805
806 void FileSystem::set_date(const char *path, int64_t value)
807 {
808         struct utimbuf new_time;
809         new_time.actime = value;
810         new_time.modtime = value;
811         utime(path, &new_time);
812 }
813
814 int64_t FileSystem::get_size(char *filename)
815 {
816         struct stat file_status;
817         bzero(&file_status, sizeof(struct stat));
818         int result = stat(filename, &file_status);
819         return !result ? file_status.st_size : -1;
820 }
821
822 int FileSystem::change_dir(const char *new_dir, int update)
823 {
824         char new_dir_full[BCTEXTLEN];
825
826         strcpy(new_dir_full, new_dir);
827
828         complete_path(new_dir_full);
829 // cut ending slash
830         if(strcmp(new_dir_full, "/") &&
831                 new_dir_full[strlen(new_dir_full) - 1] == '/')
832                 new_dir_full[strlen(new_dir_full) - 1] = 0;
833
834         if(update)
835                 this->update(new_dir_full);
836         else
837         {
838                 strcpy(current_dir, new_dir_full);
839         }
840         return 0;
841 }
842
843 int FileSystem::set_current_dir(const char *new_dir)
844 {
845         strcpy(current_dir, new_dir);
846         return 0;
847 }
848
849 int FileSystem::add_end_slash(char *new_dir)
850 {
851         if(new_dir[strlen(new_dir) - 1] != '/') strcat(new_dir, "/");
852         return 0;
853 }
854
855 char* FileSystem::get_current_dir()
856 {
857         return current_dir;
858 }
859
860 int FileSystem::total_files()
861 {
862         return dir_list.total;
863 }
864
865
866 FileItem* FileSystem::get_entry(int entry)
867 {
868         return dir_list.values[entry];
869 }
870
871
872 // collapse ".",  "..", "//"  eg. x/./..//y = y
873 char *FileSystem::basepath(const char *path)
874 {
875         char fpath[BCTEXTLEN];
876         unsigned len = strlen(path);
877         if( len >= sizeof(fpath) ) return 0;
878         strcpy(fpath, path);
879         char *flat = cstrdup("");
880
881         int r = 0;
882         char *fn = fpath + len;
883         while( fn > fpath ) {
884                 while( --fn >= fpath )
885                         if( *fn == '/' ) { *fn = 0;  break; }
886                 fn = fn < fpath ? fpath : fn+1;
887                 if( !*fn || !strcmp(fn, ".") ) continue;
888                 if( !strcmp(fn, "..") ) { ++r; continue; }
889                 if( r < 0 ) continue;
890                 if( r > 0 ) { --r;  continue; }
891                 char *cp = cstrcat(3, "/",fn,flat);
892                 delete [] flat;  flat = cp;
893         }
894
895         if( *path != '/' ) {
896                 char *cp = cstrcat(2, ".",flat);
897                 delete [] flat;  flat = cp;
898         }
899
900         return flat;
901 }
902