
git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/trunk@7245 212acab6-be3b-0410-9dea-997c60f758d6
238 lines
5.4 KiB
C++
238 lines
5.4 KiB
C++
/*
|
|
* dirreader.cpp
|
|
*
|
|
* This file is a part of NSIS.
|
|
*
|
|
* Copyright (C) 1999-2021 Nullsoft and Contributors
|
|
*
|
|
* Licensed under the zlib/libpng license (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
*
|
|
* Licence details can be found in the file COPYING.
|
|
*
|
|
* This software is provided 'as-is', without any express or implied
|
|
* warranty.
|
|
*/
|
|
|
|
#include "Platform.h"
|
|
#include "dirreader.h"
|
|
#include "tstring.h"
|
|
#include "util.h"
|
|
#include <set>
|
|
|
|
#include <string.h> // for stricmp()
|
|
#include <ctype.h> // for tolower()
|
|
#ifdef _UNICODE
|
|
# include <wctype.h> // towlower()
|
|
#endif
|
|
|
|
using namespace std;
|
|
|
|
dir_reader::dir_reader() {
|
|
exclude(_T("."));
|
|
exclude(_T(".."));
|
|
}
|
|
|
|
const set<tstring>& dir_reader::files() {
|
|
return m_files;
|
|
}
|
|
|
|
const set<tstring>& dir_reader::dirs() {
|
|
return m_dirs;
|
|
}
|
|
|
|
void dir_reader::exclude(const tstring& spec) {
|
|
if (spec.find_first_of(_T("?*")) != tstring::npos) {
|
|
m_wildcard_excluded.insert(spec);
|
|
} else {
|
|
m_excluded.insert(spec);
|
|
}
|
|
}
|
|
|
|
void dir_reader::exclude(const set<tstring>& specs) {
|
|
iterator i = specs.begin();
|
|
iterator e = specs.end();
|
|
|
|
for (; i != e; i++) {
|
|
exclude(*i);
|
|
}
|
|
}
|
|
|
|
bool dir_reader::matches(const tstring& name, const tstring& spec) {
|
|
tstring::const_iterator name_itr = name.begin();
|
|
tstring::const_iterator name_end = name.end();
|
|
|
|
tstring::const_iterator spec_itr = spec.begin();
|
|
tstring::const_iterator spec_end = spec.end();
|
|
|
|
tstring::const_iterator last_good_spec = spec_end;
|
|
tstring::const_iterator last_good_name = name_end;
|
|
|
|
while (name_itr != name_end && spec_itr != spec_end) {
|
|
switch (*spec_itr) {
|
|
case _T('?'):
|
|
// question mark mathes one char
|
|
name_itr++;
|
|
spec_itr++;
|
|
break;
|
|
|
|
case _T('*'):
|
|
// double asterisk is the same as a single asterisk
|
|
while (*spec_itr == _T('*')) {
|
|
spec_itr++;
|
|
// asterisk at the end of the spec matches the end of the name
|
|
if (spec_itr == spec_end)
|
|
return true;
|
|
}
|
|
|
|
// remember last good name and spec for prematurely stopped asterisk
|
|
last_good_spec = spec_itr;
|
|
last_good_name = name_itr;
|
|
|
|
break;
|
|
|
|
default:
|
|
// Jim Park: This should work since tolower is templated with Chartype.
|
|
if (::tolower(*name_itr) != ::tolower(*spec_itr)) {
|
|
if (last_good_spec != spec_end) {
|
|
// matched wrong part of the name, try again
|
|
spec_itr = last_good_spec;
|
|
name_itr = ++last_good_name;
|
|
} else {
|
|
// no match and no asterisk to use
|
|
return false;
|
|
}
|
|
} else {
|
|
// remember last good name for prematurely stopped asterisk
|
|
last_good_name = name_itr;
|
|
|
|
spec_itr++;
|
|
name_itr++;
|
|
|
|
if (spec_itr == spec_end && name_itr != name_end && last_good_spec != spec_end) {
|
|
// asterisk hasn't matched enough, keep matching
|
|
spec_itr = last_good_spec;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// skip any redundant asterisks and periods at the end of the name
|
|
while (spec_itr != spec_end) {
|
|
if (*spec_itr != _T('.') && *spec_itr != _T('*')) {
|
|
break;
|
|
}
|
|
spec_itr++;
|
|
}
|
|
|
|
// return true only if managed to match everything
|
|
return name_itr == name_end && spec_itr == spec_end;
|
|
}
|
|
|
|
void dir_reader::add_file(const tstring& file) {
|
|
if (!is_excluded(file)) {
|
|
m_files.insert(file);
|
|
}
|
|
}
|
|
|
|
void dir_reader::add_dir(const tstring& dir) {
|
|
if (!is_excluded(dir)) {
|
|
m_dirs.insert(dir);
|
|
}
|
|
}
|
|
|
|
bool dir_reader::is_excluded(const tstring& name) const {
|
|
iterator i = m_excluded.begin();
|
|
iterator e = m_excluded.end();
|
|
|
|
for (; i != e; i++) {
|
|
if (!::_tcsicmp(name.c_str(), i->c_str())) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
i = m_wildcard_excluded.begin();
|
|
e = m_wildcard_excluded.end();
|
|
|
|
for (; i != e; i++) {
|
|
if (matches(name, *i)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
|
|
class win32_dir_reader : public dir_reader {
|
|
public:
|
|
|
|
virtual void read(const tstring& dir) {
|
|
WIN32_FIND_DATA fd;
|
|
|
|
tstring spec = dir + PLATFORM_PATH_SEPARATOR_STR + _T("*.*");
|
|
|
|
HANDLE h = ::FindFirstFile(spec.c_str(), &fd);
|
|
if (h != INVALID_HANDLE_VALUE) {
|
|
do {
|
|
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
|
dir_reader::add_dir(fd.cFileName);
|
|
else
|
|
dir_reader::add_file(fd.cFileName);
|
|
} while (::FindNextFile(h, &fd));
|
|
::FindClose(h);
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
#else
|
|
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <dirent.h>
|
|
|
|
class posix_dir_reader : public dir_reader {
|
|
public:
|
|
|
|
virtual void read(const tstring& dir) {
|
|
|
|
static const char platformpathsep[2] = {(char)PLATFORM_PATH_SEPARATOR_C, '\0'};
|
|
|
|
char *nativedir = NSISRT_ttombpath(dir.c_str());
|
|
if (!nativedir) return ;
|
|
|
|
DIR *dip = ::opendir(nativedir);
|
|
if (dip) {
|
|
dirent *dit;
|
|
while ((dit = ::readdir(dip))) {
|
|
struct stat st;
|
|
string file = nativedir;
|
|
file += platformpathsep, file += dit->d_name;
|
|
|
|
if (!stat(file.c_str(), &st)) {
|
|
tstring name;
|
|
name = PosixBug_CtoTString(dit->d_name);
|
|
if (S_ISDIR(st.st_mode))
|
|
dir_reader::add_dir(name);
|
|
else
|
|
dir_reader::add_file(name);
|
|
}
|
|
}
|
|
::closedir(dip);
|
|
}
|
|
NSISRT_free(nativedir);
|
|
}
|
|
};
|
|
|
|
#endif
|
|
|
|
dir_reader* new_dir_reader() {
|
|
#ifdef _WIN32
|
|
return new win32_dir_reader();
|
|
#else
|
|
return new posix_dir_reader();
|
|
#endif
|
|
}
|