coan 4.2.4
file_tree.c
Go to the documentation of this file.
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*/
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines