Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 * Copyright (c) 2023, Sam Atkins <atkinssj@serenityos.org>
4 *
5 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8#include <AK/Vector.h>
9#include <LibCore/DirIterator.h>
10#include <errno.h>
11
12namespace Core {
13
14DirIterator::DirIterator(DeprecatedString path, Flags flags)
15 : m_path(move(path))
16 , m_flags(flags)
17{
18 m_dir = opendir(m_path.characters());
19 if (!m_dir) {
20 m_error = Error::from_errno(errno);
21 }
22}
23
24DirIterator::~DirIterator()
25{
26 if (m_dir) {
27 closedir(m_dir);
28 m_dir = nullptr;
29 }
30}
31
32DirIterator::DirIterator(DirIterator&& other)
33 : m_dir(other.m_dir)
34 , m_error(move(other.m_error))
35 , m_next(move(other.m_next))
36 , m_path(move(other.m_path))
37 , m_flags(other.m_flags)
38{
39 other.m_dir = nullptr;
40}
41
42bool DirIterator::advance_next()
43{
44 if (!m_dir)
45 return false;
46
47 while (true) {
48 errno = 0;
49 auto* de = readdir(m_dir);
50 if (!de) {
51 m_error = Error::from_errno(errno);
52 m_next.clear();
53 return false;
54 }
55
56 m_next = DirectoryEntry::from_dirent(*de);
57
58 if (m_next->name.is_empty())
59 return false;
60
61 if (m_flags & Flags::SkipDots && m_next->name.starts_with('.'))
62 continue;
63
64 if (m_flags & Flags::SkipParentAndBaseDir && (m_next->name == "." || m_next->name == ".."))
65 continue;
66
67 return !m_next->name.is_empty();
68 }
69}
70
71bool DirIterator::has_next()
72{
73 if (m_next.has_value())
74 return true;
75
76 return advance_next();
77}
78
79Optional<DirectoryEntry> DirIterator::next()
80{
81 if (!m_next.has_value())
82 advance_next();
83
84 auto result = m_next;
85 m_next.clear();
86 return result;
87}
88
89DeprecatedString DirIterator::next_path()
90{
91 auto entry = next();
92 if (entry.has_value())
93 return entry->name;
94 return "";
95}
96
97DeprecatedString DirIterator::next_full_path()
98{
99 StringBuilder builder;
100 builder.append(m_path);
101 if (!m_path.ends_with('/'))
102 builder.append('/');
103 builder.append(next_path());
104 return builder.to_deprecated_string();
105}
106
107int DirIterator::fd() const
108{
109 if (!m_dir)
110 return -1;
111 return dirfd(m_dir);
112}
113
114}