3 * Copyright (C) 2010 Adam Williams <broadcast at earthling dot net>
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.
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.
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
21 #include "bcsignals.h"
31 #include <sys/types.h>
37 #include "filesystem.h"
46 FileItem::FileItem(char *path,
53 int64_t calendar_time)
55 this->path = new char[strlen(path)];
56 this->name = new char[strlen(name)];
57 if(this->path) strcpy(this->path, path);
58 if(this->name) strcpy(this->name, name);
59 this->is_dir = is_dir;
64 this->calendar_time = calendar_time;
74 if(this->path) delete [] this->path;
75 if(this->name) delete [] this->name;
87 int FileItem::set_path(char *path)
89 if(this->path) delete [] this->path;
90 this->path = new char[strlen(path) + 1];
91 strcpy(this->path, path);
95 int FileItem::set_name(char *name)
97 if(this->name) delete [] this->name;
98 this->name = new char[strlen(name) + 1];
99 strcpy(this->name, name);
103 const char* FileItem::get_path()
108 const char* FileItem::get_name()
113 int FileItem::get_is_dir()
120 FileSystem::FileSystem()
123 (void)getcwd(current_dir, BCTEXTLEN);
126 FileSystem::~FileSystem()
131 int FileSystem::reset_parameters()
136 strcpy(current_dir, "");
137 sort_order = SORT_ASCENDING;
138 sort_field = SORT_PATH;
142 int FileSystem::delete_directory()
144 for(int i = 0; i < dir_list.total; i++)
146 delete dir_list.values[i];
148 dir_list.remove_all();
152 int FileSystem::set_sort_order(int value)
154 this->sort_order = value;
158 int FileSystem::set_sort_field(int field)
160 this->sort_field = field;
164 // filename.with.dots.extension
166 // extension.dots.with.filename
168 int FileSystem::dot_reverse_filename(char *out, const char *in)
170 int i, i2, j=0, lastdot;
171 lastdot = strlen(in);
172 for ( i=strlen(in); --i >= 0; ) {
181 if (in[++i] != '.') {
182 while (i < lastdot) out[j++] = in[i++];
188 int FileSystem::path_ascending(const void *ptr1, const void *ptr2)
190 FileItem *item1 = *(FileItem**)ptr1;
191 FileItem *item2 = *(FileItem**)ptr2;
192 //printf("path_ascending %p %p\n", ptr1, ptr2);
193 return strcasecmp(item1->name, item2->name);
196 int FileSystem::path_descending(const void *ptr1, const void *ptr2)
198 FileItem *item1 = *(FileItem**)ptr1;
199 FileItem *item2 = *(FileItem**)ptr2;
200 return strcasecmp(item2->name, item1->name);
204 int FileSystem::size_ascending(const void *ptr1, const void *ptr2)
206 FileItem *item1 = *(FileItem**)ptr1;
207 FileItem *item2 = *(FileItem**)ptr2;
208 return item1->size >= item2->size;
211 int FileSystem::size_descending(const void *ptr1, const void *ptr2)
213 FileItem *item1 = *(FileItem**)ptr1;
214 FileItem *item2 = *(FileItem**)ptr2;
215 return item1->size <= item2->size;
219 int FileSystem::date_ascending(const void *ptr1, const void *ptr2)
221 FileItem *item1 = *(FileItem**)ptr1;
222 FileItem *item2 = *(FileItem**)ptr2;
223 return item1->calendar_time >= item2->calendar_time;
226 int FileSystem::date_descending(const void *ptr1, const void *ptr2)
228 FileItem *item1 = *(FileItem**)ptr1;
229 FileItem *item2 = *(FileItem**)ptr2;
230 return item1->calendar_time <= item2->calendar_time;
233 int FileSystem::ext_ascending(const void *ptr1, const void *ptr2)
235 char dotreversedname1[BCTEXTLEN], dotreversedname2[BCTEXTLEN];
236 FileItem *item1 = *(FileItem**)ptr1;
237 FileItem *item2 = *(FileItem**)ptr2;
238 dot_reverse_filename(dotreversedname1,item1->name);
239 dot_reverse_filename(dotreversedname2,item2->name);
240 return strcasecmp(dotreversedname1, dotreversedname2);
243 int FileSystem::ext_descending(const void *ptr1, const void *ptr2)
245 char dotreversedname1[BCTEXTLEN], dotreversedname2[BCTEXTLEN];
246 FileItem *item1 = *(FileItem**)ptr1;
247 FileItem *item2 = *(FileItem**)ptr2;
248 dot_reverse_filename(dotreversedname1,item1->name);
249 dot_reverse_filename(dotreversedname2,item2->name);
250 return strcasecmp(dotreversedname2, dotreversedname1);
253 int FileSystem::sort_table(ArrayList<FileItem*> *dir_list)
255 #define SORT_MACRO(compare) \
256 qsort(dir_list->values, dir_list->size(), sizeof(FileItem*), compare);
258 if(!dir_list || !dir_list->size()) return 0;
260 //printf("FileSystem::sort_table %p\n", dir_list->values);
264 if(sort_order == SORT_ASCENDING)
265 SORT_MACRO(path_ascending)
267 SORT_MACRO(path_descending)
270 if(sort_order == SORT_ASCENDING)
271 SORT_MACRO(size_ascending)
273 SORT_MACRO(size_descending)
276 if(sort_order == SORT_ASCENDING)
277 SORT_MACRO(date_ascending)
279 SORT_MACRO(date_descending)
282 if(sort_order == SORT_ASCENDING)
283 SORT_MACRO(ext_ascending)
285 SORT_MACRO(ext_descending)
292 int FileSystem::combine(ArrayList<FileItem*> *dir_list, ArrayList<FileItem*> *file_list)
296 sort_table(dir_list);
297 for(i = 0; i < dir_list->total; i++)
299 this->dir_list.append(dir_list->values[i]);
302 sort_table(file_list);
303 for(i = 0; i < file_list->total; i++)
305 this->dir_list.append(file_list->values[i]);
310 void FileSystem::alphabetize()
312 sort_table(&dir_list);
315 int FileSystem::is_root_dir(char *path)
317 if(!strcmp(current_dir, "/")) return 1;
321 int FileSystem::test_filter(FileItem *file)
323 char *filter1 = 0, *filter2 = filter, *subfilter1, *subfilter2;
325 int done = 0, token_done;
326 int token_number = 0;
328 // Don't filter directories
329 if(file->is_dir) return 0;
331 // Empty filename string
332 if(!file->name) return 1;
337 filter1 = strchr(filter2, '[');
344 filter2 = strchr(filter1, ']');
349 for(i = 0; filter1 + i < filter2; i++)
350 string[i] = filter1[i];
355 strcpy(string, filter1);
362 strcpy(string, filter);
370 char *path = file->name;
378 subfilter2 = strchr(subfilter1, '*');
383 for(i = 0; subfilter1 + i < subfilter2; i++)
384 string2[i] = subfilter1[i];
390 strcpy(string2, subfilter1);
396 // Subfilter must exist at some later point in the string
397 if(subfilter1 > string)
399 if(!strstr(path, string2))
405 path = strstr(path, string2) + strlen(string2);
408 // Subfilter must exist at this point in the string
410 if(strncmp(path, string2, strlen(string2)))
411 // if(strncasecmp(path, string2, strlen(string2)))
417 path += strlen(string2);
420 // String must terminate after subfilter
430 subfilter1 = subfilter2 + 1;
431 // Let pass if no subfilter
432 }while(!token_done && !result);
435 }while(!done && result);
441 int FileSystem::update(const char *new_dir)
444 struct dirent64 *new_filename;
449 char full_path[BCTEXTLEN], name_only[BCTEXTLEN];
450 ArrayList<FileItem*>directories;
451 ArrayList<FileItem*>files;
455 if(new_dir != 0) strcpy(current_dir, new_dir);
456 dirstream = opendir(current_dir);
457 if(!dirstream) return 1; // failed to open directory
459 while( (new_filename = readdir64(dirstream)) != 0 )
463 // File is directory heirarchy
464 if(!strcmp(new_filename->d_name, ".") ||
465 !strcmp(new_filename->d_name, "..")) include_this = 0;
467 // File is hidden and we don't want all files
470 new_filename->d_name[0] == '.') include_this = 0;
475 new_file = new FileItem;
476 sprintf(full_path, "%s", current_dir);
477 if(!is_root_dir(current_dir)) strcat(full_path, "/");
478 strcat(full_path, new_filename->d_name);
479 strcpy(name_only, new_filename->d_name);
480 new_file->set_path(full_path);
481 new_file->set_name(name_only);
483 // Get information about the file.
484 if(!stat(full_path, &ostat))
486 new_file->size = ostat.st_size;
487 mod_time = localtime(&(ostat.st_mtime));
488 new_file->month = mod_time->tm_mon + 1;
489 new_file->day = mod_time->tm_mday;
490 new_file->year = mod_time->tm_year + 1900;
491 new_file->calendar_time = ostat.st_mtime;
493 if(S_ISDIR(ostat.st_mode))
495 strcat(name_only, "/"); // is a directory
496 new_file->is_dir = 1;
499 // File is excluded from filter
500 if(include_this && test_filter(new_file)) include_this = 0;
501 //printf("FileSystem::update 3 %d %d\n", include_this, test_filter(new_file));
503 // File is not a directory and we just want directories
504 if(include_this && want_directory && !new_file->is_dir) include_this = 0;
508 printf("FileSystem::update %s %s\n", full_path, strerror(errno));
515 if(new_file->is_dir) directories.append(new_file);
516 else files.append(new_file);
522 //printf("FileSystem::update %d\n", __LINE__);
525 // combine the directories and files in the master list
526 combine(&directories, &files);
528 directories.remove_all();
530 //printf("FileSystem::update %d\n", __LINE__);
536 int FileSystem::set_filter(const char *new_filter)
538 strcpy(filter, new_filter);
542 int FileSystem::set_show_all()
548 int FileSystem::set_want_directory()
554 int FileSystem::is_dir(const char *path) // return 0 if the text is a directory
556 if(!strlen(path)) return 0;
558 char new_dir[BCTEXTLEN];
559 struct stat ostat; // entire name is a directory
561 strcpy(new_dir, path);
562 complete_path(new_dir);
563 if(!stat(new_dir, &ostat) && S_ISDIR(ostat.st_mode))
569 int FileSystem::create_dir(const char *new_dir_)
571 char new_dir[BCTEXTLEN];
572 strcpy(new_dir, new_dir_);
573 complete_path(new_dir);
575 mkdir(new_dir, S_IREAD | S_IWRITE | S_IEXEC);
579 int FileSystem::parse_tildas(char *new_dir)
581 if(new_dir[0] == 0) return 1;
583 // Our home directory
584 if(new_dir[0] == '~')
587 if(new_dir[1] == '/' || new_dir[1] == 0)
589 // user's home directory
591 char string[BCTEXTLEN];
592 home = getenv("HOME");
594 // print starting after tilda
595 if(home) sprintf(string, "%s%s", home, &new_dir[1]);
596 strcpy(new_dir, string);
600 // Another user's home directory
602 char string[BCTEXTLEN], new_user[BCTEXTLEN];
606 for(i = 1, j = 0; new_dir[i] != 0 && new_dir[i] != '/'; i++, j++)
608 new_user[j] = new_dir[i];
613 while( (pw = getpwent()) != 0 )
616 if(!strcmp(pw->pw_name, new_user))
618 // print starting after tilda
619 sprintf(string, "%s%s", pw->pw_dir, &new_dir[i]);
620 strcpy(new_dir, string);
631 int FileSystem::parse_directories(char *new_dir)
633 //printf("FileSystem::parse_directories 1 %s\n", new_dir);
634 if(new_dir[0] != '/')
636 // extend path completely
637 char string[BCTEXTLEN];
638 //printf("FileSystem::parse_directories 2 %s\n", current_dir);
639 if(!strlen(current_dir))
641 // no current directory
642 strcpy(string, new_dir);
645 if(!is_root_dir(current_dir))
647 // current directory is not root
648 if(current_dir[strlen(current_dir) - 1] == '/')
649 // current_dir already has ending /
650 sprintf(string, "%s%s", current_dir, new_dir);
653 sprintf(string, "%s/%s", current_dir, new_dir);
656 sprintf(string, "%s%s", current_dir, new_dir);
658 //printf("FileSystem::parse_directories 3 %s %s\n", new_dir, string);
659 strcpy(new_dir, string);
660 //printf("FileSystem::parse_directories 4\n");
665 int FileSystem::parse_dots(char *new_dir)
667 // recursively remove ..s
672 len = strlen(new_dir);
674 for(i = 0, j = 1; !changed && j < len; i++, j++)
677 if(new_dir[i] == '.' && new_dir[j] == '.')
679 // Ignore if character after .. doesn't qualify
681 new_dir[j + 1] != ' ' &&
682 new_dir[j + 1] != '/')
685 // Ignore if character before .. doesn't qualify
687 new_dir[i - 1] != '/') continue;
690 while(new_dir[i] != '/' && i > 0)
692 // look for first / before ..
696 // find / before this /
698 while(new_dir[i] != '/' && i > 0)
700 // look for first / before first / before ..
704 // i now equals /first filename before ..
705 // look for first / after ..
706 while(new_dir[j] != '/' && j < len)
711 // j now equals /first filename after ..
714 new_dir[i++] = new_dir[j++];
718 // default to root directory
719 if((new_dir[0]) == 0) sprintf(new_dir, "/");
727 int FileSystem::complete_path(char *filename)
729 //printf("FileSystem::complete_path 1\n");
730 if(!strlen(filename)) return 1;
731 //printf("FileSystem::complete_path 1\n");
732 parse_tildas(filename);
733 //printf("FileSystem::complete_path 1\n");
734 parse_directories(filename);
735 //printf("FileSystem::complete_path 1\n");
736 parse_dots(filename);
737 // don't add end slash since this requires checking if dir
738 //printf("FileSystem::complete_path 2\n");
742 int FileSystem::extract_dir(char *out, const char *in)
747 // complete string is not directory
752 for(i = strlen(out); i > 0 && out[i - 1] != '/'; i--)
756 if(i >= 0) out[i] = 0;
761 int FileSystem::extract_name(char *out, const char *in, int test_dir)
765 if(test_dir && is_dir(in))
766 out[0] = 0; // complete string is directory
769 for(i = strlen(in)-1; i > 0 && in[i] != '/'; i--)
773 if(in[i] == '/') i++;
779 int FileSystem::join_names(char *out, const char *dir_in, const char *name_in)
782 int len = strlen(out);
786 if(len == 0 || out[len] != 0) result = 1; else len--;
790 if(out[len] != '/') strcat(out, "/");
793 strcat(out, name_in);
797 int64_t FileSystem::get_date(const char *filename)
799 struct stat file_status;
800 bzero(&file_status, sizeof(struct stat));
801 int result = stat(filename, &file_status);
802 return !result ? file_status.st_mtime : -1;
805 void FileSystem::set_date(const char *path, int64_t value)
807 struct utimbuf new_time;
808 new_time.actime = value;
809 new_time.modtime = value;
810 utime(path, &new_time);
813 int64_t FileSystem::get_size(char *filename)
815 struct stat file_status;
816 bzero(&file_status, sizeof(struct stat));
817 int result = stat(filename, &file_status);
818 return !result ? file_status.st_size : -1;
821 int FileSystem::change_dir(const char *new_dir, int update)
823 char new_dir_full[BCTEXTLEN];
825 strcpy(new_dir_full, new_dir);
827 complete_path(new_dir_full);
829 if(strcmp(new_dir_full, "/") &&
830 new_dir_full[strlen(new_dir_full) - 1] == '/')
831 new_dir_full[strlen(new_dir_full) - 1] = 0;
834 this->update(new_dir_full);
837 strcpy(current_dir, new_dir_full);
842 int FileSystem::set_current_dir(const char *new_dir)
844 strcpy(current_dir, new_dir);
848 int FileSystem::add_end_slash(char *new_dir)
850 if(new_dir[strlen(new_dir) - 1] != '/') strcat(new_dir, "/");
854 char* FileSystem::get_current_dir()
859 int FileSystem::total_files()
861 return dir_list.total;
865 FileItem* FileSystem::get_entry(int entry)
867 return dir_list.values[entry];
871 // collapse ".", "..", "//" eg. x/./..//y = y
872 char *FileSystem::basepath(const char *path)
874 char fpath[BCTEXTLEN];
875 unsigned len = strlen(path);
876 if( len >= sizeof(fpath) ) return 0;
878 char *flat = cstrdup("");
881 char *fn = fpath + len;
882 while( fn > fpath ) {
883 while( --fn >= fpath )
884 if( *fn == '/' ) { *fn = 0; break; }
885 fn = fn < fpath ? fpath : fn+1;
886 if( !*fn || !strcmp(fn, ".") ) continue;
887 if( !strcmp(fn, "..") ) { ++r; continue; }
888 if( r < 0 ) continue;
889 if( r > 0 ) { --r; continue; }
890 char *cp = cstrcat(3, "/",fn,flat);
891 delete [] flat; flat = cp;
895 char *cp = cstrcat(2, ".",flat);
896 delete [] flat; flat = cp;