ui->setupUi(this);
initializeHome();
m_statusMessage = new QLabel(tr("Welcome at reidos"));
+ ui->statusBar->addWidget(m_statusMessage);
if (!startDir.isEmpty())
ui->fileTableTop->comboBoxPath->setCurrentText(startDir);
ui->fileTableTop->fileSystem = new ReLocalFileSystem("/", m_logger);
ui->fileTableTop->fillTable();
+ ui->fileTableTop->announcer = this;
ui->fileTableBottom->fileSystem = new ReLocalFileSystem("/", m_logger);
ui->fileTableBottom->fillTable();
+ ui->fileTableTop->announcer = this;
QString dir(ui->fileTableTop->comboBoxPath->currentText());
if (dir.isEmpty())
dir = startDir;
} else {
rc = path;
}
- return rc = cleanPath(rc);
+ return cleanPath(rc.constData());
}
/**
/** Normalizes a file path.
*
- * Removes duplicated slashes and "." and "..", but not leading ".."
+ * Removes duplicated slashes and "." and "..", but not leading "..".
+ * Change the 2nd separator to the native separator, e.g. "/" to "\\"
*
* @param path path to clean
* @return the path without duplicated separators and "." and ".."
int minLength = 0;
#ifdef __WIN32__
// UNC path, e.g. "\\server\share"?
- if (path[0] == OS_SEPARATOR && path[1] == OS_SEPARATOR) {
+ if ((path[0] == OS_SEPARATOR || path[0] == OS_2nd_SEPARATOR)
+ && (path[1] == OS_SEPARATOR || path[1] == OS_2nd_SEPARATOR)) {
rc.append("\\\\");
path += 2;
minLength = 2;
}
#endif
- const char* ptr;
- if (path[0] == OS_SEPARATOR) {
- rc.append(OS_SEPARATOR);
+ char cc = *path;
+ int startNode = 0;
+ if (cc == OS_SEPARATOR || cc == OS_2nd_SEPARATOR){
path++;
+ startNode++;
+ rc.append(OS_SEPARATOR);
}
- while ((ptr = strchr(path, OS_SEPARATOR)) != NULL) {
+ while ((cc = *path++) != '\0') {
+ if (cc != OS_SEPARATOR && cc != OS_2nd_SEPARATOR)
+ rc.append(cc);
// ignore duplicated slashes:
- if (ptr != path) {
- int length = ptr - path;
- if (length == 1 && path[0] == '.') {
- // ignore ".": do nothing
- } else if (length == 2 && path[0] == '.' && path[1] == '.') {
+ else if (rc.length() > 0 && rc.at(rc.length() - 1) != OS_SEPARATOR){
+ int length = rc.length() - startNode;
+ if (length == 1 && rc.at(startNode) == '.') {
+ // ignore ".": remove it:
+ rc.resize(startNode);
+ } else if (length == 2 && rc.at(startNode) == '.' &&rc.at(startNode + 1) == '.') {
+ // remove "..":
+ rc.resize(startNode);
// remove the last slash and node
if (rc.length() > minLength) {
rc.resize(rc.size() - 1);
int ix = rc.lastIndexOf(OS_SEPARATOR);
if (ix > minLength)
rc.resize(ix + 1);
+ startNode = rc.length();
}
-
} else {
- // copy with separator:
- rc.append(path, length + 1);
+ rc.append(OS_SEPARATOR);
+ startNode = rc.length();
}
-
}
- path = ptr + 1;
}
- if (path[0] != '\0') {
- if (path[0] == '.' && path[1] == '\0') {
- if (rc.size() == 0)
- rc.append('.');
- } else if (path[0] == '.' && path[1] == '.' && path[2] == '\0' && rc.length() > 0) {
- // remove the last slash and node
- if (rc.size() > minLength) {
- rc.resize(rc.size() - 1);
- int ix = rc.lastIndexOf(OS_SEPARATOR);
- if (ix > minLength)
- rc.resize(ix);
- }
- } else {
- // copy with separator:
- rc.append(path);
+ length = rc.length() - startNode;
+ if (length == 1 && rc.at(startNode) == '.') {
+ // ignore ".": remove it:
+ rc.resize(startNode);
+ } else if (length == 2 && rc.at(startNode) == '.'
+ && startNode > 0 &&rc.at(startNode + 1) == '.') {
+ // remove "..":
+ rc.resize(startNode);
+ // remove the last slash and node
+ if (rc.length() > minLength) {
+ rc.resize(rc.size() - 1);
+ int ix = rc.lastIndexOf(OS_SEPARATOR);
+ if (ix > minLength)
+ rc.resize(ix);
}
}
return rc;
}
+/** Normalizes a file path.
+ *
+ * Removes duplicated slashes and "." and "..", but not leading "..".
+ * Change the 2nd separator to the native separator, e.g. "/" to "\\"
+ *
+ * @param path path to clean
+ * @return the path without duplicated separators and "." and ".."
+ */
+QString ReFileUtils::cleanPath(const QString& path) {
+ return (QString) cleanPath(path.toUtf8().constData());
+}
+
/**
* Reads a string from a given file.
*
static bool deleteTree(const QString& path, bool withBase,
ReLogger* logger);
static QByteArray cleanPath(const char* path);
+ static QString cleanPath(const QString& path);
static QString extensionOf(const QString& filename);
static QByteArray extensionOf(const char* filename);
static bool isAbsolutPath(const QString& path);
* matches the text<br>
* <code>false</code>: none of the patterns matches
*/
-bool ReListMatcher::matches(const QString& text) {
+bool ReListMatcher::matches(const QString& text) const {
QList<ReMatcher*>::const_iterator it;
bool rc = m_list.size() == 0;
for (it = m_list.cbegin(); !rc && it != m_list.cend(); ++it) {
* @return <code>true</code>: at least one of the include patterns
* matches and none of the exclude patterns matches
*/
-bool ReIncludeExcludeMatcher::matches(const QString& text, bool excludeToo) {
+bool ReIncludeExcludeMatcher::matches(const QString& text, bool excludeToo) const {
bool rc = m_includes.matches(text);
if (rc && excludeToo && !m_excludes.empty())
rc = !m_excludes.matches(text);
}
start = ix + 1;
}
+ if (patterns.length() > start){
+ if (patterns.at(start) == excludeMarker)
+ excludes.append(patterns.mid(start + 1));
+ else
+ includes.append(patterns.mid(start));
+ }
m_includes.setPatterns(includes, m_includes.caseSensivitiy(),
m_includes.anchored());
m_excludes.setPatterns(excludes, m_excludes.caseSensivitiy(),
bool allMatching() const;
Qt::CaseSensitivity caseSensivitiy() const;
bool empty() const;
- bool matches(const QString& text);
+ bool matches(const QString& text) const;
const QStringList& patterns() const;
void setCaseSensivitiy(const Qt::CaseSensitivity& caseSensivitiy);
void setPatterns(const QStringList& patterns,
Qt::CaseSensitive, bool anchored = false);
public:
Qt::CaseSensitivity caseSensivitiy() const;
- bool matches(const QString& text, bool excludeToo = true);
+ bool matches(const QString& text, bool excludeToo = true) const;
const ReListMatcher& includes() const;
const ReListMatcher& excludes() const;
void setCaseSensivitiy(const Qt::CaseSensitivity& caseSensivitiy);
#define _strcasecmp strcasecmp
#define OS_SEPARATOR '/'
#define OS_SEPARATOR_STR "/"
+#define OS_2nd_SEPARATOR '\\'
+#define OS_2nd_SEPARATOR_STR "\\"
#define _mkdir(path) mkdir(path, -1)
#define _memicmp memicmp
#else
#define _strcasecmp _stricmp
#define OS_SEPARATOR '\\'
#define OS_SEPARATOR_STR "\\"
+#define OS_2nd_SEPARATOR '/'
+#define OS_2nd_SEPARATOR_STR "/"
#endif
#define UNUSED_VAR(var) (void) var
void testReException();
void testReQStringUtil();
void testReStringUtil();
+ void testReFileUtils();
void testReWriter();
void testReFile();
- void testReFileUtils();
void testReMatcher();
testReFileUtils();
testReRandomizer();
testReFileSystem();
}
void allTests() {
- testOs();
testBase();
+ testOs();
testGui();
if (s_allTest) {
testBase();
class TestReFileUtils: public ReTest {
public:
TestReFileUtils() :
- ReTest("ReFileUtils") {
+ ReTest("ReFileUtils") {
doIt();
}
}
void testTempDirEmpty() {
QByteArray dir(
- ReFileUtils::tempDirEmpty("subdir2", "cuReFileUtils", true));
+ ReFileUtils::tempDirEmpty("subdir2", "cuReFileUtils", true));
QByteArray subdir(dir);
subdir.append("subdirX");
mkdir(subdir.constData(), ALLPERMS);
QByteArray fn(ReFileUtils::tempFile("timetest.txt", NULL, true));
ReFileUtils::writeToFile(fn.constData(), "");
QDateTime time = QDateTime::fromString("03.09.2015 07:14:24.432",
- "dd.MM.yyyy hh:mm:ss.zzz");
+ "dd.MM.yyyy hh:mm:ss.zzz");
checkT(
- ReFileUtils::setTimes(fn.constData(), time,
- ReFileUtils::m_undefinedTime, &m_logger));
+ ReFileUtils::setTimes(fn.constData(), time,
+ ReFileUtils::m_undefinedTime, &m_logger));
QFileInfo info(fn);
checkEqu(time, info.lastModified());
}
checkEqu("x/y/z.x/", ReFileUtils::cleanPath("x//y/////z.x//"));
// remove "./"
checkEqu("x/y/z.x", ReFileUtils::cleanPath("./x/././y/z.x"));
+ checkEqu("/x/y/z.x", ReFileUtils::cleanPath("/x/././y/z.x"));
// remove "..":
// inside...
+ checkEqu("x/a/b", ReFileUtils::cleanPath("x/y/../a/b"));
checkEqu("x/y/a/b", ReFileUtils::cleanPath("x/y/z/../a/b"));
checkEqu("x/a/b", ReFileUtils::cleanPath("x/y/z/../../a/b"));
// at the end..
checkEqu("x", ReFileUtils::cleanPath("x/y/z/../.."));
+ checkEqu("x/", ReFileUtils::cleanPath("x/y/z/../../"));
// wrong forms:
checkEqu("..", ReFileUtils::cleanPath(".."));
checkEqu("../..", ReFileUtils::cleanPath("../.."));
assertEquals(QString(exp), ReFileUtils::extensionOf(QString(sArg1)),
__FILE__, lineNo);
assertEquals(exp.constData(),
- ReFileUtils::extensionOf(sArg1.constData()),
- __FILE__, lineNo);
+ ReFileUtils::extensionOf(sArg1.constData()),
+ __FILE__, lineNo);
}
void testExtensionOf() {
checkNodeOf("", "", __LINE__);
}
+ void testParentOf(){
+ checkEqu("/abc/", ReFileUtils::parentOf("/abc/def"));
+ checkEqu("/abc/def/x.y/", ReFileUtils::parentOf("/abc/def/x.y/"));
+ checkEqu("/", ReFileUtils::parentOf("/"));
+ checkEqu("", ReFileUtils::parentOf("abc.def"));
+ }
+
void checkPathAppend(const char* expected, const char* arg1,
- const char* arg2, int lineNo) {
+ const char* arg2, int lineNo) {
assertEquals(QString(expected),
- ReFileUtils::pathAppend(QString(arg1), QString(arg2)),
- __FILE__, lineNo);
+ ReFileUtils::pathAppend(QString(arg1), QString(arg2)),
+ __FILE__, lineNo);
assertEquals(expected, ReFileUtils::pathAppend(arg1, arg2),
__FILE__, lineNo);
QByteArray exp(expected);
sArg1.replace("/", "\\");
sArg2.replace("/", "\\");
assertEquals(QString(exp),
- ReFileUtils::pathAppend(QString(sArg1), QString(sArg2)),
- __FILE__, lineNo);
+ ReFileUtils::pathAppend(QString(sArg1), QString(sArg2)),
+ __FILE__, lineNo);
assertEquals(exp,
- ReFileUtils::pathAppend(sArg1.constData(), sArg2.constData()),
- __FILE__, lineNo);
+ ReFileUtils::pathAppend(sArg1.constData(), sArg2.constData()),
+ __FILE__, lineNo);
}
void testPathAppend() {
checkPathAppend("/abc", "/", "bef", __LINE__);
}
void checkReplaceExt(const char* expected, const char* arg1,
- const char* arg2, int lineNo) {
+ const char* arg2, int lineNo) {
assertEquals(QString(expected),
- ReFileUtils::replaceExtension(QString(arg1), QString(arg2)),
- __FILE__, lineNo);
+ ReFileUtils::replaceExtension(QString(arg1), QString(arg2)),
+ __FILE__, lineNo);
assertEquals(expected, ReFileUtils::replaceExtension(arg1, arg2),
__FILE__, lineNo);
QByteArray exp(expected);
sArg1.replace("/", "\\");
sArg2.replace("/", "\\");
assertEquals(QString(exp),
- ReFileUtils::replaceExtension(QString(sArg1), QString(sArg2)),
- __FILE__, lineNo);
+ ReFileUtils::replaceExtension(QString(sArg1), QString(sArg2)),
+ __FILE__, lineNo);
assertEquals(exp,
- ReFileUtils::replaceExtension(sArg1.constData(), sArg2.constData()),
- __FILE__, lineNo);
+ ReFileUtils::replaceExtension(sArg1.constData(), sArg2.constData()),
+ __FILE__, lineNo);
}
void testReplaceExtension() {
}
virtual void run() {
+ testParentOf();
+ testCleanPath();
testReplaceExtension();
testNodeOf();
testExtensionOf();
- testCleanPath();
testSetTimes();
testSeekTell();
testIsAbsolutePath();
}
if (combo->count() > 20)
combo->removeItem(20);
+ if (combo->currentText() != value)
+ combo->setCurrentText(value);
}
}
tableWidget->setColumnWidth(MODIFIED, 175);
connect(pushButtonUp, SIGNAL(clicked()), SLOT(pushButtonUpClicked()));
connect(pushButtonRoot, SIGNAL(clicked()), SLOT(pushButtonRootClicked()));
-
+ connect(tableWidget, SIGNAL(cellDoubleClicked(int, int)), this,
+ SLOT(tableDoubleClicked(int, int)));
tableWidget->setHorizontalHeaderLabels(labels);
tableWidget->horizontalHeader()->setStretchLastSection(true);
tableWidget->setSelectionMode(QAbstractItemView::ExtendedSelection);
}
}
+/**
+ * Change the include/exclude patterns of the file table.
+ *
+ * @param patterns a comma separated list of patterns
+ */
+void ReFileTable::changePatterns(const QString& patterns)
+{
+ // update the history:
+ comboText(comboBoxPatterns);
+ matcher.setPatterns(patterns, ',', '-');
+ fillTable();
+}
+
/**
* Changes the current directory.
*
* @param directory full path of the new directory
+ * @return <code>true</code>: success<br>
+ * <code>false</code>: error
*/
-void ReFileTable::chDir(const QString& directory){
+bool ReFileTable::changeDirectory(QString directory){
+ bool rc = true;
+ directory = ReFileUtils::cleanPath(directory);
+ ReQStringUtils::ensureLastChar(directory, OS_SEPARATOR);
if (directory != fileSystem->directory()){
if (directory.indexOf('*') < 0){
- if (fileSystem->setDirectory(directory) != ReFileSystem::EC_SUCCESS){
- say(LOG_ERROR, tr("unknown directory:") + " " + directory);
+ ReFileSystem::ErrorCode rc2 = fileSystem->setDirectory(directory);
+ if (rc2 != ReFileSystem::EC_SUCCESS){
+ rc = say(LOG_ERROR, fileSystem->errorMessage(rc2) + " " + directory);
} else {
comboBoxPath->setCurrentText(directory);
// update the history:
}
}
+ return rc;
}
/**
if (sender == comboBoxPath){
if (key == Qt::Key_Return){
if (modifiers == Qt::NoModifier)
- chDir(comboBoxPath->currentText());
+ changeDirectory(comboBoxPath->currentText());
}
} else if (sender == comboBoxPatterns){
-
+ if (key == Qt::Key_Return && modifiers == Qt::NoModifier)
+ changePatterns(comboBoxPatterns->currentText());
} else if (sender == tableWidget){
-
+ if (key == Qt::Key_Return && modifiers == Qt::NoModifier)
+ openEntry(tableWidget->currentRow());
}
}
+
+/**
+ * Starts the standard program for the given table entry.
+ *
+ * @param row the row containing the entry to open
+ */
+void ReFileTable::openEntry(int row){
+ bool isDir = tableWidget->item(row, TYPE)->text().startsWith('<');
+ QString node = tableWidget->item(row, NAME)->text();
+ if (isDir){
+ changeDirectory(fileSystem->directory() + node);
+ } else {
+
+ }
+}
+
/**
* Handles the event push button "up" clicked()
*/
void ReFileTable::pushButtonUpClicked()
{
- chDir(ReFileUtils::parentOf(comboBoxPath->currentText()));
+ QString path(fileSystem->directory());
+ path.resize(max(0, path.length() - 1));
+ if (! path.isEmpty())
+ changeDirectory(ReFileUtils::parentOf(path));
}
/**
*/
void ReFileTable::pushButtonRootClicked()
{
- chDir("/");
+ changeDirectory("/");
}
/**
return level >= LOG_INFO;
}
-
+/**
+ * Handles the double click of a table cell.
+ *
+ * @param index the abstract index of the cell
+ */
+void ReFileTable::tableDoubleClicked(int row, int column){
+ openEntry(row);
+}
public:
void fillTable();
- void chDir(const QString& directory);
-protected slots:
- void keyPressEvent(QKeyEvent* event);
+ void changePatterns(const QString& patterns);
+ bool changeDirectory(QString directory);
+public:
+ virtual void keyPressEvent(QKeyEvent* event);
+public slots:
+ void tableDoubleClicked(int row, int column);
void pushButtonUpClicked();
void pushButtonRootClicked();
+protected:
+ void openEntry(int row);
protected:
QVBoxLayout* mainLayout;
QHBoxLayout* horizontalLayout;
return rc;
}
+/**
+ * Returns a message describing the given error code.
+ *
+ * @param errorCode code to convert
+ * @return a description of the error code
+ */
+QString ReFileSystem::errorMessage(ReFileSystem::ErrorCode errorCode)
+{
+ QString rc;
+ switch(errorCode){
+ case EC_SUCCESS:
+ rc = QObject::tr("Success");
+ break;
+ case EC_PATH_NOT_FOUND:
+ rc = QObject::tr("Path not found");
+ break;
+ case EC_NOT_ACCESSIBLE:
+ rc = QObject::tr("not accessable");
+ break;
+ case EC_NOT_READABLE:
+ rc = QObject::tr("not readable");
+ break;
+ case EC_READ:
+ rc = QObject::tr("cannot read");
+ break;
+ case EC_FS_READ_ONLY:
+ rc = QObject::tr("file is read only");
+ break;
+ case EC_NOT_WRITEABLE:
+ rc = QObject::tr("file is not writeable");
+ break;
+ case EC_WRITE:
+ rc = QObject::tr("cannot write");
+ break;
+ case EC_POSITION:
+ rc = QObject::tr("cannot set new file position");
+ break;
+ case EC_ALREADY_EXISTS:
+ rc = QObject::tr("file already exists");
+ break;
+ case EC_NOT_EXISTS:
+ rc = QObject::tr("file does not exist");
+ break;
+ case EC_RENAME:
+ rc = QObject::tr("file cannot renamed");
+ break;
+ case EC_HEADER_LENGTH:
+ rc = QObject::tr("Header length mismatch");
+ break;
+ case EC_MARKER:
+ rc = QObject::tr("marker mismatch");
+ break;
+ default:
+ rc = QObject::tr("unknown error code: ") + QString::number(errorCode);
+ break;
+ }
+ return rc;
+}
+
/**
* Returns the name of the current directory.
*
* Fills a list with the items of the current directory.
*
* @param matcher the matching processor
+ * @param list OUT: the list of the found files
+ * @param options a set (bitmap) of options, e.g. LO_FILES | LO_DIRS
* @return the count of the found entries (<code>list.size()</code>)
*/
int ReLocalFileSystem::listInfos(const ReIncludeExcludeMatcher& matcher,
- ReFileMetaDataList& list) {
+ ReFileMetaDataList& list, ListOptions options) {
list.clear();
+ bool withDirs = (options & LO_DIRS) != 0;
+ bool withFiles = (options & LO_FILES) != 0;
+ if (! (withDirs | withFiles))
+ withDirs = withFiles = true;
+ bool matchDirs = (options & LO_NAME_FILTER_FOR_DIRS) != 0;
+ bool earlyMatching = matchDirs || ! withDirs;
const QStringList& patterns = matcher.includes().patterns();
QStringList nodes =
- patterns.size() == 0 ? m_dir.entryList() : m_dir.entryList(patterns);
+ ! earlyMatching || patterns.size() == 0
+ ? m_dir.entryList() : m_dir.entryList(patterns);
QStringList::const_iterator it;
QByteArray full = m_directory.toUtf8();
full.append(OS_SEPARATOR);
int pathLength = full.length();
struct stat info;
+ const ReListMatcher& excludeMatcher = matcher.excludes();
+ bool excludeActive = excludeMatcher.patterns().length() > 0;
for (it = nodes.cbegin(); it != nodes.cend(); it++) {
QString node = *it;
if (node != "." && node != ".."){
+ if (earlyMatching){
+ if (excludeActive && excludeMatcher.matches(node))
+ continue;
+ }
full.resize(pathLength);
full.append(node.toUtf8());
if (stat(full.constData(), &info) == 0) {
+ bool isDir = S_ISDIR(info.st_mode);
+ if (isDir && ! withDirs || ! isDir && ! withFiles)
+ continue;
+ if (! earlyMatching){
+ if ( (! isDir || matchDirs) && ! matcher.matches(node))
+ continue;
+ }
list.append(
ReFileMetaData(node, QDateTime::fromTime_t(info.st_mtime),
QDateTime::fromTime_t(info.st_ctime), info.st_uid,
class ReFileSystem {
public:
- enum ListOption {
+ enum ListOptions {
LO_UNDEF = 0,
LO_FILES = 1,
LO_DIRS = 2,
- LO_EXCLUDE_MATCH_FILES = 4,
- LO_EXCLUDE_MATCH_DIRS = 8
+ LO_NAME_FILTER_FOR_DIRS = 4,
};
enum ErrorCode {
virtual const QString& directory() const;
/** Fills a list with the items of the current directory.
* @param matcher the matching processor
+ * @param options a set (bitmap) of options, e.g. LO_FILES | LO_DIRS
* @return the count of the found entries (<code>list.size()</code>)
*/
virtual int listInfos(const ReIncludeExcludeMatcher& matcher,
- ReFileMetaDataList& list) = 0;
+ ReFileMetaDataList& list, ListOptions opts
+ = ListOptions(LO_FILES | LO_DIRS )) = 0;
/** Creates a directory.
* @param node the name without path (in the current directory)
* @return EC_SUCCESS or error code
const QByteArray& buffer) = 0;
public:
virtual ErrorCode copy(ReFileMetaData& source, ReFileSystem& sourceFS);
+ virtual QString errorMessage(ErrorCode rc);
public:
int blocksize() const;
bool first(const QString& pattern, ReFileMetaData& file);
// ReFileSystem interface
virtual void close();
virtual int listInfos(const ReIncludeExcludeMatcher& matcher,
- ReFileMetaDataList& list);
+ ReFileMetaDataList& list, ListOptions options);
ErrorCode makeDir(const QString& node);
virtual ErrorCode read(const ReFileMetaData& source, int64_t offset,
int size, QByteArray& buffer);