"Fossies" - the Fresh Open Source Software Archive

Member "sarg-2.4.0/filelist.c" (30 Jan 2017, 13204 Bytes) of package /linux/privat/sarg-2.4.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "filelist.c" see the Fossies "Dox" file reference documentation.

    1 /*
    2  * SARG Squid Analysis Report Generator      http://sarg.sourceforge.net
    3  *                                                            1998, 2015
    4  *
    5  * SARG donations:
    6  *      please look at http://sarg.sourceforge.net/donations.php
    7  * Support:
    8  *     http://sourceforge.net/projects/sarg/forums/forum/363374
    9  * ---------------------------------------------------------------------
   10  *
   11  *  This program is free software; you can redistribute it and/or modify
   12  *  it under the terms of the GNU General Public License as published by
   13  *  the Free Software Foundation; either version 2 of the License, or
   14  *  (at your option) any later version.
   15  *
   16  *  This program is distributed in the hope that it will be useful,
   17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
   18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   19  *  GNU General Public License for more details.
   20  *
   21  *  You should have received a copy of the GNU General Public License
   22  *  along with this program; if not, write to the Free Software
   23  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
   24  *
   25  */
   26 
   27 #include "include/conf.h"
   28 #include "include/defs.h"
   29 #include "include/stringbuffer.h"
   30 #include "include/filelist.h"
   31 #ifdef HAVE_GLOB_H
   32 #include <glob.h>
   33 #endif
   34 
   35 struct DirEntryStruct
   36 {
   37     //! Next entry at the same level.
   38     struct DirEntryStruct *Sibbling;
   39     //! First child of this entry.
   40     struct DirEntryStruct *Child;
   41     //! Name of this entry.
   42     char *Name;
   43     //! \c True if it contains any wildcard.
   44     bool IsMask;
   45 };
   46 
   47 /*!
   48  * \brief List of files.
   49  *
   50  * The list may contain wildcards.
   51  */
   52 struct FileListStruct
   53 {
   54     //! Root of the tree.
   55     struct DirEntryStruct *First;
   56     //! Buffer containing the file name strings.
   57     StringBufferObject Buffer;
   58     //! Deepest level of the tree.
   59     int TreeDepth;
   60     //! \c True if the tree depth is correct.
   61     bool TreeDepthOk;
   62 };
   63 
   64 struct DirEntryIterator
   65 {
   66     //! The current node at each level.
   67     struct DirEntryStruct *Dir;
   68     //! Length of the path up to that level.
   69     int PathLength;
   70 };
   71 
   72 /*!
   73  * \brief Iterator of the file list.
   74  */
   75 struct _FileListIterator
   76 {
   77     //! File list object from which we are iterating.
   78     FileListObject Parent;
   79     //! Current path being stored in the object.
   80     char *CurrentPath;
   81     //! Number of bytes allocated for the current path.
   82     int CurrentPathSize;
   83     //! Level known to be stored in the path.
   84     int CurrentPathLevel;
   85     //! Tree depth when the iteration started.
   86     int TreeDepth;
   87     //! Current level being iterated over.
   88     int Level;
   89     //! The current node at each level.
   90     struct DirEntryIterator *DirNodes;
   91 #ifdef HAVE_GLOB_H
   92     //! Next globbed file to return
   93     int NextGlob;
   94     //! Buffer with the globbed files.
   95     glob_t Glob;
   96 #endif
   97 };
   98 
   99 
  100 /*!
  101  * Create an object to store the files to process.
  102  *
  103  * \return The created object or NULL if it failed.
  104  * The object must be destroyed with a call to FileList_Destroy().
  105  */
  106 FileListObject FileList_Create(void)
  107 {
  108     FileListObject FObj;
  109 
  110     FObj=(FileListObject)calloc(1,sizeof(*FObj));
  111     if (!FObj)
  112         return(NULL);
  113 
  114     return(FObj);
  115 }
  116 
  117 /*!
  118  * Destroy the entries tree.
  119  */
  120 static void FileList_DestroyEntry(struct DirEntryStruct *Entry)
  121 {
  122     struct DirEntryStruct *Next;
  123 
  124     while (Entry)
  125     {
  126         if (Entry->Child)
  127             FileList_DestroyEntry(Entry->Child);
  128         Next=Entry->Sibbling;
  129         free(Entry);
  130         Entry=Next;
  131     }
  132 }
  133 
  134 /*!
  135  * Destroy the object created by FileList_Create().
  136  *
  137  * \param FPtr A pointer to the object to destroy. It is
  138  * reset to NULL before the function returns.
  139  */
  140 void FileList_Destroy(FileListObject *FPtr)
  141 {
  142     FileListObject FObj;
  143 
  144     if (!FPtr || !*FPtr) return;
  145     FObj=*FPtr;
  146     *FPtr=NULL;
  147 
  148     FileList_DestroyEntry(FObj->First);
  149     StringBuffer_Destroy(&FObj->Buffer);
  150     free(FObj);
  151 }
  152 
  153 /*!
  154  * Store an entry in the tree.
  155  *
  156  * \param FObj The file list object created by FileList_Create().
  157  * \param FileName Name of the file to store recursively.
  158  *
  159  * \return The branch created with all the entries in \c FileName.
  160  * The returned value is NULL if \c FileName could not be added.
  161  */
  162 static struct DirEntryStruct *FileList_StoreEntry(FileListObject FObj,const char *FileName)
  163 {
  164     struct DirEntryStruct *Entry;
  165     int i;
  166     bool IsMask=false;
  167     int LastDir=-1;
  168     int Next=-1;
  169 
  170     Entry=(struct DirEntryStruct *)calloc(1,sizeof(*Entry));
  171     if (!Entry) return(NULL);
  172     for (i=0 ; FileName[i] ; i++)
  173     {
  174         if (FileName[i]=='/')
  175         {
  176             if (IsMask)
  177             {
  178                 /* The path contains a wildcard. There are no directories
  179                  * before this path or it would have been caught by the other
  180                  * break in this loop. We store it.
  181                  */
  182                 Next=i;
  183                 break;
  184             }
  185             LastDir=i;
  186         }
  187         else if (FileName[i]=='*' || FileName[i]=='?')
  188         {
  189             if (LastDir>=0)
  190             {
  191                 /* Some directories without wildcards before this directory
  192                  * with wildcard. We store the previous directories in one
  193                  * entry and disregard, for now, the current path level.
  194                  */
  195                 Next=LastDir;
  196                 break;
  197             }
  198             IsMask=true;
  199         }
  200     }
  201     Entry->Name=StringBuffer_StoreLength(FObj->Buffer,FileName,(Next<0) ? i : Next);
  202     if (!Entry->Name)
  203     {
  204         free(Entry);
  205         return(NULL);
  206     }
  207     Entry->IsMask=IsMask;
  208     if (Next>0)
  209     {
  210         FObj->TreeDepthOk=false; //it will have to be recomputed
  211         Entry->Child=FileList_StoreEntry(FObj,FileName+Next+1);
  212         if (!Entry->Child)
  213         {
  214             free(Entry);
  215             return(NULL);
  216         }
  217     }
  218     return(Entry);
  219 }
  220 
  221 /*!
  222  * Store a file in the internal data structure.
  223  *
  224  * \param FObj The file list object created by FileList_Create().
  225  * \param EntryPtr Pointer to the tree node to add or create.
  226  * \param FileName The name of the file.
  227  *
  228  * \return \c True on success or \c false on failure.
  229  */
  230 static bool FileList_AddFileRecursive(FileListObject FObj,struct DirEntryStruct **EntryPtr,const char *FileName)
  231 {
  232     int i;
  233     struct DirEntryStruct *Entry;
  234     struct DirEntryStruct *Last;
  235     int LastDir;
  236 
  237     if (!*EntryPtr)
  238     {
  239         Entry=FileList_StoreEntry(FObj,FileName);
  240         if (!Entry) return(false);
  241         *EntryPtr=Entry;
  242         return(true);
  243     }
  244 
  245     // find where to store the file name in the existing tree
  246     Last=NULL;
  247     for (Entry=*EntryPtr ; Entry ; Entry=Entry->Sibbling)
  248     {
  249         LastDir=-1;
  250         for (i=0 ; Entry->Name[i] && FileName[i] && Entry->Name[i]==FileName[i] ; i++)
  251         {
  252             if (FileName[i]=='/')
  253                 LastDir=i;
  254         }
  255         if (FileName[i]=='/' && Entry->Name[i]=='\0')
  256         {
  257             //root is matching, check sub level
  258             return(FileList_AddFileRecursive(FObj,&Entry->Child,FileName+i+1));
  259         }
  260         if (LastDir>0)
  261         {
  262             //paths begin with the same directory but diverges at LastDir
  263             struct DirEntryStruct *Split;
  264 
  265             Split=(struct DirEntryStruct *)calloc(1,sizeof(*Split));
  266             if (!Split) return(false);
  267             Split->Name=Entry->Name+LastDir+1;
  268             Split->Child=Entry->Child;
  269             Entry->Name[LastDir]='\0';
  270             Entry->Child=Split;
  271             return(FileList_AddFileRecursive(FObj,&Entry->Child,FileName+LastDir+1));
  272         }
  273         Last=Entry;
  274     }
  275 
  276     // add a new entry
  277     Entry=FileList_StoreEntry(FObj,FileName);
  278     if (!Entry) return(false);
  279     Last->Sibbling=Entry;
  280 
  281     return(true);
  282 }
  283 
  284 /*!
  285  * Add a file to the object.
  286  *
  287  * \param FObj The object created by FileList_Create().
  288  * \param FileName The file name to add to the list.
  289  *
  290  * \return \c True if the file was added or \c false if it
  291  * failed. The function may fail if a parameter is invalid.
  292  * It will also fail if the memory cannot be allocated.
  293  */
  294 bool FileList_AddFile(FileListObject FObj,const char *FileName)
  295 {
  296     if (!FObj || !FileName) return(false);
  297 
  298     if (!FObj->Buffer)
  299     {
  300         FObj->Buffer=StringBuffer_Create();
  301         if (!FObj->Buffer)
  302             return(false);
  303     }
  304 
  305     return(FileList_AddFileRecursive(FObj,&FObj->First,FileName));
  306 }
  307 
  308 /*!
  309  * \brief Is the file list empty?
  310  *
  311  * \param FObj The file list to check.
  312  *
  313  * \return \c True if the file list is empty or \c false if
  314  * there is at least one file in the list.
  315  */
  316 bool FileList_IsEmpty(FileListObject FObj)
  317 {
  318     if (!FObj) return(true);
  319     if (FObj->First==NULL) return(true);
  320     return(false);
  321 }
  322 
  323 /*!
  324  * Recursively measure the tree depth.
  325  *
  326  * \param FObj File list object created by FileList_Create().
  327  * \param Entry Node whose child are to be processed.
  328  * \param Level Current level.
  329  */
  330 static void FileList_SetDepth(FileListObject FObj,struct DirEntryStruct *Entry,int Level)
  331 {
  332     if (Level>FObj->TreeDepth) FObj->TreeDepth=Level;
  333     while (Entry)
  334     {
  335         if (Entry->Child)
  336             FileList_SetDepth(FObj,Entry->Child,Level+1);
  337         Entry=Entry->Sibbling;
  338     }
  339 }
  340 
  341 /*!
  342  * Start the iteration over the files in the list.
  343  *
  344  * \param FObj The object to iterate over.
  345  *
  346  * \return The iterator structure to pass ot FileListIter_Next()
  347  * to get the first file name or NULL if an error occured.
  348  */
  349 FileListIterator FileListIter_Open(FileListObject FObj)
  350 {
  351     struct _FileListIterator *FIter;
  352     struct DirEntryStruct *Dir;
  353 
  354     if (!FObj) return(NULL);
  355     FIter=(FileListIterator)calloc(1,sizeof(*FIter));
  356     if (!FIter) return(NULL);
  357     FIter->Parent=FObj;
  358 
  359     // compute the depth of the tree.
  360     /*
  361      * The tree depth computation is not thread safe. A lock is necessary around
  362      * the following code to make it thread safe.
  363      */
  364     if (!FObj->TreeDepthOk)
  365     {
  366         FObj->TreeDepth=0;
  367         if (FObj->First) FileList_SetDepth(FObj,FObj->First,1);
  368         FObj->TreeDepthOk=true;
  369     }
  370     FIter->TreeDepth=FObj->TreeDepth;
  371     FIter->Level=-1;
  372     FIter->CurrentPathSize=0;
  373     FIter->CurrentPathLevel=0;
  374     if (FIter->TreeDepth>0)
  375     {
  376         FIter->DirNodes=(struct DirEntryIterator *)calloc(FIter->TreeDepth,sizeof(struct DirEntryIterator));
  377         if (!FIter->DirNodes)
  378         {
  379             FileListIter_Close(FIter);
  380             return(NULL);
  381         }
  382         for (Dir=FObj->First ; Dir ; Dir=Dir->Child)
  383         {
  384             FIter->DirNodes[++FIter->Level].Dir=Dir;
  385         }
  386     }
  387 
  388     return(FIter);
  389 }
  390 
  391 /*!
  392  * Get the next entry in the directory tree.
  393  */
  394 static void FileListIter_GetNext(struct _FileListIterator *FIter)
  395 {
  396     struct DirEntryStruct *Dir;
  397 
  398     FIter->CurrentPathLevel=0;
  399     while (FIter->Level>=0)
  400     {
  401         Dir=FIter->DirNodes[FIter->Level].Dir;
  402         if (Dir->Sibbling)
  403         {
  404             Dir=Dir->Sibbling;
  405             FIter->DirNodes[FIter->Level].Dir=Dir;
  406             FIter->CurrentPathLevel=FIter->Level;
  407             while (Dir->Child)
  408             {
  409                 if (FIter->Level>=FIter->TreeDepth) break;
  410                 Dir=Dir->Child;
  411                 FIter->DirNodes[++FIter->Level].Dir=Dir;
  412             }
  413             break;
  414         }
  415         FIter->Level--;
  416     }
  417 }
  418 
  419 /*!
  420  * Get the next file in the list.
  421  *
  422  * \param FIter The iterator created by FileListIter_Open().
  423  *
  424  * \return The iterator function containing the next file name or NULL
  425  * if there are no more files.
  426  */
  427 const char *FileListIter_Next(struct _FileListIterator *FIter)
  428 {
  429     const char *Path;
  430 
  431 #ifdef HAVE_GLOB_H
  432     if (FIter->NextGlob>0)
  433     {
  434         if (FIter->NextGlob<FIter->Glob.gl_pathc)
  435         {
  436             Path=FIter->Glob.gl_pathv[FIter->NextGlob++];
  437             return(Path);
  438         }
  439         globfree(&FIter->Glob);
  440         FIter->NextGlob=0;
  441     }
  442     Path=FileListIter_NextWithMask(FIter);
  443     if (Path!=NULL && (Path[0]!='-' || Path[1]!='\0'))
  444     {
  445         int ErrCode=glob(Path,GLOB_ERR | GLOB_NOSORT,NULL,&FIter->Glob);
  446         if (ErrCode!=0)
  447         {
  448             switch (ErrCode)
  449             {
  450             case GLOB_NOSPACE:
  451                 debuga(__FILE__,__LINE__,_("Not enough memory to read the files matching \"%s\"\n"),Path);
  452                 break;
  453             case GLOB_ABORTED:
  454                 debuga(__FILE__,__LINE__,_("Read error while listing the files matching \"%s\"\n"),Path);
  455                 break;
  456             case GLOB_NOMATCH:
  457                 debuga(__FILE__,__LINE__,_("No files matching \"%s\"\n"),Path);
  458                 break;
  459             default:
  460                 debuga(__FILE__,__LINE__,_("Failed to glob file pattern \"%s\" with unspecified error code %d"),Path,ErrCode);
  461                 break;
  462             }
  463             exit(EXIT_FAILURE);
  464         }
  465         Path=FIter->Glob.gl_pathv[0];
  466         FIter->NextGlob=1;
  467     }
  468 #else
  469     /*
  470      * Fall back to a simple enumeration. In that case, the user cannot use
  471      * wildcards as they won't be expended.
  472      */
  473     Path=FileListIter_NextWithMask(FIter);
  474 #endif
  475     return(Path);
  476 }
  477 
  478 /*!
  479  * Get the next file entry in the list without expanding the
  480  * wildcards.
  481  *
  482  * \param FIter The iterator created by FileListIter_Open().
  483  *
  484  * \return The iterator function containing the next file name or NULL
  485  * if there are no more files.
  486  */
  487 const char *FileListIter_NextWithMask(struct _FileListIterator *FIter)
  488 {
  489     int Length;
  490     int Level;
  491     struct DirEntryIterator *DIter;
  492 
  493     if (!FIter) return(NULL);
  494     if (!FIter->DirNodes) return(NULL);
  495     if (FIter->Level<0 || FIter->Level>=FIter->TreeDepth) return(NULL);
  496 
  497     // how much space to store the path
  498     Length=FIter->DirNodes[FIter->CurrentPathLevel].PathLength;
  499     for (Level=FIter->CurrentPathLevel ; Level<=FIter->Level ; Level++)
  500     {
  501         DIter=FIter->DirNodes+Level;
  502         DIter->PathLength=Length;
  503         Length+=strlen(DIter->Dir->Name)+1;
  504     }
  505 
  506     // get the memory to store the path
  507     if (Length>FIter->CurrentPathSize)
  508     {
  509         char *temp=realloc(FIter->CurrentPath,Length);
  510         if (!temp) return(NULL);
  511         FIter->CurrentPath=temp;
  512         FIter->CurrentPathSize=Length;
  513     }
  514 
  515     for (Level=FIter->CurrentPathLevel ; Level<=FIter->Level ; Level++)
  516     {
  517         DIter=FIter->DirNodes+Level;
  518         if (Level>0) FIter->CurrentPath[DIter->PathLength-1]='/';
  519         strcpy(FIter->CurrentPath+DIter->PathLength,DIter->Dir->Name);
  520     }
  521     FIter->CurrentPathLevel=Level;
  522 
  523     FileListIter_GetNext(FIter);
  524     return(FIter->CurrentPath);
  525 }
  526 
  527 /*!
  528  * Destroy the iterator created by FileListIter_Open().
  529  */
  530 void FileListIter_Close(struct _FileListIterator *FIter)
  531 {
  532     if (FIter)
  533     {
  534 #ifdef HAVE_GLOB_H
  535         if (FIter->NextGlob>0) globfree(&FIter->Glob);
  536 #endif
  537         if (FIter->CurrentPath) free(FIter->CurrentPath);
  538         if (FIter->DirNodes) free(FIter->DirNodes);
  539         free(FIter);
  540     }
  541 }