41 Commits

Author SHA1 Message Date
f9cdac5f92 Merge pull request 'feature/logging' (#3) from feature/logging into main
Reviewed-on: #3
2023-06-29 17:01:40 +00:00
e99ca810f6 Add instructions for verbose output 2023-06-29 03:58:28 +02:00
184e525adb Add logging 2023-06-29 03:49:13 +02:00
1ee5114dab Merge pull request 'feature/multi-threading' (#2) from feature/multi-threading into main
Reviewed-on: #2
2023-06-29 00:15:11 +00:00
f403b77864 Code cleanup 2023-06-29 02:03:41 +02:00
42320ebf8d Use ConcurrentDictionary 2023-06-28 13:18:33 +02:00
787721381d refactor doTheThing 2023-06-28 00:54:55 +02:00
7d7e9bac6c refactor checkIfFileWasDeleted 2023-06-28 00:54:30 +02:00
2b4019d7cf move return outside of using 2023-06-28 00:48:14 +02:00
be8180a60d refactor doTheThing 2023-06-28 00:41:38 +02:00
7f6f4c5253 Streamline code 2023-06-27 23:56:43 +02:00
e12117fba8 Small fix 2023-06-27 23:12:50 +02:00
763cde4e2d Use dictionary 2023-06-27 22:58:07 +02:00
531a4676e9 First implementation of parralel checksumming 2023-06-27 22:31:24 +02:00
bc42dd542a Merge pull request 'feature/SQlite' (#1) from feature/SQlite into main
Reviewed-on: #1
2023-06-26 16:22:27 +00:00
836b850f3f Refactor getFilehashesFromDatabase 2023-06-26 18:20:47 +02:00
944b61a1ac Refactor compareDatabases 2023-06-26 17:21:56 +02:00
6fbc53fa53 Add option to compare databases 2023-06-26 16:25:42 +02:00
b899c4c5b6 Add option to check for deleted files 2023-06-26 15:59:06 +02:00
2d42842fb2 Minor tweaks 2023-06-26 12:58:05 +02:00
8aec649781 code cleanup
removed unused methods
changed output of methods
2023-06-25 22:37:50 +02:00
c51e02fa05 Add library
Embed the library into the executable and extract at runtime
Cleanup after executing the program
2023-06-25 21:12:42 +02:00
132894e924 Minor fixes 2023-06-25 16:48:43 +02:00
b05ddd5e18 Fix formatting 2023-06-25 03:32:43 +02:00
64995434ab Check if file moved
If the file moved the path will be updated
2023-06-25 03:28:34 +02:00
5a3becb3b4 Major additons
Converted to new project structure
Check if db already exists
Add vacuum for db cleanup
Check if filehash already exits and write info to console
2023-06-25 02:45:33 +02:00
f99ca8bb26 revert a6c994fa65
revert previous commit
2023-06-23 23:08:14 +00:00
846b983caa Major changes
Now using an SQlite database to store the file hash plus a bunch of other information
2023-06-24 01:04:11 +02:00
a6c994fa65 Major changes
Now using an SQlite database to store the file hash plus a bunch of other information
2023-06-24 01:02:03 +02:00
951068c750 Add SQlite package 2023-06-23 22:38:32 +02:00
6d2880907b Update README.md 2023-06-23 22:34:00 +02:00
afde0af0a4 Merge branch 'AustrianToast-main' 2023-06-05 00:33:25 +02:00
6f295b0e06 Merge http://192.168.0.69:3000/AustrianToast/chksum-fork into AustrianToast-main 2023-06-05 00:32:48 +02:00
743e1eec20 Minor code cleanup and some more stuff 2023-06-04 23:44:03 +02:00
8a8521b9b4 Add debug version 2023-05-21 00:13:23 +02:00
38d4330892 Compare checksums 2023-05-17 14:52:30 +02:00
dfb7cd8405 Change readme 2023-05-17 12:22:18 +02:00
454717431f Commandline args && New features
chksum can now take arguments to execute diffrent functions
functions include checksumming, counting all checksums, deleting all checksums
2023-05-17 12:18:20 +02:00
2a8bf7e8fe Count all files 2023-05-16 23:21:43 +02:00
23f8158ebf Remove old comment 2023-05-16 22:54:19 +02:00
64d2400292 Major rewrite 2023-05-16 22:50:29 +02:00
13 changed files with 559 additions and 69 deletions

26
.editorconfig Normal file
View File

@ -0,0 +1,26 @@
# Remove the line below if you want to inherit .editorconfig settings from higher directories
root = true
# C# files
[*.cs]
# Indentation and spacing
indent_size = 4
indent_style = space
tab_width = 4
# Naming Conventions
dotnet_naming_style.camel_case.capitalization = camel_case
# New line preferences
csharp_new_line_before_open_brace = none
csharp_new_line_before_else = false
csharp_new_line_before_catch = false
csharp_new_line_before_finally = false
# Wrapping preferences
csharp_preserve_single_line_statements = true
csharp_preserve_single_line_blocks = true
# Switch case
csharp_indent_case_contents = true

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
.vscode/
obj/
bin/
Libraries/

View File

@ -1,50 +0,0 @@
// Go into folder
// Check if any file is in there
// If there is a file. Calculate md5sum > filename.md5
// If there is no file. Repeat
public class Program {
static void Main(string[] args) {
// int getDirectoryCount() {
// int folderCount = Directory.GetDirectories(Directory.GetCurrentDirectory()).Length; // Get folder count in current directory
// return folderCount;
// }
int getFileCount() {
int fileCount = Directory.GetFiles(Directory.GetCurrentDirectory()).Length; // Get file count in current directory
return fileCount;
}
// string getParentFolder() {
// string parentFolder = Directory.GetParent(Directory.GetCurrentDirectory()).ToString(); // Get parent folder of current directory
// return parentFolder;
// }
string CalculateMD5(string filename) {
using (var md5 = System.Security.Cryptography.MD5.Create())
{
using (var stream = File.OpenRead(filename))
{
var hash = md5.ComputeHash(stream);
return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
}
}
}
foreach (var directory in Directory.GetDirectories(Directory.GetCurrentDirectory())) {
string parentFolder = directory;
Directory.SetCurrentDirectory(directory); // Set new root
if (getFileCount() >= 1) {
DirectoryInfo dir = new DirectoryInfo(Directory.GetCurrentDirectory());
FileInfo[] files = dir.GetFiles();
foreach (FileInfo file in files) {
string fileName = file.Name;
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileName);
string checksumFile = Directory.GetCurrentDirectory() + "/" + fileNameWithoutExtension + ".md5";
File.AppendAllText(checksumFile, CalculateMD5(fileName) + " " + fileName);
}
}
Directory.SetCurrentDirectory(parentFolder); // Go back to the original root
}
}
}

View File

@ -7,7 +7,7 @@ Checksums every file under the current directory
Clone the project
```bash
git clone http://192.168.0.69:3000/ProfessionalUwU/chksum.git
git clone https://gitea.hopeless-cloud.xyz/ProfessionalUwU/chksum.git
```
Go to the project directory
@ -25,22 +25,37 @@ pacman -S dotnet-runtime dotnet-sdk
Build project
```bash
dotnet build chksum.csproj
just build
```
Publish project
```bash
dotnet publish --configuration Release --arch x64 --use-current-runtime --self-contained
just publish
```
Go to the publish folder
```bash
cd bin/Release/net7.0/linux-x64/publish
cd src/Chksum/bin/Release/net7.0/linux-x64/publish
```
Run executable
```bash
./chksum
./Chksum
```
## Enabling verbose output for troubleshooting
1. Open the file called chksum.cs with your editor of choice.
2. At the top there will be the logger configuration which you can change. Should look like this.
```cs
private ILogger logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console(restrictedToMinimumLevel: LogEventLevel.Error)
.WriteTo.File("chksum.log")
.CreateLogger();
```
3. Change the minimum level of the logger to Verbose.
4. Compile the program
5. Profit. Now you will be able to see how what the program is doing in detail.

View File

@ -1,14 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<PublishSingleFile>true</PublishSingleFile>
<SelfContained>true</SelfContained>
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
<PublishTrimmed>true</PublishTrimmed>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

31
justfile Normal file
View File

@ -0,0 +1,31 @@
default:
@just --list
project_name := `printf '%s\n' "${PWD##*/}"`
uppercase_project_name := capitalize(project_name)
setup:
@mkdir src
@dotnet new sln --name src/{{project_name}}
@dotnet new classlib -o src/{{uppercase_project_name}}
@dotnet new xunit -o src/{{uppercase_project_name}}.Tests
@dotnet sln add src/{{uppercase_project_name}}/{{uppercase_project_name}}.csproj
@dotnet sln add src/{{uppercase_project_name}}.Tests/{{uppercase_project_name}}.Tests.csproj
@dotnet add src/{{uppercase_project_name}}/{{uppercase_project_name}}.csproj reference src/{{uppercase_project_name}}.Tests/{{uppercase_project_name}}.Tests.csproj
run:
@dotnet run
build:
@dotnet build src/{{uppercase_project_name}}/{{uppercase_project_name}}.csproj
@dotnet build src/{{uppercase_project_name}}.Tests/{{uppercase_project_name}}.Tests.csproj
publish:
@dotnet publish --configuration Release src/{{uppercase_project_name}}/{{uppercase_project_name}}.csproj
format:
@dotnet format src/{{uppercase_project_name}}
@dotnet format src/{{uppercase_project_name}}.Tests
test: build
@dotnet test src/{{uppercase_project_name}}.Tests

View File

@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.11.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
</Project>

View File

@ -0,0 +1,2 @@
global using Xunit;
global using FluentAssertions;

View File

@ -0,0 +1,8 @@
namespace Chksum.Tests;
public class doTheThingTest {
[Fact]
public void doTheThing_willNotThrowAnException() {
}
}

26
src/Chksum/Chksum.csproj Normal file
View File

@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\Chksum.Tests\Chksum.Tests.csproj" />
<EmbeddedResource Include="Libraries/libe_sqlite3.so" />
</ItemGroup>
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<PublishSingleFile>true</PublishSingleFile>
<SelfContained>true</SelfContained>
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
<PublishTrimmed>true</PublishTrimmed>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Data.Sqlite" Version="7.0.8" />
<PackageReference Include="Serilog" Version="3.0.1" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
</ItemGroup>
</Project>

73
src/Chksum/Program.cs Normal file
View File

@ -0,0 +1,73 @@
using Chksum.Utils;
public class Program {
static void Main(string[] args) {
Console.ForegroundColor = ConsoleColor.Red;
if (args.Length == 0) {
Console.WriteLine("Please specify an option.");
PrintAvailableOptions();
return;
} else if (args.Length > 1 && args[0] != "compareDatabases") {
Console.WriteLine("Too many options.");
return;
}
ChksumUtils utils = new ChksumUtils();
utils.getBaseDir();
utils.ExtractEmbeddedLibrary();
Console.ForegroundColor = ConsoleColor.Green;
switch (args[0]) {
case "checksum":
Console.WriteLine("Starting the checksum process.");
Console.ResetColor();
utils.doTheThing();
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("Checksum process finished");
break;
case "compareDatabases":
Console.ResetColor();
utils.compareDatabases(args[1]);
break;
case "createDB":
utils.initializeDB();
break;
case "checkIfFileWasDeleted":
Console.ResetColor();
utils.checkIfFileWasDeleted();
break;
case "help":
PrintAvailableOptions();
break;
default:
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Invalid option. Maybe you mistyped it?");
PrintAvailableOptions();
break;
}
utils.cleanup();
}
static void PrintAvailableOptions() {
String[] options = {
"checksum",
"compareChecksums",
"createDB",
"checkIfFileWasDeleted",
"help"
};
Console.ResetColor();
Console.WriteLine("usage: chksum [option] \nHere is a list of all available options:");
foreach (String option in options) {
Console.WriteLine("\t" + option);
}
}
}

319
src/Chksum/chksum.cs Normal file
View File

@ -0,0 +1,319 @@
using System.Collections.Concurrent;
using System.Reflection;
using System.Security.Cryptography;
using Microsoft.Data.Sqlite;
using Serilog;
using Serilog.Events;
namespace Chksum.Utils;
public class ChksumUtils {
private ILogger logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console(restrictedToMinimumLevel: LogEventLevel.Error)
.WriteTo.File("chksum.log")
.CreateLogger();
private int getTotalFileCount() {
int totalFileCount = Directory.GetFiles(Directory.GetCurrentDirectory(), "*", SearchOption.AllDirectories).Length;
logger.Debug("Total file count is {totalFileCount}", totalFileCount);
return totalFileCount - 3; // Remove the program, datbase and library from the totalFileCount
}
private string[] indexFiles() {
string[] indexedFiles = Directory.GetFiles(Directory.GetCurrentDirectory(), "*", SearchOption.AllDirectories);
string[] filesToExclude = { "Chksum", "chksum.db", "libe_sqlite3.so" };
indexedFiles = indexedFiles.Where(file => !filesToExclude.Contains(Path.GetFileName(file))).ToArray();
logger.Information("All files were indexed");
return indexedFiles;
}
public string DatabaseRoot { get; set; } = string.Empty;
public void getBaseDir() {
DatabaseRoot = AppDomain.CurrentDomain.BaseDirectory;
logger.Debug("DatabaseRoot is {DatabaseRoot}", DatabaseRoot);
}
public string libraryPath { get; set; } = string.Empty;
public void ExtractEmbeddedLibrary() {
libraryPath = Path.Combine(DatabaseRoot, "libe_sqlite3.so");
using (Stream? resourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Chksum.Libraries.libe_sqlite3.so")) {
if (resourceStream != null) {
byte[] buffer = new byte[resourceStream.Length];
resourceStream.Read(buffer, 0, buffer.Length);
File.WriteAllBytes(libraryPath, buffer);
logger.Debug("libe_sqlite3.so was successfully created");
} else {
logger.Error("libe_sqlite3.so could not be loaded");
throw new Exception(libraryPath + " could not be loaded");
}
}
}
public void initializeDB() {
if (File.Exists("chksum.db")) {
logger.Information("A database already exits");
return;
}
using (var connection = new SqliteConnection("Data Source=chksum.db")) {
connection.Open();
var command = connection.CreateCommand();
command.CommandText =
@"
CREATE TABLE file (
filehash TEXT NOT NULL PRIMARY KEY,
filename TEXT NOT NULL,
pathtofile TEXT NOT NULL,
artist TEXT,
playbacklength INTEGER
);
";
command.ExecuteNonQuery();
logger.Information("Database was successfully created");
}
}
public void cleanDB() {
using (var connection = new SqliteConnection("Data Source=" + DatabaseRoot + "chksum.db")) {
var command = connection.CreateCommand();
command.CommandText =
@"
vacuum;
";
command.ExecuteNonQuery();
logger.Debug("Database was successfully vacuumed");
}
}
private Dictionary<string, string> CalculateChecksums(string[] filenames) {
ConcurrentDictionary<string, string> checksums = new ConcurrentDictionary<string, string>();
Parallel.ForEach(filenames, (filename, state) => {
using (var md5 = MD5.Create()) {
using (var stream = File.OpenRead(filename)) {
var hash = md5.ComputeHash(stream);
var checksum = BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
lock (checksums) {
checksums.TryAdd(filename, checksum);
}
}
}
});
logger.Debug("All files were checksummed");
return new Dictionary<string, string>(checksums);
}
public void doTheThing() {
using (var connection = new SqliteConnection("Data Source=" + DatabaseRoot + "chksum.db;Mode=ReadWrite")) {
if (getTotalFileCount() < 1) {
logger.Information("There were no files to checksum");
return;
}
connection.Open();
Dictionary<string, string> fileHashes = CalculateChecksums(indexFiles());
foreach (var file in fileHashes) {
string absolutePathToFile = file.Key;
string fileName = Path.GetFileName(absolutePathToFile);
string pathToFile = Path.GetRelativePath(DatabaseRoot, absolutePathToFile);
string fileHash = file.Value;
if (checkIfFileMovedAndUpdatePathToFile(fileHash, fileName, pathToFile) == false && checkIfFileAlreadyExistsInDatabase(fileHash, fileName) == false) {
var command = connection.CreateCommand();
command.CommandText =
@"
INSERT INTO file (filehash, filename, pathtofile)
VALUES ($filehash, $filename, $pathtofile)
";
command.Parameters.AddWithValue("$filehash", fileHash);
command.Parameters.AddWithValue("$filename", fileName);
command.Parameters.AddWithValue("$pathtofile", pathToFile);
command.ExecuteNonQuery();
logger.Verbose("{fileName} which is located at {pathToFile} relative to the database with the hash {fileHash} was successfully inserted into the database", fileName, pathToFile, fileHash);
}
}
logger.Information("All files were successfully written to the database");
}
}
private bool checkIfFileAlreadyExistsInDatabase(string fileHash, string pathToFile) {
string filehash = string.Empty;
string pathtofile = string.Empty;
bool doesExist = false;
using (var connection = new SqliteConnection("Data Source=" + DatabaseRoot + "chksum.db;Mode=ReadOnly")) {
connection.Open();
var command = connection.CreateCommand();
command.CommandText =
@"
SELECT filehash, pathtofile FROM file WHERE filehash = $filehash
";
command.Parameters.AddWithValue("$filehash", fileHash);
using (var reader = command.ExecuteReader()) {
while (reader.Read()) {
filehash = reader.GetString(0);
pathtofile = reader.GetString(1);
}
}
logger.Verbose("{pathToFile} with the hash {fileHash} was successfully loaded", pathToFile, fileHash);
}
if (fileHash == filehash) {
logger.Verbose("File with filehash {filehash} already exists in the database", filehash);
doesExist = true;
}
return doesExist;
}
private bool checkIfFileMovedAndUpdatePathToFile(string fileHash, string fileName, string pathToFile) {
string pathtofile = string.Empty;
bool wasMoved = false;
using (var connection = new SqliteConnection("Data Source=" + DatabaseRoot + "chksum.db;Mode=ReadWrite")) {
connection.Open();
var command = connection.CreateCommand();
command.CommandText =
@"
SELECT pathtofile FROM file WHERE filehash = $filehash
";
command.Parameters.AddWithValue("$filehash", fileHash);
using (var reader = command.ExecuteReader()) {
while (reader.Read()) {
pathtofile = reader.GetString(0);
}
}
if (pathToFile != pathtofile && pathtofile != "") {
var command2 = connection.CreateCommand();
command2.CommandText =
@"
UPDATE file
SET pathtofile = $newpathtofile
WHERE filehash = $filehash
";
command2.Parameters.AddWithValue("$newpathtofile", pathToFile);
command2.Parameters.AddWithValue("$filehash", fileHash);
command2.ExecuteNonQuery();
Console.WriteLine("File moved or is a duplicate:");
Console.WriteLine($"\tfrom\t{pathToFile}");
Console.WriteLine($"\tto \t{pathtofile}\n");
wasMoved = true;
}
logger.Verbose("{fileName} which is located at {pathToFile} relative to the database with the hash {fileHash} was successfully checked", fileName, pathToFile, fileHash);
}
return wasMoved;
}
public void checkIfFileWasDeleted() {
string pathToFile = string.Empty;
using (var connection = new SqliteConnection("Data Source=" + DatabaseRoot + "chksum.db;Mode=ReadWrite")) {
connection.Open();
var selectCommand = connection.CreateCommand();
selectCommand.CommandText =
@"
Select pathtofile FROM file
";
using (var reader = selectCommand.ExecuteReader()) {
while (reader.Read()) {
pathToFile = reader.GetString(0);
if (File.Exists(pathToFile)) {
logger.Verbose("{pathToFile} exists", pathToFile);
continue;
}
var deleteCommand = connection.CreateCommand();
deleteCommand.CommandText =
@"
DELETE FROM file
WHERE pathtofile = $pathtofile
";
deleteCommand.Parameters.AddWithValue("$pathtofile", pathToFile);
deleteCommand.ExecuteNonQuery();
Console.WriteLine("File deleted:");
Console.WriteLine($"\t{pathToFile}\n");
logger.Verbose("File deleted: {pathToFile}", pathToFile);
}
}
logger.Information("All deleted files were successfully removed from the database");
}
}
private List<string> getFilehashesFromDatabase(string connectionString) {
List<string> filehashesFromDatabase = new List<string>();
using (var connection = new SqliteConnection(connectionString)) {
string filehash = string.Empty;
connection.Open();
var selectCommand = connection.CreateCommand();
selectCommand.CommandText =
@"
Select filehash FROM file
";
using (var reader = selectCommand.ExecuteReader()) {
while (reader.Read()) {
filehash = reader.GetString(0);
filehashesFromDatabase.Add(filehash);
}
}
}
logger.Debug("All filehashes were successfully retrived from the database");
return filehashesFromDatabase;
}
public void compareDatabases(string filePathToOtherDatabase) {
if (!File.Exists(filePathToOtherDatabase)) {
logger.Error("No database could be found at {filePathToOtherDatabase}", filePathToOtherDatabase);
throw new Exception("No database could be found at " + filePathToOtherDatabase);
}
List<string> filesThatDoNotExistsInTheRemote = getFilehashesFromDatabase("Data Source=" + DatabaseRoot + "chksum.db;Mode=ReadOnly").Except(getFilehashesFromDatabase("Data Source=" + filePathToOtherDatabase + ";Mode=ReadOnly")).ToList();
foreach (string file in filesThatDoNotExistsInTheRemote) {
using (var connection = new SqliteConnection("Data Source=" + DatabaseRoot + "chksum.db;Mode=ReadOnly")) {
string filename = string.Empty;
connection.Open();
var selectCommand = connection.CreateCommand();
selectCommand.CommandText =
@"
Select filename FROM file WHERE filehash = $filehash
";
selectCommand.Parameters.AddWithValue("$filehash", file);
using (var reader = selectCommand.ExecuteReader()) {
while (reader.Read()) {
filename = reader.GetString(0);
Console.WriteLine("File not found in remote:");
Console.WriteLine($"\t{filename}\n");
logger.Verbose("{filename} could not be found in the remote database", filename);
}
}
}
}
logger.Information("Compared both databases successfully");
}
public void cleanup() {
File.Delete(libraryPath);
logger.Debug("Successfully deleted libe_sqlite3.so");
}
}

28
src/chksum.sln Normal file
View File

@ -0,0 +1,28 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Chksum", "Chksum\Chksum.csproj", "{BBC56294-03CF-42E0-A838-75AF41EEE32B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Chksum.Tests", "Chksum.Tests\Chksum.Tests.csproj", "{239727BC-7124-4985-A6F3-2700295AA06F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{BBC56294-03CF-42E0-A838-75AF41EEE32B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BBC56294-03CF-42E0-A838-75AF41EEE32B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BBC56294-03CF-42E0-A838-75AF41EEE32B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BBC56294-03CF-42E0-A838-75AF41EEE32B}.Release|Any CPU.Build.0 = Release|Any CPU
{239727BC-7124-4985-A6F3-2700295AA06F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{239727BC-7124-4985-A6F3-2700295AA06F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{239727BC-7124-4985-A6F3-2700295AA06F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{239727BC-7124-4985-A6F3-2700295AA06F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal