Coverage Report

Created: 2024-02-29 06:05

/src/strongswan/src/libstrongswan/collections/enumerator.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (C) 2008-2017 Tobias Brunner
3
 * Copyright (C) 2007 Martin Willi
4
 *
5
 * Copyright (C) secunet Security Networks AG
6
 *
7
 * This program is free software; you can redistribute it and/or modify it
8
 * under the terms of the GNU General Public License as published by the
9
 * Free Software Foundation; either version 2 of the License, or (at your
10
 * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
11
 *
12
 * This program is distributed in the hope that it will be useful, but
13
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15
 * for more details.
16
 */
17
18
#include "enumerator.h"
19
20
#include <sys/types.h>
21
#include <sys/stat.h>
22
#include <unistd.h>
23
#include <limits.h>
24
#include <stdio.h>
25
#include <dirent.h>
26
#include <errno.h>
27
#include <string.h>
28
29
#ifdef HAVE_GLOB_H
30
#include <glob.h>
31
#elif defined(WIN32)
32
#include <fileapi.h>
33
#endif /* HAVE_GLOB_H */
34
35
#include <utils/debug.h>
36
37
/*
38
 * Described in header.
39
 */
40
bool enumerator_enumerate_default(enumerator_t *enumerator, ...)
41
3.25M
{
42
3.25M
  va_list args;
43
3.25M
  bool result;
44
45
3.25M
  if (!enumerator->venumerate)
46
0
  {
47
0
    DBG1(DBG_LIB, "!!! ENUMERATE DEFAULT: venumerate() missing !!!");
48
0
    return FALSE;
49
0
  }
50
3.25M
  va_start(args, enumerator);
51
3.25M
  result = enumerator->venumerate(enumerator, args);
52
3.25M
  va_end(args);
53
3.25M
  return result;
54
3.25M
}
55
56
METHOD(enumerator_t, enumerate_empty, bool,
57
  enumerator_t *enumerator, va_list args)
58
7.84k
{
59
7.84k
  return FALSE;
60
7.84k
}
61
62
/*
63
 * Described in header
64
 */
65
enumerator_t* enumerator_create_empty()
66
7.84k
{
67
7.84k
  enumerator_t *this;
68
69
7.84k
  INIT(this,
70
7.84k
    .enumerate = enumerator_enumerate_default,
71
7.84k
    .venumerate = _enumerate_empty,
72
7.84k
    .destroy = (void*)free,
73
7.84k
  );
74
7.84k
  return this;
75
7.84k
}
76
77
/**
78
 * Enumerator implementation for directory enumerator
79
 */
80
typedef struct {
81
  /** implements enumerator_t */
82
  enumerator_t public;
83
  /** directory handle */
84
  DIR *dir;
85
  /** absolute path of current file */
86
  char full[PATH_MAX];
87
  /** where directory part of full ends and relative file gets written */
88
  char *full_end;
89
} dir_enum_t;
90
91
METHOD(enumerator_t, destroy_dir_enum, void,
92
  dir_enum_t *this)
93
0
{
94
0
  closedir(this->dir);
95
0
  free(this);
96
0
}
97
98
METHOD(enumerator_t, enumerate_dir_enum, bool,
99
  dir_enum_t *this, va_list args)
100
0
{
101
0
  struct dirent *entry = readdir(this->dir);
102
0
  struct stat *st;
103
0
  size_t remaining;
104
0
  char **relative, **absolute;
105
0
  int len;
106
107
0
  VA_ARGS_VGET(args, relative, absolute, st);
108
109
0
  if (!entry)
110
0
  {
111
0
    return FALSE;
112
0
  }
113
0
  if (streq(entry->d_name, ".") || streq(entry->d_name, ".."))
114
0
  {
115
0
    return this->public.enumerate(&this->public, relative, absolute, st);
116
0
  }
117
0
  if (relative)
118
0
  {
119
0
    *relative = entry->d_name;
120
0
  }
121
0
  if (absolute || st)
122
0
  {
123
0
    remaining = sizeof(this->full) - (this->full_end - this->full);
124
0
    len = snprintf(this->full_end, remaining, "%s", entry->d_name);
125
0
    if (len < 0 || len >= remaining)
126
0
    {
127
0
      DBG1(DBG_LIB, "buffer too small to enumerate file '%s'",
128
0
         entry->d_name);
129
0
      return FALSE;
130
0
    }
131
0
    if (absolute)
132
0
    {
133
0
      *absolute = this->full;
134
0
    }
135
0
    if (st && stat(this->full, st))
136
0
    {
137
      /* try lstat() e.g. if a symlink is not valid anymore */
138
0
      if ((errno != ENOENT && errno != ENOTDIR) || lstat(this->full, st))
139
0
      {
140
0
        DBG1(DBG_LIB, "stat() on '%s' failed: %s", this->full,
141
0
           strerror(errno));
142
0
        return FALSE;
143
0
      }
144
0
    }
145
0
  }
146
0
  return TRUE;
147
0
}
148
149
/*
150
 * Described in header
151
 */
152
enumerator_t* enumerator_create_directory(const char *path)
153
0
{
154
0
  dir_enum_t *this;
155
0
  int len;
156
157
0
  INIT(this,
158
0
    .public = {
159
0
      .enumerate = enumerator_enumerate_default,
160
0
      .venumerate = _enumerate_dir_enum,
161
0
      .destroy = _destroy_dir_enum,
162
0
    },
163
0
  );
164
165
0
  if (*path == '\0')
166
0
  {
167
0
    path = "./";
168
0
  }
169
0
  len = snprintf(this->full, sizeof(this->full)-1, "%s", path);
170
0
  if (len < 0 || len >= sizeof(this->full)-1)
171
0
  {
172
0
    DBG1(DBG_LIB, "path string '%s' too long", path);
173
0
    free(this);
174
0
    return NULL;
175
0
  }
176
  /* append a '/' if not already done */
177
0
  if (!path_is_separator(this->full[len-1]))
178
0
  {
179
0
    this->full[len++] = DIRECTORY_SEPARATOR[0];
180
0
    this->full[len] = '\0';
181
0
  }
182
0
  this->full_end = &this->full[len];
183
184
0
  this->dir = opendir(path);
185
0
  if (!this->dir)
186
0
  {
187
0
    DBG1(DBG_LIB, "opening directory '%s' failed: %s", path,
188
0
       strerror(errno));
189
0
    free(this);
190
0
    return NULL;
191
0
  }
192
0
  return &this->public;
193
0
}
194
195
#ifdef HAVE_GLOB_H
196
197
/**
198
 * Enumerator implementation for glob enumerator
199
 */
200
typedef struct {
201
  /** implements enumerator_t */
202
  enumerator_t public;
203
  /** glob data */
204
  glob_t glob;
205
  /** iteration count */
206
  u_int pos;
207
} glob_enum_t;
208
209
METHOD(enumerator_t, destroy_glob_enum, void,
210
  glob_enum_t *this)
211
3.92k
{
212
3.92k
  globfree(&this->glob);
213
3.92k
  free(this);
214
3.92k
}
215
216
METHOD(enumerator_t, enumerate_glob_enum, bool,
217
  glob_enum_t *this, va_list args)
218
3.92k
{
219
3.92k
  struct stat *st;
220
3.92k
  char *match;
221
3.92k
  char **file;
222
223
3.92k
  VA_ARGS_VGET(args, file, st);
224
225
3.92k
  if (this->pos >= this->glob.gl_pathc)
226
3.92k
  {
227
3.92k
    return FALSE;
228
3.92k
  }
229
0
  match = this->glob.gl_pathv[this->pos++];
230
0
  if (file)
231
0
  {
232
0
    *file = match;
233
0
  }
234
0
  if (st && stat(match, st))
235
0
  {
236
0
    DBG1(DBG_LIB, "stat() on '%s' failed: %s", match,
237
0
       strerror(errno));
238
0
    return FALSE;
239
0
  }
240
0
  return TRUE;
241
0
}
242
243
/*
244
 * Described in header
245
 */
246
enumerator_t* enumerator_create_glob(const char *pattern)
247
3.92k
{
248
3.92k
  glob_enum_t *this;
249
3.92k
  int status;
250
251
3.92k
  if (!pattern)
252
0
  {
253
0
    return enumerator_create_empty();
254
0
  }
255
256
3.92k
  INIT(this,
257
3.92k
    .public = {
258
3.92k
      .enumerate = enumerator_enumerate_default,
259
3.92k
      .venumerate = _enumerate_glob_enum,
260
3.92k
      .destroy = _destroy_glob_enum,
261
3.92k
    },
262
3.92k
  );
263
264
3.92k
  status = glob(pattern, GLOB_ERR, NULL, &this->glob);
265
3.92k
  if (status == GLOB_NOMATCH)
266
3.92k
  {
267
3.92k
    DBG1(DBG_LIB, "no files found matching '%s'", pattern);
268
3.92k
  }
269
0
  else if (status != 0)
270
0
  {
271
0
    DBG1(DBG_LIB, "expanding file pattern '%s' failed: %s", pattern,
272
0
       strerror(errno));
273
0
  }
274
3.92k
  return &this->public;
275
3.92k
}
276
277
#elif defined(WIN32) /* HAVE_GLOB_H */
278
279
/**
280
 * Enumerator implementation for glob enumerator on Windows
281
 */
282
typedef struct {
283
  /** implements enumerator_t */
284
  enumerator_t public;
285
  /** search handle */
286
  HANDLE handle;
287
  /** current file path */
288
  char path[PATH_MAX];
289
  /** base path */
290
  char *base;
291
} glob_enum_t;
292
293
METHOD(enumerator_t, destroy_glob_enum, void,
294
  glob_enum_t *this)
295
{
296
  if (this->handle != INVALID_HANDLE_VALUE)
297
  {
298
    FindClose(this->handle);
299
  }
300
  free(this->base);
301
  free(this);
302
}
303
304
/**
305
 * Create the combined path from the given file data
306
 */
307
static bool combine_glob_path(glob_enum_t *this, WIN32_FIND_DATA *data)
308
{
309
  if (snprintf(this->path, sizeof(this->path), "%s%s%s", this->base,
310
         DIRECTORY_SEPARATOR, data->cFileName) >= sizeof(this->path))
311
  {
312
    DBG1(DBG_LIB, "path for '%s' too long, ignored", data->cFileName);
313
    return FALSE;
314
  }
315
  return TRUE;
316
}
317
318
/**
319
 * Return the path and stat data for the current file
320
 */
321
static bool enumerate_glob_enum_data(glob_enum_t *this, va_list args)
322
{
323
  struct stat *st;
324
  char **file;
325
326
  VA_ARGS_VGET(args, file, st);
327
328
  if (file)
329
  {
330
    *file = this->path;
331
  }
332
  if (st && stat(this->path, st))
333
  {
334
    DBG1(DBG_LIB, "stat() on '%s' failed: %s", this->path,
335
       strerror(errno));
336
    return FALSE;
337
  }
338
  return TRUE;
339
}
340
341
METHOD(enumerator_t, enumerate_glob_enum, bool,
342
  glob_enum_t *this, va_list args)
343
{
344
  WIN32_FIND_DATA data;
345
346
  do
347
  {
348
    if (!FindNextFile(this->handle, &data))
349
    {
350
      return FALSE;
351
    }
352
  }
353
  while (!combine_glob_path(this, &data));
354
355
  return enumerate_glob_enum_data(this, args);
356
}
357
358
METHOD(enumerator_t, enumerate_glob_enum_first, bool,
359
  glob_enum_t *this, va_list args)
360
{
361
  if (enumerate_glob_enum_data(this, args))
362
  {
363
    this->public.venumerate = _enumerate_glob_enum;
364
    return TRUE;
365
  }
366
  return FALSE;
367
}
368
369
/*
370
 * Described in header
371
 */
372
enumerator_t *enumerator_create_glob(const char *pattern)
373
{
374
  glob_enum_t *this;
375
  WIN32_FIND_DATA data;
376
377
  if (!pattern)
378
  {
379
    return enumerator_create_empty();
380
  }
381
382
  INIT(this,
383
    .public = {
384
      .enumerate = enumerator_enumerate_default,
385
      .venumerate = _enumerate_glob_enum_first,
386
      .destroy = _destroy_glob_enum,
387
    },
388
    .base = path_dirname(pattern),
389
  );
390
391
  this->handle = FindFirstFile(pattern, &data);
392
  if (this->handle == INVALID_HANDLE_VALUE)
393
  {
394
    if (GetLastError() == ERROR_FILE_NOT_FOUND ||
395
      GetLastError() == ERROR_PATH_NOT_FOUND)
396
    {
397
      DBG1(DBG_LIB, "no files found matching '%s'", pattern);
398
    }
399
    else
400
    {
401
      DBG1(DBG_LIB, "FindFirstFile failed for pattern '%s' (%d)", pattern,
402
         GetLastError());
403
    }
404
    destroy_glob_enum(this);
405
    return enumerator_create_empty();
406
  }
407
  else if (!combine_glob_path(this, &data))
408
  { /* check the next file if we can't combine the path for the first one */
409
    this->public.venumerate = _enumerate_glob_enum;
410
  }
411
  return &this->public;
412
}
413
414
#else /* HAVE_GLOB_H */
415
416
enumerator_t* enumerator_create_glob(const char *pattern)
417
{
418
  return NULL;
419
}
420
421
#endif /* HAVE_GLOB_H */
422
423
/**
424
 * Enumerator implementation for token enumerator
425
 */
426
typedef struct {
427
  /** implements enumerator_t */
428
  enumerator_t public;
429
  /** string to parse */
430
  char *string;
431
  /** current position */
432
  char *pos;
433
  /** separator chars */
434
  const char *sep;
435
  /** trim chars */
436
  const char *trim;
437
} token_enum_t;
438
439
METHOD(enumerator_t, destroy_token_enum, void,
440
  token_enum_t *this)
441
11.7k
{
442
11.7k
  free(this->string);
443
11.7k
  free(this);
444
11.7k
}
445
446
METHOD(enumerator_t, enumerate_token_enum, bool,
447
  token_enum_t *this, va_list args)
448
82.3k
{
449
82.3k
  const char *sep, *trim;
450
82.3k
  char *pos = NULL, *tmp, **token;
451
82.3k
  bool last = FALSE;
452
453
82.3k
  VA_ARGS_VGET(args, token);
454
455
  /* trim leading characters/separators */
456
86.2k
  while (*this->pos)
457
74.4k
  {
458
74.4k
    trim = this->trim;
459
145k
    while (*trim)
460
74.4k
    {
461
74.4k
      if (*trim == *this->pos)
462
3.92k
      {
463
3.92k
        this->pos++;
464
3.92k
        break;
465
3.92k
      }
466
70.5k
      trim++;
467
70.5k
    }
468
74.4k
    sep = this->sep;
469
148k
    while (*sep)
470
74.4k
    {
471
74.4k
      if (*sep == *this->pos)
472
0
      {
473
0
        this->pos++;
474
0
        break;
475
0
      }
476
74.4k
      sep++;
477
74.4k
    }
478
74.4k
    if (!*trim && !*sep)
479
70.5k
    {
480
70.5k
      break;
481
70.5k
    }
482
74.4k
  }
483
484
82.3k
  switch (*this->pos)
485
82.3k
  {
486
0
    case '"':
487
0
    case '\'':
488
0
    {
489
      /* read quoted token */
490
0
      tmp = strchr(this->pos + 1, *this->pos);
491
0
      if (tmp)
492
0
      {
493
0
        *token = this->pos + 1;
494
0
        *tmp = '\0';
495
0
        this->pos = tmp + 1;
496
0
        return TRUE;
497
0
      }
498
      /* unterminated string, FALL-THROUGH */
499
0
    }
500
82.3k
    default:
501
82.3k
    {
502
      /* find nearest separator */
503
82.3k
      sep = this->sep;
504
164k
      while (*sep)
505
82.3k
      {
506
82.3k
        tmp = strchr(this->pos, *sep);
507
82.3k
        if (tmp && (pos == NULL || tmp < pos))
508
58.8k
        {
509
58.8k
          pos = tmp;
510
58.8k
        }
511
82.3k
        sep++;
512
82.3k
      }
513
82.3k
      *token = this->pos;
514
82.3k
      if (pos)
515
58.8k
      {
516
58.8k
        *pos = '\0';
517
58.8k
        this->pos = pos + 1;
518
58.8k
      }
519
23.5k
      else
520
23.5k
      {
521
23.5k
        last = TRUE;
522
23.5k
        pos = this->pos = strchr(this->pos, '\0');
523
23.5k
      }
524
82.3k
      break;
525
0
    }
526
82.3k
  }
527
528
  /* trim trailing characters */
529
82.3k
  pos--;
530
82.3k
  while (pos >= *token)
531
70.5k
  {
532
70.5k
    trim = this->trim;
533
141k
    while (*trim)
534
70.5k
    {
535
70.5k
      if (*trim == *pos)
536
0
      {
537
0
        *(pos--) = '\0';
538
0
        break;
539
0
      }
540
70.5k
      trim++;
541
70.5k
    }
542
70.5k
    if (!*trim)
543
70.5k
    {
544
70.5k
      break;
545
70.5k
    }
546
70.5k
  }
547
548
82.3k
  if (!last || pos >= *token)
549
70.5k
  {
550
70.5k
    return TRUE;
551
70.5k
  }
552
11.7k
  return FALSE;
553
82.3k
}
554
555
/*
556
 * Described in header
557
 */
558
enumerator_t* enumerator_create_token(const char *string, const char *sep,
559
                    const char *trim)
560
11.7k
{
561
11.7k
  token_enum_t *this;
562
563
11.7k
  INIT(this,
564
11.7k
    .public = {
565
11.7k
      .enumerate = enumerator_enumerate_default,
566
11.7k
      .venumerate = _enumerate_token_enum,
567
11.7k
      .destroy = _destroy_token_enum,
568
11.7k
    },
569
11.7k
    .string = strdup(string),
570
11.7k
    .sep = sep,
571
11.7k
    .trim = trim,
572
11.7k
  );
573
11.7k
  this->pos = this->string;
574
575
11.7k
  return &this->public;
576
11.7k
}
577
578
/**
579
 * Enumerator for nested enumerations
580
 */
581
typedef struct {
582
  enumerator_t public;
583
  enumerator_t *outer;
584
  enumerator_t *inner;
585
  enumerator_t *(*create_inner)(void *outer, void *data);
586
  void *data;
587
  void (*destructor)(void *data);
588
} nested_enumerator_t;
589
590
591
METHOD(enumerator_t, enumerate_nested, bool,
592
  nested_enumerator_t *this, va_list args)
593
5
{
594
5
  while (TRUE)
595
5
  {
596
10
    while (!this->inner)
597
10
    {
598
10
      void *outer;
599
600
10
      if (!this->outer->enumerate(this->outer, &outer))
601
5
      {
602
5
        return FALSE;
603
5
      }
604
5
      this->inner = this->create_inner(outer, this->data);
605
5
      if (this->inner && !this->inner->venumerate)
606
0
      {
607
0
        DBG1(DBG_LIB, "!!! ENUMERATE NESTED: venumerate() missing !!!");
608
0
        return FALSE;
609
0
      }
610
5
    }
611
0
    if (this->inner->venumerate(this->inner, args))
612
0
    {
613
0
      return TRUE;
614
0
    }
615
0
    this->inner->destroy(this->inner);
616
0
    this->inner = NULL;
617
0
  }
618
5
}
619
620
METHOD(enumerator_t, destroy_nested, void,
621
  nested_enumerator_t *this)
622
5
{
623
5
  if (this->destructor)
624
5
  {
625
5
    this->destructor(this->data);
626
5
  }
627
5
  DESTROY_IF(this->inner);
628
5
  this->outer->destroy(this->outer);
629
5
  free(this);
630
5
}
631
632
/*
633
 * Described in header
634
 */
635
enumerator_t *enumerator_create_nested(enumerator_t *outer,
636
          enumerator_t *(inner_constructor)(void *outer, void *data),
637
          void *data, void (*destructor)(void *data))
638
5
{
639
5
  nested_enumerator_t *this;
640
641
5
  INIT(this,
642
5
    .public = {
643
5
      .enumerate = enumerator_enumerate_default,
644
5
      .venumerate = _enumerate_nested,
645
5
      .destroy = _destroy_nested,
646
5
    },
647
5
    .outer = outer,
648
5
    .create_inner = inner_constructor,
649
5
    .data = data,
650
5
    .destructor = destructor,
651
5
  );
652
5
  return &this->public;
653
5
}
654
655
/**
656
 * Enumerator for filtered enumerator
657
 */
658
typedef struct {
659
  enumerator_t public;
660
  enumerator_t *orig;
661
  void *data;
662
  bool (*filter)(void*,enumerator_t*,va_list);
663
  void (*destructor)(void *data);
664
} filter_enumerator_t;
665
666
METHOD(enumerator_t, destroy_filter, void,
667
  filter_enumerator_t *this)
668
31.3k
{
669
31.3k
  if (this->destructor)
670
0
  {
671
0
    this->destructor(this->data);
672
0
  }
673
31.3k
  this->orig->destroy(this->orig);
674
31.3k
  free(this);
675
31.3k
}
676
677
METHOD(enumerator_t, enumerate_filter, bool,
678
  filter_enumerator_t *this, va_list args)
679
137k
{
680
137k
  bool result = FALSE;
681
682
137k
  if (this->filter(this->data, this->orig, args))
683
105k
  {
684
105k
    result = TRUE;
685
105k
  }
686
137k
  return result;
687
137k
}
688
689
/*
690
 * Described in header
691
 */
692
enumerator_t *enumerator_create_filter(enumerator_t *orig,
693
      bool (*filter)(void *data, enumerator_t *orig, va_list args),
694
      void *data, void (*destructor)(void *data))
695
31.3k
{
696
31.3k
  filter_enumerator_t *this;
697
698
31.3k
  INIT(this,
699
31.3k
    .public = {
700
31.3k
      .enumerate = enumerator_enumerate_default,
701
31.3k
      .venumerate = _enumerate_filter,
702
31.3k
      .destroy = _destroy_filter,
703
31.3k
    },
704
31.3k
    .orig = orig,
705
31.3k
    .filter = filter,
706
31.3k
    .data = data,
707
31.3k
    .destructor = destructor,
708
31.3k
  );
709
31.3k
  return &this->public;
710
31.3k
}
711
712
/**
713
 * Enumerator for cleaner enumerator
714
 */
715
typedef struct {
716
  enumerator_t public;
717
  enumerator_t *wrapped;
718
  void (*cleanup)(void *data);
719
  void *data;
720
} cleaner_enumerator_t;
721
722
METHOD(enumerator_t, destroy_cleaner, void,
723
  cleaner_enumerator_t *this)
724
0
{
725
0
  this->cleanup(this->data);
726
0
  this->wrapped->destroy(this->wrapped);
727
0
  free(this);
728
0
}
729
730
METHOD(enumerator_t, enumerate_cleaner, bool,
731
  cleaner_enumerator_t *this, va_list args)
732
0
{
733
0
  if (!this->wrapped->venumerate)
734
0
  {
735
0
    DBG1(DBG_LIB, "!!! CLEANER ENUMERATOR: venumerate() missing !!!");
736
0
    return FALSE;
737
0
  }
738
0
  return this->wrapped->venumerate(this->wrapped, args);
739
0
}
740
741
/*
742
 * Described in header
743
 */
744
enumerator_t *enumerator_create_cleaner(enumerator_t *wrapped,
745
                    void (*cleanup)(void *data), void *data)
746
0
{
747
0
  cleaner_enumerator_t *this;
748
749
0
  INIT(this,
750
0
    .public = {
751
0
      .enumerate = enumerator_enumerate_default,
752
0
      .venumerate = _enumerate_cleaner,
753
0
      .destroy = _destroy_cleaner,
754
0
    },
755
0
    .wrapped = wrapped,
756
0
    .cleanup = cleanup,
757
0
    .data = data,
758
0
  );
759
0
  return &this->public;
760
0
}
761
762
/**
763
 * Enumerator for single enumerator
764
 */
765
typedef struct {
766
  enumerator_t public;
767
  void *item;
768
  void (*cleanup)(void *item);
769
  bool done;
770
} single_enumerator_t;
771
772
METHOD(enumerator_t, destroy_single, void,
773
  single_enumerator_t *this)
774
0
{
775
0
  if (this->cleanup)
776
0
  {
777
0
    this->cleanup(this->item);
778
0
  }
779
0
  free(this);
780
0
}
781
782
METHOD(enumerator_t, enumerate_single, bool,
783
  single_enumerator_t *this, va_list args)
784
0
{
785
0
  void **item;
786
787
0
  VA_ARGS_VGET(args, item);
788
0
  if (this->done)
789
0
  {
790
0
    return FALSE;
791
0
  }
792
0
  *item = this->item;
793
0
  this->done = TRUE;
794
0
  return TRUE;
795
0
}
796
797
/*
798
 * Described in header
799
 */
800
enumerator_t *enumerator_create_single(void *item, void (*cleanup)(void *item))
801
0
{
802
0
  single_enumerator_t *this;
803
804
0
  INIT(this,
805
0
    .public = {
806
0
      .enumerate = enumerator_enumerate_default,
807
0
      .venumerate = _enumerate_single,
808
0
      .destroy = _destroy_single,
809
0
    },
810
0
    .item = item,
811
0
    .cleanup = cleanup,
812
0
  );
813
0
  return &this->public;
814
0
}