bemenu 1.0.0
Dynamic menu library and client program inspired by dmenu
tinydir.h
1 /*
2 Copyright (c) 2013-2014, Cong Xu, Baudouin Feildel
3 All rights reserved.
4 
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7 
8 1. Redistributions of source code must retain the above copyright notice, this
9  list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright notice,
11  this list of conditions and the following disclaimer in the documentation
12  and/or other materials provided with the distribution.
13 
14 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
18 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25 #ifndef TINYDIR_H
26 #define TINYDIR_H
27 
28 #include <errno.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #ifdef _MSC_VER
32 #define WIN32_LEAN_AND_MEAN
33 #include <windows.h>
34 #pragma warning (disable : 4996)
35 #else
36 #include <dirent.h>
37 #include <libgen.h>
38 #include <sys/stat.h>
39 #endif
40 
41 
42 /* types */
43 
44 #define _TINYDIR_PATH_MAX 4096
45 #ifdef _MSC_VER
46 /* extra chars for the "\\*" mask */
47 #define _TINYDIR_PATH_EXTRA 2
48 #else
49 #define _TINYDIR_PATH_EXTRA 0
50 #endif
51 #define _TINYDIR_FILENAME_MAX 256
52 
53 #ifdef _MSC_VER
54 #define _TINYDIR_FUNC static __inline
55 #else
56 #define _TINYDIR_FUNC static __inline__
57 #endif
58 
59 typedef struct
60 {
61  char path[_TINYDIR_PATH_MAX];
62  char name[_TINYDIR_FILENAME_MAX];
63  char *extension;
64  int is_dir;
65  int is_reg;
66 
67 #ifdef _MSC_VER
68 #else
69  struct stat _s;
70 #endif
71 } tinydir_file;
72 
73 typedef struct
74 {
75  char path[_TINYDIR_PATH_MAX];
76  int has_next;
77  size_t n_files;
78 
79  tinydir_file *_files;
80 #ifdef _MSC_VER
81  HANDLE _h;
82  WIN32_FIND_DATA _f;
83 #else
84  DIR *_d;
85  struct dirent *_e;
86 #endif
87 } tinydir_dir;
88 
89 
90 /* declarations */
91 
92 _TINYDIR_FUNC
93 int tinydir_open(tinydir_dir *dir, const char *path);
94 _TINYDIR_FUNC
95 int tinydir_open_sorted(tinydir_dir *dir, const char *path);
96 _TINYDIR_FUNC
97 void tinydir_close(tinydir_dir *dir);
98 
99 _TINYDIR_FUNC
100 int tinydir_next(tinydir_dir *dir);
101 _TINYDIR_FUNC
102 int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file);
103 _TINYDIR_FUNC
104 int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i);
105 _TINYDIR_FUNC
106 int tinydir_open_subdir_n(tinydir_dir *dir, size_t i);
107 
108 _TINYDIR_FUNC
109 void _tinydir_get_ext(tinydir_file *file);
110 _TINYDIR_FUNC
111 int _tinydir_file_cmp(const void *a, const void *b);
112 
113 
114 /* definitions*/
115 
116 _TINYDIR_FUNC
117 int tinydir_open(tinydir_dir *dir, const char *path)
118 {
119  if (dir == NULL || path == NULL || strlen(path) == 0)
120  {
121  errno = EINVAL;
122  return -1;
123  }
124  if (strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX)
125  {
126  errno = ENAMETOOLONG;
127  return -1;
128  }
129 
130  /* initialise dir */
131  dir->_files = NULL;
132 #ifdef _MSC_VER
133  dir->_h = INVALID_HANDLE_VALUE;
134 #else
135  dir->_d = NULL;
136 #endif
137  tinydir_close(dir);
138 
139  strcpy(dir->path, path);
140 #ifdef _MSC_VER
141  strcat(dir->path, "\\*");
142  dir->_h = FindFirstFile(dir->path, &dir->_f);
143  dir->path[strlen(dir->path) - 2] = '\0';
144  if (dir->_h == INVALID_HANDLE_VALUE)
145 #else
146  dir->_d = opendir(path);
147  if (dir->_d == NULL)
148 #endif
149  {
150  errno = ENOENT;
151  goto bail;
152  }
153 
154  /* read first file */
155  dir->has_next = 1;
156 #ifndef _MSC_VER
157  dir->_e = readdir(dir->_d);
158  if (dir->_e == NULL)
159  {
160  dir->has_next = 0;
161  }
162 #endif
163 
164  return 0;
165 
166 bail:
167  tinydir_close(dir);
168  return -1;
169 }
170 
171 _TINYDIR_FUNC
172 int tinydir_open_sorted(tinydir_dir *dir, const char *path)
173 {
174  /* Count the number of files first, to pre-allocate the files array */
175  size_t n_files = 0;
176  if (tinydir_open(dir, path) == -1)
177  {
178  return -1;
179  }
180  while (dir->has_next)
181  {
182  n_files++;
183  if (tinydir_next(dir) == -1)
184  {
185  goto bail;
186  }
187  }
188  tinydir_close(dir);
189 
190  if (tinydir_open(dir, path) == -1)
191  {
192  return -1;
193  }
194 
195  dir->n_files = 0;
196  dir->_files = (tinydir_file *)malloc(sizeof *dir->_files * n_files);
197  if (dir->_files == NULL)
198  {
199  errno = ENOMEM;
200  goto bail;
201  }
202  while (dir->has_next)
203  {
204  tinydir_file *p_file;
205  dir->n_files++;
206 
207  p_file = &dir->_files[dir->n_files - 1];
208  if (tinydir_readfile(dir, p_file) == -1)
209  {
210  goto bail;
211  }
212 
213  if (tinydir_next(dir) == -1)
214  {
215  goto bail;
216  }
217 
218  /* Just in case the number of files has changed between the first and
219  second reads, terminate without writing into unallocated memory */
220  if (dir->n_files == n_files)
221  {
222  break;
223  }
224  }
225 
226  qsort(dir->_files, dir->n_files, sizeof(tinydir_file), _tinydir_file_cmp);
227 
228  return 0;
229 
230 bail:
231  tinydir_close(dir);
232  return -1;
233 }
234 
235 _TINYDIR_FUNC
236 void tinydir_close(tinydir_dir *dir)
237 {
238  if (dir == NULL)
239  {
240  return;
241  }
242 
243  memset(dir->path, 0, sizeof(dir->path));
244  dir->has_next = 0;
245  dir->n_files = 0;
246  if (dir->_files != NULL)
247  {
248  free(dir->_files);
249  }
250  dir->_files = NULL;
251 #ifdef _MSC_VER
252  if (dir->_h != INVALID_HANDLE_VALUE)
253  {
254  FindClose(dir->_h);
255  }
256  dir->_h = INVALID_HANDLE_VALUE;
257 #else
258  if (dir->_d)
259  {
260  closedir(dir->_d);
261  }
262  dir->_d = NULL;
263  dir->_e = NULL;
264 #endif
265 }
266 
267 _TINYDIR_FUNC
268 int tinydir_next(tinydir_dir *dir)
269 {
270  if (dir == NULL)
271  {
272  errno = EINVAL;
273  return -1;
274  }
275  if (!dir->has_next)
276  {
277  errno = ENOENT;
278  return -1;
279  }
280 
281 #ifdef _MSC_VER
282  if (FindNextFile(dir->_h, &dir->_f) == 0)
283 #else
284  dir->_e = readdir(dir->_d);
285  if (dir->_e == NULL)
286 #endif
287  {
288  dir->has_next = 0;
289 #ifdef _MSC_VER
290  if (GetLastError() != ERROR_SUCCESS &&
291  GetLastError() != ERROR_NO_MORE_FILES)
292  {
293  tinydir_close(dir);
294  errno = EIO;
295  return -1;
296  }
297 #endif
298  }
299 
300  return 0;
301 }
302 
303 _TINYDIR_FUNC
304 int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file)
305 {
306  if (dir == NULL || file == NULL)
307  {
308  errno = EINVAL;
309  return -1;
310  }
311 #ifdef _MSC_VER
312  if (dir->_h == INVALID_HANDLE_VALUE)
313 #else
314  if (dir->_e == NULL)
315 #endif
316  {
317  errno = ENOENT;
318  return -1;
319  }
320  if (strlen(dir->path) +
321  strlen(
322 #ifdef _MSC_VER
323  dir->_f.cFileName
324 #else
325  dir->_e->d_name
326 #endif
327  ) + 1 + _TINYDIR_PATH_EXTRA >=
328  _TINYDIR_PATH_MAX)
329  {
330  /* the path for the file will be too long */
331  errno = ENAMETOOLONG;
332  return -1;
333  }
334  if (strlen(
335 #ifdef _MSC_VER
336  dir->_f.cFileName
337 #else
338  dir->_e->d_name
339 #endif
340  ) >= _TINYDIR_FILENAME_MAX)
341  {
342  errno = ENAMETOOLONG;
343  return -1;
344  }
345 
346  strcpy(file->path, dir->path);
347  strcat(file->path, "/");
348  strcpy(file->name,
349 #ifdef _MSC_VER
350  dir->_f.cFileName
351 #else
352  dir->_e->d_name
353 #endif
354  );
355  strcat(file->path, file->name);
356 #ifndef _MSC_VER
357  if (stat(file->path, &file->_s) == -1)
358  {
359  return -1;
360  }
361 #endif
362  _tinydir_get_ext(file);
363 
364  file->is_dir =
365 #ifdef _MSC_VER
366  !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
367 #else
368  S_ISDIR(file->_s.st_mode);
369 #endif
370  file->is_reg =
371 #ifdef _MSC_VER
372  !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) ||
373  (
374  !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) &&
375  !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
376  !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) &&
377 #ifdef FILE_ATTRIBUTE_INTEGRITY_STREAM
378  !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_INTEGRITY_STREAM) &&
379 #endif
380 #ifdef FILE_ATTRIBUTE_NO_SCRUB_DATA
381  !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NO_SCRUB_DATA) &&
382 #endif
383  !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) &&
384  !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY));
385 #else
386  S_ISREG(file->_s.st_mode);
387 #endif
388 
389  return 0;
390 }
391 
392 _TINYDIR_FUNC
393 int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i)
394 {
395  if (dir == NULL || file == NULL)
396  {
397  errno = EINVAL;
398  return -1;
399  }
400  if (i >= dir->n_files)
401  {
402  errno = ENOENT;
403  return -1;
404  }
405 
406  memcpy(file, &dir->_files[i], sizeof(tinydir_file));
407  _tinydir_get_ext(file);
408 
409  return 0;
410 }
411 
412 _TINYDIR_FUNC
413 int tinydir_open_subdir_n(tinydir_dir *dir, size_t i)
414 {
415  char path[_TINYDIR_PATH_MAX];
416  if (dir == NULL)
417  {
418  errno = EINVAL;
419  return -1;
420  }
421  if (i >= dir->n_files || !dir->_files[i].is_dir)
422  {
423  errno = ENOENT;
424  return -1;
425  }
426 
427  strcpy(path, dir->_files[i].path);
428  tinydir_close(dir);
429  if (tinydir_open_sorted(dir, path) == -1)
430  {
431  return -1;
432  }
433 
434  return 0;
435 }
436 
437 /* Open a single file given its path */
438 _TINYDIR_FUNC
439 int tinydir_file_open(tinydir_file *file, const char *path)
440 {
441  tinydir_dir dir;
442  int result = 0;
443  int found = 0;
444  char dir_name_buf[_TINYDIR_PATH_MAX];
445  char file_name_buf[_TINYDIR_FILENAME_MAX];
446  char *dir_name;
447  char *base_name;
448 #ifdef _MSC_VER
449  char drive_buf[_TINYDIR_PATH_MAX];
450  char ext_buf[_TINYDIR_FILENAME_MAX];
451 #endif
452 
453  if (file == NULL || path == NULL || strlen(path) == 0)
454  {
455  errno = EINVAL;
456  return -1;
457  }
458  if (strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX)
459  {
460  errno = ENAMETOOLONG;
461  return -1;
462  }
463 
464  /* Get the parent path */
465 #ifdef _MSC_VER
466  if (_splitpath_s(
467  path,
468  drive_buf, sizeof drive_buf,
469  dir_name_buf, sizeof dir_name_buf,
470  file_name_buf, sizeof file_name_buf,
471  ext_buf, sizeof ext_buf))
472  {
473  errno = EINVAL;
474  return -1;
475  }
476  /* Concatenate the drive letter and dir name to form full dir name */
477  strcat(drive_buf, dir_name_buf);
478  dir_name = drive_buf;
479  /* Concatenate the file name and extension to form base name */
480  strcat(file_name_buf, ext_buf);
481  base_name = file_name_buf;
482 #else
483  strcpy(dir_name_buf, path);
484  dir_name = dirname(dir_name_buf);
485  strcpy(file_name_buf, path);
486  base_name = basename(file_name_buf);
487 #endif
488 
489  /* Open the parent directory */
490  if (tinydir_open(&dir, dir_name) == -1)
491  {
492  return -1;
493  }
494 
495  /* Read through the parent directory and look for the file */
496  while (dir.has_next)
497  {
498  if (tinydir_readfile(&dir, file) == -1)
499  {
500  result = -1;
501  goto bail;
502  }
503  if (strcmp(file->name, base_name) == 0)
504  {
505  /* File found */
506  found = 1;
507  goto bail;
508  }
509  tinydir_next(&dir);
510  }
511  if (!found)
512  {
513  result = -1;
514  errno = ENOENT;
515  }
516 
517 bail:
518  tinydir_close(&dir);
519  return result;
520 }
521 
522 _TINYDIR_FUNC
523 void _tinydir_get_ext(tinydir_file *file)
524 {
525  char *period = strrchr(file->name, '.');
526  if (period == NULL)
527  {
528  file->extension = &(file->name[strlen(file->name)]);
529  }
530  else
531  {
532  file->extension = period + 1;
533  }
534 }
535 
536 _TINYDIR_FUNC
537 int _tinydir_file_cmp(const void *a, const void *b)
538 {
539  const tinydir_file *fa = (const tinydir_file *)a;
540  const tinydir_file *fb = (const tinydir_file *)b;
541  if (fa->is_dir != fb->is_dir)
542  {
543  return -(fa->is_dir - fb->is_dir);
544  }
545  return strncmp(fa->name, fb->name, _TINYDIR_FILENAME_MAX);
546 }
547 
548 #endif
Definition: tinydir.h:59
Definition: tinydir.h:73