r1022: Restructure overlay and label code for efficiency; change the graphic display...
[cinelerra_cv.git] / guicast / filesystem.C
blob0885d495dc40f06fdb5d477ed18612969c2c4bf0
1 #include <dirent.h>
2 #include <errno.h>
3 #include <pwd.h>
4 #include <stddef.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <sys/stat.h>
9 #include <sys/types.h>
10 #include <time.h>
11 #include <unistd.h>
13 #include "filesystem.h"
15 FileItem::FileItem()
17         path = 0;
18         name = 0;
19         reset();
22 FileItem::FileItem(char *path, 
23         char *name, 
24         int is_dir, 
25         int64_t size, 
26         int month, 
27         int day, 
28         int year,
29         int64_t calendar_time)
31         this->path = new char[strlen(path)];
32         this->name = new char[strlen(name)];
33         if(this->path) strcpy(this->path, path);
34         if(this->name) strcpy(this->name, name);
35         this->is_dir = is_dir;
36         this->size = size;
37         this->month = month;
38         this->day = day;
39         this->year = year;
40         this->calendar_time = calendar_time;
43 FileItem::~FileItem()
45         reset();
48 int FileItem::reset()
50         if(this->path) delete [] this->path;
51         if(this->name) delete [] this->name;
52         path = 0;
53         name = 0;
54         is_dir = 0;
55         size = 0;
56         month = 0;
57         day = 0;
58         year = 0;
59         calendar_time = 0;
60         return 0;
63 int FileItem::set_path(char *path)
65         if(this->path) delete [] this->path;
66         this->path = new char[strlen(path) + 1];
67         strcpy(this->path, path);
68         return 0;
71 int FileItem::set_name(char *name)
73         if(this->name) delete [] this->name;
74         this->name = new char[strlen(name) + 1];
75         strcpy(this->name, name);
76         return 0;
80 FileSystem::FileSystem()
82         reset_parameters();
83         getcwd(current_dir, BCTEXTLEN);
84         
87 FileSystem::~FileSystem()
89         delete_directory();
92 int FileSystem::reset_parameters()
94         show_all_files = 0;
95         want_directory = 0;
96         strcpy(filter, "");
97         strcpy(current_dir, "");
98         sort_order = SORT_ASCENDING;
99         sort_field = SORT_PATH;
100         return 0;
103 int FileSystem::delete_directory()
105         for(int i = 0; i < dir_list.total; i++)
106         {
107                 delete dir_list.values[i];
108         }
109         dir_list.remove_all();
110         return 0;
113 int FileSystem::set_sort_order(int value)
115         this->sort_order = value;
118 int FileSystem::set_sort_field(int field)
120         this->sort_field = field;
123 // filename.with.dots.extension
124 //   becomes
125 // extension.dots.with.filename
127 int FileSystem::dot_reverse_filename(char *out, const char *in)
129         int i, i2, j=0, lastdot;
130         lastdot = strlen(in);
131         for (i = strlen(in); i >= 0; i--){
133         if (in[i] == '.') { 
134                 i2 = i+1;
135                 while (i2 < lastdot) 
136                         out[j++] = in[i2++];
137                 out[j++] = in[i];
138                 lastdot = i;
139                 }
140         }
141         i++;
142         if (in[i] != '.') {
143                 while (i < lastdot) out[j++] = in[i++];
144         }
145         out[j++] = '\0';
146         return 0;
149 int FileSystem::compare_items(ArrayList<FileItem*> *dir_list, 
150         int item1, 
151         int item2)
153         int result = 0;
154         FileItem *ptr1 = dir_list->values[item1];
155         FileItem *ptr2 = dir_list->values[item2];
157 // Default to name in ascending order
158         switch(sort_field)
159         {
160                 char dotreversedname1[BCTEXTLEN], dotreversedname2[BCTEXTLEN];
162                 case SORT_PATH:
163                         result = (sort_order == SORT_ASCENDING) ? 
164                                 strcasecmp(ptr1->name, ptr2->name) :
165                                 strcasecmp(ptr2->name, ptr1->name);
166                         break;
167                 case SORT_SIZE:
168                         if(ptr1->size == ptr2->size || ptr1->is_dir)
169                                 result = strcasecmp(ptr1->name, ptr2->name);
170                         else
171                                 result = (sort_order == SORT_ASCENDING) ?
172                                         (ptr1->size > ptr2->size) :
173                                         (ptr2->size > ptr1->size);
174                         break;
175                 case SORT_DATE:
176                         if(ptr1->calendar_time == ptr2->calendar_time)
177                                 result = strcasecmp(ptr1->name, ptr2->name);
178                         else
179                                 result = (sort_order == SORT_ASCENDING) ?
180                                         (ptr1->calendar_time > ptr2->calendar_time) :
181                                         (ptr2->calendar_time > ptr1->calendar_time);
182                         break;
183                 case SORT_EXTENSION:
184                         dot_reverse_filename(dotreversedname1,ptr1->name);
185                         dot_reverse_filename(dotreversedname2,ptr2->name);
187                         result = (sort_order == SORT_ASCENDING) ? 
188                         strcasecmp(dotreversedname1, dotreversedname2) :
189                         strcasecmp(dotreversedname2, dotreversedname1);
190                         break;
191         }
192         return result;
196 int FileSystem::sort_table(ArrayList<FileItem*> *dir_list)
198         int changed;
199         FileItem *temp;
200         int i;
201         
202         changed = 1;
203         while(changed)
204         {
205                 changed = 0;
206                 for(i = 0; i < dir_list->total - 1; i++)
207                 {
208                         if(compare_items(dir_list, i, i + 1) > 0)
209 //                      if(strcasecmp(dir_list->values[i]->name, dir_list->values[i + 1]->name) > 0)
210                         {
211                                 temp = dir_list->values[i];
212                                 dir_list->values[i] = dir_list->values[i+1];
213                                 dir_list->values[i+1] = temp;
214                                 changed = 1;
215                         }
216                 }
217         }
218         return 0;
221 int FileSystem::combine(ArrayList<FileItem*> *dir_list, ArrayList<FileItem*> *file_list)
223         int i;
224         FileItem *new_entry, *entry;
225         
226         sort_table(dir_list);
227         for(i = 0; i < dir_list->total; i++)
228         {
229                 this->dir_list.append(dir_list->values[i]);
230         }
232         sort_table(file_list);
233         for(i = 0; i < file_list->total; i++)
234         {
235                 this->dir_list.append(file_list->values[i]);
236         }
237         return 0;
240 void FileSystem::alphabetize()
242         sort_table(&dir_list);
245 int FileSystem::is_root_dir(char *path)
247         if(!strcmp(current_dir, "/")) return 1;
248         return 0;
251 int FileSystem::test_filter(FileItem *file)
253         char *filter1 = 0, *filter2 = filter, *subfilter1, *subfilter2;
254         int total_filters = 0;
255         int result = 0;
256         int done = 0, token_done;
257         int token_number = 0;
259 // Don't filter directories
260         if(file->is_dir) return 0;
262 // Empty filename string
263         if(!file->name) return 1;
265         do
266         {
267 // Get next token
268                 filter1 = strchr(filter2, '[');
269                 string[0] = 0;
271 // Get next filter
272                 if(filter1)
273                 {
274                         filter1++;
275                         filter2 = strchr(filter1, ']');
277                         if(filter2)
278                         {
279                                 int i;
280                                 for(i = 0; filter1 + i < filter2; i++)
281                                         string[i] = filter1[i];
282                                 string[i] = 0;
283                         }
284                         else
285                         {
286                                 strcpy(string, filter1);
287                                 done = 1;
288                         }
289                 }
290                 else
291                 {
292                         if(!token_number) 
293                                 strcpy(string, filter);
294                         else
295                                 done = 1;
296                 }
298 // Process the token
299                 if(string[0] != 0)
300                 {
301                         char *path = file->name;
302                         subfilter1 = string;
303                         token_done = 0;
304                         result = 0;
306                         do
307                         {
308                                 string2[0] = 0;
309                                 subfilter2 = strchr(subfilter1, '*');
311                                 if(subfilter2)
312                                 {
313                                         int i;
314                                         for(i = 0; subfilter1 + i < subfilter2; i++)
315                                                 string2[i] = subfilter1[i];
316                                         
317                                         string2[i] = 0;
318                                 }
319                                 else
320                                 {
321                                         strcpy(string2, subfilter1);
322                                         token_done = 1;
323                                 }
325                                 if(string2[0] != 0)
326                                 {
327 // Subfilter must exist at some later point in the string
328                                         if(subfilter1 > string)
329                                         {
330                                                 if(!strstr(path, string2)) 
331                                                 {
332                                                         result = 1;
333                                                         token_done = 1;
334                                                 }
335                                                 else
336                                                 path = strstr(path, string2) + strlen(string2);
337                                         }
338                                         else
339 // Subfilter must exist at this point in the string
340                                         {
341                                                 if(strncmp(path, string2, strlen(string2))) 
342 //                                              if(strncasecmp(path, string2, strlen(string2))) 
343                                                 {
344                                                         result = 1;
345                                                         token_done = 1;
346                                                 }
347                                                 else
348                                                 path += strlen(string2);
349                                         }
351 // String must terminate after subfilter
352                                         if(!subfilter2)
353                                         {
354                                                 if(*path != 0)
355                                                 {
356                                                         result = 1;
357                                                         token_done = 1;
358                                                 }
359                                         }
360                                 }
361                                 subfilter1 = subfilter2 + 1;
362 // Let pass if no subfilter
363                         }while(!token_done && !result);
364                 }
365                 token_number++;
366         }while(!done && result);
368         return result;
372 int FileSystem::update(char *new_dir)
374         DIR *dirstream;
375         struct dirent64 *new_filename;
376         struct stat ostat;
377         struct tm *mod_time;
378         int i, j, k, include_this;
379         FileItem *new_file;
380         char full_path[BCTEXTLEN], name_only[BCTEXTLEN];
381         ArrayList<FileItem*>directories;
382         ArrayList<FileItem*>files;
383         int result = 0;
385         delete_directory();
386         if(new_dir != 0) strcpy(current_dir, new_dir);
387         dirstream = opendir(current_dir);
388         if(!dirstream) return 1;          // failed to open directory
390         while(new_filename = readdir64(dirstream))
391         {
392                 include_this = 1;
394 // File is directory heirarchy
395                 if(!strcmp(new_filename->d_name, ".") || 
396                         !strcmp(new_filename->d_name, "..")) include_this = 0;
398 // File is hidden and we don't want all files
399                 if(include_this && !show_all_files && new_filename->d_name[0] == '.') include_this = 0;
401 // file not hidden
402                 if(include_this)
403         {
404                         new_file = new FileItem;
405                         sprintf(full_path, "%s", current_dir);
406                         if(!is_root_dir(current_dir)) strcat(full_path, "/");
407                         strcat(full_path, new_filename->d_name);
408                         strcpy(name_only, new_filename->d_name);
409                         new_file->set_path(full_path);
410                         new_file->set_name(name_only);
412 // Get information about the file.
413                         if(!stat(full_path, &ostat))
414                         {
415                                 new_file->size = ostat.st_size;
416                                 mod_time = localtime(&(ostat.st_mtime));
417                                 new_file->month = mod_time->tm_mon + 1;
418                                 new_file->day = mod_time->tm_mday;
419                                 new_file->year = mod_time->tm_year + 1900;
420                                 new_file->calendar_time = ostat.st_mtime;
422                                 if(S_ISDIR(ostat.st_mode))
423                                 {
424                                         strcat(name_only, "/"); // is a directory
425                                         new_file->is_dir = 1;
426                                 }
428 // File is excluded from filter
429                                 if(include_this && test_filter(new_file)) include_this = 0;
430 //printf("FileSystem::update 3 %d %d\n", include_this, test_filter(new_file));
432 // File is not a directory and we just want directories
433                                 if(include_this && want_directory && !new_file->is_dir) include_this = 0;
434                         }
435                         else
436                         {
437 //printf("FileSystem::update 3 %s\n", full_path);
438                                 printf("FileSystem::update %s: %s\n",
439                                         full_path,
440                                         strerror(errno));
441                                 include_this = 0;
442                                 result = 1;
443                         }
445 // add to list
446                         if(include_this)
447                         {
448                                 if(new_file->is_dir) directories.append(new_file);
449                                 else files.append(new_file);
450                         }
451                         else
452                                 delete new_file;
453                 }
454         }
456         closedir(dirstream);
457 // combine the directories and files in the master list
458         combine(&directories, &files);
459 // remove pointers
460         directories.remove_all();
461         files.remove_all();
463         return result;
464 // success
467 int FileSystem::set_filter(char *new_filter)
469         strcpy(filter, new_filter);
470         return 0;
473 int FileSystem::set_show_all()
475         show_all_files = 1;
476         return 0;
479 int FileSystem::set_want_directory()
481         want_directory = 1;
482         return 0;
485 int FileSystem::is_dir(const char *path)      // return 0 if the text is a directory
487         if(!strlen(path)) return 0;
489         char new_dir[BCTEXTLEN];
490         struct stat ostat;    // entire name is a directory
492         strcpy(new_dir, path);
493         complete_path(new_dir);
494         if(!stat(new_dir, &ostat) && S_ISDIR(ostat.st_mode)) 
495                 return 1;
496         else
497                 return 0;
500 int FileSystem::create_dir(char *new_dir_)
502         char new_dir[BCTEXTLEN];
503         strcpy(new_dir, new_dir_);
504         complete_path(new_dir);
506         mkdir(new_dir, S_IREAD | S_IWRITE | S_IEXEC);
507         return 0;
510 int FileSystem::parse_tildas(char *new_dir)
512         if(new_dir[0] == 0) return 1;
514 // Our home directory
515         if(new_dir[0] == '~')
516         {
518                 if(new_dir[1] == '/' || new_dir[1] == 0)
519                 {
520 // user's home directory
521                         char *home;
522                         char string[BCTEXTLEN];
523                         home = getenv("HOME");
525 // print starting after tilda
526                         if(home) sprintf(string, "%s%s", home, &new_dir[1]);
527                         strcpy(new_dir, string);
528                         return 0;
529                 }
530                 else
531 // Another user's home directory
532                 {                
533                         char string[BCTEXTLEN], new_user[BCTEXTLEN];
534                         struct passwd *pw;
535                         int i, j;
536       
537                         for(i = 1, j = 0; new_dir[i] != 0 && new_dir[i] != '/'; i++, j++)
538                         {                // copy user name
539                                 new_user[j] = new_dir[i];
540                         }
541                         new_user[j] = 0;
542       
543                         setpwent();
544                         while(pw = getpwent())
545                         {
546 // get info for user
547                                 if(!strcmp(pw->pw_name, new_user))
548                                 {
549 // print starting after tilda
550                                 sprintf(string, "%s%s", pw->pw_dir, &new_dir[i]);
551                                 strcpy(new_dir, string);
552                                 break;
553                         }
554                         }
555                         endpwent();
556                         return 0;
557                 }
558         }
559         return 0;
562 int FileSystem::parse_directories(char *new_dir)
564 //printf("FileSystem::parse_directories 1 %s\n", new_dir);
565         if(new_dir[0] != '/')
566         {
567 // extend path completely
568                 char string[BCTEXTLEN];
569 //printf("FileSystem::parse_directories 2 %s\n", current_dir);
570                 if(!strlen(current_dir))
571                 {
572 // no current directory
573                         strcpy(string, new_dir);
574                 }
575                 else
576                 if(!is_root_dir(current_dir))
577                 {
578 // current directory is not root
579                         if(current_dir[strlen(current_dir) - 1] == '/')
580 // current_dir already has ending /
581                         sprintf(string, "%s%s", current_dir, new_dir);
582                         else
583 // need ending /
584                         sprintf(string, "%s/%s", current_dir, new_dir);
585                 }
586                 else
587                         sprintf(string, "%s%s", current_dir, new_dir);
588                 
589 //printf("FileSystem::parse_directories 3 %s %s\n", new_dir, string);
590                 strcpy(new_dir, string);
591 //printf("FileSystem::parse_directories 4\n");
592         }
593         return 0;
596 int FileSystem::parse_dots(char *new_dir)
598 // recursively remove ..s
599         int changed = 1;
600         while(changed)
601         {
602                 int i, j, len;
603                 len = strlen(new_dir);
604                 changed = 0;
605                 for(i = 0, j = 1; !changed && j < len; i++, j++)
606                 {
607                         if(new_dir[i] == '.' && new_dir[j] == '.')
608                         {
609                                 changed = 1;
610                                 while(new_dir[i] != '/' && i > 0)
611                                 {
612 // look for first / before ..
613                                         i--;
614                                 }
616 // find / before this /
617                                 if(i > 0) i--;  
618                                 while(new_dir[i] != '/' && i > 0)
619                                 {
620 // look for first / before first / before ..
621                                         i--;
622                                 }
624 // i now equals /first filename before ..
625 // look for first / after ..
626                                 while(new_dir[j] != '/' && j < len)
627                                 {
628                                         j++;
629                                 }
631 // j now equals /first filename after ..
632                                 while(j < len)
633                                 {
634                                         new_dir[i++] = new_dir[j++];
635                                 }
637                                 new_dir[i] = 0;
638 // default to root directory
639                                 if((new_dir[0]) == 0) sprintf(new_dir, "/");
640                                 break;
641                         }
642                 }
643         }
644         return 0;
647 int FileSystem::complete_path(char *filename)
649 //printf("FileSystem::complete_path 1\n");
650         if(!strlen(filename)) return 1;
651 //printf("FileSystem::complete_path 1\n");
652         parse_tildas(filename);
653 //printf("FileSystem::complete_path 1\n");
654         parse_directories(filename);
655 //printf("FileSystem::complete_path 1\n");
656         parse_dots(filename);
657 // don't add end slash since this requires checking if dir
658 //printf("FileSystem::complete_path 2\n");
659         return 0;
662 int FileSystem::extract_dir(char *out, const char *in)
664         strcpy(out, in);
665         if(!is_dir(in))
666         {
667 // complete string is not directory
668                 int i;
670                 complete_path(out);
672                 for(i = strlen(out); i > 0 && out[i - 1] != '/'; i--)
673                 {
674                         ;
675                 }
676                 if(i >= 0) out[i] = 0;
677         }
678         return 0;
681 int FileSystem::extract_name(char *out, const char *in, int test_dir)
683         int i;
685         if(test_dir && is_dir(in))
686                 sprintf(out, "");    // complete string is directory
687         else
688         {
689                 for(i = strlen(in)-1; i > 0 && in[i] != '/'; i--)
690                 {
691                         ;
692                 }
693                 if(in[i] == '/') i++;
694                 strcpy(out, &in[i]);
695         }
696         return 0;
699 int FileSystem::join_names(char *out, char *dir_in, char *name_in)
701         strcpy(out, dir_in);
702         int len = strlen(out);
703         int result = 0;
705         while(!result)
706                 if(len == 0 || out[len] != 0) result = 1; else len--;
707         
708         if(len != 0)
709         {
710                 if(out[len] != '/') strcat(out, "/");
711         }
712         
713         strcat(out, name_in);
714         return 0;
717 int64_t FileSystem::get_date(char *filename)
719         struct stat file_status;
720         bzero(&file_status, sizeof(struct stat));
721         stat(filename, &file_status);
722         return file_status.st_mtime;
725 int64_t FileSystem::get_size(char *filename)
727         struct stat file_status;
728         bzero(&file_status, sizeof(struct stat));
729         stat(filename, &file_status);
730         return file_status.st_size;
733 int FileSystem::change_dir(char *new_dir)
735         char new_dir_full[BCTEXTLEN];
736         
737         strcpy(new_dir_full, new_dir);
739         complete_path(new_dir_full);
740 // cut ending slash
741         if(strcmp(new_dir_full, "/") && 
742                 new_dir_full[strlen(new_dir_full) - 1] == '/') 
743                 new_dir_full[strlen(new_dir_full) - 1] = 0;
744         update(new_dir_full);
745         return 0;
748 int FileSystem::set_current_dir(char *new_dir)
750         strcpy(current_dir, new_dir);
751         return 0;
754 int FileSystem::add_end_slash(char *new_dir)
756         if(new_dir[strlen(new_dir) - 1] != '/') strcat(new_dir, "/");
757         return 0;
760 char* FileSystem::get_current_dir()
762         return current_dir;
765 int FileSystem::total_files()
767         return dir_list.total;
771 FileItem* FileSystem::get_entry(int entry)
773         return dir_list.values[entry];