libarmutils  1.4
 All Data Structures Files Functions Variables Typedefs Enumerations Macros Groups
dir_utils.c
Go to the documentation of this file.
1 /*******************************************************************************
2 *
3 * COPYRIGHT (C) 2010 Battelle Memorial Institute. All Rights Reserved.
4 *
5 ********************************************************************************
6 *
7 * Author:
8 * name: Brian Ermold
9 * phone: (509) 375-2277
10 * email: brian.ermold@pnl.gov
11 *
12 ********************************************************************************
13 *
14 * REPOSITORY INFORMATION:
15 * $Revision: 6689 $
16 * $Author: ermold $
17 * $Date: 2011-05-16 19:14:17 +0000 (Mon, 16 May 2011) $
18 *
19 ********************************************************************************
20 *
21 * NOTE: DOXYGEN is used to generate documentation for this file.
22 *
23 *******************************************************************************/
24 
25 /** @file dir_utils.c
26  * Directory Utilities
27  */
28 
29 #include "armutils.h"
30 
31 /*******************************************************************************
32  * Private Functions
33  */
34 /** @privatesection */
35 
36 /*******************************************************************************
37  * Public Functions
38  */
39 /** @publicsection */
40 
41 /**
42  * Free all memory used by a directory list.
43  *
44  * @param dirlist - pointer to the DirList.
45  */
46 void dirlist_free(DirList *dirlist)
47 {
48  int fi;
49 
50  if (dirlist) {
51 
52  if (dirlist->path) free(dirlist->path);
53  if (dirlist->patterns) relist_free(dirlist->patterns);
54 
55  if (dirlist->file_list) {
56  for (fi = 0; fi < dirlist->nfiles; fi++) {
57  free(dirlist->file_list[fi]);
58  }
59 
60  free(dirlist->file_list);
61  }
62 
63  free(dirlist);
64  }
65 }
66 
67 /**
68  * Create a new directory list.
69  *
70  * Error messages from this function are sent to the message handler
71  * (see msngr_init_log() and msngr_init_mail()).
72  *
73  * @param path - full path to the directory
74  * @param flags - control flags
75  *
76  * <b>Control Flags</b>
77  *
78  * - DL_SHOW_DOT_FILES =
79  * Incude files starting with '.' in the file list. Note: the '.' and
80  * '..' directories will always be excluded from the file list.
81  *
82  * @return
83  * - pointer to the new DirList
84  * - NULL if an error occurred
85  */
87  const char *path,
88  int flags)
89 {
90  DirList *dirlist;
91 
92  /* Create the new DirList */
93 
94  dirlist = (DirList *)calloc(1, sizeof(DirList));
95  if (!dirlist) {
96 
98  "Could not create directory list for: %s\n",
99  " -> memory allocation error\n", path);
100 
101  return((DirList *)NULL);
102  }
103 
104  dirlist->flags = flags;
105  dirlist->qsort_compare = qsort_strcmp;
106  dirlist->path = strdup(path);
107 
108  if (!dirlist->path) {
109 
111  "Could not create directory list for: %s\n",
112  " -> memory allocation error\n", path);
113 
114  dirlist_free(dirlist);
115  return((DirList *)NULL);
116  }
117 
118  /* Initialize the file list */
119 
120  dirlist->nalloced = 128;
121  dirlist->nfiles = 0;
122 
123  dirlist->file_list = (char **)calloc(dirlist->nalloced, sizeof(char *));
124  if (!dirlist->file_list) {
125 
127  "Could not create directory list for: %s\n",
128  " -> memory allocation error\n", path);
129 
130  dirlist_free(dirlist);
131  return((DirList *)NULL);
132  }
133 
134  return(dirlist);
135 }
136 
137 /**
138  * Add file patterns to a directory list.
139  *
140  * Error messages from this function are sent to the message handler
141  * (see msngr_init_log() and msngr_init_mail()).
142  *
143  * @param dirlist - pointer to the DirList
144  * @param npatterns - number of file patterns
145  * @param patterns - list of extended regex file patterns (man regcomp)
146  * @param ignore_case - ingnore case in file patterns
147  *
148  * @return
149  * - 1 if succesful
150  * - 0 if an error occurred
151  */
153  DirList *dirlist,
154  int npatterns,
155  const char **patterns,
156  int ignore_case)
157 {
158  REList *new_list;
159  int cflags;
160 
161  if (!npatterns || !patterns) {
162  return(1);
163  }
164 
165  cflags = REG_EXTENDED | REG_NOSUB;
166 
167  if (ignore_case) {
168  cflags |= REG_ICASE;
169  }
170 
171  new_list = relist_compile(
172  dirlist->patterns, npatterns, patterns, cflags);
173 
174  if (!new_list) {
175 
177  "Could not add file patterns for directory: %s\n",
178  " -> regular expression error\n",
179  dirlist->path);
180 
181  return(0);
182  }
183 
184  dirlist->patterns = new_list;
185  dirlist->stats.st_mtime = 0;
186 
187  return(1);
188 }
189 
190 /**
191  * Get the list of files in a directory.
192  *
193  * By default the returned list will be sorted alphanumerically using
194  * the qsort_strcmp() function. A different file name compare function
195  * can be set using dirlist_set_qsort_compare().
196  *
197  * The memory used by the returned file list belongs to the DirList
198  * structure and must *not* be freed by the calling process.
199  *
200  * Error messages from this function are sent to the message handler
201  * (see msngr_init_log() and msngr_init_mail()).
202  *
203  * @param dirlist - pointer to the DirList
204  * @param file_list - output: pointer to the file list
205  *
206  * @return
207  * - number of files
208  * - -1 if an error occurred
209  */
210 int dirlist_get_file_list(DirList *dirlist, char ***file_list)
211 {
212  struct stat dir_stats;
213  DIR *dirp;
214  struct dirent *direntp;
215  char **new_list;
216  size_t new_size;
217  int status;
218  int fi;
219 
220  /* Initialize output */
221 
222  *file_list = (char **)NULL;
223 
224  /* Check to see if the directory exists */
225 
226  if (access(dirlist->path, F_OK) != 0) {
227 
228  if (errno == ENOENT) {
229  return(0);
230  }
231  else {
232 
234  "Could not access directory: %s\n",
235  " -> %s\n", dirlist->path, strerror(errno));
236 
237  return(-1);
238  }
239  }
240 
241  /* Check if the directory has been modified */
242 
243  if (stat(dirlist->path, &dir_stats) != 0 ) {
244 
246  "Could not stat directory: %s\n",
247  " -> %s\n", dirlist->path, strerror(errno));
248 
249  return(-1);
250  }
251 
252  if (dirlist->stats.st_mtime == dir_stats.st_mtime) {
253  dirlist->stats = dir_stats;
254  *file_list = dirlist->file_list;
255  return(dirlist->nfiles);
256  }
257 
258  /* Free memory used by the file names in the previous list */
259 
260  for (fi = 0; fi < dirlist->nfiles; fi++) {
261  free(dirlist->file_list[fi]);
262  dirlist->file_list[fi] = (char *)NULL;
263  }
264 
265  dirlist->nfiles = 0;
266 
267  /* Open the directory */
268 
269  dirp = opendir(dirlist->path);
270  if (!dirp) {
271 
273  "Could not open directory: %s\n",
274  " -> %s\n", dirlist->path, strerror(errno));
275 
276  return(-1);
277  }
278 
279  /* Loop over directory entries */
280 
281  dirlist->nfiles = 0;
282 
283  for (;;) {
284 
285  /* Get the next directory entry */
286 
287  errno = 0;
288  if (!(direntp = readdir(dirp))) {
289 
290  if (errno) {
291 
293  "Could not read directory: %s\n",
294  " -> %s\n", dirlist->path, strerror(errno));
295 
296  closedir(dirp);
297  return(-1);
298  }
299 
300  break;
301  }
302 
303  /* Skip dot files unless the DL_SHOW_DOT_FILES flag is set.
304  * Always skip the . and .. directories */
305 
306  if (direntp->d_name[0] == '.') {
307 
308  if (!(dirlist->flags & DL_SHOW_DOT_FILES)) {
309  continue;
310  }
311 
312  if ((direntp->d_name[1] == '\0') ||
313  (direntp->d_name[1] == '.' && direntp->d_name[2] == '\0')) {
314 
315  continue;
316  }
317  }
318 
319  /* Check if this file matches one of the specified patterns */
320 
321  if (dirlist->patterns) {
322 
323  status = relist_execute(
324  dirlist->patterns, direntp->d_name, 0, NULL, NULL, NULL, NULL);
325 
326  if (status == 0) {
327  continue;
328  }
329 
330  if (status < 0) {
331 
333  "Could not get file list for directory: %s\n",
334  " -> regular expression error\n",
335  dirlist->path);
336 
337  closedir(dirp);
338  return(-1);
339  }
340  }
341 
342  /* Check if we need to increase the length of the file list */
343 
344  if (dirlist->nfiles == dirlist->nalloced - 1) {
345 
346  new_size = dirlist->nalloced * 2;
347  new_list = (char **)realloc(
348  dirlist->file_list, new_size * sizeof(char *));
349 
350  if (!new_list) {
351 
353  "Could not get file list for directory: %s\n",
354  " -> memory allocation error\n",
355  dirlist->path);
356 
357  closedir(dirp);
358  return(-1);
359  }
360 
361  dirlist->nalloced = new_size;
362  dirlist->file_list = new_list;
363 
364  for (fi = dirlist->nfiles; fi < dirlist->nalloced; fi++) {
365  dirlist->file_list[fi] = (char *)NULL;
366  }
367  }
368 
369  /* Add this file to the list */
370 
371  dirlist->file_list[dirlist->nfiles] = strdup(direntp->d_name);
372 
373  if (!dirlist->file_list[dirlist->nfiles]) {
374 
376  "Could not get file list for directory: %s\n",
377  " -> memory allocation error\n",
378  dirlist->path);
379 
380  closedir(dirp);
381  return(-1);
382  }
383 
384  dirlist->nfiles++;
385  }
386 
387  closedir(dirp);
388 
389  dirlist->stats = dir_stats;
390  *file_list = dirlist->file_list;
391 
392  /* Check if any files were found */
393 
394  if (dirlist->nfiles) {
395 
396  dirlist->file_list[dirlist->nfiles] = (char *)NULL;
397 
398  /* Sort the file list */
399 
400  if (dirlist->qsort_compare) {
401 
402  qsort(dirlist->file_list, dirlist->nfiles, sizeof(char *),
403  dirlist->qsort_compare);
404  }
405  }
406 
407  return(dirlist->nfiles);
408 }
409 
410 /**
411  * Set the file name compare function.
412  *
413  * The file name compare function will be passed to qsort to sort the
414  * file list. By default the qsort_strcmp() function will be used. The
415  * qsort_numeric_strcmp() function is also provided in the "String Utils"
416  * module.
417  *
418  * @param dirlist - pointer to the DirList
419  * @param qsort_compare - file name compare function
420  */
422  DirList *dirlist,
423  int (*qsort_compare)(const void *, const void *))
424 {
425  dirlist->qsort_compare = qsort_compare;
426  dirlist->stats.st_mtime = 0;
427 }
428 
429 /**
430  * Make the full path to a directory.
431  *
432  * This function will create the specified path if it does not already exist.
433  *
434  * Error messages from this function are sent to the message handler
435  * (see msngr_init_log() and msngr_init_mail()).
436  *
437  * @param path - directory path to create
438  * @param mode - mode of the new directory
439  *
440  * @return
441  * - 1 if the path exists or was created
442  * - 0 if an error occurred
443  */
445  const char *path,
446  mode_t mode)
447 {
448  char errstr[MAX_LOG_ERROR];
449  int status;
450 
451  status = msngr_make_path(path, mode, MAX_LOG_ERROR, errstr);
452 
453  if (!status) {
454  ERROR( ARMUTILS_LIB_NAME, errstr);
455  }
456 
457  return(status);
458 }