enumerator.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675
  1. /*
  2. * Copyright (C) 2008-2017 Tobias Brunner
  3. * Copyright (C) 2007 Martin Willi
  4. * HSR Hochschule fuer Technik Rapperswil
  5. *
  6. * This program is free software; you can redistribute it and/or modify it
  7. * under the terms of the GNU General Public License as published by the
  8. * Free Software Foundation; either version 2 of the License, or (at your
  9. * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
  10. *
  11. * This program is distributed in the hope that it will be useful, but
  12. * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  13. * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  14. * for more details.
  15. */
  16. #include "enumerator.h"
  17. #include <sys/types.h>
  18. #include <sys/stat.h>
  19. #include <unistd.h>
  20. #include <limits.h>
  21. #include <stdio.h>
  22. #include <dirent.h>
  23. #include <errno.h>
  24. #include <string.h>
  25. #ifdef HAVE_GLOB_H
  26. #include <glob.h>
  27. #endif /* HAVE_GLOB_H */
  28. #include <utils/debug.h>
  29. /*
  30. * Described in header.
  31. */
  32. bool enumerator_enumerate_default(enumerator_t *enumerator, ...)
  33. {
  34. va_list args;
  35. bool result;
  36. if (!enumerator->venumerate)
  37. {
  38. DBG1(DBG_LIB, "!!! ENUMERATE DEFAULT: venumerate() missing !!!");
  39. return FALSE;
  40. }
  41. va_start(args, enumerator);
  42. result = enumerator->venumerate(enumerator, args);
  43. va_end(args);
  44. return result;
  45. }
  46. METHOD(enumerator_t, enumerate_empty, bool,
  47. enumerator_t *enumerator, va_list args)
  48. {
  49. return FALSE;
  50. }
  51. /*
  52. * Described in header
  53. */
  54. enumerator_t* enumerator_create_empty()
  55. {
  56. enumerator_t *this;
  57. INIT(this,
  58. .enumerate = enumerator_enumerate_default,
  59. .venumerate = _enumerate_empty,
  60. .destroy = (void*)free,
  61. );
  62. return this;
  63. }
  64. /**
  65. * Enumerator implementation for directory enumerator
  66. */
  67. typedef struct {
  68. /** implements enumerator_t */
  69. enumerator_t public;
  70. /** directory handle */
  71. DIR *dir;
  72. /** absolute path of current file */
  73. char full[PATH_MAX];
  74. /** where directory part of full ends and relative file gets written */
  75. char *full_end;
  76. } dir_enum_t;
  77. METHOD(enumerator_t, destroy_dir_enum, void,
  78. dir_enum_t *this)
  79. {
  80. closedir(this->dir);
  81. free(this);
  82. }
  83. METHOD(enumerator_t, enumerate_dir_enum, bool,
  84. dir_enum_t *this, va_list args)
  85. {
  86. struct dirent *entry = readdir(this->dir);
  87. struct stat *st;
  88. size_t remaining;
  89. char **relative, **absolute;
  90. int len;
  91. VA_ARGS_VGET(args, relative, absolute, st);
  92. if (!entry)
  93. {
  94. return FALSE;
  95. }
  96. if (streq(entry->d_name, ".") || streq(entry->d_name, ".."))
  97. {
  98. return this->public.enumerate(&this->public, relative, absolute, st);
  99. }
  100. if (relative)
  101. {
  102. *relative = entry->d_name;
  103. }
  104. if (absolute || st)
  105. {
  106. remaining = sizeof(this->full) - (this->full_end - this->full);
  107. len = snprintf(this->full_end, remaining, "%s", entry->d_name);
  108. if (len < 0 || len >= remaining)
  109. {
  110. DBG1(DBG_LIB, "buffer too small to enumerate file '%s'",
  111. entry->d_name);
  112. return FALSE;
  113. }
  114. if (absolute)
  115. {
  116. *absolute = this->full;
  117. }
  118. if (st)
  119. {
  120. if (stat(this->full, st))
  121. {
  122. DBG1(DBG_LIB, "stat() on '%s' failed: %s", this->full,
  123. strerror(errno));
  124. return FALSE;
  125. }
  126. }
  127. }
  128. return TRUE;
  129. }
  130. /*
  131. * Described in header
  132. */
  133. enumerator_t* enumerator_create_directory(const char *path)
  134. {
  135. dir_enum_t *this;
  136. int len;
  137. INIT(this,
  138. .public = {
  139. .enumerate = enumerator_enumerate_default,
  140. .venumerate = _enumerate_dir_enum,
  141. .destroy = _destroy_dir_enum,
  142. },
  143. );
  144. if (*path == '\0')
  145. {
  146. path = "./";
  147. }
  148. len = snprintf(this->full, sizeof(this->full)-1, "%s", path);
  149. if (len < 0 || len >= sizeof(this->full)-1)
  150. {
  151. DBG1(DBG_LIB, "path string '%s' too long", path);
  152. free(this);
  153. return NULL;
  154. }
  155. /* append a '/' if not already done */
  156. if (this->full[len-1] != '/')
  157. {
  158. this->full[len++] = '/';
  159. this->full[len] = '\0';
  160. }
  161. this->full_end = &this->full[len];
  162. this->dir = opendir(path);
  163. if (!this->dir)
  164. {
  165. DBG1(DBG_LIB, "opening directory '%s' failed: %s", path,
  166. strerror(errno));
  167. free(this);
  168. return NULL;
  169. }
  170. return &this->public;
  171. }
  172. #ifdef HAVE_GLOB_H
  173. /**
  174. * Enumerator implementation for glob enumerator
  175. */
  176. typedef struct {
  177. /** implements enumerator_t */
  178. enumerator_t public;
  179. /** glob data */
  180. glob_t glob;
  181. /** iteration count */
  182. u_int pos;
  183. /** absolute path of current file */
  184. char full[PATH_MAX];
  185. } glob_enum_t;
  186. METHOD(enumerator_t, destroy_glob_enum, void,
  187. glob_enum_t *this)
  188. {
  189. globfree(&this->glob);
  190. free(this);
  191. }
  192. METHOD(enumerator_t, enumerate_glob_enum, bool,
  193. glob_enum_t *this, va_list args)
  194. {
  195. struct stat *st;
  196. char *match;
  197. char **file;
  198. VA_ARGS_VGET(args, file, st);
  199. if (this->pos >= this->glob.gl_pathc)
  200. {
  201. return FALSE;
  202. }
  203. match = this->glob.gl_pathv[this->pos++];
  204. if (file)
  205. {
  206. *file = match;
  207. }
  208. if (st && stat(match, st))
  209. {
  210. DBG1(DBG_LIB, "stat() on '%s' failed: %s", match,
  211. strerror(errno));
  212. return FALSE;
  213. }
  214. return TRUE;
  215. }
  216. /*
  217. * Described in header
  218. */
  219. enumerator_t* enumerator_create_glob(const char *pattern)
  220. {
  221. glob_enum_t *this;
  222. int status;
  223. if (!pattern)
  224. {
  225. return enumerator_create_empty();
  226. }
  227. INIT(this,
  228. .public = {
  229. .enumerate = enumerator_enumerate_default,
  230. .venumerate = _enumerate_glob_enum,
  231. .destroy = _destroy_glob_enum,
  232. },
  233. );
  234. status = glob(pattern, GLOB_ERR, NULL, &this->glob);
  235. if (status == GLOB_NOMATCH)
  236. {
  237. DBG1(DBG_LIB, "no files found matching '%s'", pattern);
  238. }
  239. else if (status != 0)
  240. {
  241. DBG1(DBG_LIB, "expanding file pattern '%s' failed: %s", pattern,
  242. strerror(errno));
  243. }
  244. return &this->public;
  245. }
  246. #else /* HAVE_GLOB_H */
  247. enumerator_t* enumerator_create_glob(const char *pattern)
  248. {
  249. return NULL;
  250. }
  251. #endif /* HAVE_GLOB_H */
  252. /**
  253. * Enumerator implementation for token enumerator
  254. */
  255. typedef struct {
  256. /** implements enumerator_t */
  257. enumerator_t public;
  258. /** string to parse */
  259. char *string;
  260. /** current position */
  261. char *pos;
  262. /** separator chars */
  263. const char *sep;
  264. /** trim chars */
  265. const char *trim;
  266. } token_enum_t;
  267. METHOD(enumerator_t, destroy_token_enum, void,
  268. token_enum_t *this)
  269. {
  270. free(this->string);
  271. free(this);
  272. }
  273. METHOD(enumerator_t, enumerate_token_enum, bool,
  274. token_enum_t *this, va_list args)
  275. {
  276. const char *sep, *trim;
  277. char *pos = NULL, *tmp, **token;
  278. bool last = FALSE;
  279. VA_ARGS_VGET(args, token);
  280. /* trim leading characters/separators */
  281. while (*this->pos)
  282. {
  283. trim = this->trim;
  284. while (*trim)
  285. {
  286. if (*trim == *this->pos)
  287. {
  288. this->pos++;
  289. break;
  290. }
  291. trim++;
  292. }
  293. sep = this->sep;
  294. while (*sep)
  295. {
  296. if (*sep == *this->pos)
  297. {
  298. this->pos++;
  299. break;
  300. }
  301. sep++;
  302. }
  303. if (!*trim && !*sep)
  304. {
  305. break;
  306. }
  307. }
  308. switch (*this->pos)
  309. {
  310. case '"':
  311. case '\'':
  312. {
  313. /* read quoted token */
  314. tmp = strchr(this->pos + 1, *this->pos);
  315. if (tmp)
  316. {
  317. *token = this->pos + 1;
  318. *tmp = '\0';
  319. this->pos = tmp + 1;
  320. return TRUE;
  321. }
  322. /* unterminated string, FALL-THROUGH */
  323. }
  324. default:
  325. {
  326. /* find nearest separator */
  327. sep = this->sep;
  328. while (*sep)
  329. {
  330. tmp = strchr(this->pos, *sep);
  331. if (tmp && (pos == NULL || tmp < pos))
  332. {
  333. pos = tmp;
  334. }
  335. sep++;
  336. }
  337. *token = this->pos;
  338. if (pos)
  339. {
  340. *pos = '\0';
  341. this->pos = pos + 1;
  342. }
  343. else
  344. {
  345. last = TRUE;
  346. pos = this->pos = strchr(this->pos, '\0');
  347. }
  348. break;
  349. }
  350. }
  351. /* trim trailing characters */
  352. pos--;
  353. while (pos >= *token)
  354. {
  355. trim = this->trim;
  356. while (*trim)
  357. {
  358. if (*trim == *pos)
  359. {
  360. *(pos--) = '\0';
  361. break;
  362. }
  363. trim++;
  364. }
  365. if (!*trim)
  366. {
  367. break;
  368. }
  369. }
  370. if (!last || pos >= *token)
  371. {
  372. return TRUE;
  373. }
  374. return FALSE;
  375. }
  376. /*
  377. * Described in header
  378. */
  379. enumerator_t* enumerator_create_token(const char *string, const char *sep,
  380. const char *trim)
  381. {
  382. token_enum_t *this;
  383. INIT(this,
  384. .public = {
  385. .enumerate = enumerator_enumerate_default,
  386. .venumerate = _enumerate_token_enum,
  387. .destroy = _destroy_token_enum,
  388. },
  389. .string = strdup(string),
  390. .sep = sep,
  391. .trim = trim,
  392. );
  393. this->pos = this->string;
  394. return &this->public;
  395. }
  396. /**
  397. * Enumerator for nested enumerations
  398. */
  399. typedef struct {
  400. enumerator_t public;
  401. enumerator_t *outer;
  402. enumerator_t *inner;
  403. enumerator_t *(*create_inner)(void *outer, void *data);
  404. void *data;
  405. void (*destructor)(void *data);
  406. } nested_enumerator_t;
  407. METHOD(enumerator_t, enumerate_nested, bool,
  408. nested_enumerator_t *this, va_list args)
  409. {
  410. while (TRUE)
  411. {
  412. while (!this->inner)
  413. {
  414. void *outer;
  415. if (!this->outer->enumerate(this->outer, &outer))
  416. {
  417. return FALSE;
  418. }
  419. this->inner = this->create_inner(outer, this->data);
  420. if (this->inner && !this->inner->venumerate)
  421. {
  422. DBG1(DBG_LIB, "!!! ENUMERATE NESTED: venumerate() missing !!!");
  423. return FALSE;
  424. }
  425. }
  426. if (this->inner->venumerate(this->inner, args))
  427. {
  428. return TRUE;
  429. }
  430. this->inner->destroy(this->inner);
  431. this->inner = NULL;
  432. }
  433. }
  434. METHOD(enumerator_t, destroy_nested, void,
  435. nested_enumerator_t *this)
  436. {
  437. if (this->destructor)
  438. {
  439. this->destructor(this->data);
  440. }
  441. DESTROY_IF(this->inner);
  442. this->outer->destroy(this->outer);
  443. free(this);
  444. }
  445. /*
  446. * Described in header
  447. */
  448. enumerator_t *enumerator_create_nested(enumerator_t *outer,
  449. enumerator_t *(inner_constructor)(void *outer, void *data),
  450. void *data, void (*destructor)(void *data))
  451. {
  452. nested_enumerator_t *this;
  453. INIT(this,
  454. .public = {
  455. .enumerate = enumerator_enumerate_default,
  456. .venumerate = _enumerate_nested,
  457. .destroy = _destroy_nested,
  458. },
  459. .outer = outer,
  460. .create_inner = inner_constructor,
  461. .data = data,
  462. .destructor = destructor,
  463. );
  464. return &this->public;
  465. }
  466. /**
  467. * Enumerator for filtered enumerator
  468. */
  469. typedef struct {
  470. enumerator_t public;
  471. enumerator_t *orig;
  472. void *data;
  473. bool (*filter)(void*,enumerator_t*,va_list);
  474. void (*destructor)(void *data);
  475. } filter_enumerator_t;
  476. METHOD(enumerator_t, destroy_filter, void,
  477. filter_enumerator_t *this)
  478. {
  479. if (this->destructor)
  480. {
  481. this->destructor(this->data);
  482. }
  483. this->orig->destroy(this->orig);
  484. free(this);
  485. }
  486. METHOD(enumerator_t, enumerate_filter, bool,
  487. filter_enumerator_t *this, va_list args)
  488. {
  489. bool result = FALSE;
  490. if (this->filter(this->data, this->orig, args))
  491. {
  492. result = TRUE;
  493. }
  494. return result;
  495. }
  496. /*
  497. * Described in header
  498. */
  499. enumerator_t *enumerator_create_filter(enumerator_t *orig,
  500. bool (*filter)(void *data, enumerator_t *orig, va_list args),
  501. void *data, void (*destructor)(void *data))
  502. {
  503. filter_enumerator_t *this;
  504. INIT(this,
  505. .public = {
  506. .enumerate = enumerator_enumerate_default,
  507. .venumerate = _enumerate_filter,
  508. .destroy = _destroy_filter,
  509. },
  510. .orig = orig,
  511. .filter = filter,
  512. .data = data,
  513. .destructor = destructor,
  514. );
  515. return &this->public;
  516. }
  517. /**
  518. * Enumerator for cleaner enumerator
  519. */
  520. typedef struct {
  521. enumerator_t public;
  522. enumerator_t *wrapped;
  523. void (*cleanup)(void *data);
  524. void *data;
  525. } cleaner_enumerator_t;
  526. METHOD(enumerator_t, destroy_cleaner, void,
  527. cleaner_enumerator_t *this)
  528. {
  529. this->cleanup(this->data);
  530. this->wrapped->destroy(this->wrapped);
  531. free(this);
  532. }
  533. METHOD(enumerator_t, enumerate_cleaner, bool,
  534. cleaner_enumerator_t *this, va_list args)
  535. {
  536. if (!this->wrapped->venumerate)
  537. {
  538. DBG1(DBG_LIB, "!!! CLEANER ENUMERATOR: venumerate() missing !!!");
  539. return FALSE;
  540. }
  541. return this->wrapped->venumerate(this->wrapped, args);
  542. }
  543. /*
  544. * Described in header
  545. */
  546. enumerator_t *enumerator_create_cleaner(enumerator_t *wrapped,
  547. void (*cleanup)(void *data), void *data)
  548. {
  549. cleaner_enumerator_t *this;
  550. INIT(this,
  551. .public = {
  552. .enumerate = enumerator_enumerate_default,
  553. .venumerate = _enumerate_cleaner,
  554. .destroy = _destroy_cleaner,
  555. },
  556. .wrapped = wrapped,
  557. .cleanup = cleanup,
  558. .data = data,
  559. );
  560. return &this->public;
  561. }
  562. /**
  563. * Enumerator for single enumerator
  564. */
  565. typedef struct {
  566. enumerator_t public;
  567. void *item;
  568. void (*cleanup)(void *item);
  569. bool done;
  570. } single_enumerator_t;
  571. METHOD(enumerator_t, destroy_single, void,
  572. single_enumerator_t *this)
  573. {
  574. if (this->cleanup)
  575. {
  576. this->cleanup(this->item);
  577. }
  578. free(this);
  579. }
  580. METHOD(enumerator_t, enumerate_single, bool,
  581. single_enumerator_t *this, va_list args)
  582. {
  583. void **item;
  584. VA_ARGS_VGET(args, item);
  585. if (this->done)
  586. {
  587. return FALSE;
  588. }
  589. *item = this->item;
  590. this->done = TRUE;
  591. return TRUE;
  592. }
  593. /*
  594. * Described in header
  595. */
  596. enumerator_t *enumerator_create_single(void *item, void (*cleanup)(void *item))
  597. {
  598. single_enumerator_t *this;
  599. INIT(this,
  600. .public = {
  601. .enumerate = enumerator_enumerate_default,
  602. .venumerate = _enumerate_single,
  603. .destroy = _destroy_single,
  604. },
  605. .item = item,
  606. .cleanup = cleanup,
  607. );
  608. return &this->public;
  609. }