2 * SARG Squid Analysis Report Generator http://sarg.sourceforge.net
6 * please look at http://sarg.sourceforge.net/donations.php
8 * http://sourceforge.net/projects/sarg/forums/forum/363374
9 * ---------------------------------------------------------------------
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.
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.
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.
27 #include "include/conf.h"
28 #include "include/defs.h"
29 #include "include/stringbuffer.h"
30 #include "include/filelist.h"
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.
43 //! \c True if it contains any wildcard.
48 * \brief List of files.
50 * The list may contain wildcards.
55 struct DirEntryStruct
*First
;
56 //! Buffer containing the file name strings.
57 StringBufferObject Buffer
;
58 //! Deepest level of the tree.
60 //! \c True if the tree depth is correct.
64 struct DirEntryIterator
66 //! The current node at each level.
67 struct DirEntryStruct
*Dir
;
68 //! Length of the path up to that level.
73 * \brief Iterator of the file list.
75 struct _FileListIterator
77 //! File list object from which we are iterating.
78 FileListObject Parent
;
79 //! Current path being stored in the object.
81 //! Number of bytes allocated for the current path.
83 //! Level known to be stored in the path.
85 //! Tree depth when the iteration started.
87 //! Current level being iterated over.
89 //! The current node at each level.
90 struct DirEntryIterator
*DirNodes
;
92 //! Next globbed file to return
94 //! Buffer with the globbed files.
101 * Create an object to store the files to process.
103 * \return The created object or NULL if it failed.
104 * The object must be destroyed with a call to FileList_Destroy().
106 FileListObject
FileList_Create(void)
110 FObj
=(FileListObject
)calloc(1,sizeof(*FObj
));
118 * Destroy the entries tree.
120 static void FileList_DestroyEntry(struct DirEntryStruct
*Entry
)
122 struct DirEntryStruct
*Next
;
127 FileList_DestroyEntry(Entry
->Child
);
128 Next
=Entry
->Sibbling
;
135 * Destroy the object created by FileList_Create().
137 * \param FPtr A pointer to the object to destroy. It is
138 * reset to NULL before the function returns.
140 void FileList_Destroy(FileListObject
*FPtr
)
144 if (!FPtr
|| !*FPtr
) return;
148 FileList_DestroyEntry(FObj
->First
);
149 StringBuffer_Destroy(&FObj
->Buffer
);
154 * Store an entry in the tree.
156 * \param FObj The file list object created by FileList_Create().
157 * \param FileName Name of the file to store recursively.
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.
162 static struct DirEntryStruct
*FileList_StoreEntry(FileListObject FObj
,const char *FileName
)
164 struct DirEntryStruct
*Entry
;
170 Entry
=(struct DirEntryStruct
*)calloc(1,sizeof(*Entry
));
171 if (!Entry
) return(NULL
);
172 for (i
=0 ; FileName
[i
] ; i
++)
174 if (FileName
[i
]=='/')
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.
187 else if (FileName
[i
]=='*' || FileName
[i
]=='?')
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.
201 Entry
->Name
=StringBuffer_StoreLength(FObj
->Buffer
,FileName
,(Next
<0) ? i
: Next
);
207 Entry
->IsMask
=IsMask
;
210 FObj
->TreeDepthOk
=false; //it will have to be recomputed
211 Entry
->Child
=FileList_StoreEntry(FObj
,FileName
+Next
+1);
222 * Store a file in the internal data structure.
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.
228 * \return \c True on success or \c false on failure.
230 static bool FileList_AddFileRecursive(FileListObject FObj
,struct DirEntryStruct
**EntryPtr
,const char *FileName
)
233 struct DirEntryStruct
*Entry
;
234 struct DirEntryStruct
*Last
;
239 Entry
=FileList_StoreEntry(FObj
,FileName
);
240 if (!Entry
) return(false);
245 // find where to store the file name in the existing tree
247 for (Entry
=*EntryPtr
; Entry
; Entry
=Entry
->Sibbling
)
250 for (i
=0 ; Entry
->Name
[i
] && FileName
[i
] && Entry
->Name
[i
]==FileName
[i
] ; i
++)
252 if (FileName
[i
]=='/')
255 if (FileName
[i
]=='/' && Entry
->Name
[i
]=='\0')
257 //root is matching, check sub level
258 return(FileList_AddFileRecursive(FObj
,&Entry
->Child
,FileName
+i
+1));
262 //paths begin with the same directory but diverges at LastDir
263 struct DirEntryStruct
*Split
;
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';
271 return(FileList_AddFileRecursive(FObj
,&Entry
->Child
,FileName
+LastDir
+1));
277 Entry
=FileList_StoreEntry(FObj
,FileName
);
278 if (!Entry
) return(false);
279 Last
->Sibbling
=Entry
;
285 * Add a file to the object.
287 * \param FObj The object created by FileList_Create().
288 * \param FileName The file name to add to the list.
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.
294 bool FileList_AddFile(FileListObject FObj
,const char *FileName
)
296 if (!FObj
|| !FileName
) return(false);
300 FObj
->Buffer
=StringBuffer_Create();
305 return(FileList_AddFileRecursive(FObj
,&FObj
->First
,FileName
));
309 * \brief Is the file list empty?
311 * \param FObj The file list to check.
313 * \return \c True if the file list is empty or \c false if
314 * there is at least one file in the list.
316 bool FileList_IsEmpty(FileListObject FObj
)
318 if (!FObj
) return(true);
319 if (FObj
->First
==NULL
) return(true);
324 * Recursively measure the tree depth.
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.
330 static void FileList_SetDepth(FileListObject FObj
,struct DirEntryStruct
*Entry
,int Level
)
332 if (Level
>FObj
->TreeDepth
) FObj
->TreeDepth
=Level
;
336 FileList_SetDepth(FObj
,Entry
->Child
,Level
+1);
337 Entry
=Entry
->Sibbling
;
342 * Start the iteration over the files in the list.
344 * \param FObj The object to iterate over.
346 * \return The iterator structure to pass ot FileListIter_Next()
347 * to get the first file name or NULL if an error occured.
349 FileListIterator
FileListIter_Open(FileListObject FObj
)
351 struct _FileListIterator
*FIter
;
352 struct DirEntryStruct
*Dir
;
354 if (!FObj
) return(NULL
);
355 FIter
=(FileListIterator
)calloc(1,sizeof(*FIter
));
356 if (!FIter
) return(NULL
);
359 // compute the depth of the tree.
361 * The tree depth computation is not thread safe. A lock is necessary around
362 * the following code to make it thread safe.
364 if (!FObj
->TreeDepthOk
)
367 if (FObj
->First
) FileList_SetDepth(FObj
,FObj
->First
,1);
368 FObj
->TreeDepthOk
=true;
370 FIter
->TreeDepth
=FObj
->TreeDepth
;
372 FIter
->CurrentPathSize
=0;
373 FIter
->CurrentPathLevel
=0;
374 if (FIter
->TreeDepth
>0)
376 FIter
->DirNodes
=(struct DirEntryIterator
*)calloc(FIter
->TreeDepth
,sizeof(struct DirEntryIterator
));
377 if (!FIter
->DirNodes
)
379 FileListIter_Close(FIter
);
382 for (Dir
=FObj
->First
; Dir
; Dir
=Dir
->Child
)
384 FIter
->DirNodes
[++FIter
->Level
].Dir
=Dir
;
392 * Get the next entry in the directory tree.
394 static void FileListIter_GetNext(struct _FileListIterator
*FIter
)
396 struct DirEntryStruct
*Dir
;
398 FIter
->CurrentPathLevel
=0;
399 while (FIter
->Level
>=0)
401 Dir
=FIter
->DirNodes
[FIter
->Level
].Dir
;
405 FIter
->DirNodes
[FIter
->Level
].Dir
=Dir
;
406 FIter
->CurrentPathLevel
=FIter
->Level
;
409 if (FIter
->Level
>=FIter
->TreeDepth
) break;
411 FIter
->DirNodes
[++FIter
->Level
].Dir
=Dir
;
420 * Get the next file in the list.
422 * \param FIter The iterator created by FileListIter_Open().
424 * \return The iterator function containing the next file name or NULL
425 * if there are no more files.
427 const char *FileListIter_Next(struct _FileListIterator
*FIter
)
432 if (FIter
->NextGlob
>0)
434 if (FIter
->NextGlob
<FIter
->Glob
.gl_pathc
)
436 Path
=FIter
->Glob
.gl_pathv
[FIter
->NextGlob
++];
439 globfree(&FIter
->Glob
);
442 Path
=FileListIter_NextWithMask(FIter
);
443 if (Path
!=NULL
&& (Path
[0]!='-' || Path
[1]!='\0'))
445 int ErrCode
=glob(Path
,GLOB_ERR
| GLOB_NOSORT
,NULL
,&FIter
->Glob
);
451 debuga(__FILE__
,__LINE__
,_("Not enough memory to read the files matching \"%s\"\n"),Path
);
454 debuga(__FILE__
,__LINE__
,_("Read error while listing the files matching \"%s\"\n"),Path
);
457 debuga(__FILE__
,__LINE__
,_("No files matching \"%s\"\n"),Path
);
460 debuga(__FILE__
,__LINE__
,_("Failed to glob file pattern \"%s\" with unspecified error code %d"),Path
,ErrCode
);
465 Path
=FIter
->Glob
.gl_pathv
[0];
470 * Fall back to a simple enumeration. In that case, the user cannot use
471 * wildcards as they won't be expended.
473 Path
=FileListIter_NextWithMask(FIter
);
479 * Get the next file entry in the list without expanding the
482 * \param FIter The iterator created by FileListIter_Open().
484 * \return The iterator function containing the next file name or NULL
485 * if there are no more files.
487 const char *FileListIter_NextWithMask(struct _FileListIterator
*FIter
)
491 struct DirEntryIterator
*DIter
;
493 if (!FIter
) return(NULL
);
494 if (!FIter
->DirNodes
) return(NULL
);
495 if (FIter
->Level
<0 || FIter
->Level
>=FIter
->TreeDepth
) return(NULL
);
497 // how much space to store the path
498 Length
=FIter
->DirNodes
[FIter
->CurrentPathLevel
].PathLength
;
499 for (Level
=FIter
->CurrentPathLevel
; Level
<=FIter
->Level
; Level
++)
501 DIter
=FIter
->DirNodes
+Level
;
502 DIter
->PathLength
=Length
;
503 Length
+=strlen(DIter
->Dir
->Name
)+1;
506 // get the memory to store the path
507 if (Length
>FIter
->CurrentPathSize
)
509 char *temp
=realloc(FIter
->CurrentPath
,Length
);
510 if (!temp
) return(NULL
);
511 FIter
->CurrentPath
=temp
;
512 FIter
->CurrentPathSize
=Length
;
515 for (Level
=FIter
->CurrentPathLevel
; Level
<=FIter
->Level
; Level
++)
517 DIter
=FIter
->DirNodes
+Level
;
518 if (Level
>0) FIter
->CurrentPath
[DIter
->PathLength
-1]='/';
519 strcpy(FIter
->CurrentPath
+DIter
->PathLength
,DIter
->Dir
->Name
);
521 FIter
->CurrentPathLevel
=Level
;
523 FileListIter_GetNext(FIter
);
524 return(FIter
->CurrentPath
);
528 * Destroy the iterator created by FileListIter_Open().
530 void FileListIter_Close(struct _FileListIterator
*FIter
)
535 if (FIter
->NextGlob
>0) globfree(&FIter
->Glob
);
537 if (FIter
->CurrentPath
) free(FIter
->CurrentPath
);
538 if (FIter
->DirNodes
) free(FIter
->DirNodes
);