blob: dd22b2254dc19c4e0026670fffd62db30f862a56 [file] [log] [blame]
Elliott Hughes701bec22013-02-25 13:14:31 -08001/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <dirent.h>
18
19#include <errno.h>
20#include <stdlib.h>
21
22#include "private/ScopedReaddir.h"
23
24// A smart pointer to the scandir dirent**.
25class ScandirResult {
26 public:
27 ScandirResult() : names_(NULL), size_(0), capacity_(0) {
28 }
29
30 ~ScandirResult() {
31 while (size_ > 0) {
32 free(names_[--size_]);
33 }
34 free(names_);
35 }
36
37 size_t size() {
38 return size_;
39 }
40
41 dirent** release() {
42 dirent** result = names_;
43 names_ = NULL;
44 size_ = capacity_ = 0;
45 return result;
46 }
47
48 bool Add(dirent* entry) {
49 if (size_ >= capacity_) {
50 size_t new_capacity = capacity_ + 32;
51 dirent** new_names = (dirent**) realloc(names_, new_capacity * sizeof(dirent*));
52 if (new_names == NULL) {
53 return false;
54 }
55 names_ = new_names;
56 capacity_ = new_capacity;
57 }
58
59 dirent* copy = CopyDirent(entry);
60 if (copy == NULL) {
61 return false;
62 }
63 names_[size_++] = copy;
64 return true;
65 }
66
67 void Sort(int (*comparator)(const dirent**, const dirent**)) {
68 // If we have entries and a comparator, sort them.
69 if (size_ > 0 && comparator != NULL) {
70 qsort(names_, size_, sizeof(dirent*), (int (*)(const void*, const void*)) comparator);
71 }
72 }
73
74 private:
75 dirent** names_;
76 size_t size_;
77 size_t capacity_;
78
79 static dirent* CopyDirent(dirent* original) {
80 // Allocate the minimum number of bytes necessary, rounded up to a 4-byte boundary.
81 size_t size = ((original->d_reclen + 3) & ~3);
82 dirent* copy = (dirent*) malloc(size);
83 memcpy(copy, original, original->d_reclen);
84 return copy;
85 }
86
87 // Disallow copy and assignment.
88 ScandirResult(const ScandirResult&);
89 void operator=(const ScandirResult&);
90};
91
92int scandir(const char* dirname, dirent*** name_list,
93 int (*filter)(const dirent*),
94 int (*comparator)(const dirent**, const dirent**)) {
95 ScopedReaddir reader(dirname);
96 if (reader.IsBad()) {
97 return -1;
98 }
99
100 ScandirResult names;
101 dirent* entry;
102 while ((entry = reader.ReadEntry()) != NULL) {
103 // If we have a filter, skip names that don't match.
104 if (filter != NULL && !(*filter)(entry)) {
105 continue;
106 }
107 names.Add(entry);
108 }
109
110 names.Sort(comparator);
111
112 size_t size = names.size();
113 *name_list = names.release();
114 return size;
115}