1 /*
2 SPDX-FileCopyrightText: 2020 Alexander Trufanov <trufanovan@gmail.com>
3 SPDX-License-Identifier: GPL-2.0-or-later
4 */
5
6 #include "nodeoperations.h"
7
8 #include <QThread>
9
10 namespace kt
11 {
getChild(FNode * root,const QString & name,bool is_dir)12 FNode *NodeOperations::getChild(FNode *root, const QString &name, bool is_dir)
13 {
14 FNode *child = root->first_child;
15 while (child && (child->name != name || child->is_dir != is_dir)) {
16 child = child->next;
17 }
18 return child;
19 }
20
addChild(FNode * root,const QString & name,bool is_dir)21 FNode *NodeOperations::addChild(FNode *root, const QString &name, bool is_dir)
22 {
23 FNode *n = new FNode();
24 n->parent = root;
25 n->name = name;
26 n->is_dir = is_dir;
27 if (!root->first_child) {
28 root->first_child = n;
29 } else {
30 FNode *last_child = root->first_child;
31 while (last_child->next)
32 last_child = last_child->next;
33 last_child->next = n;
34 n->prev = last_child;
35 }
36 return n;
37 }
38
removeNode(FNode * n)39 void NodeOperations::removeNode(FNode *n)
40 {
41 while (n->first_child) {
42 removeNode(n->first_child);
43 }
44
45 if (n->parent) {
46 if (n->parent->first_child == n) {
47 n->parent->first_child = n->next;
48 }
49 }
50
51 if (n->prev) {
52 n->prev->next = n->next;
53 }
54
55 if (n->next) {
56 n->next->prev = n->prev;
57 }
58
59 free(n);
60 }
61
makePath(FNode * root,const QString & fname,bool is_dir)62 FNode *NodeOperations::makePath(FNode *root, const QString &fname, bool is_dir)
63 {
64 int idx = fname.indexOf(QLatin1Char('/'));
65 FNode *existing;
66
67 if (idx == -1) {
68 existing = getChild(root, fname, is_dir);
69 if (existing)
70 return existing;
71 return addChild(root, fname, is_dir);
72 } else {
73 existing = getChild(root, fname.left(idx), true);
74 if (!existing)
75 existing = addChild(root, fname.left(idx), true);
76 return makePath(existing, fname.right(fname.size() - 1 - idx), is_dir);
77 }
78 }
79
findChild(FNode * root,const QString & fname,bool is_dir)80 FNode *NodeOperations::findChild(FNode *root, const QString &fname, bool is_dir)
81 {
82 int idx = fname.indexOf(QLatin1Char('/'));
83 if (idx == -1) {
84 return getChild(root, fname, is_dir);
85 } else {
86 FNode *n = getChild(root, fname.left(idx), true);
87 if (n)
88 n = findChild(n, fname.right(fname.size() - 1 - idx), is_dir);
89 return n;
90 }
91 }
92
fillFromDir(FNode * root,const QDir & dir)93 void NodeOperations::fillFromDir(FNode *root, const QDir &dir)
94 {
95 if (QThread::currentThread()->isInterruptionRequested()) {
96 return;
97 }
98
99 // QStringLists must be const to suppress "warning: c++11 range-loop might detach Qt container"
100 const QStringList sl_f = dir.entryList(QDir::NoDotAndDotDot | QDir::Files | QDir::Hidden);
101 for (const QString &s : sl_f) {
102 addChild(root, s, false);
103 }
104
105 const QStringList sl_d = dir.entryList(QDir::NoDotAndDotDot | QDir::Dirs | QDir::Hidden);
106 QDir next_dir;
107 for (const QString &s : sl_d) {
108 FNode *d = addChild(root, s, true);
109 next_dir.setPath(dir.path() + QLatin1String("/") + s);
110 fillFromDir(d, next_dir);
111 }
112 }
113
subtractTreesOnFiles(FNode * tree1,FNode * tree2)114 void NodeOperations::subtractTreesOnFiles(FNode *tree1, FNode *tree2)
115 {
116 if (QThread::currentThread()->isInterruptionRequested()) {
117 return;
118 }
119
120 FNode *c = tree2->first_child;
121 while (c) {
122 FNode *f = getChild(tree1, c->name, c->is_dir);
123 if (f) {
124 if (c->is_dir)
125 subtractTreesOnFiles(f, c);
126 else
127 removeNode(f);
128 }
129 c = c->next;
130 }
131 }
132
pruneEmptyFolders(FNode * start_folder)133 void NodeOperations::pruneEmptyFolders(FNode *start_folder)
134 {
135 FNode *c = start_folder->first_child;
136 while (c) {
137 if (c->is_dir)
138 pruneEmptyFolders(c);
139 c = c->next;
140 }
141
142 if (!start_folder->first_child) {
143 removeNode(start_folder);
144 }
145 }
146
pruneEmptyFolders(FNode * tree1,FNode * tree2)147 void NodeOperations::pruneEmptyFolders(FNode *tree1, FNode *tree2)
148 {
149 if (QThread::currentThread()->isInterruptionRequested()) {
150 return;
151 }
152
153 FNode *c = tree2->first_child;
154 while (c) {
155 if (c->is_dir) {
156 FNode *f = getChild(tree1, c->name, c->is_dir);
157 if (f) {
158 pruneEmptyFolders(f, c);
159 }
160 }
161 c = c->next;
162 }
163
164 if (!tree2->first_child) {
165 pruneEmptyFolders(tree1);
166 }
167 }
168
printTree(FNode * root,const QString & path,QSet<QString> & set)169 void NodeOperations::printTree(FNode *root, const QString &path, QSet<QString> &set)
170 {
171 if (QThread::currentThread()->isInterruptionRequested()) {
172 return;
173 }
174
175 QString new_path;
176 if (!root->name.isEmpty()) {
177 new_path = path + QLatin1String("/") + root->name;
178 set += new_path;
179 }
180
181 FNode *c = root->first_child;
182
183 while (c) {
184 if (c->is_dir) {
185 printTree(c, new_path, set);
186 } else {
187 set += new_path + QLatin1String("/") + c->name;
188 }
189 c = c->next;
190 }
191 }
192
printTree(FNode * root,QSet<QString> & set)193 void NodeOperations::printTree(FNode *root, QSet<QString> &set)
194 {
195 QString path;
196 printTree(root, path, set);
197 }
198
199 }
200