Root/src/filelister.cpp

Source at commit 73ceffa51d3450d2d20ce933e7fc6da187e09fc8 created 7 years 5 months ago.
By Maarten ter Huurne, Fix bug in section directory creation method
1/***************************************************************************
2 * Copyright (C) 2006 by Massimiliano Torromeo *
3 * massimiliano.torromeo@gmail.com *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
20
21#include "filelister.h"
22
23#include "debug.h"
24#include "utilities.h"
25
26//for browsing the filesystem
27#include <sys/stat.h>
28#include <sys/types.h>
29#include <dirent.h>
30#include <errno.h>
31#include <iostream>
32#include <algorithm>
33#include <cstring>
34#include <set>
35
36using namespace std;
37
38FileLister::FileLister()
39    : showDirectories(true)
40    , showUpdir(true)
41    , showFiles(true)
42{
43}
44
45void FileLister::setFilter(const string &filter)
46{
47    if (filter.empty() || filter == "*") {
48        this->filter.clear();
49    } else {
50        split(this->filter, filter, ",");
51    }
52}
53
54static void moveNames(set<string, case_less>&& from, vector<string>& to)
55{
56    to.reserve(from.size());
57    for (string const& name : from) {
58        // Casting away the const is necessary to make the move work.
59        // It will leave the set in an invalid state where it contains multiple
60        // empty strings, but since the set is an rvalue we don't care.
61        to.emplace_back(move(const_cast<string&>(name)));
62    }
63    to.shrink_to_fit();
64}
65
66bool FileLister::browse(const string& path, bool clean)
67{
68    if (clean) {
69        directories.clear();
70        files.clear();
71    }
72
73    string slashedPath = path;
74    if (!path.empty() && path[path.length() - 1] != '/') {
75        slashedPath.push_back('/');
76    }
77
78    DIR *dirp;
79    if ((dirp = opendir(slashedPath.c_str())) == NULL) {
80        if (errno != ENOENT) {
81            ERROR("Unable to open directory: %s\n", slashedPath.c_str());
82        }
83        return false;
84    }
85
86    set<string, case_less> directorySet;
87    set<string, case_less> fileSet;
88
89    while (struct dirent *dptr = readdir(dirp)) {
90        // Ignore hidden files and optionally "..".
91        if (dptr->d_name[0] == '.') {
92            if (!(dptr->d_name[1] == '.' && showUpdir && slashedPath != "/")) {
93                continue;
94            }
95        }
96
97        bool isDir, isFile;
98#ifdef _DIRENT_HAVE_D_TYPE
99        if (dptr->d_type != DT_UNKNOWN && dptr->d_type != DT_LNK) {
100            isDir = dptr->d_type == DT_DIR;
101            isFile = dptr->d_type == DT_REG;
102        } else
103#endif
104        {
105            string filepath = slashedPath + dptr->d_name;
106            struct stat st;
107            int statRet = stat(filepath.c_str(), &st);
108            if (statRet == -1) {
109                ERROR("Stat failed on '%s' with error '%s'\n", filepath.c_str(), strerror(errno));
110                continue;
111            }
112            isDir = S_ISDIR(st.st_mode);
113            isFile = S_ISREG(st.st_mode);
114        }
115
116        if (isDir) {
117            if (!showDirectories)
118                continue;
119
120            directorySet.insert(string(dptr->d_name));
121        } else if (isFile) {
122            if (!showFiles)
123                continue;
124
125            if (filter.empty()) {
126                fileSet.insert(string(dptr->d_name));
127                continue;
128            }
129
130            // Determine file extension.
131            const char *ext = strrchr(dptr->d_name, '.');
132            if (ext) ext++; else ext = "";
133
134            for (auto& filterExt : filter) {
135                // Note: strcasecmp can't compare multi-byte UTF-8 characters,
136                // but the filtered file extensions don't contain any of
137                // those.
138                if (strcasecmp(ext, filterExt.c_str()) == 0) {
139                    fileSet.insert(string(dptr->d_name));
140                    break;
141                }
142            }
143        }
144    }
145
146    closedir(dirp);
147
148    if (!directorySet.empty()) {
149        for (string& dir : directories) {
150            directorySet.emplace(move(dir));
151        }
152        directories.clear();
153        moveNames(move(directorySet), directories);
154    }
155
156    if (!fileSet.empty()) {
157        for (string& file : files) {
158            fileSet.emplace(move(file));
159        }
160        files.clear();
161        moveNames(move(fileSet), files);
162    }
163
164    return true;
165}
166
167string FileLister::operator[](uint x)
168{
169    const auto dirCount = directories.size();
170    return x < dirCount ? directories[x] : files[x - dirCount];
171}
172

Archive Download this file



interactive