blob: ee62feee429243ac1cc9b2b4ae314050cd32ca98 [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>
Elliott Hughes76f89162015-01-26 13:34:58 -080021#include <string.h>
Elliott Hughes701bec22013-02-25 13:14:31 -080022
Elliott Hughes8eac9af2014-05-09 19:12:08 -070023#include "private/bionic_macros.h"
Elliott Hughes701bec22013-02-25 13:14:31 -080024#include "private/ScopedReaddir.h"
25
26// A smart pointer to the scandir dirent**.
27class ScandirResult {
28 public:
29 ScandirResult() : names_(NULL), size_(0), capacity_(0) {
30 }
31
32 ~ScandirResult() {
33 while (size_ > 0) {
34 free(names_[--size_]);
35 }
36 free(names_);
37 }
38
39 size_t size() {
40 return size_;
41 }
42
43 dirent** release() {
44 dirent** result = names_;
45 names_ = NULL;
46 size_ = capacity_ = 0;
47 return result;
48 }
49
50 bool Add(dirent* entry) {
51 if (size_ >= capacity_) {
52 size_t new_capacity = capacity_ + 32;
Elliott Hughes8b5df392015-01-21 16:19:07 -080053 dirent** new_names =
54 reinterpret_cast<dirent**>(realloc(names_, new_capacity * sizeof(dirent*)));
Elliott Hughes701bec22013-02-25 13:14:31 -080055 if (new_names == NULL) {
56 return false;
57 }
58 names_ = new_names;
59 capacity_ = new_capacity;
60 }
61
62 dirent* copy = CopyDirent(entry);
63 if (copy == NULL) {
64 return false;
65 }
66 names_[size_++] = copy;
67 return true;
68 }
69
70 void Sort(int (*comparator)(const dirent**, const dirent**)) {
71 // If we have entries and a comparator, sort them.
72 if (size_ > 0 && comparator != NULL) {
Elliott Hughes8b5df392015-01-21 16:19:07 -080073 qsort(names_, size_, sizeof(dirent*),
74 reinterpret_cast<int (*)(const void*, const void*)>(comparator));
Elliott Hughes701bec22013-02-25 13:14:31 -080075 }
76 }
77
78 private:
79 dirent** names_;
80 size_t size_;
81 size_t capacity_;
82
83 static dirent* CopyDirent(dirent* original) {
84 // Allocate the minimum number of bytes necessary, rounded up to a 4-byte boundary.
85 size_t size = ((original->d_reclen + 3) & ~3);
Elliott Hughes8b5df392015-01-21 16:19:07 -080086 dirent* copy = reinterpret_cast<dirent*>(malloc(size));
Elliott Hughes701bec22013-02-25 13:14:31 -080087 memcpy(copy, original, original->d_reclen);
88 return copy;
89 }
90
Elliott Hughes8eac9af2014-05-09 19:12:08 -070091 DISALLOW_COPY_AND_ASSIGN(ScandirResult);
Elliott Hughes701bec22013-02-25 13:14:31 -080092};
93
94int scandir(const char* dirname, dirent*** name_list,
95 int (*filter)(const dirent*),
96 int (*comparator)(const dirent**, const dirent**)) {
97 ScopedReaddir reader(dirname);
98 if (reader.IsBad()) {
99 return -1;
100 }
101
102 ScandirResult names;
103 dirent* entry;
104 while ((entry = reader.ReadEntry()) != NULL) {
105 // If we have a filter, skip names that don't match.
106 if (filter != NULL && !(*filter)(entry)) {
107 continue;
108 }
109 names.Add(entry);
110 }
111
112 names.Sort(comparator);
113
114 size_t size = names.size();
115 *name_list = names.release();
116 return size;
117}
Elliott Hughesdb1ea342014-01-17 18:42:49 -0800118__strong_alias(scandir64, scandir);