]>
Commit | Line | Data |
---|---|---|
6068ae56 FM |
1 | /* |
2 | * SARG Squid Analysis Report Generator http://sarg.sourceforge.net | |
110ce984 | 3 | * 1998, 2015 |
6068ae56 FM |
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" | |
2b41f02c FM |
31 | #ifdef HAVE_GLOB_H |
32 | #include <glob.h> | |
33 | #endif | |
6068ae56 FM |
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; | |
2b41f02c FM |
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 | |
6068ae56 FM |
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 | ||
137eb63d FM |
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 | ||
6068ae56 FM |
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 | ||
137eb63d | 354 | if (!FObj) return(NULL); |
6068ae56 FM |
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; | |
0691fa24 | 411 | FIter->DirNodes[++FIter->Level].Dir=Dir; |
6068ae56 FM |
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 | { | |
2b41f02c | 429 | const char *Path; |
6068ae56 | 430 | |
2b41f02c FM |
431 | #ifdef HAVE_GLOB_H |
432 | if (FIter->NextGlob>0) | |
6068ae56 | 433 | { |
2b41f02c FM |
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; | |
6068ae56 | 441 | } |
69a910ad FM |
442 | Path=FileListIter_NextWithMask(FIter); |
443 | if (Path!=NULL) | |
6068ae56 | 444 | { |
2b41f02c | 445 | int ErrCode=glob(Path,GLOB_ERR | GLOB_NOSORT,NULL,&FIter->Glob); |
69a910ad | 446 | if (ErrCode!=0) |
2b41f02c FM |
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; | |
69a910ad FM |
456 | case GLOB_NOMATCH: |
457 | debuga(__FILE__,__LINE__,_("No files matching \"%s\"\n"),Path); | |
458 | break; | |
2b41f02c FM |
459 | } |
460 | exit(EXIT_FAILURE); | |
461 | } | |
69a910ad FM |
462 | Path=FIter->Glob.gl_pathv[0]; |
463 | FIter->NextGlob=1; | |
6068ae56 | 464 | } |
2b41f02c FM |
465 | #else |
466 | /* | |
467 | * Fall back to a simple enumeration. In that case, the user cannot use | |
468 | * wildcards as they won't be expended. | |
469 | */ | |
470 | Path=FileListIter_NextWithMask(FIter); | |
471 | #endif | |
472 | return(Path); | |
6068ae56 FM |
473 | } |
474 | ||
475 | /*! | |
476 | * Get the next file entry in the list without expanding the | |
477 | * wildcards. | |
478 | * | |
479 | * \param FIter The iterator created by FileListIter_Open(). | |
480 | * | |
481 | * \return The iterator function containing the next file name or NULL | |
482 | * if there are no more files. | |
483 | */ | |
484 | const char *FileListIter_NextWithMask(struct _FileListIterator *FIter) | |
485 | { | |
486 | int Length; | |
487 | int Level; | |
488 | struct DirEntryIterator *DIter; | |
489 | ||
490 | if (!FIter) return(NULL); | |
491 | if (!FIter->DirNodes) return(NULL); | |
492 | if (FIter->Level<0 || FIter->Level>=FIter->TreeDepth) return(NULL); | |
493 | ||
494 | // how much space to store the path | |
495 | Length=FIter->DirNodes[FIter->CurrentPathLevel].PathLength; | |
496 | for (Level=FIter->CurrentPathLevel ; Level<=FIter->Level ; Level++) | |
497 | { | |
498 | DIter=FIter->DirNodes+Level; | |
2b41f02c | 499 | DIter->PathLength=Length; |
6068ae56 FM |
500 | Length+=strlen(DIter->Dir->Name)+1; |
501 | } | |
502 | ||
503 | // get the memory to store the path | |
504 | if (Length>FIter->CurrentPathSize) | |
505 | { | |
506 | char *temp=realloc(FIter->CurrentPath,Length); | |
507 | if (!temp) return(NULL); | |
508 | FIter->CurrentPath=temp; | |
509 | FIter->CurrentPathSize=Length; | |
510 | } | |
511 | ||
512 | for (Level=FIter->CurrentPathLevel ; Level<=FIter->Level ; Level++) | |
513 | { | |
514 | DIter=FIter->DirNodes+Level; | |
515 | if (Level>0) FIter->CurrentPath[DIter->PathLength-1]='/'; | |
516 | strcpy(FIter->CurrentPath+DIter->PathLength,DIter->Dir->Name); | |
517 | } | |
518 | FIter->CurrentPathLevel=Level; | |
519 | ||
520 | FileListIter_GetNext(FIter); | |
521 | return(FIter->CurrentPath); | |
522 | } | |
523 | ||
524 | /*! | |
525 | * Destroy the iterator created by FileListIter_Open(). | |
526 | */ | |
527 | void FileListIter_Close(struct _FileListIterator *FIter) | |
528 | { | |
529 | if (FIter) | |
530 | { | |
2b41f02c FM |
531 | #ifdef HAVE_GLOB_H |
532 | if (FIter->NextGlob>0) globfree(&FIter->Glob); | |
533 | #endif | |
6068ae56 FM |
534 | if (FIter->CurrentPath) free(FIter->CurrentPath); |
535 | if (FIter->DirNodes) free(FIter->DirNodes); | |
536 | free(FIter); | |
537 | } | |
538 | } |