]> git.ipfire.org Git - thirdparty/sarg.git/blame - filelist.c
Indent the configure script for more readability.
[thirdparty/sarg.git] / filelist.c
CommitLineData
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
35struct 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 */
52struct 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
64struct 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 */
75struct _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 */
106FileListObject 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 */
120static 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 */
140void 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 */
162static 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 */
230static 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 */
294bool 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 */
316bool 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 */
330static 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 */
349FileListIterator 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 */
394static 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 */
427const 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 */
484const 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 */
527void 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}