OSDN Git Service

Merge "Add tests for <endian.h>."
[android-x86/bionic.git] / libc / bionic / scandir.cpp
1 /*
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 <fcntl.h>
20 #include <errno.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <unistd.h>
24
25 #include "private/bionic_macros.h"
26 #include "private/ScopedReaddir.h"
27
28 // A smart pointer to the scandir dirent**.
29 class ScandirResult {
30  public:
31   ScandirResult() : names_(nullptr), size_(0), capacity_(0) {
32   }
33
34   ~ScandirResult() {
35     while (size_ > 0) {
36       free(names_[--size_]);
37     }
38     free(names_);
39   }
40
41   size_t size() {
42     return size_;
43   }
44
45   dirent** release() {
46     dirent** result = names_;
47     names_ = nullptr;
48     size_ = capacity_ = 0;
49     return result;
50   }
51
52   bool Add(dirent* entry) {
53     if (size_ >= capacity_) {
54       size_t new_capacity = capacity_ + 32;
55       dirent** new_names =
56           reinterpret_cast<dirent**>(realloc(names_, new_capacity * sizeof(dirent*)));
57       if (new_names == nullptr) {
58         return false;
59       }
60       names_ = new_names;
61       capacity_ = new_capacity;
62     }
63
64     dirent* copy = CopyDirent(entry);
65     if (copy == nullptr) {
66       return false;
67     }
68     names_[size_++] = copy;
69     return true;
70   }
71
72   void Sort(int (*comparator)(const dirent**, const dirent**)) {
73     // If we have entries and a comparator, sort them.
74     if (size_ > 0 && comparator != nullptr) {
75       qsort(names_, size_, sizeof(dirent*),
76             reinterpret_cast<int (*)(const void*, const void*)>(comparator));
77     }
78   }
79
80  private:
81   dirent** names_;
82   size_t size_;
83   size_t capacity_;
84
85   static dirent* CopyDirent(dirent* original) {
86     // Allocate the minimum number of bytes necessary, rounded up to a 4-byte boundary.
87     size_t size = ((original->d_reclen + 3) & ~3);
88     dirent* copy = reinterpret_cast<dirent*>(malloc(size));
89     memcpy(copy, original, original->d_reclen);
90     return copy;
91   }
92
93   DISALLOW_COPY_AND_ASSIGN(ScandirResult);
94 };
95
96 int scandirat(int parent_fd, const char* dir_name, dirent*** name_list,
97               int (*filter)(const dirent*),
98               int (*comparator)(const dirent**, const dirent**)) {
99   DIR* dir = nullptr;
100   if (parent_fd == AT_FDCWD) {
101     dir = opendir(dir_name);
102   } else {
103     int dir_fd = openat(parent_fd, dir_name, O_CLOEXEC | O_DIRECTORY | O_RDONLY);
104     if (dir_fd != -1) {
105       dir = fdopendir(dir_fd);
106     }
107   }
108
109   ScopedReaddir reader(dir);
110   if (reader.IsBad()) {
111     return -1;
112   }
113
114   ScandirResult names;
115   dirent* entry;
116   while ((entry = reader.ReadEntry()) != nullptr) {
117     // If we have a filter, skip names that don't match.
118     if (filter != nullptr && !(*filter)(entry)) {
119       continue;
120     }
121     names.Add(entry);
122   }
123
124   names.Sort(comparator);
125
126   size_t size = names.size();
127   *name_list = names.release();
128   return size;
129 }
130 __strong_alias(scandirat64, scandirat);
131
132 int scandir(const char* dir_path, dirent*** name_list,
133             int (*filter)(const dirent*),
134             int (*comparator)(const dirent**, const dirent**)) {
135   return scandirat(AT_FDCWD, dir_path, name_list, filter, comparator);
136 }
137 __strong_alias(scandir64, scandir);