OSDN Git Service

am b245bc75: am 0f455612: Merge "Fix flaky test of timer_create_multiple in L." into...
[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 <errno.h>
20 #include <stdlib.h>
21 #include <string.h>
22
23 #include "private/bionic_macros.h"
24 #include "private/ScopedReaddir.h"
25
26 // A smart pointer to the scandir dirent**.
27 class 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;
53       dirent** new_names =
54           reinterpret_cast<dirent**>(realloc(names_, new_capacity * sizeof(dirent*)));
55       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) {
73       qsort(names_, size_, sizeof(dirent*),
74             reinterpret_cast<int (*)(const void*, const void*)>(comparator));
75     }
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);
86     dirent* copy = reinterpret_cast<dirent*>(malloc(size));
87     memcpy(copy, original, original->d_reclen);
88     return copy;
89   }
90
91   DISALLOW_COPY_AND_ASSIGN(ScandirResult);
92 };
93
94 int 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 }
118 __strong_alias(scandir64, scandir);