coan 4.2.4
|
00001 /*************************************************************************** 00002 * Copyright (C) 2004, 2006 Symbian Software Ltd. * 00003 * All rights reserved. * 00004 * Copyright (C) 2007-2011 Mike Kinghan, imk@strudl.org * 00005 * All rights reserved. * 00006 * * 00007 * Redistribution and use in source and binary forms, with or without * 00008 * modification, are permitted provided that the following conditions * 00009 * are met: * 00010 * * 00011 * Redistributions of source code must retain the above copyright * 00012 * notice, this list of conditions and the following disclaimer. * 00013 * * 00014 * Redistributions in binary form must reproduce the above copyright * 00015 * notice, this list of conditions and the following disclaimer in the * 00016 * documentation and/or other materials provided with the distribution. * 00017 * * 00018 * Neither the name of Symbian Software Ltd. nor the names of its * 00019 * contributors may be used to endorse or promote products derived from * 00020 * this software without specific prior written permission. * 00021 * * 00022 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * 00023 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * 00024 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * 00025 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * 00026 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * 00027 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * 00028 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * 00029 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * 00030 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,* 00031 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF * 00032 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * 00033 * DAMAGE. * 00034 * * 00035 **************************************************************************/ 00036 00037 #include "file_tree.h" 00038 #include "filesys.h" 00039 #include "platform.h" 00040 #include "report.h" 00041 #include "swiss_army.h" 00042 00050 00054 #define PARENT_PENDING ((void *)-1) 00055 00059 #define IS_UNLINKED(ftree) ((ftree)->parent == PARENT_PENDING) 00060 00063 #define IS_ROOT(ftree) ((ftree)->parent == NULL) 00064 00068 #define IS_FILE(ftree) ((ftree)->children == NULL) 00069 00073 #define IS_DIR(ftree) (!IS_FILE(ftree)) 00074 00079 #define COUNT_FILES(flags) (((flags) & FT_COUNT_FILES) == FT_COUNT_FILES) 00080 00084 #define COUNT_FILES_ONLY(flags) (((flags) & FT_COUNT_ALL) == FT_COUNT_FILES) 00085 00089 #define COUNT_DIRS(flags) (((flags) & FT_COUNT_DIRS) == FT_COUNT_DIRS) 00090 00094 #define COUNT_DIRS_ONLY(flags) (((flags) & FT_COUNT_ALL) == FT_COUNT_DIRS) 00095 00099 #define COUNT_ALL(flags) (((flags) & FT_COUNT_ALL) == FT_COUNT_ALL) 00100 00105 #define COUNT_CHILDREN(flags) (((flags) & FT_COUNT_CHILDREN) == FT_COUNT_CHILDREN) 00106 00111 struct file_tree { 00112 union { 00113 heap_str leafname; 00115 file_filter_t filter; 00117 } var; 00118 struct file_tree * parent; 00121 ptr_vector_h children; 00123 unsigned files; 00125 }; 00126 00131 static bool 00132 null_filter(char const *leafname) 00133 { 00134 return true; 00135 } 00136 00141 static void 00142 null_callback( file_tree_h ft, 00143 char const *name, 00144 file_tree_traverse_state_t context) 00145 { 00146 } 00147 00157 static file_tree_h 00158 new_node(char const *leafname, size_t len) 00159 { 00160 file_tree_h ft = callocate(1,sizeof(struct file_tree)); 00161 if (!len) { 00162 len = strlen(leafname); 00163 } 00164 ft->parent = PARENT_PENDING; 00165 ft->var.leafname = zallocate(len + 1); 00166 strncpy(ft->var.leafname,leafname,len); 00167 return ft; 00168 } 00169 00170 00181 static file_tree_h 00182 new_file_node(char const *leafname, file_filter_t filter) 00183 { 00184 file_tree_h ft = NULL; 00185 if (filter(leafname)) { 00186 ft = new_node(leafname,0); 00187 ft->files = 1; 00188 } 00189 return ft; 00190 } 00191 00200 static file_tree_h 00201 get_parent(file_tree_h child) 00202 { 00203 return IS_UNLINKED(child) || IS_ROOT(child) ? NULL : child->parent; 00204 } 00215 static void 00216 link_in(file_tree_h parent, file_tree_h child) 00217 { 00218 unsigned new_files; 00219 assert(!IS_ROOT(child)); 00220 if (!parent->children) { 00221 parent->children = ptr_vector_new((dtor_t)file_tree_dispose,NULL); 00222 } 00223 child->parent = parent; 00224 ptr_vector_append(parent->children,child); 00225 new_files = child->files; 00226 if (new_files) { 00227 for ( ; parent; child = parent,parent = get_parent(child)) { 00228 parent->files += new_files; 00229 } 00230 } 00231 } 00232 00233 /* Forward declaration*/ 00234 static void 00235 file_tree_add_symlink( file_tree_h root, 00236 fs_dir_t parent, 00237 char const *symlink, 00238 file_tree_callback_t callback); 00239 00240 00262 static file_tree_h 00263 new_dir_node( file_tree_h root, 00264 char const *leafname, 00265 fs_dir_t dir, 00266 file_filter_t filter, 00267 file_tree_callback_t callback) 00268 { 00269 file_tree_h ft = new_node(leafname,0); 00270 char const *cur_entry; 00271 char const *fullname = fs_cur_dir_entry(dir,&cur_entry); 00272 assert(cur_entry[0] == '\0'); 00273 callback(ft,fullname,FT_ENTERING_DIR); 00274 ft->children = ptr_vector_new((dtor_t)file_tree_dispose,NULL); 00275 while((leafname = fs_read_dir(dir,&fullname)) != NULL) { 00276 fs_obj_type_t obj_type = fs_obj_type(fullname); 00277 if (FS_IS_SLINK(obj_type)) { 00278 file_tree_add_symlink(ft,dir,fullname,callback); 00279 } else { 00280 file_tree_h child = NULL; 00281 fs_dir_t subdir = fs_open_dir(fullname,dir); 00282 if (subdir) { 00283 child = new_dir_node(root,leafname,subdir,filter,callback); 00284 fs_close_dir(subdir); 00285 } else { 00286 child = new_file_node(leafname,filter); 00287 if (child) { 00288 callback(child,fullname,FT_AT_FILE); 00289 } 00290 } 00291 if (child) { 00292 assert(child->files); 00293 link_in(ft,child); 00294 } 00295 } 00296 } 00297 fullname = fs_cur_dir_entry(dir,&cur_entry); 00298 assert(cur_entry[0] == '\0'); 00299 callback(ft,fullname,FT_LEAVING_DIR); 00300 if (!ft->files) { 00301 file_tree_dispose(ft); 00302 ft = NULL; 00303 } 00304 return ft; 00305 } 00306 00308 static file_tree_h 00309 get_root(file_tree_h ft) 00310 { 00311 if (IS_ROOT(ft)) { 00312 return ft; 00313 } 00314 return get_root(ft->parent); 00315 } 00316 00317 00324 static file_tree_h 00325 seek_child(file_tree_h node, char const *childname) 00326 { 00327 file_tree_h child = NULL; 00328 if (node->children) { 00329 file_tree_h *start = 00330 (file_tree_h *)ptr_vector_begin(node->children); 00331 file_tree_h *end = 00332 (file_tree_h *)ptr_vector_end(node->children); 00333 for ( ; start != end; ++start) { 00334 if (!strcmp(childname,(*start)->var.leafname)) { 00335 child = *start; 00336 break; 00337 } 00338 } 00339 } 00340 return child; 00341 } 00342 00343 00362 static file_tree_h 00363 seek(file_tree_h ft, char const **path) 00364 { 00365 char const *posn = *path; 00366 char const *end = strchr(posn,PATH_DELIM); 00367 fs_path_type_t path_type = fs_path_type(posn); 00368 if (!end) { 00369 end = posn + strlen(posn); 00370 } 00371 if (!IS_ROOT(ft)) { 00372 size_t elmlen = end - posn; 00373 if (fs_windows_path(path_type) && fs_absolute_path(path_type)) { 00374 path_type = 0; 00375 } 00376 if (ft->var.leafname[elmlen] =='\0' && 00377 !strncmp(ft->var.leafname,posn,elmlen)) { 00378 *path = posn += elmlen; 00379 } else { 00380 return ft; 00381 } 00382 } 00383 if (*posn) { 00384 if (!fs_windows_path(path_type) || !fs_absolute_path(path_type)) { 00385 *path = posn = end + 1; 00386 } 00387 if (ft->children) { 00388 file_tree_h child = NULL; 00389 file_tree_h *start = 00390 (file_tree_h *)ptr_vector_begin(ft->children); 00391 file_tree_h *end = 00392 (file_tree_h *)ptr_vector_end(ft->children); 00393 for ( ; start != end && posn == *path; ++start) { 00394 child = seek(*start,&posn); 00395 } 00396 if (posn != *path) { 00397 *path = posn; 00398 ft = child; 00399 } 00400 } 00401 } 00402 return ft; 00403 } 00404 00417 static file_tree_h 00418 deepen(file_tree_h ft, char const **path) 00419 { 00420 char const *end = strchr(*path,PATH_DELIM); 00421 if (end) { 00422 file_tree_h child = new_node(*path,end - *path); 00423 child->children = ptr_vector_new((dtor_t)file_tree_dispose,NULL); 00424 link_in(ft,child); 00425 *path = end + 1; 00426 ft = deepen(child,path); 00427 } 00428 return ft; 00429 } 00430 00448 static void 00449 traverse( file_tree_h ft, 00450 file_tree_callback_t callback, 00451 char *path_start, 00452 char *path_end) 00453 { 00454 strcpy(path_end,ft->var.leafname); 00455 if (IS_DIR(ft)) { 00456 size_t leaflen = strlen(path_end); 00457 file_tree_h * start = (file_tree_h *)ptr_vector_begin(ft->children); 00458 file_tree_h * end = (file_tree_h *)ptr_vector_end(ft->children); 00459 callback(ft,path_start,FT_ENTERING_DIR); 00460 path_end[leaflen++] = PATH_DELIM; 00461 for ( ; start != end; ++start) { 00462 traverse(*start,callback,path_start,path_end + leaflen); 00463 } 00464 path_end[--leaflen] = '\0'; 00465 callback(ft,path_start,FT_LEAVING_DIR); 00466 } else { 00467 callback(ft,path_start,FT_AT_FILE); 00468 } 00469 *path_end = '\0'; 00470 } 00471 00472 00479 static void 00480 file_tree_add_canon( file_tree_h root, 00481 char const *path, 00482 file_tree_callback_t callback) 00483 { 00484 char const * fullpath = path; 00485 heap_str parent_path = fs_split_filename(path,NULL); 00486 fs_dir_t parent_dir = parent_path ? fs_open_dir(parent_path,NULL) : NULL; 00487 fs_dir_t dir = fs_open_dir(path,parent_dir); 00488 file_filter_t filter; 00489 file_tree_h lowest; 00490 if (parent_path) { 00491 free(parent_path); 00492 } 00493 root = get_root(root); 00494 filter = root->var.filter; 00495 if (!callback) { 00496 callback = null_callback; 00497 } 00498 lowest = seek(root,&path); 00499 if (*path) { 00500 file_tree_h child; 00501 lowest = deepen(lowest,&path); 00502 if (!dir) { 00503 child = new_file_node(path,filter); 00504 if (child) { 00505 callback(child,fullpath,FT_AT_FILE); 00506 } 00507 } else { 00508 child = new_dir_node(root,path,dir,filter,callback); 00509 } 00510 if (child) { 00511 link_in(lowest,child); 00512 } 00513 } else if (dir) { 00514 char const * leafname; 00515 char const *fullname; 00516 while((leafname = fs_read_dir(dir,&fullname)) != NULL) { 00517 fs_obj_type_t obj_type = fs_obj_type(fullname); 00518 if (FS_IS_SLINK(obj_type)) { 00519 file_tree_add_symlink(root,dir,fullname,callback); 00520 } else { 00521 file_tree_h child = seek_child(lowest,leafname); 00522 if (!child) { 00523 fs_dir_t subdir = fs_open_dir(fullname,dir); 00524 if (!subdir) { 00525 child = new_file_node(leafname,filter); 00526 if (child) { 00527 callback(child,fullname,FT_AT_FILE); 00528 } 00529 } else { 00530 child = 00531 new_dir_node(root,leafname,subdir,filter,callback); 00532 fs_close_dir(subdir); 00533 } 00534 if (child) { 00535 link_in(lowest,child); 00536 } 00537 } 00538 } 00539 } 00540 } 00541 if (parent_dir) { 00542 fs_close_dir(parent_dir); 00543 } 00544 if (dir) { 00545 fs_close_dir(dir); 00546 } 00547 } 00548 00549 00597 static void 00598 file_tree_add_symlink( file_tree_h root, 00599 fs_dir_t dir, 00600 char const *symlink, 00601 file_tree_callback_t callback) 00602 { 00603 heap_str realpath; 00604 realpath = fs_real_path(symlink,NULL); 00605 report(GRIPE_SYMLINK,NULL,"Resolved symbolic link \"%s\"",symlink); 00606 if (dir) { 00607 size_t shared_len = 0; 00608 char const *entry; 00609 char const * dirname = fs_cur_dir_entry(dir,&entry); 00610 char const *shared_path = 00611 fs_path_comp(dirname,realpath,&shared_len); 00612 while (!(shared_path == dirname && dirname[shared_len] == 0)) { 00613 if ((dir = fs_get_parent(dir)) == NULL) { 00614 break; 00615 } 00616 dirname = fs_cur_dir_entry(dir,&entry); 00617 shared_path = fs_path_comp(dirname,realpath,&shared_len); 00618 } 00619 } 00620 if (!dir) { 00621 file_tree_add_canon(root,realpath,callback); 00622 } 00623 free(realpath); 00624 } 00625 00630 static void 00631 file_tree_init(file_tree_h ft) 00632 { 00633 memset(ft,0,sizeof(struct file_tree)); 00634 ft->var.filter = null_filter; 00635 } 00636 00644 static void 00645 file_tree_copy_init(file_tree_h dest, file_tree_const_h src) 00646 { 00647 memset(dest,0,sizeof(struct file_tree)); 00648 if (src->children) { 00649 file_tree_const_h *start = 00650 (file_tree_const_h *)ptr_vector_begin_const(src->children); 00651 file_tree_const_h *end = 00652 (file_tree_const_h *)ptr_vector_end_const(src->children); 00653 for ( ; start != end; ++start) { 00654 file_tree_h child = file_tree_copy(*start); 00655 link_in(dest,child); 00656 } 00657 } else if (IS_ROOT(src)) { 00658 dest->var.filter = src->var.filter; 00659 } else { 00660 dest->var.leafname = clone(src->var.leafname,0); 00661 dest->files = src->files; 00662 } 00663 } 00664 00668 static void 00669 file_tree_finis(file_tree_h ft) 00670 { 00671 if (ft->children) { 00672 ptr_vector_dispose(ft->children); 00673 ft->children = NULL; 00674 } 00675 if (!IS_ROOT(ft) && ft->var.leafname) { 00676 free(ft->var.leafname); 00677 ft->var.leafname = NULL; 00678 } 00679 } 00680 00681 #ifdef DEBUG_FILE_TREE 00682 static void 00683 file_tree_print_name(file_tree_h ft, char const *name, int context) 00684 { 00685 (void)context; 00686 puts(name); 00687 } 00688 00689 void 00690 file_tree_dump(file_tree_h ft) 00691 { 00692 if (!IS_ROOT(ft)) { 00693 file_tree_dump(ft->parent); 00694 } else { 00695 file_tree_traverse(ft,file_tree_print_name); 00696 } 00697 } 00698 00699 #endif 00700 00703 /* API*/ 00704 00705 heap_str 00706 file_tree_name(file_tree_const_h ft) 00707 { 00708 heap_str full_name; 00709 if (IS_ROOT(ft)) { 00710 full_name = callocate(1,1); 00711 } else { 00712 heap_str path_name = file_tree_name(ft->parent); 00713 full_name = 00714 fs_compose_filename(path_name,ft->var.leafname); 00715 free(path_name); 00716 } 00717 return full_name; 00718 } 00719 00720 file_tree_h 00721 file_tree_new(void) 00722 { 00723 file_tree_h ft = allocate(sizeof(struct file_tree)); 00724 file_tree_init(ft); 00725 return ft; 00726 } 00727 00728 file_tree_h 00729 file_tree_copy(file_tree_const_h src) 00730 { 00731 file_tree_h dest; 00732 assert(src); 00733 dest = allocate(sizeof(struct file_tree)); 00734 file_tree_copy_init(dest,src); 00735 return dest; 00736 } 00737 00738 void 00739 file_tree_swap(file_tree_h lhs, file_tree_h rhs) 00740 { 00741 assert(lhs); 00742 assert(rhs); 00743 PODSWAP(heap_str,&lhs->var.leafname,&rhs->var.leafname); 00744 PODSWAP(file_tree_h,&lhs->parent,&rhs->parent); 00745 PODSWAP(ptr_vector_h,&lhs->children,&rhs->children); 00746 PODSWAP(unsigned,&lhs->files,&rhs->files); 00747 } 00748 00749 void 00750 file_tree_assign(file_tree_h dest, file_tree_const_h src) 00751 { 00752 if (dest != src) { 00753 file_tree_h tmp = file_tree_copy(src); 00754 file_tree_swap(dest,tmp); 00755 file_tree_dispose(tmp); 00756 } 00757 } 00758 00759 bool 00760 file_tree_equal(file_tree_const_h lhs, file_tree_const_h rhs) 00761 { 00762 if (lhs == rhs) { 00763 return true; 00764 } 00765 if (!lhs->children ^ !rhs->children) { 00766 return false; 00767 } 00768 if (lhs->children) { 00769 if (ptr_vector_count(lhs->children) != ptr_vector_count(rhs->children)) { 00770 return false; 00771 } 00772 { 00773 file_tree_const_h *lh_iter = 00774 (file_tree_const_h *)ptr_vector_begin_const(lhs->children); 00775 file_tree_const_h *rh_iter = 00776 (file_tree_const_h *)ptr_vector_begin_const(rhs->children); 00777 file_tree_const_h *lh_end = 00778 (file_tree_const_h *)ptr_vector_end_const(lhs->children); 00779 for ( ; lh_iter != lh_end; ++lh_iter,++rh_iter) { 00780 if (!file_tree_equal(*lh_iter,*rh_iter)) { 00781 return false; 00782 } 00783 } 00784 } 00785 } 00786 if (IS_ROOT(lhs) ^ IS_ROOT(rhs)) { 00787 return false; 00788 } 00789 if (IS_ROOT(lhs)) { 00790 return true; 00791 } else { 00792 heap_str lh_name = file_tree_name(lhs); 00793 heap_str rh_name = file_tree_name(rhs); 00794 bool eq = !strcmp(lh_name,rh_name); 00795 free(lh_name); 00796 free(rh_name); 00797 return eq; 00798 } 00799 } 00800 00801 00802 bool 00803 file_tree_set_filter(file_tree_h ft, file_filter_t filter) 00804 { 00805 if (!file_tree_is_empty(ft)) { 00806 return false; 00807 } 00808 ft->var.filter = filter; 00809 return true; 00810 } 00811 00812 void 00813 file_tree_dispose(file_tree_h ft) 00814 { 00815 if (ft) { 00816 file_tree_finis(ft); 00817 free(ft); 00818 } 00819 } 00820 00821 void 00822 file_tree_add( file_tree_h root, 00823 char const *path, 00824 file_tree_callback_t callback) 00825 { 00826 fs_obj_type_t obj_type = fs_file_or_dir(path); 00827 if (FS_IS_SLINK(obj_type)) { 00828 file_tree_add_symlink(root,NULL,path,callback); 00829 } else { 00830 heap_str fullpath = fs_real_path(path,NULL); 00831 file_tree_add_canon(root,fullpath,callback); 00832 free(fullpath); 00833 } 00834 } 00835 00836 void 00837 file_tree_traverse( file_tree_h ft, 00838 file_tree_callback_t callback) 00839 { 00840 callback(ft,NULL,FT_ENTERING_TREE); 00841 if (ft->children) { 00842 file_tree_h * start; 00843 file_tree_h * end; 00844 heap_str pathstack = callocate(1,PATH_MAX); 00845 strcpy(pathstack,FS_ROOT_PREFIX); 00846 start = (file_tree_h *)ptr_vector_begin(ft->children); 00847 end = (file_tree_h *)ptr_vector_end(ft->children); 00848 for ( ; start != end; ++start) { 00849 traverse(*start,callback,pathstack,pathstack + strlen(pathstack)); 00850 } 00851 free(pathstack); 00852 } 00853 callback(ft,NULL,FT_LEAVING_TREE); 00854 } 00855 00856 bool 00857 file_tree_is_empty(file_tree_const_h ft) 00858 { 00859 if (!IS_ROOT(ft)) { 00860 return false; 00861 } 00862 return !ft->children || ptr_vector_count(ft->children) == 0; 00863 } 00864 00865 size_t 00866 file_tree_count(file_tree_const_h ft, unsigned flags, file_tree_count_t *counter) 00867 { 00868 file_tree_count_t count = {0,0,0}; 00869 int count_children = COUNT_CHILDREN(flags); 00870 int count_dirs = COUNT_DIRS(flags); 00871 int count_files = COUNT_FILES(flags); 00872 if (!count_children && count_files && !count_dirs) { 00873 count.files = ft->files; 00874 } else { 00875 if (!IS_ROOT(ft)) { 00876 if (!count_children) { 00877 if (IS_DIR(ft)) { 00878 count.dirs += count_dirs; 00879 } else { 00880 count.files += count_files; 00881 } 00882 } 00883 } 00884 if (IS_DIR(ft)) { 00885 file_tree_h * start = (file_tree_h *)ptr_vector_begin(ft->children); 00886 file_tree_h * end = (file_tree_h *)ptr_vector_end(ft->children); 00887 for ( ; start != end; ++ start) { 00888 if (count_children) { 00889 ++count.children; 00890 if (IS_DIR(*start)) { 00891 count.dirs += count_dirs; 00892 } else { 00893 count.files += count_files; 00894 } 00895 } else { 00896 file_tree_count(*start,flags,&count); 00897 } 00898 } 00899 } 00900 } 00901 if (counter) { 00902 *counter = count; 00903 } 00904 return count.files + count.dirs; 00905 } 00906 00907 00908 file_tree_const_h 00909 file_tree_child(file_tree_const_h ft, size_t which) 00910 { 00911 file_tree_h child = NULL; 00912 if (IS_DIR(ft)) { 00913 size_t children = ptr_vector_count(ft->children); 00914 if (children) { 00915 if (which == FT_LAST) { 00916 which = children - 1; 00917 } 00918 if (which < children) { 00919 child = *(file_tree_h *)ptr_vector_at(ft->children,which); 00920 } 00921 } 00922 } 00923 return child; 00924 } 00925 00926 /* EOF*/