Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f1f0599267 |
65
.clang-format
Normal file
65
.clang-format
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
---
|
||||||
|
Language: Cpp
|
||||||
|
# BasedOnStyle: LLVM
|
||||||
|
AccessModifierOffset: -2
|
||||||
|
AlignAfterOpenBracket: true
|
||||||
|
AlignEscapedNewlinesLeft: false
|
||||||
|
AlignOperands: true
|
||||||
|
AlignTrailingComments: true
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: true
|
||||||
|
AllowShortBlocksOnASingleLine: false
|
||||||
|
AllowShortCaseLabelsOnASingleLine: false
|
||||||
|
AllowShortIfStatementsOnASingleLine: false
|
||||||
|
AllowShortLoopsOnASingleLine: false
|
||||||
|
AllowShortFunctionsOnASingleLine: All
|
||||||
|
AlwaysBreakAfterDefinitionReturnType: false
|
||||||
|
AlwaysBreakTemplateDeclarations: false
|
||||||
|
AlwaysBreakBeforeMultilineStrings: false
|
||||||
|
BreakBeforeBinaryOperators: None
|
||||||
|
BreakBeforeTernaryOperators: true
|
||||||
|
BreakConstructorInitializersBeforeComma: false
|
||||||
|
BinPackParameters: true
|
||||||
|
BinPackArguments: true
|
||||||
|
ColumnLimit: 80
|
||||||
|
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||||
|
ConstructorInitializerIndentWidth: 4
|
||||||
|
DerivePointerAlignment: false
|
||||||
|
ExperimentalAutoDetectBinPacking: false
|
||||||
|
IndentCaseLabels: false
|
||||||
|
IndentWrappedFunctionNames: false
|
||||||
|
IndentFunctionDeclarationAfterType: false
|
||||||
|
MaxEmptyLinesToKeep: 1
|
||||||
|
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||||
|
NamespaceIndentation: None
|
||||||
|
ObjCBlockIndentWidth: 2
|
||||||
|
ObjCSpaceAfterProperty: false
|
||||||
|
ObjCSpaceBeforeProtocolList: true
|
||||||
|
PenaltyBreakBeforeFirstCallParameter: 19
|
||||||
|
PenaltyBreakComment: 300
|
||||||
|
PenaltyBreakString: 1000
|
||||||
|
PenaltyBreakFirstLessLess: 120
|
||||||
|
PenaltyExcessCharacter: 1000000
|
||||||
|
PenaltyReturnTypeOnItsOwnLine: 60
|
||||||
|
PointerAlignment: Right
|
||||||
|
SpacesBeforeTrailingComments: 1
|
||||||
|
Cpp11BracedListStyle: true
|
||||||
|
Standard: Cpp11
|
||||||
|
IndentWidth: 4
|
||||||
|
TabWidth: 4
|
||||||
|
UseTab: true
|
||||||
|
BreakBeforeBraces: Attach
|
||||||
|
SpacesInParentheses: false
|
||||||
|
SpacesInSquareBrackets: false
|
||||||
|
SpacesInAngles: false
|
||||||
|
SpaceInEmptyParentheses: false
|
||||||
|
SpacesInCStyleCastParentheses: false
|
||||||
|
SpaceAfterCStyleCast: false
|
||||||
|
SpacesInContainerLiterals: true
|
||||||
|
SpaceBeforeAssignmentOperators: true
|
||||||
|
ContinuationIndentWidth: 4
|
||||||
|
CommentPragmas: '^ IWYU pragma:'
|
||||||
|
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
|
||||||
|
SpaceBeforeParens: ControlStatements
|
||||||
|
DisableFormat: false
|
||||||
|
...
|
||||||
|
|
||||||
25
.gitignore
vendored
Normal file
25
.gitignore
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
*.o
|
||||||
|
*.d
|
||||||
|
*.exe
|
||||||
|
build
|
||||||
|
|
||||||
|
*/.vs/*
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
VisualStudio/Debug
|
||||||
|
VisualStudio/filesync/Debug
|
||||||
|
VisualStudio/Release
|
||||||
|
VisualStudio/filesync/Release
|
||||||
|
MonoDevelop/FileSync/bin
|
||||||
|
*.user
|
||||||
|
/filesync.exe
|
||||||
|
/filesync
|
||||||
|
VisualStudio/filesync/x64/*
|
||||||
|
*.pdb
|
||||||
|
*.ilk
|
||||||
|
*.VC.db
|
||||||
|
*.VC.opendb
|
||||||
|
tests/*
|
||||||
|
.vs
|
||||||
|
|
||||||
|
/.issues/
|
||||||
50
.vscode/c_cpp_properties.json
vendored
Normal file
50
.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
{
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Mac",
|
||||||
|
"includePath": [
|
||||||
|
"/usr/include",
|
||||||
|
"${workspaceFolder}",
|
||||||
|
"${workspaceFolder}/src/"
|
||||||
|
],
|
||||||
|
"intelliSenseMode": "clang-x64",
|
||||||
|
"macFrameworkPath": [
|
||||||
|
"/System/Library/Frameworks",
|
||||||
|
"/Library/Frameworks"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Linux",
|
||||||
|
"includePath": [
|
||||||
|
"/usr/include",
|
||||||
|
"${workspaceFolder}",
|
||||||
|
"${workspaceFolder}/src/"
|
||||||
|
],
|
||||||
|
"intelliSenseMode": "linux-gcc-x64",
|
||||||
|
"compilerPath": "/usr/bin/gcc",
|
||||||
|
"cStandard": "c17",
|
||||||
|
"cppStandard": "c++17"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Win32",
|
||||||
|
"includePath": [
|
||||||
|
"C:/msys64/mingw64/include",
|
||||||
|
"C:/msys64/mingw64/x86_64-w64-mingw32/include",
|
||||||
|
"${workspaceFolder}",
|
||||||
|
"${workspaceFolder}/src/"
|
||||||
|
],
|
||||||
|
"defines": [
|
||||||
|
"WIN32",
|
||||||
|
"_DEBUG",
|
||||||
|
"UNICODE",
|
||||||
|
"_UNICODE"
|
||||||
|
],
|
||||||
|
"windowsSdkVersion": "10.0.18362.0",
|
||||||
|
"compilerPath": "C:/msys64/mingw64/bin/g++.exe",
|
||||||
|
"cStandard": "c17",
|
||||||
|
"cppStandard": "c++17",
|
||||||
|
"intelliSenseMode": "windows-gcc-x64"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version": 4
|
||||||
|
}
|
||||||
25
.vscode/launch.json
vendored
Normal file
25
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "(gdb) Launch",
|
||||||
|
"type": "cppdbg",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${workspaceRoot}/filesync.exe",
|
||||||
|
"args": ["copy","D:\\source","F:\\source"],
|
||||||
|
"stopAtEntry": false,
|
||||||
|
"cwd": "${workspaceRoot}",
|
||||||
|
"environment": [],
|
||||||
|
"externalConsole": true,
|
||||||
|
"MIMode": "gdb",
|
||||||
|
"miDebuggerPath": "C:\\msys64\\mingw64\\bin\\gdb.exe",
|
||||||
|
"setupCommands": [
|
||||||
|
{
|
||||||
|
"description": "Enable pretty-printing for gdb",
|
||||||
|
"text": "-enable-pretty-printing",
|
||||||
|
"ignoreFailures": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
5
.vscode/settings.json
vendored
Normal file
5
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"files.associations": {
|
||||||
|
"*.h": "c"
|
||||||
|
}
|
||||||
|
}
|
||||||
21
LICENSE.txt
Normal file
21
LICENSE.txt
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014-2021 Valeriano Alfonso Rodriguez
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
102
Makefile
Normal file
102
Makefile
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
# Copyright (c) 2014-2021 Valeriano Alfonso Rodriguez
|
||||||
|
|
||||||
|
IsMinGW := $(findstring MSYS,$(shell uname -s))$(findstring MINGW,$(shell uname -s))
|
||||||
|
ifneq (,$(IsMinGW))
|
||||||
|
RES_APP := filesync.exe
|
||||||
|
BUILDDIR := build-$(shell gcc -dumpmachine)
|
||||||
|
else
|
||||||
|
RES_APP := filesync
|
||||||
|
BUILDDIR := build-$(shell gcc -dumpmachine)
|
||||||
|
endif
|
||||||
|
VERBOSE_BUILD=false
|
||||||
|
|
||||||
|
CC := gcc
|
||||||
|
RM := rm -f
|
||||||
|
ECHO := echo
|
||||||
|
MKDIR := mkdir
|
||||||
|
|
||||||
|
HEADS := \
|
||||||
|
src/util.h \
|
||||||
|
src/crc.h \
|
||||||
|
src/fileutil.h \
|
||||||
|
src/parameteroperation.h \
|
||||||
|
src/filenode.h \
|
||||||
|
src/actionfilenode.h \
|
||||||
|
src/actionfilenodesync.h \
|
||||||
|
src/actionfilenodecopy.h
|
||||||
|
|
||||||
|
OBJS_BASE := \
|
||||||
|
$(BUILDDIR)/util.o \
|
||||||
|
$(BUILDDIR)/crc.o \
|
||||||
|
$(BUILDDIR)/fileutil.o \
|
||||||
|
$(BUILDDIR)/parameteroperation.o \
|
||||||
|
$(BUILDDIR)/filenode.o \
|
||||||
|
$(BUILDDIR)/actionfilenode.o \
|
||||||
|
$(BUILDDIR)/actionfilenodesync.o \
|
||||||
|
$(BUILDDIR)/actionfilenodecopy.o
|
||||||
|
|
||||||
|
OBJS_APP := \
|
||||||
|
$(OBJS_BASE) \
|
||||||
|
$(BUILDDIR)/main.o
|
||||||
|
|
||||||
|
CFLAGS := -g
|
||||||
|
LIBS := -lm
|
||||||
|
|
||||||
|
|
||||||
|
ifeq ($(VERBOSE_BUILD),true)
|
||||||
|
DO_CC=$(CC) $(CFLAGS) -o $@ -c $<
|
||||||
|
DO_CXX=$(CXX) $(CFLAGS) -o $@ -c $<
|
||||||
|
else
|
||||||
|
DO_CC=@$(ECHO) "CC: $@" ;\
|
||||||
|
$(CC) $(CFLAGS) -o $@ -c $<
|
||||||
|
DO_CXX=@$(ECHO) "CXX: $@" ;\
|
||||||
|
$(CXX) $(CFLAGS) -o $@ -c $<
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
all: $(RES_APP)
|
||||||
|
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(RM) $(RES_APP)
|
||||||
|
$(RM) $(OBJS_APP)
|
||||||
|
|
||||||
|
$(BUILDDIR):
|
||||||
|
@-$(MKDIR) $(BUILDDIR)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$(BUILDDIR)/util.o: src/util.c $(HEADS)
|
||||||
|
$(DO_CC)
|
||||||
|
|
||||||
|
$(BUILDDIR)/crc.o: src/crc.c $(HEADS)
|
||||||
|
$(DO_CC)
|
||||||
|
|
||||||
|
$(BUILDDIR)/fileutil.o: src/fileutil.c $(HEADS)
|
||||||
|
$(DO_CC)
|
||||||
|
|
||||||
|
$(BUILDDIR)/parameteroperation.o: src/parameteroperation.c $(HEADS)
|
||||||
|
$(DO_CC)
|
||||||
|
|
||||||
|
$(BUILDDIR)/filenode.o: src/filenode.c $(HEADS)
|
||||||
|
$(DO_CC)
|
||||||
|
|
||||||
|
$(BUILDDIR)/actionfilenode.o: src/actionfilenode.c $(HEADS)
|
||||||
|
$(DO_CC)
|
||||||
|
|
||||||
|
$(BUILDDIR)/actionfilenodesync.o: src/actionfilenodesync.c $(HEADS)
|
||||||
|
$(DO_CC)
|
||||||
|
|
||||||
|
$(BUILDDIR)/actionfilenodecopy.o: src/actionfilenodecopy.c $(HEADS)
|
||||||
|
$(DO_CC)
|
||||||
|
|
||||||
|
$(BUILDDIR)/main.o: src/main.c $(HEADS)
|
||||||
|
$(DO_CC)
|
||||||
|
|
||||||
|
|
||||||
|
$(RES_APP): $(BUILDDIR) $(OBJS_APP)
|
||||||
|
@$(ECHO) "LINK: $@"
|
||||||
|
@$(CC) $(OBJS_APP) \
|
||||||
|
-o $(RES_APP) $(LIBS)
|
||||||
|
|
||||||
72
README.md
Normal file
72
README.md
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
# FileSync
|
||||||
|
Simple local filesystem file synchronization. For use with external devices (usbdisk) and SneakerNet like usage.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
Copy the resulting executable to the desired location.
|
||||||
|
|
||||||
|
The executable is completelly portable, there are no dependencies.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
Basic usage to syncronize two directories, dirA and dirB:
|
||||||
|
|
||||||
|
filesync -sync -dir dirA -dir dirB
|
||||||
|
|
||||||
|
To make a efficient copy from dirA to dirB:
|
||||||
|
|
||||||
|
filesync -copy -dir dirA -dir dirB
|
||||||
|
|
||||||
|
The rest of options are listed with no parameters:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ filesync
|
||||||
|
Parameters:
|
||||||
|
-dir [Item]: Specify a directory.
|
||||||
|
-nocheck: Do not check for changes on directories.
|
||||||
|
-dummy: Do not perform operations.
|
||||||
|
-copy: Copy first directory to second directory.
|
||||||
|
-sync: Synchronize between two directories.
|
||||||
|
-log [Item]: Log actions to file.
|
||||||
|
-scan [Item] [Item]: Scan directory and save to filenode file.
|
||||||
|
-rescan [Item] [Item]: Rescan directory and save to filenode file.
|
||||||
|
-read [Item]: Read filenode file.
|
||||||
|
-check [Item]: Check changes on a directory.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Building
|
||||||
|
There is a GNU Make compatible Makefile usable on Linux and MingGW.
|
||||||
|
|
||||||
|
make
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
1. Fork it!
|
||||||
|
2. Create your feature branch: `git checkout -b my-new-feature`
|
||||||
|
3. Commit your changes: `git commit -am 'Add some feature'`
|
||||||
|
4. Push to the branch: `git push origin my-new-feature`
|
||||||
|
5. Submit a pull request :D
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
* Valeriano Alfonso Rodriguez.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014-2021 Valeriano Alfonso Rodriguez
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
id: 1
|
|
||||||
title: Catalogo de directorios sincronizables en cada volumen
|
|
||||||
state: open
|
|
||||||
tags: enhancement
|
|
||||||
|
|
||||||
Catalogo de directorios sincronizables en cada volumen. Para agilizar utilización.
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
id: 2
|
|
||||||
title: Corregir bug con caracteres especiales (no ASCII-7) en Windows
|
|
||||||
state: open
|
|
||||||
tags: bug
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
id: 3
|
|
||||||
title: Mejorar informacion de progreso
|
|
||||||
state: open
|
|
||||||
tags: enhancement
|
|
||||||
|
|
||||||
|
|
||||||
469
src/actionfilenode.c
Normal file
469
src/actionfilenode.c
Normal file
@@ -0,0 +1,469 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright (c) 2014-2021 Valeriano Alfonso Rodriguez
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "actionfilenode.h"
|
||||||
|
#include "crc.h"
|
||||||
|
#include "filenode.h"
|
||||||
|
#include "fileutil.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
ActionFileNode _actionFileNodeFree = NULL;
|
||||||
|
#define AccionFileNode_Block 1024
|
||||||
|
ActionFileNode ActionFileNode_Create() {
|
||||||
|
ActionFileNode actionFileNode;
|
||||||
|
|
||||||
|
if (_actionFileNodeFree == NULL) {
|
||||||
|
ActionFileNode actionFileNodeFreeAux;
|
||||||
|
int i;
|
||||||
|
// Allocate block
|
||||||
|
actionFileNodeFreeAux =
|
||||||
|
malloc(sizeof(TActionFileNode) * AccionFileNode_Block);
|
||||||
|
if (actionFileNodeFreeAux == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
for (i = 0; i < AccionFileNode_Block - 1; i++) {
|
||||||
|
actionFileNodeFreeAux[i].next = &actionFileNodeFreeAux[i + 1];
|
||||||
|
}
|
||||||
|
actionFileNodeFreeAux[AccionFileNode_Block - 1].next = NULL;
|
||||||
|
_actionFileNodeFree = &actionFileNodeFreeAux[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the first free
|
||||||
|
actionFileNode = _actionFileNodeFree;
|
||||||
|
_actionFileNodeFree = actionFileNode->next;
|
||||||
|
|
||||||
|
// Initialize
|
||||||
|
actionFileNode->action = ActionFileCmp_Nothing;
|
||||||
|
actionFileNode->left = NULL;
|
||||||
|
actionFileNode->right = NULL;
|
||||||
|
actionFileNode->result = ActionFileNodeResult_Nothing;
|
||||||
|
actionFileNode->next = NULL;
|
||||||
|
|
||||||
|
return (actionFileNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AccionFileNode_Destroy(ActionFileNode actionFileNode) {
|
||||||
|
actionFileNode->next = _actionFileNodeFree;
|
||||||
|
_actionFileNodeFree = actionFileNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
ActionFileNode ActionFileNode_CreateNormal(FileNode fileNodeLeft,
|
||||||
|
FileNode fileNodeRight) {
|
||||||
|
ActionFileNode actionFileNode;
|
||||||
|
actionFileNode = ActionFileNode_Create();
|
||||||
|
actionFileNode->action = ActionFileCmp_Nothing;
|
||||||
|
actionFileNode->left = fileNodeLeft;
|
||||||
|
actionFileNode->right = fileNodeRight;
|
||||||
|
return actionFileNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AccionFileNode_CompareChilds(
|
||||||
|
ActionFileNode actionFileNodeRoot, ActionFileNode *actionFileNodeQueue,
|
||||||
|
void (*CheckPair)(FileNode fileNodeLeft, FileNode fileNodeRight,
|
||||||
|
ActionFileNode *actionFileNodeQueue)) {
|
||||||
|
FileNode fileNodeLeft;
|
||||||
|
FileNode fileNodeRight;
|
||||||
|
FileNode fileNodeRightQueue;
|
||||||
|
FileNode fileNodeRightProcessed;
|
||||||
|
FileNode fileNodeRightPrevious;
|
||||||
|
|
||||||
|
// Check if there is something to do
|
||||||
|
if (!actionFileNodeRoot->left && !actionFileNodeRoot->right) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// There is no left part
|
||||||
|
if (!actionFileNodeRoot->left) {
|
||||||
|
fileNodeRight = actionFileNodeRoot->right->child;
|
||||||
|
while (fileNodeRight) {
|
||||||
|
CheckPair(NULL, fileNodeRight, actionFileNodeQueue);
|
||||||
|
|
||||||
|
fileNodeRight = fileNodeRight->next;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// There is no right part
|
||||||
|
if (!actionFileNodeRoot->right) {
|
||||||
|
fileNodeLeft = actionFileNodeRoot->left->child;
|
||||||
|
while (fileNodeLeft) {
|
||||||
|
CheckPair(fileNodeLeft, NULL, actionFileNodeQueue);
|
||||||
|
|
||||||
|
fileNodeLeft = fileNodeLeft->next;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare chains
|
||||||
|
fileNodeRightQueue = actionFileNodeRoot->right->child;
|
||||||
|
fileNodeRightProcessed = NULL;
|
||||||
|
|
||||||
|
// Iterate left child FileNodes
|
||||||
|
fileNodeLeft = actionFileNodeRoot->left->child;
|
||||||
|
while (fileNodeLeft) {
|
||||||
|
fileNodeRightPrevious = NULL;
|
||||||
|
fileNodeRight = fileNodeRightQueue;
|
||||||
|
while (fileNodeRight) {
|
||||||
|
if (!strcmp(fileNodeLeft->name, fileNodeRight->name)) {
|
||||||
|
// Match, extract right child FileNode to the processed chain
|
||||||
|
if (fileNodeRightPrevious) {
|
||||||
|
fileNodeRightPrevious->next = fileNodeRight->next;
|
||||||
|
} else {
|
||||||
|
fileNodeRightQueue = fileNodeRight->next;
|
||||||
|
}
|
||||||
|
fileNodeRight->next = fileNodeRightProcessed;
|
||||||
|
fileNodeRightProcessed = fileNodeRight;
|
||||||
|
|
||||||
|
CheckPair(fileNodeLeft, fileNodeRight, actionFileNodeQueue);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
// Next right child
|
||||||
|
fileNodeRightPrevious = fileNodeRight;
|
||||||
|
fileNodeRight = fileNodeRight->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!fileNodeRight) {
|
||||||
|
CheckPair(fileNodeLeft, NULL, actionFileNodeQueue);
|
||||||
|
}
|
||||||
|
fileNodeLeft = fileNodeLeft->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate unprocessed right childs
|
||||||
|
fileNodeRight = fileNodeRightQueue;
|
||||||
|
while (fileNodeRight) {
|
||||||
|
CheckPair(NULL, fileNodeRight, actionFileNodeQueue);
|
||||||
|
fileNodeRightPrevious = fileNodeRight;
|
||||||
|
fileNodeRight = fileNodeRight->next;
|
||||||
|
fileNodeRightPrevious->next = fileNodeRightProcessed;
|
||||||
|
fileNodeRightProcessed = fileNodeRightPrevious;
|
||||||
|
}
|
||||||
|
actionFileNodeRoot->right->child = fileNodeRightProcessed;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ActionFileNode_Statistics(ActionFileNode actionFileNode,
|
||||||
|
ActionQueueStatistics *statistics,
|
||||||
|
ActionFileNodeResult result) {
|
||||||
|
statistics->readLeft = 0;
|
||||||
|
statistics->writeLeft = 0;
|
||||||
|
statistics->readRight = 0;
|
||||||
|
statistics->writeRight = 0;
|
||||||
|
statistics->deleteLeft = 0;
|
||||||
|
statistics->deleteRight = 0;
|
||||||
|
|
||||||
|
statistics->fullCopyCount = 0;
|
||||||
|
statistics->dateCopyCount = 0;
|
||||||
|
statistics->directoryCount = 0;
|
||||||
|
statistics->deleteCount = 0;
|
||||||
|
|
||||||
|
while (actionFileNode != NULL) {
|
||||||
|
if (actionFileNode->result != result) {
|
||||||
|
actionFileNode = actionFileNode->next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (actionFileNode->action) {
|
||||||
|
case ActionFileCmp_Nothing:
|
||||||
|
break;
|
||||||
|
case ActionFileCmp_LeftToRight:
|
||||||
|
statistics->fullCopyCount++;
|
||||||
|
statistics->readLeft += actionFileNode->left->size;
|
||||||
|
statistics->writeRight += actionFileNode->left->size;
|
||||||
|
break;
|
||||||
|
case ActionFileCmp_RightToLeft:
|
||||||
|
statistics->fullCopyCount++;
|
||||||
|
statistics->writeLeft += actionFileNode->right->size;
|
||||||
|
statistics->readRight += actionFileNode->right->size;
|
||||||
|
break;
|
||||||
|
case ActionFileCmp_DeleteLeft:
|
||||||
|
statistics->deleteCount++;
|
||||||
|
statistics->deleteLeft += actionFileNode->left->size;
|
||||||
|
break;
|
||||||
|
case ActionFileCmp_DeleteRight:
|
||||||
|
statistics->deleteCount++;
|
||||||
|
statistics->deleteRight += actionFileNode->right->size;
|
||||||
|
break;
|
||||||
|
case ActionFileCmp_DateLeftToRight:
|
||||||
|
statistics->dateCopyCount++;
|
||||||
|
break;
|
||||||
|
case ActionFileCmp_DateRightToLeft:
|
||||||
|
statistics->dateCopyCount++;
|
||||||
|
break;
|
||||||
|
case ActionFileCmp_MakeRightDirectory:
|
||||||
|
statistics->directoryCount++;
|
||||||
|
break;
|
||||||
|
case ActionFileCmp_MakeLeftDirectory:
|
||||||
|
statistics->directoryCount++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
actionFileNode = actionFileNode->next;
|
||||||
|
}
|
||||||
|
return (statistics->fullCopyCount + statistics->dateCopyCount +
|
||||||
|
statistics->directoryCount + statistics->deleteCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionFileNode_Print(ActionFileNode actionFileNode) {
|
||||||
|
char showPath[MaxPath];
|
||||||
|
while (actionFileNode != NULL) {
|
||||||
|
if (actionFileNode->left) {
|
||||||
|
FileNode_GetFullPath(actionFileNode->left, "", showPath);
|
||||||
|
} else {
|
||||||
|
FileNode_GetFullPath(actionFileNode->right, "", showPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (actionFileNode->action) {
|
||||||
|
case ActionFileCmp_Nothing:
|
||||||
|
// printff("%s == %s\n",pathIzq,pathDer);
|
||||||
|
break;
|
||||||
|
case ActionFileCmp_LeftToRight:
|
||||||
|
Print(". => %s\n", showPath);
|
||||||
|
break;
|
||||||
|
case ActionFileCmp_RightToLeft:
|
||||||
|
Print(". <= %s\n", showPath);
|
||||||
|
break;
|
||||||
|
case ActionFileCmp_DeleteLeft:
|
||||||
|
Print(". *- %s\n", showPath);
|
||||||
|
break;
|
||||||
|
case ActionFileCmp_DeleteRight:
|
||||||
|
Print(". -* %s\n", showPath);
|
||||||
|
break;
|
||||||
|
case ActionFileCmp_DateLeftToRight:
|
||||||
|
Print(". -> %s\n", showPath);
|
||||||
|
break;
|
||||||
|
case ActionFileCmp_DateRightToLeft:
|
||||||
|
Print(". <- %s\n", showPath);
|
||||||
|
break;
|
||||||
|
case ActionFileCmp_MakeRightDirectory:
|
||||||
|
Print(". -D %s\n", showPath);
|
||||||
|
break;
|
||||||
|
case ActionFileCmp_MakeLeftDirectory:
|
||||||
|
Print(". D- %s\n", showPath);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
actionFileNode = actionFileNode->next;
|
||||||
|
}
|
||||||
|
Print("End\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AccionFileNodeAux_CopyDate(char *pathOrig, char *pathDest) {
|
||||||
|
FileTime ft = FileTime_Get(pathOrig);
|
||||||
|
FileTime_Set(pathDest, ft);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AccionFileNodeAux_Copy(char *pathOrig, char *pathDest) {
|
||||||
|
if (File_Copy(pathOrig, pathDest)) {
|
||||||
|
return AccionFileNodeAux_CopyDate(pathOrig, pathDest);
|
||||||
|
} else {
|
||||||
|
File_Delete(pathDest);
|
||||||
|
Print("Error Copying to: %s, %s\n", pathDest, GetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool AccionFileNodeAux_Delete(char *pathOrig, char *pathDest) {
|
||||||
|
if (File_IsDirectory(pathDest)) {
|
||||||
|
if (File_DeleteDirectory(pathDest) == 0) {
|
||||||
|
Print("Error Deleting Directory: %s, %s\n", pathDest, GetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (File_Delete(pathDest) == 0) {
|
||||||
|
Print("Error Deleting File: %s, %s\n", pathDest, GetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool AccionFileNodeAux_MakeDir(char *pathOrig, char *pathDest) {
|
||||||
|
if (File_MakeDirectory(pathDest) == 0) {
|
||||||
|
Print("Error Making Directory: %s, %s\n", pathDest, GetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int ActionFileNode_RunList(ActionFileNode actionFileNode, char *pathLeft,
|
||||||
|
char *pathRight) {
|
||||||
|
int numActions = 0;
|
||||||
|
char fullPathLeft[MaxPath], fullPathRight[MaxPath], showPath[MaxPath];
|
||||||
|
while (actionFileNode != NULL) {
|
||||||
|
if (actionFileNode->left) {
|
||||||
|
FileNode_GetFullPath(actionFileNode->left, pathLeft, fullPathLeft);
|
||||||
|
FileNode_GetFullPath(actionFileNode->left, "", showPath);
|
||||||
|
} else {
|
||||||
|
FileNode_GetFullPath(actionFileNode->right, pathLeft, fullPathLeft);
|
||||||
|
FileNode_GetFullPath(actionFileNode->right, "", showPath);
|
||||||
|
}
|
||||||
|
if (actionFileNode->right) {
|
||||||
|
FileNode_GetFullPath(actionFileNode->right, pathRight,
|
||||||
|
fullPathRight);
|
||||||
|
} else {
|
||||||
|
FileNode_GetFullPath(actionFileNode->left, pathRight,
|
||||||
|
fullPathRight);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (actionFileNode->action) {
|
||||||
|
case ActionFileCmp_Nothing:
|
||||||
|
// printff("%s == %s\n",pathIzq,pathDer);
|
||||||
|
break;
|
||||||
|
case ActionFileCmp_LeftToRight:
|
||||||
|
Print(" => %s\n", showPath);
|
||||||
|
if (AccionFileNodeAux_Copy(fullPathLeft, fullPathRight)) {
|
||||||
|
actionFileNode->result = ActionFileNodeResult_Ok;
|
||||||
|
} else {
|
||||||
|
actionFileNode->result = ActionFileNodeResult_Error;
|
||||||
|
}
|
||||||
|
numActions++;
|
||||||
|
break;
|
||||||
|
case ActionFileCmp_RightToLeft:
|
||||||
|
Print(" <= %s\n", showPath);
|
||||||
|
if (AccionFileNodeAux_Copy(fullPathRight, fullPathLeft)) {
|
||||||
|
actionFileNode->result = ActionFileNodeResult_Ok;
|
||||||
|
} else {
|
||||||
|
actionFileNode->result = ActionFileNodeResult_Error;
|
||||||
|
}
|
||||||
|
numActions++;
|
||||||
|
break;
|
||||||
|
case ActionFileCmp_DeleteLeft:
|
||||||
|
Print(" *- %s\n", showPath);
|
||||||
|
if (AccionFileNodeAux_Delete(fullPathRight, fullPathLeft)) {
|
||||||
|
actionFileNode->result = ActionFileNodeResult_Ok;
|
||||||
|
} else {
|
||||||
|
actionFileNode->result = ActionFileNodeResult_Error;
|
||||||
|
}
|
||||||
|
numActions++;
|
||||||
|
break;
|
||||||
|
case ActionFileCmp_DeleteRight:
|
||||||
|
Print(" -* %s\n", showPath);
|
||||||
|
if (AccionFileNodeAux_Delete(fullPathLeft, fullPathRight)) {
|
||||||
|
actionFileNode->result = ActionFileNodeResult_Ok;
|
||||||
|
} else {
|
||||||
|
actionFileNode->result = ActionFileNodeResult_Error;
|
||||||
|
}
|
||||||
|
numActions++;
|
||||||
|
break;
|
||||||
|
case ActionFileCmp_DateLeftToRight:
|
||||||
|
Print(" -> %s\n", showPath);
|
||||||
|
if (AccionFileNodeAux_CopyDate(fullPathLeft, fullPathRight)) {
|
||||||
|
actionFileNode->result = ActionFileNodeResult_Ok;
|
||||||
|
} else {
|
||||||
|
actionFileNode->result = ActionFileNodeResult_Error;
|
||||||
|
}
|
||||||
|
numActions++;
|
||||||
|
break;
|
||||||
|
case ActionFileCmp_DateRightToLeft:
|
||||||
|
Print(" <- %s\n", showPath);
|
||||||
|
if (AccionFileNodeAux_CopyDate(fullPathRight, fullPathLeft)) {
|
||||||
|
actionFileNode->result = ActionFileNodeResult_Ok;
|
||||||
|
} else {
|
||||||
|
actionFileNode->result = ActionFileNodeResult_Error;
|
||||||
|
}
|
||||||
|
numActions++;
|
||||||
|
break;
|
||||||
|
case ActionFileCmp_MakeRightDirectory:
|
||||||
|
Print(" -D %s\n", showPath);
|
||||||
|
if (AccionFileNodeAux_MakeDir(fullPathLeft, fullPathRight)) {
|
||||||
|
actionFileNode->result = ActionFileNodeResult_Ok;
|
||||||
|
} else {
|
||||||
|
actionFileNode->result = ActionFileNodeResult_Error;
|
||||||
|
}
|
||||||
|
numActions++;
|
||||||
|
break;
|
||||||
|
case ActionFileCmp_MakeLeftDirectory:
|
||||||
|
Print(" D- %s\n", showPath);
|
||||||
|
if (AccionFileNodeAux_MakeDir(fullPathRight, fullPathLeft)) {
|
||||||
|
actionFileNode->result = ActionFileNodeResult_Ok;
|
||||||
|
} else {
|
||||||
|
actionFileNode->result = ActionFileNodeResult_Error;
|
||||||
|
}
|
||||||
|
numActions++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
actionFileNode = actionFileNode->next;
|
||||||
|
}
|
||||||
|
Print("End\n");
|
||||||
|
return numActions;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Common utilities
|
||||||
|
|
||||||
|
#define QueueNode(queue, node) \
|
||||||
|
(queue)->next = (node); \
|
||||||
|
(queue) = (node);
|
||||||
|
|
||||||
|
void AccionFileNode_DeletePair(FileNode fileNodeLeft, FileNode fileNodeRight,
|
||||||
|
ActionFileNode *actionFileNodeQueue) {
|
||||||
|
ActionFileNode actionFileNodeNew =
|
||||||
|
ActionFileNode_CreateNormal(fileNodeLeft, fileNodeRight);
|
||||||
|
|
||||||
|
if (!fileNodeLeft && !fileNodeRight) {
|
||||||
|
AccionFileNode_Destroy(actionFileNodeNew);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!fileNodeLeft && fileNodeRight) {
|
||||||
|
if (fileNodeRight->flags & FileFlag_Directory) {
|
||||||
|
// Iterate childs for deletion
|
||||||
|
AccionFileNode_CompareChilds(actionFileNodeNew, actionFileNodeQueue,
|
||||||
|
AccionFileNode_DeletePair);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileNodeRight->status != FileStatus_Deleted) {
|
||||||
|
// Node delete action
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_DeleteRight;
|
||||||
|
QueueNode(*actionFileNodeQueue, actionFileNodeNew);
|
||||||
|
} else {
|
||||||
|
AccionFileNode_Destroy(actionFileNodeNew);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fileNodeLeft && !fileNodeRight) {
|
||||||
|
if (fileNodeLeft->flags & FileFlag_Directory) {
|
||||||
|
// Iterate childs for deletion
|
||||||
|
AccionFileNode_CompareChilds(actionFileNodeNew, actionFileNodeQueue,
|
||||||
|
AccionFileNode_DeletePair);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileNodeLeft->status != FileStatus_Deleted) {
|
||||||
|
// Node delete action
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_DeleteLeft;
|
||||||
|
QueueNode(*actionFileNodeQueue, actionFileNodeNew);
|
||||||
|
} else {
|
||||||
|
AccionFileNode_Destroy(actionFileNodeNew);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fileNodeLeft && fileNodeRight) {
|
||||||
|
if ((fileNodeLeft->flags & FileFlag_Directory) ||
|
||||||
|
(fileNodeRight->flags & FileFlag_Directory)) {
|
||||||
|
// One is Directory
|
||||||
|
|
||||||
|
// Iterate childs for deletion
|
||||||
|
AccionFileNode_CompareChilds(actionFileNodeNew, actionFileNodeQueue,
|
||||||
|
AccionFileNode_DeletePair);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileNodeLeft->status != FileStatus_Deleted) {
|
||||||
|
// Left node delete action
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_DeleteLeft;
|
||||||
|
QueueNode(*actionFileNodeQueue, actionFileNodeNew);
|
||||||
|
actionFileNodeNew = NULL;
|
||||||
|
}
|
||||||
|
if (fileNodeRight->status != FileStatus_Deleted) {
|
||||||
|
if (!actionFileNodeNew) {
|
||||||
|
actionFileNodeNew =
|
||||||
|
ActionFileNode_CreateNormal(fileNodeLeft, fileNodeRight);
|
||||||
|
}
|
||||||
|
// Right node delete action
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_DeleteRight;
|
||||||
|
QueueNode(*actionFileNodeQueue, actionFileNodeNew);
|
||||||
|
actionFileNodeNew = NULL;
|
||||||
|
}
|
||||||
|
if (actionFileNodeNew) {
|
||||||
|
AccionFileNode_Destroy(actionFileNodeNew);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
75
src/actionfilenode.h
Normal file
75
src/actionfilenode.h
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright (c) 2014-2021 Valeriano Alfonso Rodriguez
|
||||||
|
|
||||||
|
#ifndef _ACTIONFILENODE_H_
|
||||||
|
#define _ACTIONFILENODE_H_
|
||||||
|
|
||||||
|
#include "filenode.h"
|
||||||
|
|
||||||
|
typedef enum EActionFileCmp {
|
||||||
|
ActionFileCmp_Nothing,
|
||||||
|
ActionFileCmp_LeftToRight,
|
||||||
|
ActionFileCmp_RightToLeft,
|
||||||
|
ActionFileCmp_DeleteLeft,
|
||||||
|
ActionFileCmp_DeleteRight,
|
||||||
|
ActionFileCmp_DateLeftToRight,
|
||||||
|
ActionFileCmp_DateRightToLeft,
|
||||||
|
ActionFileCmp_MakeRightDirectory,
|
||||||
|
ActionFileCmp_MakeLeftDirectory
|
||||||
|
} ActionFileCmp;
|
||||||
|
|
||||||
|
typedef enum EActionFileNodeResult {
|
||||||
|
ActionFileNodeResult_Nothing,
|
||||||
|
ActionFileNodeResult_Ok,
|
||||||
|
ActionFileNodeResult_Error
|
||||||
|
} ActionFileNodeResult;
|
||||||
|
|
||||||
|
typedef struct SActionFileNode TActionFileNode, *ActionFileNode;
|
||||||
|
struct SActionFileNode {
|
||||||
|
ActionFileCmp action;
|
||||||
|
FileNode left;
|
||||||
|
FileNode right;
|
||||||
|
|
||||||
|
ActionFileNodeResult result;
|
||||||
|
|
||||||
|
ActionFileNode next;
|
||||||
|
};
|
||||||
|
|
||||||
|
ActionFileNode ActionFileNode_Create();
|
||||||
|
void AccionFileNode_Destroy(ActionFileNode actionFileNode);
|
||||||
|
ActionFileNode ActionFileNode_CreateNormal(FileNode fileNodeLeft,
|
||||||
|
FileNode fileNodeRight);
|
||||||
|
|
||||||
|
void AccionFileNode_CompareChilds(
|
||||||
|
ActionFileNode actionFileNodeRoot, ActionFileNode *actionFileNodeQueue,
|
||||||
|
void (*CheckPair)(FileNode fileNodeLeft, FileNode fileNodeRight,
|
||||||
|
ActionFileNode *actionFileNodeQueue));
|
||||||
|
|
||||||
|
typedef struct SActionQueueStatistics {
|
||||||
|
long long readLeft;
|
||||||
|
long long writeLeft;
|
||||||
|
long long readRight;
|
||||||
|
long long writeRight;
|
||||||
|
int fullCopyCount;
|
||||||
|
int dateCopyCount;
|
||||||
|
int directoryCount;
|
||||||
|
int deleteCount;
|
||||||
|
long long deleteLeft;
|
||||||
|
long long deleteRight;
|
||||||
|
} ActionQueueStatistics;
|
||||||
|
|
||||||
|
int ActionFileNode_Statistics(ActionFileNode actionFileNode,
|
||||||
|
ActionQueueStatistics *statistics,
|
||||||
|
ActionFileNodeResult result);
|
||||||
|
|
||||||
|
void ActionFileNode_Print(ActionFileNode actionFileNode);
|
||||||
|
|
||||||
|
int ActionFileNode_RunList(ActionFileNode actionFileNode, char *pathLeft,
|
||||||
|
char *pathRight);
|
||||||
|
|
||||||
|
// Common utilities
|
||||||
|
|
||||||
|
void AccionFileNode_DeletePair(FileNode fileNodeLeft, FileNode fileNodeRight,
|
||||||
|
ActionFileNode *actionFileNodeQueue);
|
||||||
|
|
||||||
|
#endif
|
||||||
117
src/actionfilenodecopy.c
Normal file
117
src/actionfilenodecopy.c
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright (c) 2014-2021 Valeriano Alfonso Rodriguez
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "actionfilenode.h"
|
||||||
|
#include "actionfilenodecopy.h"
|
||||||
|
#include "crc.h"
|
||||||
|
#include "filenode.h"
|
||||||
|
#include "fileutil.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#define MaxDeltaTime 0
|
||||||
|
|
||||||
|
#define QueueNode(queue, node) \
|
||||||
|
(queue)->next = (node); \
|
||||||
|
(queue) = (node);
|
||||||
|
|
||||||
|
void AccionFileNode_CopyPair(FileNode fileNodeLeft, FileNode fileNodeRight,
|
||||||
|
ActionFileNode *actionFileNodeQueue) {
|
||||||
|
ActionFileNode actionFileNodeNew =
|
||||||
|
ActionFileNode_CreateNormal(fileNodeLeft, fileNodeRight);
|
||||||
|
|
||||||
|
if (!fileNodeLeft && !fileNodeRight) {
|
||||||
|
AccionFileNode_Destroy(actionFileNodeNew);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!fileNodeLeft && fileNodeRight) {
|
||||||
|
if (fileNodeRight->flags & FileFlag_Directory) {
|
||||||
|
AccionFileNode_CompareChilds(actionFileNodeNew, actionFileNodeQueue,
|
||||||
|
AccionFileNode_DeletePair);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileNodeRight->status != FileStatus_Deleted) {
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_DeleteRight;
|
||||||
|
} else {
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_Nothing;
|
||||||
|
}
|
||||||
|
QueueNode(*actionFileNodeQueue, actionFileNodeNew);
|
||||||
|
}
|
||||||
|
if (fileNodeLeft && !fileNodeRight) {
|
||||||
|
if (fileNodeLeft->status != FileStatus_Deleted) {
|
||||||
|
if (fileNodeLeft->flags & FileFlag_Directory) {
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_MakeRightDirectory;
|
||||||
|
QueueNode(*actionFileNodeQueue, actionFileNodeNew);
|
||||||
|
AccionFileNode_CompareChilds(actionFileNodeNew,
|
||||||
|
actionFileNodeQueue,
|
||||||
|
AccionFileNode_CopyPair);
|
||||||
|
actionFileNodeNew =
|
||||||
|
ActionFileNode_CreateNormal(fileNodeLeft, fileNodeRight);
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_DateLeftToRight;
|
||||||
|
} else {
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_LeftToRight;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_Nothing;
|
||||||
|
}
|
||||||
|
QueueNode(*actionFileNodeQueue, actionFileNodeNew);
|
||||||
|
}
|
||||||
|
if (fileNodeLeft && fileNodeRight) {
|
||||||
|
if ((fileNodeLeft->flags & FileFlag_Directory) ||
|
||||||
|
(fileNodeRight->flags & FileFlag_Directory)) {
|
||||||
|
if (fileNodeLeft->status != FileStatus_Deleted) {
|
||||||
|
if (fileNodeRight->status == FileStatus_Deleted) {
|
||||||
|
actionFileNodeNew->action =
|
||||||
|
ActionFileCmp_MakeRightDirectory;
|
||||||
|
QueueNode(*actionFileNodeQueue, actionFileNodeNew);
|
||||||
|
actionFileNodeNew = ActionFileNode_CreateNormal(
|
||||||
|
fileNodeLeft, fileNodeRight);
|
||||||
|
}
|
||||||
|
AccionFileNode_CompareChilds(actionFileNodeNew,
|
||||||
|
actionFileNodeQueue,
|
||||||
|
AccionFileNode_CopyPair);
|
||||||
|
if (abs((int)(fileNodeLeft->fileTime -
|
||||||
|
fileNodeRight->fileTime)) <=
|
||||||
|
MaxDeltaTime) { // appox. equal
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_Nothing;
|
||||||
|
} else {
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_DateLeftToRight;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
AccionFileNode_CompareChilds(actionFileNodeNew,
|
||||||
|
actionFileNodeQueue,
|
||||||
|
AccionFileNode_DeletePair);
|
||||||
|
if (fileNodeRight->status != FileStatus_Deleted) {
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_DeleteRight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (fileNodeLeft->status != FileStatus_Deleted) {
|
||||||
|
if (abs((int)(fileNodeLeft->fileTime -
|
||||||
|
fileNodeRight->fileTime)) <= MaxDeltaTime) {
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_Nothing;
|
||||||
|
} else {
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_LeftToRight;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (fileNodeRight->status != FileStatus_Deleted) {
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_DeleteRight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QueueNode(*actionFileNodeQueue, actionFileNodeNew);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ActionFileNode ActionFileNode_BuildCopy(FileNode fileNodeLeft,
|
||||||
|
FileNode fileNodeRight) {
|
||||||
|
ActionFileNode actionFileNodeRoot =
|
||||||
|
ActionFileNode_CreateNormal(fileNodeLeft, fileNodeRight);
|
||||||
|
ActionFileNode actionFileNodeQueue = actionFileNodeRoot;
|
||||||
|
AccionFileNode_CompareChilds(actionFileNodeRoot, &actionFileNodeQueue,
|
||||||
|
AccionFileNode_CopyPair);
|
||||||
|
return actionFileNodeRoot;
|
||||||
|
}
|
||||||
13
src/actionfilenodecopy.h
Normal file
13
src/actionfilenodecopy.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright (c) 2014-2021 Valeriano Alfonso Rodriguez
|
||||||
|
|
||||||
|
#ifndef _ACTIONFILENODECOPY_H_
|
||||||
|
#define _ACTIONFILENODECOPY_H_
|
||||||
|
|
||||||
|
#include "actionfilenode.h"
|
||||||
|
#include "filenode.h"
|
||||||
|
|
||||||
|
ActionFileNode ActionFileNode_BuildCopy(FileNode fileNodeLeft,
|
||||||
|
FileNode fileNodeRight);
|
||||||
|
|
||||||
|
#endif
|
||||||
220
src/actionfilenodesync.c
Normal file
220
src/actionfilenodesync.c
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright (c) 2014-2021 Valeriano Alfonso Rodriguez
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "actionfilenode.h"
|
||||||
|
#include "actionfilenodesync.h"
|
||||||
|
#include "crc.h"
|
||||||
|
#include "filenode.h"
|
||||||
|
#include "fileutil.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#define MaxDeltaTime 0
|
||||||
|
|
||||||
|
#define QueueNode(queue, node) \
|
||||||
|
(queue)->next = (node); \
|
||||||
|
(queue) = (node);
|
||||||
|
|
||||||
|
void AccionFileNode_SyncPair(FileNode fileNodeLeft, FileNode fileNodeRight,
|
||||||
|
ActionFileNode *actionFileNodeQueue) {
|
||||||
|
if (!fileNodeLeft && !fileNodeRight) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ActionFileNode actionFileNodeNew =
|
||||||
|
ActionFileNode_CreateNormal(fileNodeLeft, fileNodeRight);
|
||||||
|
|
||||||
|
if (!fileNodeLeft && fileNodeRight) {
|
||||||
|
if (fileNodeRight->flags & FileFlag_Directory) {
|
||||||
|
// Directory
|
||||||
|
if (fileNodeRight->status == FileStatus_Deleted) {
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_Nothing;
|
||||||
|
QueueNode(*actionFileNodeQueue, actionFileNodeNew);
|
||||||
|
} else {
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_MakeLeftDirectory;
|
||||||
|
QueueNode(*actionFileNodeQueue, actionFileNodeNew);
|
||||||
|
|
||||||
|
// Iterate childs
|
||||||
|
AccionFileNode_CompareChilds(actionFileNodeNew,
|
||||||
|
actionFileNodeQueue,
|
||||||
|
AccionFileNode_SyncPair);
|
||||||
|
|
||||||
|
// Create new action for date copy
|
||||||
|
actionFileNodeNew =
|
||||||
|
ActionFileNode_CreateNormal(fileNodeLeft, fileNodeRight);
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_DateRightToLeft;
|
||||||
|
QueueNode(*actionFileNodeQueue, actionFileNodeNew);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// File
|
||||||
|
if (fileNodeRight->status == FileStatus_Deleted) {
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_Nothing;
|
||||||
|
} else {
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_RightToLeft;
|
||||||
|
}
|
||||||
|
QueueNode(*actionFileNodeQueue, actionFileNodeNew);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fileNodeLeft && !fileNodeRight) {
|
||||||
|
if (fileNodeLeft->flags & FileFlag_Directory) {
|
||||||
|
// Directory
|
||||||
|
if (fileNodeLeft->status == FileStatus_Deleted) {
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_Nothing;
|
||||||
|
QueueNode(*actionFileNodeQueue, actionFileNodeNew);
|
||||||
|
} else {
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_MakeRightDirectory;
|
||||||
|
QueueNode(*actionFileNodeQueue, actionFileNodeNew);
|
||||||
|
|
||||||
|
// Iterate childs
|
||||||
|
AccionFileNode_CompareChilds(actionFileNodeNew,
|
||||||
|
actionFileNodeQueue,
|
||||||
|
AccionFileNode_SyncPair);
|
||||||
|
|
||||||
|
// Create new action for date copy
|
||||||
|
actionFileNodeNew =
|
||||||
|
ActionFileNode_CreateNormal(fileNodeLeft, fileNodeRight);
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_DateLeftToRight;
|
||||||
|
QueueNode(*actionFileNodeQueue, actionFileNodeNew);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// File
|
||||||
|
if (fileNodeLeft->status == FileStatus_Deleted) {
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_Nothing;
|
||||||
|
} else {
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_LeftToRight;
|
||||||
|
}
|
||||||
|
QueueNode(*actionFileNodeQueue, actionFileNodeNew);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fileNodeLeft && fileNodeRight) {
|
||||||
|
if ((fileNodeLeft->flags & FileFlag_Directory) &&
|
||||||
|
(fileNodeRight->flags & FileFlag_Directory)) {
|
||||||
|
// Directory
|
||||||
|
|
||||||
|
// Prepare action for directory pair
|
||||||
|
if (abs((int)(fileNodeLeft->fileTime - fileNodeRight->fileTime)) <=
|
||||||
|
MaxDeltaTime) { // aprox. equal
|
||||||
|
if (fileNodeRight->status == FileStatus_Deleted &&
|
||||||
|
fileNodeLeft->status == FileStatus_Deleted) {
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_Nothing;
|
||||||
|
} else if (fileNodeLeft->status == FileStatus_Undeleted &&
|
||||||
|
fileNodeRight->status == FileStatus_Deleted) {
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_LeftToRight;
|
||||||
|
} else if (fileNodeLeft->status == FileStatus_Deleted &&
|
||||||
|
fileNodeRight->status == FileStatus_Undeleted) {
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_RightToLeft;
|
||||||
|
} else if (fileNodeRight->status == FileStatus_Deleted) {
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_DeleteLeft;
|
||||||
|
} else if (fileNodeLeft->status == FileStatus_Deleted) {
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_DeleteRight;
|
||||||
|
} else {
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_Nothing;
|
||||||
|
}
|
||||||
|
} else if (fileNodeLeft->fileTime < fileNodeRight->fileTime) {
|
||||||
|
if (fileNodeRight->status == FileStatus_Deleted) {
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_DeleteLeft;
|
||||||
|
if (fileNodeLeft->status == FileStatus_Deleted) {
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_Nothing;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (fileNodeLeft->status == FileStatus_Deleted) {
|
||||||
|
actionFileNodeNew->action =
|
||||||
|
ActionFileCmp_MakeLeftDirectory;
|
||||||
|
QueueNode(*actionFileNodeQueue, actionFileNodeNew);
|
||||||
|
actionFileNodeNew = ActionFileNode_CreateNormal(
|
||||||
|
fileNodeLeft, fileNodeRight);
|
||||||
|
}
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_DateRightToLeft;
|
||||||
|
}
|
||||||
|
} else if (fileNodeLeft->fileTime > fileNodeRight->fileTime) {
|
||||||
|
if (fileNodeLeft->status == FileStatus_Deleted) {
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_DeleteRight;
|
||||||
|
if (fileNodeRight->status == FileStatus_Deleted) {
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_Nothing;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (fileNodeRight->status == FileStatus_Deleted) {
|
||||||
|
actionFileNodeNew->action =
|
||||||
|
ActionFileCmp_MakeRightDirectory;
|
||||||
|
QueueNode(*actionFileNodeQueue, actionFileNodeNew);
|
||||||
|
actionFileNodeNew = ActionFileNode_CreateNormal(
|
||||||
|
fileNodeLeft, fileNodeRight);
|
||||||
|
}
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_DateLeftToRight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process child nodes
|
||||||
|
if (actionFileNodeNew->action == ActionFileCmp_DeleteRight ||
|
||||||
|
actionFileNodeNew->action == ActionFileCmp_DeleteLeft) {
|
||||||
|
// Iterate child nodes for deletion
|
||||||
|
AccionFileNode_CompareChilds(actionFileNodeNew,
|
||||||
|
actionFileNodeQueue,
|
||||||
|
AccionFileNode_DeletePair);
|
||||||
|
} else {
|
||||||
|
AccionFileNode_CompareChilds(actionFileNodeNew,
|
||||||
|
actionFileNodeQueue,
|
||||||
|
AccionFileNode_SyncPair);
|
||||||
|
}
|
||||||
|
|
||||||
|
QueueNode(*actionFileNodeQueue, actionFileNodeNew);
|
||||||
|
} else if ((fileNodeLeft->flags & FileFlag_Normal) &&
|
||||||
|
(fileNodeRight->flags & FileFlag_Normal)) {
|
||||||
|
// Files
|
||||||
|
|
||||||
|
// Prepare action for file pair
|
||||||
|
if (abs((int)(fileNodeLeft->fileTime - fileNodeRight->fileTime)) <=
|
||||||
|
MaxDeltaTime) { // aprox. equal
|
||||||
|
if (fileNodeRight->status == FileStatus_Deleted &&
|
||||||
|
fileNodeLeft->status == FileStatus_Deleted) {
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_Nothing;
|
||||||
|
} else if (fileNodeLeft->status == FileStatus_Undeleted &&
|
||||||
|
fileNodeRight->status == FileStatus_Deleted) {
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_LeftToRight;
|
||||||
|
} else if (fileNodeLeft->status == FileStatus_Deleted &&
|
||||||
|
fileNodeRight->status == FileStatus_Undeleted) {
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_RightToLeft;
|
||||||
|
} else if (fileNodeRight->status == FileStatus_Deleted) {
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_DeleteLeft;
|
||||||
|
} else if (fileNodeLeft->status == FileStatus_Deleted) {
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_DeleteRight;
|
||||||
|
} else {
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_Nothing;
|
||||||
|
}
|
||||||
|
} else if (fileNodeLeft->fileTime < fileNodeRight->fileTime) {
|
||||||
|
// FIXME: Check size to determine y further checks are necessary
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_RightToLeft;
|
||||||
|
if (fileNodeRight->status == FileStatus_Deleted) {
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_DeleteLeft;
|
||||||
|
if (fileNodeLeft->status == FileStatus_Deleted) {
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_Nothing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (fileNodeLeft->fileTime > fileNodeRight->fileTime) {
|
||||||
|
// FIXME: Check size to determine y further checks are necessary
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_LeftToRight;
|
||||||
|
if (fileNodeLeft->status == FileStatus_Deleted) {
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_DeleteRight;
|
||||||
|
if (fileNodeRight->status == FileStatus_Deleted) {
|
||||||
|
actionFileNodeNew->action = ActionFileCmp_Nothing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QueueNode(*actionFileNodeQueue, actionFileNodeNew);
|
||||||
|
} else {
|
||||||
|
// FIXME: !!!!!
|
||||||
|
// Directory vs File
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ActionFileNode ActionFileNode_BuildSync(FileNode izquierda, FileNode derecha) {
|
||||||
|
ActionFileNode actionFileNodeRoot =
|
||||||
|
ActionFileNode_CreateNormal(izquierda, derecha);
|
||||||
|
ActionFileNode actionFileNodeQueue = actionFileNodeRoot;
|
||||||
|
AccionFileNode_CompareChilds(actionFileNodeRoot, &actionFileNodeQueue,
|
||||||
|
AccionFileNode_SyncPair);
|
||||||
|
return actionFileNodeRoot;
|
||||||
|
}
|
||||||
13
src/actionfilenodesync.h
Normal file
13
src/actionfilenodesync.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright (c) 2014-2021 Valeriano Alfonso Rodriguez
|
||||||
|
|
||||||
|
#ifndef _ACTIONFILENODESYNC_H_
|
||||||
|
#define _ACTIONFILENODESYNC_H_
|
||||||
|
|
||||||
|
#include "actionfilenode.h"
|
||||||
|
#include "filenode.h"
|
||||||
|
|
||||||
|
ActionFileNode ActionFileNode_BuildSync(FileNode fileNodeLeft,
|
||||||
|
FileNode fileNodeRight);
|
||||||
|
|
||||||
|
#endif
|
||||||
70
src/crc.c
Normal file
70
src/crc.c
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright (c) 2014-2021 Valeriano Alfonso Rodriguez
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
unsigned long _crcTable[256];
|
||||||
|
int _crcTableInitialized = 0;
|
||||||
|
|
||||||
|
#define CRC32_POLYNOMIAL 0xEDB88320L
|
||||||
|
|
||||||
|
void CRCTable_Init() {
|
||||||
|
int i;
|
||||||
|
int j;
|
||||||
|
unsigned long crc;
|
||||||
|
|
||||||
|
if (_crcTableInitialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_crcTableInitialized = 1;
|
||||||
|
|
||||||
|
for (i = 0; i < 256; i++) {
|
||||||
|
crc = i;
|
||||||
|
for (j = 8; j > 0; j--) {
|
||||||
|
if (crc & 1)
|
||||||
|
crc = (crc >> 1) ^ CRC32_POLYNOMIAL;
|
||||||
|
else
|
||||||
|
crc >>= 1;
|
||||||
|
}
|
||||||
|
_crcTable[i] = crc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long CRC_BufferInternal(unsigned char *buffer, unsigned long len,
|
||||||
|
unsigned long crc) {
|
||||||
|
unsigned char *p;
|
||||||
|
|
||||||
|
// Calculate CRC from buffer
|
||||||
|
p = (unsigned char *)buffer;
|
||||||
|
while (len-- != 0) {
|
||||||
|
unsigned long termA = (crc >> 8) & 0x00FFFFFFL;
|
||||||
|
unsigned long termB = _crcTable[((int)crc ^ *p++) & 0xff];
|
||||||
|
crc = termA ^ termB;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (crc);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long CRC_Buffer(unsigned char *buffer, unsigned long len,
|
||||||
|
unsigned long crc) {
|
||||||
|
CRCTable_Init();
|
||||||
|
return (CRC_BufferInternal(buffer, len, crc));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long CRC_File(FILE *file) {
|
||||||
|
unsigned long crc;
|
||||||
|
unsigned char buffer[512];
|
||||||
|
|
||||||
|
CRCTable_Init();
|
||||||
|
|
||||||
|
crc = 0xFFFFFFFFL;
|
||||||
|
for (;;) {
|
||||||
|
// Fill buffer
|
||||||
|
size_t count = fread(buffer, 1, 512, file);
|
||||||
|
if (count == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
crc = CRC_BufferInternal(buffer, (int)count, crc);
|
||||||
|
}
|
||||||
|
return (crc ^= 0xFFFFFFFFL);
|
||||||
|
}
|
||||||
13
src/crc.h
Normal file
13
src/crc.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright (c) 2014-2021 Valeriano Alfonso Rodriguez
|
||||||
|
|
||||||
|
#ifndef _CRC_
|
||||||
|
#define _CRC_
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
unsigned long CRC_Buffer(unsigned char *buffer, unsigned long len,
|
||||||
|
unsigned long crc);
|
||||||
|
unsigned long CRC_File(FILE *file);
|
||||||
|
|
||||||
|
#endif
|
||||||
596
src/filenode.c
Normal file
596
src/filenode.c
Normal file
@@ -0,0 +1,596 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright (c) 2014-2021 Valeriano Alfonso Rodriguez
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "crc.h"
|
||||||
|
#include "filenode.h"
|
||||||
|
#include "fileutil.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
FileNode _freeFileNode = NULL;
|
||||||
|
int _n_filenode = 0;
|
||||||
|
#define FileNode_Block 1024
|
||||||
|
FileNode FileNode_Create() {
|
||||||
|
FileNode fileNode;
|
||||||
|
|
||||||
|
if (_freeFileNode == NULL) {
|
||||||
|
FileNode nodes;
|
||||||
|
int i;
|
||||||
|
// Allocate a block
|
||||||
|
nodes = (FileNode)malloc(sizeof(TFileNode) * FileNode_Block);
|
||||||
|
if (nodes == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
for (i = 0; i < FileNode_Block - 1; i++) {
|
||||||
|
nodes[i].next = &nodes[i + 1];
|
||||||
|
}
|
||||||
|
nodes[FileNode_Block - 1].next = NULL;
|
||||||
|
_freeFileNode = &nodes[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get first free
|
||||||
|
fileNode = _freeFileNode;
|
||||||
|
_freeFileNode = fileNode->next;
|
||||||
|
fileNode->next = NULL;
|
||||||
|
_n_filenode++;
|
||||||
|
|
||||||
|
// Initialize
|
||||||
|
fileNode->name[0] = 0;
|
||||||
|
fileNode->flags = 0;
|
||||||
|
fileNode->status = FileStatus_None;
|
||||||
|
fileNode->size = 0;
|
||||||
|
fileNode->crc = 0;
|
||||||
|
fileNode->fileTime = 0;
|
||||||
|
fileNode->child = NULL;
|
||||||
|
fileNode->childCount = 0;
|
||||||
|
fileNode->next = NULL;
|
||||||
|
fileNode->parent = NULL;
|
||||||
|
|
||||||
|
return (fileNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
FileNode FileNode_Copy(FileNode fileNode) {
|
||||||
|
FileNode fileNodeNew;
|
||||||
|
|
||||||
|
fileNodeNew = FileNode_Create();
|
||||||
|
|
||||||
|
// Copy
|
||||||
|
strcpy(fileNodeNew->name, fileNode->name);
|
||||||
|
fileNodeNew->flags = fileNode->flags;
|
||||||
|
fileNodeNew->status = fileNode->status;
|
||||||
|
fileNodeNew->size = fileNode->size;
|
||||||
|
fileNodeNew->crc = fileNode->crc;
|
||||||
|
fileNodeNew->fileTime = fileNode->fileTime;
|
||||||
|
|
||||||
|
return fileNodeNew;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileNode_Delete(FileNode fn) {
|
||||||
|
// Delete childs
|
||||||
|
FileNode fileNodeChildAux = fn->child;
|
||||||
|
FileNode fileNodeChild = fn->child;
|
||||||
|
while (fileNodeChild) {
|
||||||
|
fn->childCount--;
|
||||||
|
fileNodeChildAux = fileNodeChild;
|
||||||
|
fileNodeChild = fileNodeChild->next;
|
||||||
|
fileNodeChildAux->next = NULL;
|
||||||
|
if (fileNodeChildAux->parent == fn) {
|
||||||
|
fileNodeChildAux->parent = NULL;
|
||||||
|
FileNode_Delete(fileNodeChildAux);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Queue freed node
|
||||||
|
fn->next = _freeFileNode;
|
||||||
|
_freeFileNode = fn;
|
||||||
|
_n_filenode--;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileNode_AddChild(FileNode fileNode, FileNode fileNodeChild) {
|
||||||
|
if (fileNodeChild == NULL || fileNode == NULL) { return; }
|
||||||
|
|
||||||
|
fileNodeChild->next = fileNode->child;
|
||||||
|
fileNode->child = fileNodeChild;
|
||||||
|
fileNode->childCount++;
|
||||||
|
fileNodeChild->parent = fileNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileNode FileNode_GetRoot(FileNode fileNode) {
|
||||||
|
FileNode fileNodeParent = fileNode->parent;
|
||||||
|
while (fileNodeParent != NULL && fileNodeParent->parent != NULL) {
|
||||||
|
fileNodeParent = fileNodeParent->parent;
|
||||||
|
}
|
||||||
|
return fileNodeParent;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileNode_SetStatusRec(FileNode fileNode, FileStatus status) {
|
||||||
|
FileNode fileNodeChild;
|
||||||
|
|
||||||
|
if (fileNode == NULL) { return; }
|
||||||
|
|
||||||
|
fileNode->status = status;
|
||||||
|
fileNodeChild = fileNode->child;
|
||||||
|
while (fileNodeChild) {
|
||||||
|
FileNode_SetStatusRec(fileNodeChild, status);
|
||||||
|
fileNodeChild = fileNodeChild->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileNode_GetPath_Rec(FileNode fileNode, char **pathNode) {
|
||||||
|
if (fileNode->parent) {
|
||||||
|
pathNode[0] = fileNode->parent->name;
|
||||||
|
FileNode_GetPath_Rec(fileNode->parent, pathNode + 1);
|
||||||
|
} else {
|
||||||
|
pathNode[0] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
char tempPath[MaxPath];
|
||||||
|
char *FileNode_GetPath(FileNode fileNode, char *path) {
|
||||||
|
char *pathNodes[MaxPathNodes];
|
||||||
|
int levels, i;
|
||||||
|
char *pathPtr = tempPath;
|
||||||
|
if (path) {
|
||||||
|
pathPtr = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileNode_GetPath_Rec(fileNode, pathNodes);
|
||||||
|
levels = 0;
|
||||||
|
while (pathNodes[levels]) {
|
||||||
|
levels++;
|
||||||
|
}
|
||||||
|
strcpy(pathPtr, "");
|
||||||
|
for (i = levels - 1; i >= 0; i--) {
|
||||||
|
strcat(pathPtr, pathNodes[i]);
|
||||||
|
strcat(pathPtr, "/");
|
||||||
|
}
|
||||||
|
strcat(pathPtr, fileNode->name);
|
||||||
|
return tempPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *FileNode_GetFullPath(FileNode fileNode, char *basePath, char *path) {
|
||||||
|
char *pathNodes[MaxPath];
|
||||||
|
int levels, i;
|
||||||
|
char *pathPtr = tempPath;
|
||||||
|
if (path)
|
||||||
|
pathPtr = path;
|
||||||
|
|
||||||
|
FileNode_GetPath_Rec(fileNode, pathNodes);
|
||||||
|
levels = 0;
|
||||||
|
while (pathNodes[levels]) {
|
||||||
|
levels++;
|
||||||
|
}
|
||||||
|
strcpy(pathPtr, basePath);
|
||||||
|
strcat(pathPtr, "/");
|
||||||
|
for (i = levels - 2; i >= 0; i--) {
|
||||||
|
strcat(pathPtr, pathNodes[i]);
|
||||||
|
strcat(pathPtr, "/");
|
||||||
|
}
|
||||||
|
strcat(pathPtr, fileNode->name);
|
||||||
|
return tempPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileNode_LoadSize(FileNode fileNode, char *file) {
|
||||||
|
fileNode->flags |= FileFlag_HasSize;
|
||||||
|
fileNode->size = File_GetSize(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileNode_LoadTime(FileNode fileNode, char *file) {
|
||||||
|
fileNode->flags |= FileFlag_HasTime;
|
||||||
|
fileNode->fileTime = FileTime_Get(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileNode_LoadSizeAndTime(FileNode fileNode, char *file) {
|
||||||
|
fileNode->flags |= FileFlag_HasSize | FileFlag_HasTime;
|
||||||
|
File_GetSizeAndTime(file, &fileNode->size, &fileNode->fileTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileNode_LoadCRC(FileNode fileNode, char *filePath) {
|
||||||
|
FILE *file;
|
||||||
|
file = fopen(filePath, "rb");
|
||||||
|
if (!file) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fileNode->flags |= FileFlag_HasCRC;
|
||||||
|
fileNode->crc = CRC_File(file);
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileNode_SaveNode(FileNode fileNode, FILE *file) {
|
||||||
|
short nameLen;
|
||||||
|
|
||||||
|
// Write name
|
||||||
|
nameLen = (short)strlen(fileNode->name);
|
||||||
|
fwrite((void *)&nameLen, sizeof(nameLen), 1, file);
|
||||||
|
if (nameLen > 0 && nameLen < MaxFilename) {
|
||||||
|
fputs(fileNode->name, file);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write flags
|
||||||
|
fwrite((void *)&fileNode->flags, sizeof(fileNode->flags), 1, file);
|
||||||
|
|
||||||
|
// Write status
|
||||||
|
fputc((char)fileNode->status, file);
|
||||||
|
|
||||||
|
// Write size
|
||||||
|
if (fileNode->flags & FileFlag_HasSize) {
|
||||||
|
fwrite((void *)&fileNode->size, sizeof(fileNode->size), 1, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write date
|
||||||
|
if (fileNode->flags & FileFlag_HasTime) {
|
||||||
|
fwrite((void *)&fileNode->fileTime, sizeof(fileNode->fileTime), 1,
|
||||||
|
file);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write CRC
|
||||||
|
if (fileNode->flags & FileFlag_HasCRC) {
|
||||||
|
fwrite((void *)&fileNode->crc, sizeof(fileNode->crc), 1, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write files of directory
|
||||||
|
if (fileNode->flags & FileFlag_Directory) {
|
||||||
|
FileNode fileNodeChild;
|
||||||
|
fwrite((void *)&fileNode->childCount, sizeof(fileNode->childCount), 1,
|
||||||
|
file);
|
||||||
|
fileNodeChild = fileNode->child;
|
||||||
|
int cnt = 0;
|
||||||
|
while (fileNodeChild) {
|
||||||
|
FileNode_SaveNode(fileNodeChild, file);
|
||||||
|
fileNodeChild = fileNodeChild->next;
|
||||||
|
cnt++;
|
||||||
|
}
|
||||||
|
if (fileNode->childCount != cnt) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileNode_Save(FileNode fileNode, char *filePath) {
|
||||||
|
FILE *file;
|
||||||
|
char mark[5];
|
||||||
|
int version;
|
||||||
|
|
||||||
|
if (!fileNode)
|
||||||
|
return;
|
||||||
|
file = fopen(filePath, "wb+");
|
||||||
|
if (!file)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Write mark and version
|
||||||
|
strcpy(mark, "sYnC");
|
||||||
|
fwrite((void *)mark, sizeof(char), 4, file);
|
||||||
|
version = FileNode_Version;
|
||||||
|
fwrite((void *)&version, sizeof(int), 1, file);
|
||||||
|
|
||||||
|
FileNode_SaveNode(fileNode, file);
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
FileNode FileNode_LoadNode(FILE *file) {
|
||||||
|
short nameLen;
|
||||||
|
FileNode fileNode;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
fileNode = FileNode_Create();
|
||||||
|
|
||||||
|
// Read name
|
||||||
|
fread((void *)&nameLen, sizeof(nameLen), 1, file);
|
||||||
|
fileNode->name[0] = 0;
|
||||||
|
if (nameLen < 0 || nameLen > MaxFilename) {
|
||||||
|
FileNode_Delete(fileNode);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (nameLen > 0) {
|
||||||
|
fread((void *)fileNode->name, sizeof(char), nameLen, file);
|
||||||
|
fileNode->name[nameLen] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read flags
|
||||||
|
fread((void *)&fileNode->flags, sizeof(fileNode->flags), 1, file);
|
||||||
|
|
||||||
|
// Read status
|
||||||
|
fileNode->status = (FileStatus)fgetc(file);
|
||||||
|
|
||||||
|
// Read status
|
||||||
|
if (fileNode->flags & FileFlag_HasSize) {
|
||||||
|
fread((void *)&fileNode->size, sizeof(fileNode->size), 1, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read date
|
||||||
|
if (fileNode->flags & FileFlag_HasTime) {
|
||||||
|
fread((void *)&fileNode->fileTime, sizeof(fileNode->fileTime), 1, file);
|
||||||
|
if (fileNode->fileTime < 0) {
|
||||||
|
fileNode->fileTime = Time_GetCurrentTime();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read CRC
|
||||||
|
if (fileNode->flags & FileFlag_HasCRC) {
|
||||||
|
fread((void *)&fileNode->crc, sizeof(fileNode->crc), 1, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read files on directory
|
||||||
|
if (fileNode->flags & FileFlag_Directory) {
|
||||||
|
FileNode fileNodeChildAux = NULL;
|
||||||
|
FileNode fileNodeChild;
|
||||||
|
fread((void *)&fileNode->childCount, sizeof(fileNode->childCount), 1,
|
||||||
|
file);
|
||||||
|
for (i = 0; i < fileNode->childCount; i++) {
|
||||||
|
fileNodeChild = FileNode_LoadNode(file);
|
||||||
|
if (fileNodeChild == NULL) {
|
||||||
|
FileNode_Delete(fileNode);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
fileNodeChild->parent = fileNode;
|
||||||
|
if (!fileNodeChildAux) {
|
||||||
|
fileNode->child = fileNodeChild;
|
||||||
|
} else {
|
||||||
|
fileNodeChildAux->next = fileNodeChild;
|
||||||
|
}
|
||||||
|
fileNodeChildAux = fileNodeChild;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (fileNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
FileNode FileNode_Load(char *filePath) {
|
||||||
|
FILE *file;
|
||||||
|
FileNode fileNode;
|
||||||
|
char mark[5];
|
||||||
|
int version;
|
||||||
|
|
||||||
|
file = fopen(filePath, "rb");
|
||||||
|
if (!file)
|
||||||
|
return (NULL);
|
||||||
|
|
||||||
|
// Read mark and version
|
||||||
|
fread((void *)mark, sizeof(char), 4, file);
|
||||||
|
mark[4] = 0;
|
||||||
|
if (strcmp(mark, "sYnC")) {
|
||||||
|
// Incorrect mark
|
||||||
|
fclose(file);
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
fread((void *)&version, sizeof(int), 1, file);
|
||||||
|
if (version != FileNode_Version) {
|
||||||
|
// Incorrect version
|
||||||
|
fclose(file);
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
fileNode = FileNode_LoadNode(file);
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
return (fileNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileNode_PrintNode(FileNode fileNode) {
|
||||||
|
Print(FileNode_GetPath(fileNode, NULL));
|
||||||
|
if (fileNode->flags & FileFlag_Normal) {
|
||||||
|
Print(" File");
|
||||||
|
} else {
|
||||||
|
Print(" Dir");
|
||||||
|
}
|
||||||
|
if (fileNode->status == FileStatus_New) {
|
||||||
|
Print(" New");
|
||||||
|
}
|
||||||
|
if (fileNode->status == FileStatus_Modified) {
|
||||||
|
Print(" Modified");
|
||||||
|
}
|
||||||
|
if (fileNode->status == FileStatus_Deleted) {
|
||||||
|
Print(" Deleted");
|
||||||
|
}
|
||||||
|
if (fileNode->status == FileStatus_Undeleted) {
|
||||||
|
Print(" Undeleted");
|
||||||
|
}
|
||||||
|
Print("\n");
|
||||||
|
|
||||||
|
if (fileNode->flags & FileFlag_HasSize) {
|
||||||
|
Print("\\-Size : %lld\n", fileNode->size);
|
||||||
|
}
|
||||||
|
if (fileNode->flags & FileFlag_HasTime) {
|
||||||
|
Print("\\-Date : ");
|
||||||
|
FileTime_Print(fileNode->fileTime);
|
||||||
|
Print("\n");
|
||||||
|
}
|
||||||
|
if (fileNode->flags & FileFlag_HasCRC) {
|
||||||
|
Print("\\-CRC : [%08X]\n", fileNode->crc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileNode_Print(FileNode fileNode) {
|
||||||
|
FileNode fileNodeAux = fileNode;
|
||||||
|
int end = 0;
|
||||||
|
|
||||||
|
while (fileNodeAux != NULL && !end) {
|
||||||
|
if (fileNodeAux->parent != NULL) {
|
||||||
|
FileNode_PrintNode(fileNodeAux);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileNodeAux->child) {
|
||||||
|
fileNodeAux = fileNodeAux->child;
|
||||||
|
} else {
|
||||||
|
while (fileNodeAux->next == NULL) {
|
||||||
|
fileNodeAux = fileNodeAux->parent;
|
||||||
|
if (fileNodeAux == fileNode || fileNodeAux == NULL) {
|
||||||
|
Print("End\n");
|
||||||
|
end = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!end) {
|
||||||
|
fileNodeAux = fileNodeAux->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int FileNode_Build_Iterate(char *path, char *name, void *d);
|
||||||
|
|
||||||
|
FileNode FileNode_Build(char *path) {
|
||||||
|
FileNode fileNode;
|
||||||
|
|
||||||
|
if (!File_ExistsPath(path))
|
||||||
|
return (NULL);
|
||||||
|
|
||||||
|
// Create node
|
||||||
|
fileNode = FileNode_Create();
|
||||||
|
File_GetName(path, fileNode->name);
|
||||||
|
fileNode->fileTime = Time_GetCurrentTime();
|
||||||
|
fileNode->flags |= FileFlag_HasTime;
|
||||||
|
|
||||||
|
if (File_IsDirectory(path)) {
|
||||||
|
// Get information data from directories, and child files
|
||||||
|
fileNode->flags |= FileFlag_Directory;
|
||||||
|
FileNode_LoadTime(fileNode, path);
|
||||||
|
File_IterateDir(path, FileNode_Build_Iterate, fileNode);
|
||||||
|
} else {
|
||||||
|
// Get information data from files
|
||||||
|
fileNode->flags |= FileFlag_Normal;
|
||||||
|
FileNode_LoadSizeAndTime(fileNode, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (fileNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
int FileNode_Build_Iterate(char *path, char *name, void *d) {
|
||||||
|
FileNode fileNode;
|
||||||
|
FileNode fileNodeParent = (FileNode)d;
|
||||||
|
|
||||||
|
if (!strcmp(name, FileNode_Filename)) {
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fileNode = FileNode_Build(path);
|
||||||
|
FileNode_AddChild(fileNodeParent, fileNode);
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int FileNode_Refresh_Iterate(char *path, char *name, void *d);
|
||||||
|
|
||||||
|
FileNode FileNode_Refresh(FileNode fileNode, char *filePath) {
|
||||||
|
if (!fileNode) {
|
||||||
|
Print("FileNode_Refresh: Error NULL FileNode\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!File_ExistsPath(filePath)) {
|
||||||
|
// The file/directory has been deleted
|
||||||
|
FileNode_SetStatusRec(fileNode, FileStatus_Deleted);
|
||||||
|
return (fileNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check modification
|
||||||
|
FileTime fileTime;
|
||||||
|
long long size;
|
||||||
|
|
||||||
|
// Remove mark
|
||||||
|
fileNode->flags &= ~FileFlag_MarkerForReview;
|
||||||
|
|
||||||
|
if (File_IsDirectory(filePath)) {
|
||||||
|
FileNode fileNodeChild;
|
||||||
|
|
||||||
|
// Check directory data
|
||||||
|
fileTime = FileTime_Get(filePath);
|
||||||
|
if (fileNode->status == FileStatus_Deleted) {
|
||||||
|
fileNode->status = FileStatus_Undeleted;
|
||||||
|
fileNode->fileTime = fileTime;
|
||||||
|
}
|
||||||
|
if (!(fileNode->flags & FileFlag_Directory)) {
|
||||||
|
fileNode->status = FileStatus_Modified;
|
||||||
|
fileNode->flags |= FileFlag_Directory;
|
||||||
|
fileNode->flags &= ~FileFlag_Normal;
|
||||||
|
}
|
||||||
|
if (fileTime != fileNode->fileTime) {
|
||||||
|
fileNode->status = FileStatus_Modified;
|
||||||
|
fileNode->fileTime = fileTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark childs for review
|
||||||
|
fileNodeChild = fileNode->child;
|
||||||
|
while (fileNodeChild) {
|
||||||
|
fileNodeChild->flags |= FileFlag_MarkerForReview;
|
||||||
|
fileNodeChild = fileNodeChild->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan subdirectories
|
||||||
|
File_IterateDir(filePath, FileNode_Refresh_Iterate, fileNode);
|
||||||
|
|
||||||
|
// Mark as deleted remaining files marked for review
|
||||||
|
fileNodeChild = fileNode->child;
|
||||||
|
while (fileNodeChild) {
|
||||||
|
if (fileNodeChild->flags & FileFlag_MarkerForReview) {
|
||||||
|
fileNodeChild->flags &= ~FileFlag_MarkerForReview;
|
||||||
|
FileNode_SetStatusRec(fileNodeChild, FileStatus_Deleted);
|
||||||
|
}
|
||||||
|
fileNodeChild = fileNodeChild->next;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Comprar datos de los ficheros
|
||||||
|
File_GetSizeAndTime(filePath, &size, &fileTime);
|
||||||
|
if (fileNode->status == FileStatus_Deleted) {
|
||||||
|
fileNode->status = FileStatus_Undeleted;
|
||||||
|
fileNode->fileTime = fileTime;
|
||||||
|
}
|
||||||
|
if (!(fileNode->flags & FileFlag_Normal)) {
|
||||||
|
fileNode->status = FileStatus_Modified;
|
||||||
|
fileNode->flags |= FileFlag_Normal;
|
||||||
|
fileNode->flags &= ~FileFlag_Directory;
|
||||||
|
}
|
||||||
|
if (size != fileNode->size) {
|
||||||
|
fileNode->status = FileStatus_Modified;
|
||||||
|
fileNode->size = size;
|
||||||
|
}
|
||||||
|
if (fileTime != fileNode->fileTime) {
|
||||||
|
fileNode->status = FileStatus_Modified;
|
||||||
|
fileNode->fileTime = fileTime;
|
||||||
|
}
|
||||||
|
if (fileNode->status == FileStatus_Modified) {
|
||||||
|
fileNode->flags &= ~FileFlag_HasCRC;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save update time on root FileNode
|
||||||
|
if (fileNode->parent == NULL) {
|
||||||
|
fileNode->fileTime = Time_GetCurrentTime();
|
||||||
|
fileNode->flags |= FileFlag_HasTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (fileNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
int FileNode_Refresh_Iterate(char *path, char *name, void *d) {
|
||||||
|
FileNode fileNode = (FileNode)d;
|
||||||
|
FileNode fileNodeChild;
|
||||||
|
|
||||||
|
if (!strcmp(name, FileNode_Filename)) {
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search the file on childs
|
||||||
|
fileNodeChild = fileNode->child;
|
||||||
|
while (fileNodeChild) {
|
||||||
|
if (!strcmp(fileNodeChild->name, name)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fileNodeChild = fileNodeChild->next;
|
||||||
|
}
|
||||||
|
if (fileNodeChild) {
|
||||||
|
// Exists, Refresh
|
||||||
|
FileNode_Refresh(fileNodeChild, path);
|
||||||
|
} else {
|
||||||
|
// New, Build
|
||||||
|
fileNodeChild = FileNode_Build(path);
|
||||||
|
FileNode_SetStatusRec(fileNodeChild, FileStatus_New);
|
||||||
|
FileNode_AddChild(fileNode, fileNodeChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
70
src/filenode.h
Normal file
70
src/filenode.h
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright (c) 2014-2021 Valeriano Alfonso Rodriguez
|
||||||
|
|
||||||
|
#ifndef _FILENODE_H_
|
||||||
|
#define _FILENODE_H_
|
||||||
|
|
||||||
|
#include "fileutil.h"
|
||||||
|
|
||||||
|
#define FileNode_Filename "nodesFile.fs"
|
||||||
|
|
||||||
|
#define FileNode_Version 5
|
||||||
|
|
||||||
|
#define FileFlag_Root 1
|
||||||
|
#define FileFlag_Normal 2
|
||||||
|
#define FileFlag_Directory 4
|
||||||
|
#define FileFlag_HasSize 8
|
||||||
|
#define FileFlag_HasTime 16
|
||||||
|
#define FileFlag_HasCRC 32
|
||||||
|
#define FileFlag_PlaceHolder 512
|
||||||
|
#define FileFlag_MarkerForReview 1024
|
||||||
|
|
||||||
|
typedef enum EFileStatus {
|
||||||
|
FileStatus_None,
|
||||||
|
FileStatus_New,
|
||||||
|
FileStatus_Modified,
|
||||||
|
FileStatus_Deleted,
|
||||||
|
FileStatus_Undeleted
|
||||||
|
} FileStatus;
|
||||||
|
|
||||||
|
typedef struct TFileNode TFileNode, *FileNode;
|
||||||
|
struct TFileNode {
|
||||||
|
char name[MaxFilename];
|
||||||
|
int flags;
|
||||||
|
FileStatus status;
|
||||||
|
|
||||||
|
long long size;
|
||||||
|
unsigned long crc;
|
||||||
|
FileTime fileTime;
|
||||||
|
|
||||||
|
FileNode child;
|
||||||
|
int childCount;
|
||||||
|
|
||||||
|
FileNode next;
|
||||||
|
FileNode parent;
|
||||||
|
};
|
||||||
|
|
||||||
|
FileNode FileNode_Create();
|
||||||
|
FileNode FileNode_Copy(FileNode fileNode);
|
||||||
|
void FileNode_Delete(FileNode fileNode);
|
||||||
|
void FileNode_AddChild(FileNode file, FileNode file2);
|
||||||
|
FileNode FileNode_GetRoot(FileNode fileNode);
|
||||||
|
|
||||||
|
char *FileNode_GetFullPath(FileNode fileNode, char *basePath, char *path);
|
||||||
|
|
||||||
|
void FileNode_LoadSize(FileNode fileNode, char *file);
|
||||||
|
void FileNode_LoadTime(FileNode fileNode, char *file);
|
||||||
|
void FileNode_LoadSizeAndTime(FileNode fileNode, char *file);
|
||||||
|
void FileNode_LoadCRC(FileNode fileNode, char *file);
|
||||||
|
|
||||||
|
void FileNode_Save(FileNode fileNode, char *fichero);
|
||||||
|
FileNode FileNode_Load(char *fichero);
|
||||||
|
|
||||||
|
void FileNode_PrintNode(FileNode fileNode);
|
||||||
|
void FileNode_Print(FileNode fileNode);
|
||||||
|
|
||||||
|
FileNode FileNode_Build(char *path);
|
||||||
|
|
||||||
|
FileNode FileNode_Refresh(FileNode file, char *path);
|
||||||
|
|
||||||
|
#endif
|
||||||
422
src/fileutil.c
Normal file
422
src/fileutil.c
Normal file
@@ -0,0 +1,422 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright (c) 2014-2021 Valeriano Alfonso Rodriguez
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
#define _WIN32_WINNT 0x0501
|
||||||
|
#include <direct.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <io.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#else
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <utime.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "fileutil.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
long long FileTime_to_POSIX(FILETIME fileTime) {
|
||||||
|
LARGE_INTEGER date, adjust;
|
||||||
|
|
||||||
|
// takes the last modified date
|
||||||
|
date.HighPart = fileTime.dwHighDateTime;
|
||||||
|
date.LowPart = fileTime.dwLowDateTime;
|
||||||
|
|
||||||
|
// 100-nanoseconds = milliseconds * 10000
|
||||||
|
adjust.QuadPart = 11644473600000ll * 10000;
|
||||||
|
|
||||||
|
// removes the diff between 1970 and 1601
|
||||||
|
date.QuadPart -= adjust.QuadPart;
|
||||||
|
|
||||||
|
// converts back from 100-nanoseconds to seconds
|
||||||
|
return date.QuadPart / 10000000ll;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILETIME POSIX_to_FileTime(FileTime fileTime) {
|
||||||
|
LARGE_INTEGER date, adjust;
|
||||||
|
FILETIME fileTimeOut;
|
||||||
|
|
||||||
|
// converts to 100-nanoseconds from seconds
|
||||||
|
date.QuadPart = fileTime * 10000000ll;
|
||||||
|
|
||||||
|
// 100-nanoseconds = milliseconds * 10000
|
||||||
|
adjust.QuadPart = 11644473600000ll * 10000ll;
|
||||||
|
|
||||||
|
// removes the diff between 1970 and 1601
|
||||||
|
date.QuadPart += adjust.QuadPart;
|
||||||
|
|
||||||
|
// asigns to filetime
|
||||||
|
fileTimeOut.dwHighDateTime = date.HighPart;
|
||||||
|
fileTimeOut.dwLowDateTime = date.LowPart;
|
||||||
|
return fileTimeOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// FileTime_Get
|
||||||
|
//
|
||||||
|
// Gets the current time in POSIX.
|
||||||
|
FileTime FileTime_Get(char *fileName) {
|
||||||
|
HANDLE hFile;
|
||||||
|
FILETIME ftCreate, ftAccess, ftWrite;
|
||||||
|
hFile = CreateFile(fileName, READ_CONTROL, FILE_SHARE_READ, NULL,
|
||||||
|
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||||
|
GetFileTime(hFile, &ftCreate, &ftAccess, &ftWrite);
|
||||||
|
CloseHandle(hFile);
|
||||||
|
return (FileTime_to_POSIX(ftWrite));
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// FileTime_Set
|
||||||
|
//
|
||||||
|
// Sets the current time in POSIX.
|
||||||
|
void FileTime_Set(char *fileName, FileTime fileTime) {
|
||||||
|
HANDLE hFile;
|
||||||
|
FILETIME ftWrite;
|
||||||
|
hFile = CreateFile(fileName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL,
|
||||||
|
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||||
|
ftWrite = POSIX_to_FileTime(fileTime);
|
||||||
|
SetFileTime(hFile, NULL, NULL, &ftWrite);
|
||||||
|
CloseHandle(hFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// FileTime_Get
|
||||||
|
//
|
||||||
|
// Gets the current time in POSIX.
|
||||||
|
FileTime FileTime_Get(char *fileName) {
|
||||||
|
struct stat fs;
|
||||||
|
lstat(fileName, &fs);
|
||||||
|
return (fs.st_mtime);
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// FileTime_Set
|
||||||
|
//
|
||||||
|
// Sets the current time in POSIX.
|
||||||
|
void FileTime_Set(char *fileName, FileTime t) {
|
||||||
|
struct utimbuf utb;
|
||||||
|
|
||||||
|
utb.actime = t;
|
||||||
|
utb.modtime = t;
|
||||||
|
utime(fileName, &utb);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// FileTime_Print
|
||||||
|
//
|
||||||
|
// Prints the filetime
|
||||||
|
void FileTime_Print(FileTime fileTime) {
|
||||||
|
struct tm *tms;
|
||||||
|
|
||||||
|
tms = localtime((time_t *)&fileTime);
|
||||||
|
Print("%04d-%02d-%02d %02d:%02d:%02d", tms->tm_year + 1900, tms->tm_mon + 1,
|
||||||
|
tms->tm_mday, tms->tm_hour, tms->tm_min, tms->tm_sec);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
|
||||||
|
long long File_GetSize(char *fileName) {
|
||||||
|
HANDLE hFile;
|
||||||
|
DWORD fSize;
|
||||||
|
hFile = CreateFile(fileName, READ_CONTROL, FILE_SHARE_READ, NULL,
|
||||||
|
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||||
|
fSize = GetFileSize(hFile, NULL);
|
||||||
|
CloseHandle(hFile);
|
||||||
|
return (fSize);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
|
||||||
|
long long File_GetSize(char *fileName) {
|
||||||
|
struct stat fs;
|
||||||
|
lstat(fileName, &fs);
|
||||||
|
return (fs.st_size);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
void File_GetSizeAndTime(char *fileName, long long *size, FileTime *time) {
|
||||||
|
HANDLE hFile;
|
||||||
|
DWORD fSize;
|
||||||
|
FILETIME ftCreate, ftAccess, ftWrite;
|
||||||
|
hFile = CreateFile(fileName, READ_CONTROL, FILE_SHARE_READ, NULL,
|
||||||
|
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||||
|
fSize = GetFileSize(hFile, NULL);
|
||||||
|
GetFileTime(hFile, &ftCreate, &ftAccess, &ftWrite);
|
||||||
|
CloseHandle(hFile);
|
||||||
|
*size = fSize;
|
||||||
|
*time = FileTime_to_POSIX(ftWrite);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
void File_GetSizeAndTime(char *fileName, long long *size, FileTime *time) {
|
||||||
|
struct stat fs;
|
||||||
|
lstat(fileName, &fs);
|
||||||
|
*size = fs.st_size;
|
||||||
|
*time = fs.st_mtime;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void File_GetName(char *path, char *name) {
|
||||||
|
size_t i, j;
|
||||||
|
|
||||||
|
i = strlen(path) - 1;
|
||||||
|
while (i >= 0) {
|
||||||
|
if (path[i] == '/' || path[i] == '\\') {
|
||||||
|
i++;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i < 0)
|
||||||
|
i++;
|
||||||
|
|
||||||
|
j = 0;
|
||||||
|
while (path[i]) {
|
||||||
|
name[j] = path[i];
|
||||||
|
i++;
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
name[j] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
|
||||||
|
int File_ExistsPath(char *path) {
|
||||||
|
unsigned rc;
|
||||||
|
rc = GetFileAttributes(path);
|
||||||
|
|
||||||
|
if (rc == INVALID_FILE_ATTRIBUTES) {
|
||||||
|
SetError(strerror(errno));
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
int File_IsDirectory(char *fileName) {
|
||||||
|
unsigned rc;
|
||||||
|
rc = GetFileAttributes(fileName);
|
||||||
|
|
||||||
|
if (rc == INVALID_FILE_ATTRIBUTES) {
|
||||||
|
SetError(strerror(errno));
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
if (rc & FILE_ATTRIBUTE_DIRECTORY) {
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
int File_IsFile(char *fileName) {
|
||||||
|
unsigned rc;
|
||||||
|
rc = GetFileAttributes(fileName);
|
||||||
|
|
||||||
|
if (rc == INVALID_FILE_ATTRIBUTES) {
|
||||||
|
SetError(strerror(errno));
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
if (rc & FILE_ATTRIBUTE_DIRECTORY) {
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
int File_ExistsPath(char *path) {
|
||||||
|
struct stat info;
|
||||||
|
|
||||||
|
if (lstat(path, &info) == -1) {
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
int File_IsDirectory(char *fileName) {
|
||||||
|
struct stat info;
|
||||||
|
|
||||||
|
if (lstat(fileName, &info) == -1) {
|
||||||
|
SetError(strerror(errno));
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
if (S_ISDIR(info.st_mode)) {
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
int File_IsFile(char *fileName) {
|
||||||
|
struct stat info;
|
||||||
|
|
||||||
|
if (lstat(fileName, &info) == -1) {
|
||||||
|
SetError(strerror(errno));
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
if (S_ISDIR(info.st_mode)) {
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
int File_MakeDirectory(char *path) { return (CreateDirectory(path, NULL)); }
|
||||||
|
#else
|
||||||
|
int File_MakeDirectory(char *path) {
|
||||||
|
int rc = mkdir(path, 0777);
|
||||||
|
if (rc != 0) {
|
||||||
|
SetError(strerror(errno));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
|
||||||
|
void File_IterateDir(char *path,
|
||||||
|
int (*func)(char *path, char *name, void *data),
|
||||||
|
void *data) {
|
||||||
|
intptr_t handle;
|
||||||
|
struct _finddata_t fileinfo;
|
||||||
|
char f_path[MaxPath];
|
||||||
|
int fin = 0;
|
||||||
|
int findnext_rc;
|
||||||
|
char path_aux[MaxPath];
|
||||||
|
|
||||||
|
snprintf(path_aux, MaxPath, "%s/*", path);
|
||||||
|
handle = _findfirst(path_aux, &fileinfo);
|
||||||
|
if (handle == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Iterate directory
|
||||||
|
do {
|
||||||
|
if (strcmp(fileinfo.name, ".") && strcmp(fileinfo.name, "..")) {
|
||||||
|
// Each item
|
||||||
|
snprintf(f_path, 512, "%s/%s", path, fileinfo.name);
|
||||||
|
fin = func(f_path, fileinfo.name, data);
|
||||||
|
}
|
||||||
|
findnext_rc = _findnext(handle, &fileinfo);
|
||||||
|
} while (findnext_rc != -1 && !fin);
|
||||||
|
_findclose(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
void File_IterateDir(char *path,
|
||||||
|
int (*func)(char *path, char *name, void *data),
|
||||||
|
void *data) {
|
||||||
|
DIR *directorio;
|
||||||
|
struct dirent *entidad_dir;
|
||||||
|
char f_path[MaxPath];
|
||||||
|
int fin = 0;
|
||||||
|
char *ptr;
|
||||||
|
|
||||||
|
directorio = opendir(path);
|
||||||
|
if (directorio == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Iterate directory
|
||||||
|
do {
|
||||||
|
entidad_dir = readdir(directorio);
|
||||||
|
if (entidad_dir != NULL) {
|
||||||
|
if (strcmp(entidad_dir->d_name, ".") &&
|
||||||
|
strcmp(entidad_dir->d_name, "..")) {
|
||||||
|
// Each item
|
||||||
|
snprintf(f_path, MaxPath, "%s/%s", path, entidad_dir->d_name);
|
||||||
|
fin = func(f_path, entidad_dir->d_name, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (entidad_dir != NULL && !fin);
|
||||||
|
closedir(directorio);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int File_Delete(char *path) {
|
||||||
|
#ifdef WIN32
|
||||||
|
int rc = remove(path);
|
||||||
|
#else
|
||||||
|
int rc = unlink(path);
|
||||||
|
#endif
|
||||||
|
if (rc != 0) {
|
||||||
|
SetError(strerror(errno));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int File_DeleteDirectory(char *path) {
|
||||||
|
#ifndef WIN32
|
||||||
|
int rc = rmdir(path);
|
||||||
|
#else
|
||||||
|
int rc = _rmdir(path);
|
||||||
|
#endif
|
||||||
|
if (rc != 0) {
|
||||||
|
SetError(strerror(errno));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MaxBuffer 16384
|
||||||
|
int File_Copy(const char *pathOrig, const char *pathDest) {
|
||||||
|
FILE *fOrig = NULL;
|
||||||
|
FILE *fDest = NULL;
|
||||||
|
char *buffer = NULL;
|
||||||
|
size_t readLen = 0;
|
||||||
|
size_t writeLen = 0;
|
||||||
|
int status = 0;
|
||||||
|
|
||||||
|
if ((fOrig = fopen(pathOrig, "rb")) == NULL) {
|
||||||
|
SetError(strerror(errno));
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
if ((fDest = fopen(pathDest, "wb")) == NULL) {
|
||||||
|
SetError(strerror(errno));
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer = (char *)malloc(sizeof(char) * MaxBuffer);
|
||||||
|
if (buffer == NULL) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
readLen = fread(buffer, 1, MaxBuffer, fOrig);
|
||||||
|
if (readLen > 0) {
|
||||||
|
writeLen = fwrite(buffer, 1, readLen, fDest);
|
||||||
|
if (writeLen != readLen) {
|
||||||
|
SetError("Write error");
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (readLen == MaxBuffer);
|
||||||
|
|
||||||
|
if (feof(fOrig)) {
|
||||||
|
status = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
if (fOrig != NULL) {
|
||||||
|
fclose(fOrig);
|
||||||
|
}
|
||||||
|
if (fDest != NULL) {
|
||||||
|
fclose(fDest);
|
||||||
|
}
|
||||||
|
if (buffer != NULL) {
|
||||||
|
free(buffer);
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
59
src/fileutil.h
Normal file
59
src/fileutil.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright (c) 2014-2021 Valeriano Alfonso Rodriguez
|
||||||
|
|
||||||
|
#ifndef _FILEUTIL_
|
||||||
|
#define _FILEUTIL_
|
||||||
|
|
||||||
|
////////////////////////////////////////////////
|
||||||
|
// FileTime
|
||||||
|
|
||||||
|
typedef long long FileTime;
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// FileTime_Get
|
||||||
|
//
|
||||||
|
// Gets the current time in POSIX.
|
||||||
|
FileTime FileTime_Get(char *filename);
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// FileTime_Set
|
||||||
|
//
|
||||||
|
// Sets the current time in POSIX.
|
||||||
|
void FileTime_Set(char *filename, FileTime t);
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// FileTime_Print
|
||||||
|
//
|
||||||
|
// Prints the filetime
|
||||||
|
void FileTime_Print(FileTime t);
|
||||||
|
|
||||||
|
///////////////////////////////////////////////
|
||||||
|
// File
|
||||||
|
#define MaxFilename 2048
|
||||||
|
#define MaxPath 4096
|
||||||
|
#define MaxPathNodes 512
|
||||||
|
|
||||||
|
long long File_GetSize(char *fileName);
|
||||||
|
|
||||||
|
void File_GetSizeAndTime(char *fileName, long long *size, FileTime *time);
|
||||||
|
|
||||||
|
void File_GetName(char *path, char *name);
|
||||||
|
|
||||||
|
int File_ExistsPath(char *path);
|
||||||
|
|
||||||
|
int File_IsDirectory(char *path);
|
||||||
|
|
||||||
|
int File_IsFile(char *path);
|
||||||
|
|
||||||
|
int File_MakeDirectory(char *path);
|
||||||
|
|
||||||
|
void File_IterateDir(char *path,
|
||||||
|
int (*func)(char *path, char *name, void *data),
|
||||||
|
void *data);
|
||||||
|
|
||||||
|
int File_Delete(char *path);
|
||||||
|
int File_DeleteDirectory(char *path);
|
||||||
|
|
||||||
|
int File_Copy(const char *pathOrig, const char *pathDest);
|
||||||
|
|
||||||
|
#endif
|
||||||
376
src/main.c
Normal file
376
src/main.c
Normal file
@@ -0,0 +1,376 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright (c) 2014-2021 Valeriano Alfonso Rodriguez
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "actionfilenode.h"
|
||||||
|
#include "actionfilenodecopy.h"
|
||||||
|
#include "actionfilenodesync.h"
|
||||||
|
#include "crc.h"
|
||||||
|
#include "filenode.h"
|
||||||
|
#include "fileutil.h"
|
||||||
|
#include "parameteroperation.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
FileNode CheckDir(char *path, int recheck) {
|
||||||
|
char dirNodesFile[MaxPath];
|
||||||
|
FileNode fileNode;
|
||||||
|
|
||||||
|
// Check directory
|
||||||
|
snprintf(dirNodesFile, MaxPath, "%s/" FileNode_Filename, path);
|
||||||
|
if (recheck) {
|
||||||
|
Print("Checking Directory.. %s\n", path);
|
||||||
|
long long tScan = Time_GetTime();
|
||||||
|
fileNode = FileNode_Load(dirNodesFile);
|
||||||
|
if (fileNode) {
|
||||||
|
fileNode = FileNode_Refresh(fileNode, path);
|
||||||
|
} else {
|
||||||
|
fileNode = FileNode_Build(path);
|
||||||
|
}
|
||||||
|
tScan = Time_GetTime() - tScan;
|
||||||
|
Print("\ttScan :");
|
||||||
|
PrintElapsedTime(tScan);
|
||||||
|
Print("\n");
|
||||||
|
|
||||||
|
long long tSave = Time_GetTime();
|
||||||
|
FileNode_Save(fileNode, dirNodesFile);
|
||||||
|
tSave = Time_GetTime() - tSave;
|
||||||
|
Print("\ttSave :");
|
||||||
|
PrintElapsedTime(tSave);
|
||||||
|
Print("\n");
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Print("Loading Directory.. %s\n", path);
|
||||||
|
fileNode = FileNode_Load(dirNodesFile);
|
||||||
|
if (!fileNode) {
|
||||||
|
Print("Error, no nodesFile.fs\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fileNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintStatistics(ActionFileNode actionFileNode,
|
||||||
|
ActionFileNodeResult result) {
|
||||||
|
ActionQueueStatistics statistics;
|
||||||
|
if (ActionFileNode_Statistics(actionFileNode, &statistics, result) == 0) {
|
||||||
|
Print("Noting to do.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Print("Statistics\n");
|
||||||
|
|
||||||
|
Print(" % 8s % 8s % 8s\n", "Read", "Write", "Delete");
|
||||||
|
Print("Left :");
|
||||||
|
PrintDataSize(statistics.readLeft);
|
||||||
|
PrintDataSize(statistics.writeLeft);
|
||||||
|
PrintDataSize(statistics.deleteLeft);
|
||||||
|
Print("\n");
|
||||||
|
Print("Right:");
|
||||||
|
PrintDataSize(statistics.readRight);
|
||||||
|
PrintDataSize(statistics.writeRight);
|
||||||
|
PrintDataSize(statistics.deleteRight);
|
||||||
|
Print("\n");
|
||||||
|
|
||||||
|
Print("\n");
|
||||||
|
Print("Copy count : % 10d\n", statistics.fullCopyCount);
|
||||||
|
Print("Date copy count: % 10d\n", statistics.dateCopyCount);
|
||||||
|
Print("Directory count: % 10d\n", statistics.directoryCount);
|
||||||
|
Print("Delete count : % 10d\n", statistics.deleteCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Sync(char *pathLeft, char *pathRight, int reCheck, int dryRun) {
|
||||||
|
FileNode fileNodeLeft;
|
||||||
|
FileNode fileNodeRight;
|
||||||
|
|
||||||
|
// Check and load directories
|
||||||
|
if (!File_ExistsPath(pathLeft) || !File_IsDirectory(pathLeft)) {
|
||||||
|
Print("Error, directory does not exist: %s\n", pathLeft);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!File_ExistsPath(pathRight) || !File_IsDirectory(pathRight)) {
|
||||||
|
Print("Error, directory does not exist: %s\n", pathRight);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
fileNodeLeft = CheckDir(pathLeft, reCheck);
|
||||||
|
if (!fileNodeLeft) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
fileNodeRight = CheckDir(pathRight, reCheck);
|
||||||
|
if (!fileNodeRight) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build actions
|
||||||
|
long long tBuild = Time_GetTime();
|
||||||
|
Print("Building action list.. \n");
|
||||||
|
ActionFileNode actionFileNode = NULL;
|
||||||
|
actionFileNode = ActionFileNode_BuildSync(fileNodeLeft, fileNodeRight);
|
||||||
|
tBuild = Time_GetTime() - tBuild;
|
||||||
|
Print("\ttBuild:");
|
||||||
|
PrintElapsedTime(tBuild);
|
||||||
|
Print("\n");
|
||||||
|
|
||||||
|
int postCheckDir = 0;
|
||||||
|
long long tRun = Time_GetTime();
|
||||||
|
if (dryRun) {
|
||||||
|
// Show action list
|
||||||
|
ActionFileNode_Print(actionFileNode);
|
||||||
|
PrintStatistics(actionFileNode, ActionFileNodeResult_Nothing);
|
||||||
|
} else {
|
||||||
|
// Run action list
|
||||||
|
if (ActionFileNode_RunList(actionFileNode, pathLeft, pathRight)) {
|
||||||
|
PrintStatistics(actionFileNode, ActionFileNodeResult_Ok);
|
||||||
|
postCheckDir = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tRun = Time_GetTime() - tRun;
|
||||||
|
Print("\ttRun:");
|
||||||
|
PrintElapsedTime(tRun);
|
||||||
|
Print("\n");
|
||||||
|
|
||||||
|
if (postCheckDir) {
|
||||||
|
CheckDir(pathLeft, reCheck);
|
||||||
|
CheckDir(pathRight, reCheck);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Copy(char *pathLeft, char *pathRight, int reCheck, int dryRun) {
|
||||||
|
FileNode fileNodeLeft;
|
||||||
|
FileNode fileNodeRight;
|
||||||
|
|
||||||
|
// Check and load directories
|
||||||
|
if (!File_ExistsPath(pathLeft) || !File_IsDirectory(pathLeft)) {
|
||||||
|
Print("Error, directory does not exist: %s\n", pathLeft);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!File_ExistsPath(pathRight) || !File_IsDirectory(pathRight)) {
|
||||||
|
Print("Error, directory does not exist: %s\n", pathRight);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
fileNodeLeft = CheckDir(pathLeft, reCheck);
|
||||||
|
if (!fileNodeLeft) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
fileNodeRight = CheckDir(pathRight, reCheck);
|
||||||
|
if (!fileNodeRight) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build actions
|
||||||
|
long long tBuild = Time_GetTime();
|
||||||
|
Print("Building action list.. \n");
|
||||||
|
ActionFileNode actionFileNode = NULL;
|
||||||
|
actionFileNode = ActionFileNode_BuildCopy(fileNodeLeft, fileNodeRight);
|
||||||
|
tBuild = Time_GetTime() - tBuild;
|
||||||
|
Print("\ttBuild:");
|
||||||
|
PrintElapsedTime(tBuild);
|
||||||
|
Print("\n");
|
||||||
|
|
||||||
|
int postCheckDir = 0;
|
||||||
|
long long tRun = Time_GetTime();
|
||||||
|
if (dryRun) {
|
||||||
|
// Show action list
|
||||||
|
ActionFileNode_Print(actionFileNode);
|
||||||
|
PrintStatistics(actionFileNode, ActionFileNodeResult_Nothing);
|
||||||
|
} else {
|
||||||
|
// Run action list
|
||||||
|
if (ActionFileNode_RunList(actionFileNode, pathLeft, pathRight)) {
|
||||||
|
PrintStatistics(actionFileNode, ActionFileNodeResult_Ok);
|
||||||
|
postCheckDir = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tRun = Time_GetTime() - tRun;
|
||||||
|
Print("\ttRun:");
|
||||||
|
PrintElapsedTime(tRun);
|
||||||
|
Print("\n");
|
||||||
|
|
||||||
|
if (postCheckDir) {
|
||||||
|
CheckDir(pathLeft, reCheck);
|
||||||
|
CheckDir(pathRight, reCheck);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct SApplicationConfiguration TApplicationConfiguration,
|
||||||
|
*ApplicationConfiguration;
|
||||||
|
struct SApplicationConfiguration {
|
||||||
|
char *Dirs[10];
|
||||||
|
bool NoScan;
|
||||||
|
bool Dummy;
|
||||||
|
bool Sync;
|
||||||
|
bool Copy;
|
||||||
|
bool NoAction;
|
||||||
|
char *Log;
|
||||||
|
};
|
||||||
|
TApplicationConfiguration defaultConfig = {{NULL}, false, false, false,
|
||||||
|
false, false, NULL};
|
||||||
|
|
||||||
|
bool SetParam_Dir(int argc, char *argv[], void *data) {
|
||||||
|
ApplicationConfiguration config = (ApplicationConfiguration)data;
|
||||||
|
if (File_ExistsPath(argv[0]) == 0) {
|
||||||
|
Print("Error: Path \"%s\" does not exist.\n", argv[0]);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
char **destDir = config->Dirs;
|
||||||
|
while (destDir[0] != NULL) {
|
||||||
|
destDir++;
|
||||||
|
}
|
||||||
|
destDir[0] = argv[0];
|
||||||
|
destDir++;
|
||||||
|
destDir = NULL;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SetParam_NoCheck(int argc, char *argv[], void *data) {
|
||||||
|
((ApplicationConfiguration)data)->NoScan = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SetParam_Dummy(int argc, char *argv[], void *data) {
|
||||||
|
((ApplicationConfiguration)data)->Dummy = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SetParam_Sync(int argc, char *argv[], void *data) {
|
||||||
|
((ApplicationConfiguration)data)->Sync = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SetParam_Copy(int argc, char *argv[], void *data) {
|
||||||
|
((ApplicationConfiguration)data)->Copy = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SetParam_Log(int argc, char *argv[], void *data) {
|
||||||
|
ApplicationConfiguration config = (ApplicationConfiguration)data;
|
||||||
|
config->Log = argv[0];
|
||||||
|
Print_SetOutFile(config->Log);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Func_Scan(int argc, char *argv[], void *data) {
|
||||||
|
((ApplicationConfiguration)data)->NoAction = true;
|
||||||
|
|
||||||
|
// Scan directory information tree and save
|
||||||
|
long long tScan = Time_GetTime();
|
||||||
|
FileNode fileNode;
|
||||||
|
Print("Building FileNode..\n");
|
||||||
|
fileNode = FileNode_Build(argv[0]);
|
||||||
|
tScan = Time_GetTime() - tScan;
|
||||||
|
Print("\ttScan :");
|
||||||
|
PrintElapsedTime(tScan);
|
||||||
|
Print("\n");
|
||||||
|
FileNode_Save(fileNode, argv[1]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Func_Rescan(int argc, char *argv[], void *data) {
|
||||||
|
((ApplicationConfiguration)data)->NoAction = true;
|
||||||
|
|
||||||
|
// Scan directory information and save tree
|
||||||
|
FileNode fileNode;
|
||||||
|
Print("Loading FileNode..\n");
|
||||||
|
fileNode = FileNode_Load(argv[1]);
|
||||||
|
if (fileNode) {
|
||||||
|
Print("Rebuilding FileNode..\n");
|
||||||
|
long long tScan = Time_GetTime();
|
||||||
|
fileNode = FileNode_Refresh(fileNode, argv[0]);
|
||||||
|
tScan = Time_GetTime() - tScan;
|
||||||
|
Print("\ttScan :");
|
||||||
|
PrintElapsedTime(tScan);
|
||||||
|
Print("\n");
|
||||||
|
FileNode_Save(fileNode, argv[1]);
|
||||||
|
} else {
|
||||||
|
Print("Building FileNode..\n");
|
||||||
|
long long tScan = Time_GetTime();
|
||||||
|
fileNode = FileNode_Build(argv[0]);
|
||||||
|
tScan = Time_GetTime() - tScan;
|
||||||
|
Print("\ttScan :");
|
||||||
|
PrintElapsedTime(tScan);
|
||||||
|
Print("\n");
|
||||||
|
FileNode_Save(fileNode, argv[1]);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Func_Read(int argc, char *argv[], void *data) {
|
||||||
|
((ApplicationConfiguration)data)->NoAction = true;
|
||||||
|
|
||||||
|
// Read information tree from file
|
||||||
|
FileNode fileNode;
|
||||||
|
fileNode = FileNode_Load(argv[0]);
|
||||||
|
if (fileNode) {
|
||||||
|
FileNode_Print(fileNode);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Func_Check(int argc, char *argv[], void *data) {
|
||||||
|
((ApplicationConfiguration)data)->NoAction = true;
|
||||||
|
|
||||||
|
// Read directory information tree
|
||||||
|
char *path = argv[0];
|
||||||
|
FileNode fileNode;
|
||||||
|
|
||||||
|
fileNode = CheckDir(path, 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
TParameterOperation _parameterOperations[] = {
|
||||||
|
{"dir", 1, "Specify a directory", SetParam_Dir},
|
||||||
|
{"nocheck", 0, "Do not check for changes on directories", SetParam_NoCheck},
|
||||||
|
{"dummy", 0, "Do not perform operations", SetParam_Dummy},
|
||||||
|
{"copy", 0, "Copy first directory to second directory", SetParam_Copy},
|
||||||
|
{"sync", 0, "Synchronize between two directories", SetParam_Sync},
|
||||||
|
{"log", 1, "Log actions to file", SetParam_Log},
|
||||||
|
|
||||||
|
{"scan", 2, "Scan directory and save to filenode file", Func_Scan},
|
||||||
|
{"rescan", 2, "Rescan directory and save to filenode file", Func_Rescan},
|
||||||
|
{"read", 1, "Read filenode file", Func_Read},
|
||||||
|
{"check", 1, "Check changes on a directory", Func_Check},
|
||||||
|
|
||||||
|
{NULL, 0, NULL, NULL},
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
TApplicationConfiguration config = defaultConfig;
|
||||||
|
|
||||||
|
Exceptions_Init();
|
||||||
|
|
||||||
|
int parameterParsingResult =
|
||||||
|
ParameterOperation_Parse(argc, argv, _parameterOperations, &config);
|
||||||
|
if (parameterParsingResult <= 0) {
|
||||||
|
ParameterOperation_PrintHelp(_parameterOperations);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (config.NoAction) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Print("\n================================ FileSync "
|
||||||
|
"===================================\n");
|
||||||
|
|
||||||
|
if (config.Copy == false && config.Sync == false) {
|
||||||
|
Print("Error: Action not specified.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (config.Dirs[0] == NULL || config.Dirs[1] == NULL) {
|
||||||
|
Print("Error: Two directories are needed.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (config.Copy) {
|
||||||
|
Copy(config.Dirs[0], config.Dirs[1], (config.NoScan == false),
|
||||||
|
config.Dummy);
|
||||||
|
}
|
||||||
|
if (config.Sync) {
|
||||||
|
Sync(config.Dirs[0], config.Dirs[1], (config.NoScan == false),
|
||||||
|
config.Dummy);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
70
src/parameteroperation.c
Normal file
70
src/parameteroperation.c
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright (c) 2014-2021 Valeriano Alfonso Rodriguez
|
||||||
|
|
||||||
|
#include "parameteroperation.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
int ParameterOperation_Parse(int argumentCount, char *arguments[],
|
||||||
|
TParameterOperation parameterOperations[],
|
||||||
|
void *data) {
|
||||||
|
int processedParams = 0;
|
||||||
|
char **currentArguments = arguments + 1;
|
||||||
|
for (int i = 1; i < argumentCount; i++) {
|
||||||
|
char *currentArgument = currentArguments[0];
|
||||||
|
currentArguments++;
|
||||||
|
if (currentArgument[0] != '-') {
|
||||||
|
Print("Error: Garbage found \"%s\" in position %d.\n", arguments[i],
|
||||||
|
i + 1);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
while (currentArgument[0] == '-') {
|
||||||
|
currentArgument++;
|
||||||
|
}
|
||||||
|
bool processed = false;
|
||||||
|
int j = 0;
|
||||||
|
while (parameterOperations[j].Name != NULL) {
|
||||||
|
ParameterOperation parameterOperation = ¶meterOperations[j];
|
||||||
|
if (String_CompareCaseInsensitive(currentArgument,
|
||||||
|
parameterOperation->Name) == 0) {
|
||||||
|
if ((i + parameterOperation->NumItems) >= argumentCount) {
|
||||||
|
Print("Error: Parsing parameter \"-%s\" in position %d, "
|
||||||
|
"missig parameter data.\n",
|
||||||
|
parameterOperations[j].Name, i + 1);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
bool result = parameterOperation->SetFunc(
|
||||||
|
parameterOperation->NumItems, currentArguments, data);
|
||||||
|
if (result == false) {
|
||||||
|
Print("Error: Parsing parameter \"-%s\" in position %d.\n",
|
||||||
|
parameterOperations[j].Name, i + 1);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
currentArguments += parameterOperation->NumItems;
|
||||||
|
i += parameterOperation->NumItems;
|
||||||
|
processedParams++;
|
||||||
|
processed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
if (processed == false) {
|
||||||
|
Print("Error: Unknow parameter \"%s\" in position %d.\n",
|
||||||
|
arguments[i], i + 1);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return processedParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParameterOperation_PrintHelp(TParameterOperation parameterOperations[]) {
|
||||||
|
int i = 0;
|
||||||
|
Print("Parameters:\n");
|
||||||
|
while (parameterOperations[i].Name != NULL) {
|
||||||
|
Print("\t-%s", parameterOperations[i].Name);
|
||||||
|
for (int j = 0; j < parameterOperations[i].NumItems; j++) {
|
||||||
|
Print(" [Item]");
|
||||||
|
}
|
||||||
|
Print(": %s.\n", parameterOperations[i].Description);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/parameteroperation.h
Normal file
23
src/parameteroperation.h
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright (c) 2014-2021 Valeriano Alfonso Rodriguez
|
||||||
|
|
||||||
|
#ifndef _PARAMETEROPERATION_
|
||||||
|
#define _PARAMETEROPERATION_
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
typedef struct SParameterOperation TParameterOperation, *ParameterOperation;
|
||||||
|
struct SParameterOperation {
|
||||||
|
char *Name;
|
||||||
|
int NumItems;
|
||||||
|
char *Description;
|
||||||
|
bool (*SetFunc)(int argumentCount, char *arguments[], void *data);
|
||||||
|
};
|
||||||
|
|
||||||
|
int ParameterOperation_Parse(int argumentCount, char *arguments[],
|
||||||
|
TParameterOperation parameterOperations[],
|
||||||
|
void *data);
|
||||||
|
|
||||||
|
void ParameterOperation_PrintHelp(TParameterOperation parameterOperations[]);
|
||||||
|
|
||||||
|
#endif
|
||||||
355
src/util.c
Normal file
355
src/util.c
Normal file
@@ -0,0 +1,355 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright (c) 2014-2021 Valeriano Alfonso Rodriguez
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// String_Copy
|
||||||
|
//
|
||||||
|
// Copies a string.
|
||||||
|
char *String_Copy(char *str) {
|
||||||
|
char *strnew;
|
||||||
|
size_t len;
|
||||||
|
len = strlen(str);
|
||||||
|
strnew = (char *)malloc(len + 1);
|
||||||
|
if (strnew != NULL) {
|
||||||
|
strcpy(strnew, str);
|
||||||
|
}
|
||||||
|
return (strnew);
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// String_CompareCaseInsensitive
|
||||||
|
//
|
||||||
|
// Compares a string case insensitive
|
||||||
|
int String_CompareCaseInsensitive(char *str0, char *str1) {
|
||||||
|
for (int i = 0;; i++) {
|
||||||
|
char c0 = tolower(str0[i]);
|
||||||
|
char c1 = tolower(str1[i]);
|
||||||
|
if (c0 != c1) {
|
||||||
|
return (c0 < c1) ? -1 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c0 == '\0') {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
// WIN32
|
||||||
|
/////////////////////////////
|
||||||
|
// Time_GetTime
|
||||||
|
//
|
||||||
|
// Gets the time in usecs. WIN32 version.
|
||||||
|
long long Time_GetTime() {
|
||||||
|
LARGE_INTEGER freq;
|
||||||
|
LARGE_INTEGER tim;
|
||||||
|
long long int microt;
|
||||||
|
|
||||||
|
QueryPerformanceFrequency(&freq);
|
||||||
|
QueryPerformanceCounter(&tim);
|
||||||
|
microt = (tim.QuadPart * 1000000) / freq.QuadPart;
|
||||||
|
return (microt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// Time_Pause
|
||||||
|
//
|
||||||
|
// Pauses the execution for t usecs. WIN32 version.
|
||||||
|
void Time_Pause(int pausa) {
|
||||||
|
long long tend, t, diff;
|
||||||
|
|
||||||
|
t = Time_GetTime();
|
||||||
|
tend = t + pausa;
|
||||||
|
do {
|
||||||
|
diff = tend - t;
|
||||||
|
if (diff > 1000) {
|
||||||
|
Sleep((int)diff / 1000);
|
||||||
|
} else {
|
||||||
|
Sleep(0);
|
||||||
|
}
|
||||||
|
t = Time_GetTime();
|
||||||
|
} while (tend >= t);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#include <sys/time.h>
|
||||||
|
/////////////////////////////
|
||||||
|
// Time_GetTime
|
||||||
|
//
|
||||||
|
// Gets the time in usecs. UNIX version.
|
||||||
|
long long Time_GetTime() {
|
||||||
|
struct timeval t;
|
||||||
|
long long usecs;
|
||||||
|
gettimeofday(&t, NULL);
|
||||||
|
usecs = (t.tv_sec * 1000000ll) + (t.tv_usec);
|
||||||
|
return (usecs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// Time_Pause
|
||||||
|
//
|
||||||
|
// Pauses the execution for t usecs. UNIX version.
|
||||||
|
void Time_Pause(int pausa) {
|
||||||
|
struct timeval tv;
|
||||||
|
tv.tv_sec = (long long)pausa / 1000000;
|
||||||
|
tv.tv_usec = (long long)pausa % 1000000;
|
||||||
|
select(0, NULL, NULL, NULL, &tv);
|
||||||
|
}
|
||||||
|
#endif // if WIN32
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// Time_GetTime
|
||||||
|
//
|
||||||
|
// Gets the current time in POSIX.
|
||||||
|
long long Time_GetCurrentTime() {
|
||||||
|
time_t t = time(0);
|
||||||
|
return (long long)t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// PrintElapsedTime
|
||||||
|
//
|
||||||
|
// Prints the elapsed time (input in microseconds (us))
|
||||||
|
int PrintElapsedTime(long long time) {
|
||||||
|
if (time < 1000) {
|
||||||
|
return Print("%8lld us", time);
|
||||||
|
}
|
||||||
|
double msTime = (double)time / 1000;
|
||||||
|
if (msTime < 1000) {
|
||||||
|
return Print("% 8.3f ms", msTime);
|
||||||
|
}
|
||||||
|
double seconds = msTime / 1000;
|
||||||
|
if (seconds < 60) {
|
||||||
|
return Print("% 8.3f s", seconds);
|
||||||
|
}
|
||||||
|
int minutes = (int)seconds / 60;
|
||||||
|
seconds = seconds - (minutes * 60);
|
||||||
|
int hours = minutes / 60;
|
||||||
|
minutes = minutes % 60;
|
||||||
|
return Print("%02d:%02d:%06.3f", hours, minutes, seconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// PrintDataSize
|
||||||
|
//
|
||||||
|
// Prints the data size (input in bytes)
|
||||||
|
int PrintDataSize(long long size) {
|
||||||
|
if (size < 1024) {
|
||||||
|
return Print("%8lld B ", size);
|
||||||
|
}
|
||||||
|
double kibSize = (double)size / 1024;
|
||||||
|
if (kibSize < 1024) {
|
||||||
|
return Print("% 8.3f KiB", kibSize);
|
||||||
|
}
|
||||||
|
double mibSize = kibSize / 1024;
|
||||||
|
if (mibSize < 1024) {
|
||||||
|
return Print("% 8.3f MiB", mibSize);
|
||||||
|
}
|
||||||
|
double gibSize = mibSize / 1024;
|
||||||
|
if (gibSize < 1024) {
|
||||||
|
return Print("% 8.3f GiB", gibSize);
|
||||||
|
}
|
||||||
|
double tibSize = gibSize / 1024;
|
||||||
|
return Print("% 8.3f TiB", tibSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *outFile = NULL;
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// Print_SetOutFile
|
||||||
|
//
|
||||||
|
void Print_SetOutFile(char *fileOut) {
|
||||||
|
if (fileOut == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
outFile = fopen(fileOut, "a");
|
||||||
|
}
|
||||||
|
|
||||||
|
#define Print_BuferSize 4096
|
||||||
|
/////////////////////////////
|
||||||
|
// Print
|
||||||
|
//
|
||||||
|
// Prints the formated text screen
|
||||||
|
int Print(char *fmt, ...) {
|
||||||
|
va_list ap;
|
||||||
|
char buffer[Print_BuferSize];
|
||||||
|
int n;
|
||||||
|
|
||||||
|
// Print
|
||||||
|
va_start(ap, fmt);
|
||||||
|
// n = vprintf(fmt, ap);
|
||||||
|
n = vsnprintf(buffer, Print_BuferSize, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
// Output to stdout
|
||||||
|
fputs(buffer, stdout);
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
// Output to outFile
|
||||||
|
if (outFile != NULL) {
|
||||||
|
fputs(buffer, outFile);
|
||||||
|
fflush(outFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (n);
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// SetError
|
||||||
|
// GetError
|
||||||
|
//
|
||||||
|
char _errorMessage[2048] = "";
|
||||||
|
char _errorMessageTemp[2048] = "";
|
||||||
|
void SetError(char *msg) { strcpy(_errorMessage, msg); }
|
||||||
|
char *GetError() {
|
||||||
|
strcpy(_errorMessageTemp, _errorMessage);
|
||||||
|
strcpy(_errorMessage, "");
|
||||||
|
return _errorMessageTemp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// Exceptions_Init
|
||||||
|
//
|
||||||
|
#ifdef WIN32
|
||||||
|
|
||||||
|
LONG WINAPI Win32UnhandledExceptionFilter(
|
||||||
|
struct _EXCEPTION_POINTERS *lpTopLevelExceptionFilter) {
|
||||||
|
char cadena[255];
|
||||||
|
|
||||||
|
sprintf(
|
||||||
|
cadena, "!!! Crash: Unrecoverable exception at 0x%p",
|
||||||
|
(void *)lpTopLevelExceptionFilter->ExceptionRecord->ExceptionAddress);
|
||||||
|
printf(cadena);
|
||||||
|
printf("\n");
|
||||||
|
FatalAppExit(0, cadena);
|
||||||
|
|
||||||
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Exceptions_Init() {
|
||||||
|
SetUnhandledExceptionFilter(Win32UnhandledExceptionFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
#ifdef EMSCRIPTEN
|
||||||
|
|
||||||
|
void Exceptions_Init() {}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include <signal.h>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include <execinfo.h>
|
||||||
|
#include <memory.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
void Exception_Signal(int senhal, siginfo_t *info, void *ptr) {
|
||||||
|
int kill_self = 0;
|
||||||
|
|
||||||
|
printf("!!! Crash: ");
|
||||||
|
switch (senhal) {
|
||||||
|
case SIGHUP:
|
||||||
|
printf("SIGUP");
|
||||||
|
break;
|
||||||
|
case SIGINT:
|
||||||
|
printf("SIGINT");
|
||||||
|
break;
|
||||||
|
case SIGQUIT:
|
||||||
|
printf("SIGQUIT");
|
||||||
|
break;
|
||||||
|
case SIGILL:
|
||||||
|
printf("SIGILL");
|
||||||
|
break;
|
||||||
|
case SIGTRAP:
|
||||||
|
printf("SIGTRAP");
|
||||||
|
break;
|
||||||
|
case SIGFPE:
|
||||||
|
printf("SIGFPE ");
|
||||||
|
if (info->si_code == FPE_INTDIV) {
|
||||||
|
printf("entero div zero");
|
||||||
|
}
|
||||||
|
if (info->si_code == FPE_INTOVF) {
|
||||||
|
printf("entero desbordado");
|
||||||
|
}
|
||||||
|
if (info->si_code == FPE_FLTDIV) {
|
||||||
|
printf("float div zero");
|
||||||
|
}
|
||||||
|
if (info->si_code == FPE_FLTOVF) {
|
||||||
|
printf("float desbordado");
|
||||||
|
}
|
||||||
|
if (info->si_code == FPE_FLTUND) {
|
||||||
|
printf("float desbordado por defecto");
|
||||||
|
}
|
||||||
|
if (info->si_code == FPE_FLTRES) {
|
||||||
|
printf("float inexacto");
|
||||||
|
}
|
||||||
|
if (info->si_code == FPE_FLTINV) {
|
||||||
|
printf("operacion float invalida");
|
||||||
|
}
|
||||||
|
if (info->si_code == FPE_FLTSUB) {
|
||||||
|
printf("subscript fuera de rango");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SIGSEGV:
|
||||||
|
printf("SIGSEGV");
|
||||||
|
kill_self = 1;
|
||||||
|
break;
|
||||||
|
case SIGTERM:
|
||||||
|
printf("SIGTERM");
|
||||||
|
break;
|
||||||
|
case SIGABRT:
|
||||||
|
printf("SIGABRT");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printf("%d", senhal);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
if (kill_self) {
|
||||||
|
pid_t parent_pid;
|
||||||
|
char cadena[256];
|
||||||
|
parent_pid = getpid();
|
||||||
|
sprintf(cadena, "kill -9 %d", parent_pid);
|
||||||
|
system(cadena);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Salir
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
#include <fenv.h>
|
||||||
|
int feenableexcept();
|
||||||
|
|
||||||
|
void Exceptions_Init() {
|
||||||
|
struct sigaction action;
|
||||||
|
|
||||||
|
memset(&action, 0, sizeof(action));
|
||||||
|
action.sa_sigaction = Exception_Signal;
|
||||||
|
action.sa_flags = SA_SIGINFO;
|
||||||
|
|
||||||
|
sigaction(SIGHUP, &action, 0);
|
||||||
|
sigaction(SIGINT, &action, 0);
|
||||||
|
sigaction(SIGQUIT, &action, 0);
|
||||||
|
sigaction(SIGILL, &action, 0);
|
||||||
|
sigaction(SIGTRAP, &action, 0);
|
||||||
|
sigaction(SIGFPE, &action, 0);
|
||||||
|
sigaction(SIGSEGV, &action, 0);
|
||||||
|
sigaction(SIGTERM, &action, 0);
|
||||||
|
sigaction(SIGABRT, &action, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
81
src/util.h
Normal file
81
src/util.h
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright (c) 2014-2021 Valeriano Alfonso Rodriguez
|
||||||
|
|
||||||
|
#ifndef _UTIL_
|
||||||
|
#define _UTIL_
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#ifndef bool
|
||||||
|
typedef int bool;
|
||||||
|
#endif
|
||||||
|
#define true 1
|
||||||
|
#define false 0
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// String_Copy
|
||||||
|
//
|
||||||
|
// Copies a string.
|
||||||
|
char *String_Copy(char *str);
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// String_CompareCaseInsensitive
|
||||||
|
//
|
||||||
|
// Compares a string case insensitive
|
||||||
|
int String_CompareCaseInsensitive(char *str0, char *str1);
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// Time_GetTime
|
||||||
|
//
|
||||||
|
// Gets the current time in usecs.
|
||||||
|
long long Time_GetTime();
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// Time_Pause
|
||||||
|
//
|
||||||
|
// Pauses the execution for t usecs.
|
||||||
|
void Time_Pause(int pausa);
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// Time_GetTime
|
||||||
|
//
|
||||||
|
// Gets the current time in POSIX.
|
||||||
|
long long Time_GetCurrentTime();
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// PrintElapsedTime
|
||||||
|
//
|
||||||
|
// Prints the elapsed time (input in microseconds (us))
|
||||||
|
int PrintElapsedTime(long long time);
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// PrintDataSize
|
||||||
|
//
|
||||||
|
// Prints the data size (input in bytes)
|
||||||
|
int PrintDataSize(long long size);
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// Print_SetOutFile
|
||||||
|
//
|
||||||
|
void Print_SetOutFile(char *fileOut);
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// Print
|
||||||
|
//
|
||||||
|
// Prints the formated text screen
|
||||||
|
int Print(char *fmt, ...);
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// SetError
|
||||||
|
// GetError
|
||||||
|
//
|
||||||
|
void SetError(char *msg);
|
||||||
|
char *GetError();
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// Exceptions_Init
|
||||||
|
//
|
||||||
|
void Exceptions_Init();
|
||||||
|
|
||||||
|
#endif
|
||||||
5
tests/RunAll.cmd
Normal file
5
tests/RunAll.cmd
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
@echo off
|
||||||
|
|
||||||
|
for /r %%v in (test_*.cmd) do call "%%v"
|
||||||
|
|
||||||
|
pause
|
||||||
56
tests/test_DeleteAndRestitution.cmd
Normal file
56
tests/test_DeleteAndRestitution.cmd
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
|
||||||
|
:: Prepare test
|
||||||
|
set testName=%~n0
|
||||||
|
set testDir=tmp\%testName%
|
||||||
|
IF EXIST %testDir%.A rmdir %testDir%.A /S /Q
|
||||||
|
IF EXIST %testDir%.B rmdir %testDir%.B /S /Q
|
||||||
|
IF EXIST %testDir%.txt del %testDir%.txt
|
||||||
|
|
||||||
|
echo:Test %testName% started> %testDir%.txt
|
||||||
|
echo [7mTest %testName% started[0m
|
||||||
|
IF NOT EXIST tmp md tmp
|
||||||
|
md %testDir%.A
|
||||||
|
md %testDir%.B
|
||||||
|
|
||||||
|
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
||||||
|
|
||||||
|
echo:Uno> %testDir%.A\Uno.txt
|
||||||
|
echo:Dos> %testDir%.A\Dos.txt
|
||||||
|
..\filesync.exe -sync -dir %testDir%.A -dir %testDir%.B >> %testDir%.txt
|
||||||
|
|
||||||
|
del %testDir%.A\Uno.txt
|
||||||
|
..\filesync.exe -sync -dir %testDir%.A -dir %testDir%.B >> %testDir%.txt
|
||||||
|
|
||||||
|
ping 127.0.0.1 -n 2 > nul
|
||||||
|
|
||||||
|
echo:UnoRepuesto> %testDir%.A\Uno.txt
|
||||||
|
..\filesync.exe -sync -dir %testDir%.A -dir %testDir%.B >> %testDir%.txt
|
||||||
|
|
||||||
|
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
||||||
|
|
||||||
|
..\filesync.exe -read %testDir%.A/nodesFile.fs >> %testDir%.txt
|
||||||
|
..\filesync.exe -read %testDir%.B/nodesFile.fs >> %testDir%.txt
|
||||||
|
|
||||||
|
:: Check test results
|
||||||
|
|
||||||
|
IF NOT EXIST %testDir%.A\Uno.txt goto error
|
||||||
|
IF NOT EXIST %testDir%.A\Dos.txt goto error
|
||||||
|
|
||||||
|
IF NOT EXIST %testDir%.B\Uno.txt goto error
|
||||||
|
IF NOT EXIST %testDir%.B\Dos.txt goto error
|
||||||
|
|
||||||
|
|
||||||
|
:: Display test result
|
||||||
|
goto end
|
||||||
|
:error
|
||||||
|
echo:Test %testName% Failed...>> %testDir%.txt
|
||||||
|
echo [41mTest %testName% Failed...[0m
|
||||||
|
goto eof
|
||||||
|
:end
|
||||||
|
echo:Test %testName% OK!>> %testDir%.txt
|
||||||
|
echo [42mTest %testName% OK![0m
|
||||||
|
:eof
|
||||||
|
echo:>> %testDir%.txt
|
||||||
|
echo:
|
||||||
|
echo:>> %testDir%.txt
|
||||||
|
echo:
|
||||||
54
tests/test_MoveToSubdir.cmd
Normal file
54
tests/test_MoveToSubdir.cmd
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
|
||||||
|
:: Prepare test
|
||||||
|
set testName=%~n0
|
||||||
|
set testDir=tmp\%testName%
|
||||||
|
IF EXIST %testDir%.A rmdir %testDir%.A /S /Q
|
||||||
|
IF EXIST %testDir%.B rmdir %testDir%.B /S /Q
|
||||||
|
IF EXIST %testDir%.txt del %testDir%.txt
|
||||||
|
|
||||||
|
echo:Test %testName% started> %testDir%.txt
|
||||||
|
echo [7mTest %testName% started[0m
|
||||||
|
IF NOT EXIST tmp md tmp
|
||||||
|
md %testDir%.A
|
||||||
|
md %testDir%.B
|
||||||
|
|
||||||
|
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
||||||
|
|
||||||
|
echo:Uno> %testDir%.A\Uno.txt
|
||||||
|
echo:Dos> %testDir%.A\Dos.txt
|
||||||
|
..\filesync.exe -sync -dir %testDir%.A -dir %testDir%.B >> %testDir%.txt
|
||||||
|
|
||||||
|
md %testDir%.A\dirUno
|
||||||
|
move %testDir%.A\Uno.txt %testDir%.A\dirUno\Uno.txt >NUL
|
||||||
|
..\filesync.exe -sync -dir %testDir%.A -dir %testDir%.B >> %testDir%.txt
|
||||||
|
|
||||||
|
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
||||||
|
|
||||||
|
..\filesync.exe -read %testDir%.A/nodesFile.fs >> %testDir%.txt
|
||||||
|
..\filesync.exe -read %testDir%.B/nodesFile.fs >> %testDir%.txt
|
||||||
|
|
||||||
|
:: Check test results
|
||||||
|
|
||||||
|
IF EXIST %testDir%.A\Uno.txt goto error
|
||||||
|
IF NOT EXIST %testDir%.A\dirUno\Uno.txt goto error
|
||||||
|
IF NOT EXIST %testDir%.A\Dos.txt goto error
|
||||||
|
|
||||||
|
IF EXIST %testDir%.B\Uno.txt goto error
|
||||||
|
IF NOT EXIST %testDir%.B\dirUno\Uno.txt goto error
|
||||||
|
IF NOT EXIST %testDir%.B\Dos.txt goto error
|
||||||
|
|
||||||
|
|
||||||
|
:: Display test result
|
||||||
|
goto end
|
||||||
|
:error
|
||||||
|
echo:Test %testName% Failed...>> %testDir%.txt
|
||||||
|
echo [41mTest %testName% Failed...[0m
|
||||||
|
goto eof
|
||||||
|
:end
|
||||||
|
echo:Test %testName% OK!>> %testDir%.txt
|
||||||
|
echo [42mTest %testName% OK![0m
|
||||||
|
:eof
|
||||||
|
echo:>> %testDir%.txt
|
||||||
|
echo:
|
||||||
|
echo:>> %testDir%.txt
|
||||||
|
echo:
|
||||||
62
tests/test_MoveToSubdirAndRestore.cmd
Normal file
62
tests/test_MoveToSubdirAndRestore.cmd
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
|
||||||
|
:: Prepare test
|
||||||
|
set testName=%~n0
|
||||||
|
set testDir=tmp\%testName%
|
||||||
|
IF EXIST %testDir%.A rmdir %testDir%.A /S /Q
|
||||||
|
IF EXIST %testDir%.B rmdir %testDir%.B /S /Q
|
||||||
|
IF EXIST %testDir%.txt del %testDir%.txt
|
||||||
|
|
||||||
|
echo:Test %testName% started> %testDir%.txt
|
||||||
|
echo [7mTest %testName% started[0m
|
||||||
|
IF NOT EXIST tmp md tmp
|
||||||
|
md %testDir%.A
|
||||||
|
md %testDir%.B
|
||||||
|
|
||||||
|
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
||||||
|
|
||||||
|
echo:Uno> %testDir%.A\Uno.txt
|
||||||
|
echo:Dos> %testDir%.A\Dos.txt
|
||||||
|
..\filesync.exe -sync -dir %testDir%.A -dir %testDir%.B >> %testDir%.txt
|
||||||
|
|
||||||
|
md %testDir%.A\dirUno
|
||||||
|
move %testDir%.A\Uno.txt %testDir%.A\dirUno\Uno.txt >NUL
|
||||||
|
..\filesync.exe -sync -dir %testDir%.A -dir %testDir%.B >> %testDir%.txt
|
||||||
|
|
||||||
|
ping 127.0.0.1 -n 2 > nul
|
||||||
|
|
||||||
|
move %testDir%.A\dirUno\Uno.txt %testDir%.A\Uno.txt >NUL
|
||||||
|
rd %testDir%.A\dirUno
|
||||||
|
..\filesync.exe -sync -dir %testDir%.A -dir %testDir%.B >> %testDir%.txt
|
||||||
|
|
||||||
|
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
||||||
|
|
||||||
|
..\filesync.exe -read %testDir%.A/nodesFile.fs >> %testDir%.txt
|
||||||
|
..\filesync.exe -read %testDir%.B/nodesFile.fs >> %testDir%.txt
|
||||||
|
|
||||||
|
:: Check test results
|
||||||
|
|
||||||
|
IF NOT EXIST %testDir%.A\Uno.txt goto error
|
||||||
|
IF EXIST %testDir%.A\dirUno goto error
|
||||||
|
IF EXIST %testDir%.A\dirUno\Uno.txt goto error
|
||||||
|
IF NOT EXIST %testDir%.A\Dos.txt goto error
|
||||||
|
|
||||||
|
IF NOT EXIST %testDir%.B\Uno.txt goto error
|
||||||
|
IF EXIST %testDir%.B\dirUno goto error
|
||||||
|
IF EXIST %testDir%.B\dirUno\Uno.txt goto error
|
||||||
|
IF NOT EXIST %testDir%.B\Dos.txt goto error
|
||||||
|
|
||||||
|
|
||||||
|
:: Display test result
|
||||||
|
goto end
|
||||||
|
:error
|
||||||
|
echo:Test %testName% Failed...>> %testDir%.txt
|
||||||
|
echo [41mTest %testName% Failed...[0m
|
||||||
|
goto eof
|
||||||
|
:end
|
||||||
|
echo:Test %testName% OK!>> %testDir%.txt
|
||||||
|
echo [42mTest %testName% OK![0m
|
||||||
|
:eof
|
||||||
|
echo:>> %testDir%.txt
|
||||||
|
echo:
|
||||||
|
echo:>> %testDir%.txt
|
||||||
|
echo:
|
||||||
54
tests/test_UpdateFile.cmd
Normal file
54
tests/test_UpdateFile.cmd
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
|
||||||
|
:: Prepare test
|
||||||
|
set testName=%~n0
|
||||||
|
set testDir=tmp\%testName%
|
||||||
|
IF EXIST %testDir%.A rmdir %testDir%.A /S /Q
|
||||||
|
IF EXIST %testDir%.B rmdir %testDir%.B /S /Q
|
||||||
|
IF EXIST %testDir%.txt del %testDir%.txt
|
||||||
|
|
||||||
|
echo:Test %testName% started> %testDir%.txt
|
||||||
|
echo [7mTest %testName% started[0m
|
||||||
|
IF NOT EXIST tmp md tmp
|
||||||
|
md %testDir%.A
|
||||||
|
md %testDir%.B
|
||||||
|
|
||||||
|
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
||||||
|
|
||||||
|
echo:Uno> %testDir%.A\Uno.txt
|
||||||
|
echo:Dos> %testDir%.A\Dos.txt
|
||||||
|
..\filesync.exe -sync -dir %testDir%.A -dir %testDir%.B >> %testDir%.txt
|
||||||
|
|
||||||
|
ping 127.0.0.1 -n 2 > nul
|
||||||
|
|
||||||
|
echo:Updated>> %testDir%.A\Uno.txt
|
||||||
|
..\filesync.exe -sync -dir %testDir%.A -dir %testDir%.B >> %testDir%.txt
|
||||||
|
|
||||||
|
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
||||||
|
|
||||||
|
..\filesync.exe -read %testDir%.A/nodesFile.fs >> %testDir%.txt
|
||||||
|
..\filesync.exe -read %testDir%.B/nodesFile.fs >> %testDir%.txt
|
||||||
|
|
||||||
|
:: Check test results
|
||||||
|
|
||||||
|
IF NOT EXIST %testDir%.A\Uno.txt goto error
|
||||||
|
IF NOT EXIST %testDir%.A\Dos.txt goto error
|
||||||
|
|
||||||
|
IF NOT EXIST %testDir%.B\Uno.txt goto error
|
||||||
|
IF NOT EXIST %testDir%.B\Dos.txt goto error
|
||||||
|
|
||||||
|
FC %testDir%.A\Uno.txt %testDir%.B\Uno.txt >NUL || goto error
|
||||||
|
|
||||||
|
:: Display test result
|
||||||
|
goto end
|
||||||
|
:error
|
||||||
|
echo:Test %testName% Failed...>> %testDir%.txt
|
||||||
|
echo [41mTest %testName% Failed...[0m
|
||||||
|
goto eof
|
||||||
|
:end
|
||||||
|
echo:Test %testName% OK!>> %testDir%.txt
|
||||||
|
echo [42mTest %testName% OK![0m
|
||||||
|
:eof
|
||||||
|
echo:>> %testDir%.txt
|
||||||
|
echo:
|
||||||
|
echo:>> %testDir%.txt
|
||||||
|
echo:
|
||||||
Reference in New Issue
Block a user