From d1fb33c58e63626e2e5d5e31a1b68744ab4dd1ce Mon Sep 17 00:00:00 2001 From: Shaun Reed Date: Wed, 4 May 2022 12:55:23 -0400 Subject: [PATCH] [dotnet] Add dotnet projects and examples + Sitemap generator I created while learning the dispose pattern + Testing project for learning general C# --- README.md | 1 + dotnet/README.md | 11 + dotnet/sitemap/.gitignore | 454 ++++++++ dotnet/sitemap/ConsoleApp/ConsoleApp.csproj | 14 + dotnet/sitemap/ConsoleApp/Program.cs | 12 + .../sitemap/ConsoleApp/TestFiles/robots.txt | 9 + .../sitemap/ConsoleApp/TestFiles/sitemap.xml | 975 ++++++++++++++++++ dotnet/sitemap/README.md | 45 + dotnet/sitemap/SiteMapLibrary/SiteMap.cs | 114 ++ .../SiteMapLibrary/SiteMapLibrary.csproj | 10 + dotnet/sitemap/SiteMapLibrary/XmlManager.cs | 59 ++ dotnet/sitemap/sitemap.sln | 28 + dotnet/testing/.gitignore | 454 ++++++++ .../testing/KlipsConsole/KlipsConsole.csproj | 13 + dotnet/testing/KlipsConsole/Program.cs | 14 + .../KlipsLibrary.Test.csproj | 27 + dotnet/testing/KlipsLibrary.Test/UnitTest1.cs | 22 + dotnet/testing/KlipsLibrary/Animal.cs | 70 ++ dotnet/testing/KlipsLibrary/Bag.cs | 74 ++ dotnet/testing/KlipsLibrary/Fruit.cs | 19 + dotnet/testing/KlipsLibrary/InitOrder.cs | 75 ++ dotnet/testing/KlipsLibrary/Klips.cs | 281 +++++ .../testing/KlipsLibrary/KlipsLibrary.csproj | 9 + dotnet/testing/KlipsLibrary/Shape.cs | 98 ++ dotnet/testing/README.md | 5 + dotnet/testing/dotnet-klips.sln | 34 + 26 files changed, 2927 insertions(+) create mode 100644 dotnet/README.md create mode 100644 dotnet/sitemap/.gitignore create mode 100644 dotnet/sitemap/ConsoleApp/ConsoleApp.csproj create mode 100644 dotnet/sitemap/ConsoleApp/Program.cs create mode 100644 dotnet/sitemap/ConsoleApp/TestFiles/robots.txt create mode 100644 dotnet/sitemap/ConsoleApp/TestFiles/sitemap.xml create mode 100644 dotnet/sitemap/README.md create mode 100644 dotnet/sitemap/SiteMapLibrary/SiteMap.cs create mode 100644 dotnet/sitemap/SiteMapLibrary/SiteMapLibrary.csproj create mode 100644 dotnet/sitemap/SiteMapLibrary/XmlManager.cs create mode 100644 dotnet/sitemap/sitemap.sln create mode 100644 dotnet/testing/.gitignore create mode 100644 dotnet/testing/KlipsConsole/KlipsConsole.csproj create mode 100644 dotnet/testing/KlipsConsole/Program.cs create mode 100644 dotnet/testing/KlipsLibrary.Test/KlipsLibrary.Test.csproj create mode 100644 dotnet/testing/KlipsLibrary.Test/UnitTest1.cs create mode 100644 dotnet/testing/KlipsLibrary/Animal.cs create mode 100644 dotnet/testing/KlipsLibrary/Bag.cs create mode 100644 dotnet/testing/KlipsLibrary/Fruit.cs create mode 100644 dotnet/testing/KlipsLibrary/InitOrder.cs create mode 100644 dotnet/testing/KlipsLibrary/Klips.cs create mode 100644 dotnet/testing/KlipsLibrary/KlipsLibrary.csproj create mode 100644 dotnet/testing/KlipsLibrary/Shape.cs create mode 100644 dotnet/testing/README.md create mode 100644 dotnet/testing/dotnet-klips.sln diff --git a/README.md b/README.md index 126aff9..822851c 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ github.com/shaunrd0/klips/ ├── ansible # Ansible roles, playbooks, and examples ├── blockchain # Blockchain related project templates and examples ├── cpp # C++ programs, datastructures, and other examples +├── dotnet # .NET projects and examples ├── figlet # Figlet fonts I like :) ├── javascript # Javascript projects and examples ├── python # Python scripts or tools I've made diff --git a/dotnet/README.md b/dotnet/README.md new file mode 100644 index 0000000..8430435 --- /dev/null +++ b/dotnet/README.md @@ -0,0 +1,11 @@ +# Dotnet + +```bash +shaunrd0/klips/dotnet/ +├── sitemap # Custom library to generate sitemaps +├── testing # General .NET practice +└── README.md +``` + +All of these projects were created with the `dotnet` CLI on Linux (Kubuntu 20.04). +They have not been tested on any other platform. diff --git a/dotnet/sitemap/.gitignore b/dotnet/sitemap/.gitignore new file mode 100644 index 0000000..a72f3dd --- /dev/null +++ b/dotnet/sitemap/.gitignore @@ -0,0 +1,454 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET +project.lock.json +project.fragment.lock.json +artifacts/ + +# Tye +.tye/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +## +## Visual studio for Mac +## + + +# globs +Makefile.in +*.userprefs +*.usertasks +config.make +config.status +aclocal.m4 +install-sh +autom4te.cache/ +*.tar.gz +tarballs/ +test-results/ + +# Mac bundle stuff +*.dmg +*.app + +# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# JetBrains Rider +.idea/ +*.sln.iml + +## +## Visual Studio Code +## +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json diff --git a/dotnet/sitemap/ConsoleApp/ConsoleApp.csproj b/dotnet/sitemap/ConsoleApp/ConsoleApp.csproj new file mode 100644 index 0000000..43373a9 --- /dev/null +++ b/dotnet/sitemap/ConsoleApp/ConsoleApp.csproj @@ -0,0 +1,14 @@ + + + + + + + + Exe + net6.0 + enable + enable + + + diff --git a/dotnet/sitemap/ConsoleApp/Program.cs b/dotnet/sitemap/ConsoleApp/Program.cs new file mode 100644 index 0000000..78826c1 --- /dev/null +++ b/dotnet/sitemap/ConsoleApp/Program.cs @@ -0,0 +1,12 @@ +using SiteMapLibrary; + +// Create an XmlManager to use for generating our sitemap; Provide a file path (and optional Xml settings; See ctor) +var mgr = new XmlManager("/home/kapper/Code/klips/dotnet/sitemap/ConsoleApp/TestFiles/sitemap.xml"); +// If we want to output the sitemap to the console, instead of saving to a file +// var mgr = new XmlManager("Console.Out"); + +// Provide a base URL to start crawling, an XmlManager, and a Regex pattern to use for matching URLs while crawling +using SiteMap siteMap = new SiteMap("https://knoats.com", mgr, + new("(http?s://knoats.com(?!.*/dist/|.*/settings/|.*/register/|.*/login/|.*/uploads/|.*/export/|.*/search?).*?(?=\"))")); +// Start crawling; When this returns, we have visited all found URLs and wrote them to our sitemap +await siteMap.Crawl(); diff --git a/dotnet/sitemap/ConsoleApp/TestFiles/robots.txt b/dotnet/sitemap/ConsoleApp/TestFiles/robots.txt new file mode 100644 index 0000000..bfea538 --- /dev/null +++ b/dotnet/sitemap/ConsoleApp/TestFiles/robots.txt @@ -0,0 +1,9 @@ +User-agent: * +Disallow: +Disallow: /dist/ +Disallow: /settings/ +Disallow: /register/ +Disallow: /login/ +Disallow: /uploads/ +Disallow: /export/ +Disallow: /search? diff --git a/dotnet/sitemap/ConsoleApp/TestFiles/sitemap.xml b/dotnet/sitemap/ConsoleApp/TestFiles/sitemap.xml new file mode 100644 index 0000000..c3a101a --- /dev/null +++ b/dotnet/sitemap/ConsoleApp/TestFiles/sitemap.xml @@ -0,0 +1,975 @@ + + + + https://knoats.com + 2022-5-4 + daily + 0.5 + + + https://knoats.com/tags + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books + 2022-5-4 + daily + 0.5 + + + https://knoats.com/login + 2022-5-4 + daily + 0.5 + + + https://knoats.com/shelves + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/c + 2022-5-4 + daily + 0.5 + + + https://knoats.com/register + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/pi + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/vim + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/git + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/docker + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/ansible + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/security + 2022-5-4 + daily + 0.5 + + + https://knoats.com/password/email + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/bookstack + 2022-5-4 + daily + 0.5 + + + https://knoats.com/user/shaun-reed + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/c?shelf=2 + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/javascript + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/blockchain + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/networking + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/vim?shelf=1 + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/vim?shelf=2 + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/git?shelf=2 + 2022-5-4 + daily + 0.5 + + + https://knoats.com/shelves/containers + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/c/page/basics + 2022-5-4 + daily + 0.5 + + + https://knoats.com/shelves/programming + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/docker?shelf=3 + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/docker?shelf=1 + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/c/page/classes + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/vim/page/notes + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/ansible?shelf=1 + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/git/page/basics + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/game-development + 2022-5-4 + daily + 0.5 + + + https://knoats.com/pages/recently-updated + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/security?shelf=1 + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/bookstack?shelf=3 + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/git/chapter/usage + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/networking?shelf=3 + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/networking?shelf=1 + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/docker/page/gitlab + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/docker/page/shlink + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin?shelf=3 + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin?shelf=1 + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/git/page/submodules + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin/page/i3 + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/networking/page/dns + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/docker/page/heimdall + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/pi/page/installation + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/c-s68/page/dotnet-cli + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/c/page/multithreading + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin/page/grub + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/javascript/page/webgl + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin/page/arch + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/networking/page/tcpip + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/networking/page/nginx + 2022-5-4 + daily + 0.5 + + + https://knoats.com/link/30#bkmrk-configure-ssl + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/security/page/fail2ban + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/networking/page/apache + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/docker/page/dockerfile + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/git/page/authentication + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin/page/debian + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/networking/page/tcp-udp + 2022-5-4 + daily + 0.5 + + + https://knoats.com/pages/recently-updated?page=2 + 2022-5-4 + daily + 0.5 + + + https://knoats.com/pages/recently-updated?page=3 + 2022-5-4 + daily + 0.5 + + + https://knoats.com/pages/recently-updated?page=4 + 2022-5-4 + daily + 0.5 + + + https://knoats.com/pages/recently-updated?page=5 + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/pi/chapter/magic-mirror + 2022-5-4 + daily + 0.5 + + + https://knoats.com/pages/recently-updated?page=1 + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/self-hosted-applications + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/blockchain/page/solidity + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/c/page/building-projects + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/vim/page/configuring-vim + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin/chapter/bash + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin/page/crontab + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin/page/yakuake + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/bookstack/chapter/knoats + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin/page/examples + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin/page/xps-9310 + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin/page/wireless + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/security/page/ossec-rules + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/networking/page/osi-model + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/pi/chapter/backup-scripts + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin/page/tunneling + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/networking/page/subnetting + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin/page/bluetooth + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/ansible/page/creating-roles + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin/page/virtualbox + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/security/chapter/monitoring + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/networking/chapter/protocols + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/pi/page/magic-mirror-modules + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/git/page/software-development + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin/page/boot-process + 2022-5-4 + daily + 0.5 + + + https://knoats.com/shelves/linux-server-administration + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/game-development/page/prefabs + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/networking/page/proxy-servers + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/bookstack/chapter/development + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/game-development/chapter/unity + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin/page/bash-profiles + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin/page/configure-ftp + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin/chapter/interfaces + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin/page/audio-devices + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/security/page/server-checklist + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/security/page/devsec-baselines + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/networking/chapter/web-servers + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/bookstack/chapter/installation + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/game-development/page/scripting + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/game-development/page/shortcuts + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/ansible/page/creating-playbooks + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin/page/system-sensors + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/self-hosted-applications?shelf=3 + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin/page/swap-allocation + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin/page/getting-started + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin/chapter/system-admin + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin/page/server-hostname + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin/page/disk-management + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/pi/page/staging-configs-to-a-usb + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/game-development/page/linux-setup + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/git/page/pushing-merging-branches + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin/page/systemd-services + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin/chapter/distributions + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin/chapter/customization + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin/page/installing-fonts + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/security/page/virtualbox-networks + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/security/page/ossec-ubuntu-server + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/security/chapter/server-hardening + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin/page/kernel-management + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/ansible/page/managing-remote-hosts + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin/page/configure-postfix + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/self-hosted-applications/page/hexo + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin/page/mount-google-drive + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/self-hosted-applications/page/gitea + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin/page/enabling-google-2fa + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin/page/user-administration + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin/page/unattended-upgrades + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/self-hosted-applications/page/jekyll + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/bookstack/page/welcome-to-knoats-432 + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin/page/linux-on-chromebooks + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/game-development/page/post-processing + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin/chapter/ssh-configuration + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/bookstack/page/exploring-the-database + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/game-development/page/project-settings + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/game-development/page/new-input-system + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/bookstack/page/bookstack-configuration + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/docker/chapter/docker-compose-services + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/game-development/chapter/unreal-engine-4 + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/networking/page/certbot-ssl-certificates + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin/page/yubikey-ssh-authentication + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/self-hosted-applications/page/read-the-docs + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/bookstack/page/backup-bookstack-using-docker + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/game-development/page/gameplay-ability-system + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin/page/synchronizing-time-using-ntp + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/bookstack/page/bookstack-using-docker-compose + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/self-hosted-applications/chapter/url-shortners + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/bookstack/page/updating-bookstack-using-docker + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin/page/configuring-sshd-authentication + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/self-hosted-applications/chapter/site-generators + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/vim/page/configuring-vim#bkmrk-unicode.vim-plugin + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/game-development/page/retarget-skeleton-animations + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/self-hosted-applications/page/mame-web-application + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/linux-admin/page/configuring-multi-boot-filesystems + 2022-5-4 + daily + 0.5 + + + https://knoats.com/books/self-hosted-applications/chapter/documentation-generators + 2022-5-4 + daily + 0.5 + + \ No newline at end of file diff --git a/dotnet/sitemap/README.md b/dotnet/sitemap/README.md new file mode 100644 index 0000000..6f7bd64 --- /dev/null +++ b/dotnet/sitemap/README.md @@ -0,0 +1,45 @@ + +Sitemap generator I created while learning some C#. +Example of using the library is in `ConsoleApp/Program.cs`, files used for testing are in `ConsoleApp/TestFiles/` +`ConsoleApp/TestFiles/sitemap.xml` currently contains the sitemap for my website. +If we run the console application with a different URL that targets this same file, the file will be overwritten with the new sitemap. +There is no need to delete or recreate files manually. + +I plan to check for a `robots.txt` while generating sitemaps to prevent crawling pages that aren't useful. +For now there is no use for a `robots.txt`, the `SiteMap.Crawl()` function visits the URL provided to the `SiteMap` constructor. +Regex is used to check the visited page and match URLs with the same base domain, the URLS found are logged for the crawler to visit. +Each time we finish collecting URLS on a page, we move to the next URL in the queue and repeat this process. +Once we finish crawling all URLs, an XML sitemap is generated where the URLs are sorted by their length. + +I used [sitemaps.org - XML Format](https://www.sitemaps.org/protocol.html) to determine the proper formatting for the sitemap. +For now, since the web application I used for testing does not respond with [Last-Modified](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Last-Modified) in the HTTP header, the last modified time is set to the date the sitemap was generated. +The `priority` fields are all set to the default value indicated on sitemaps.org, which is `0.5`. +This is to avoid confusing crawlers with a huge list of 'top-priority' pages to crawl. +All `changefreq` fields of the sitemap are marked as `daily`. + +The primary motivation for this project was learning about unmanaged resources in C#, and trying out the [Dispose Pattern](https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose?redirectedfrom=MSDN#implement-the-dispose-pattern) for myself. +If someone reading this were to find a problem with the way I handled disposing of the `HttpClient` in the `SiteMap` class, feel free to let me know :) Creating an issue, PR, or sending an email is all acceptable. + +### Future plans +* Parse `robots.txt` to avoid crawling pages that are not desired +* Test the generator with an application that serves `LastModified` date; Use it if available +* Set `priority` in a more useful way, or allow some form of customization of the way this is handled. +* Set `changefreq` in a more useful way, or allow some form of customization of the way this is handled. +* Generate a regex pattern to match, if one is not provided + +For now, the general use of this library is seen in the example below. + +```C# +using SiteMapLibrary; + +// Create an XmlManager to use for generating our sitemap; Provide a file path (and optional Xml settings; See ctor) +var mgr = new XmlManager("/home/kapper/Code/klips/dotnet/sitemap/ConsoleApp/TestFiles/sitemap.xml"); +// If we want to output the sitemap to the console, instead of saving to a file +// var mgr = new XmlManager("Console.Out"); + +// Provide a base URL to start crawling, an XmlManager, and a Regex pattern to use for matching URLs while crawling +using SiteMap siteMap = new SiteMap("https://knoats.com", mgr, + new("(http?s://knoats.com(?!.*/dist/|.*/settings/|.*/register/|.*/login/|.*/uploads/|.*/export/|.*/search?).*?(?=\"))")); +// Start crawling; When this returns, we have visited all found URLs and wrote them to our sitemap +await siteMap.Crawl(); +``` diff --git a/dotnet/sitemap/SiteMapLibrary/SiteMap.cs b/dotnet/sitemap/SiteMapLibrary/SiteMap.cs new file mode 100644 index 0000000..839f633 --- /dev/null +++ b/dotnet/sitemap/SiteMapLibrary/SiteMap.cs @@ -0,0 +1,114 @@ +using System.Text.RegularExpressions; + +namespace SiteMapLibrary; + +public class SiteMap : IDisposable +{ + private HttpClient _client; + private HashSet _foundUrls; + private HashSet _visitedUrls; + private Queue _visitQueue; + private bool _disposed = false; + private XmlManager XmlManager { get; set; } + public string? Url { get; private set; } + public Regex Regexp { get; set; } + + public SiteMap(string url, string savepath, Regex pattern) + { + Url = url; + _client = new HttpClient(); + _foundUrls = new HashSet(); + _visitedUrls = new HashSet(); + _visitQueue = new Queue(); + Regexp = pattern; + XmlManager = new XmlManager(savepath); + } + + public SiteMap(string url, XmlManager mgr, Regex pattern) + { + _client = new HttpClient(); + _foundUrls = new HashSet(); + _visitedUrls = new HashSet(); + _visitQueue = new Queue(); + Regexp = pattern; + Url = url; + XmlManager = mgr; + } + + public async Task Crawl() + { + while (Url != null) + { + _visitedUrls.Add(Url); + using var content = await _client.GetAsync(Url); + if (!content.IsSuccessStatusCode) + { + Console.WriteLine($"{content.StatusCode} on url: {Url}"); + NextUrl(); + continue; + } + + var m = Regexp.Match(await content.Content.ReadAsStringAsync()); + while (m.Success) + { + foreach (Group group in m.Groups) + { + if (_foundUrls.Add(group.Value)) + { + Console.WriteLine(group.Value); + // Console.WriteLine(content.Content.Headers.LastModified); + if (!_visitedUrls.Contains(group.Value) && !_visitQueue.Contains(group.Value)) + { + _visitQueue.Enqueue(group.Value); + } + } + } + + m = m.NextMatch(); + } + + NextUrl(); + content.Dispose(); + } + WriteXml(); + } + + private void WriteXml() + { + List urls = new List(_visitedUrls.OrderBy(k => k.Length).ToArray()); + foreach (string url in urls) + { + XmlManager.AddUrl(url); + } + XmlManager.Save(); + } + + private void NextUrl() + { + if (_visitQueue.Count == 0) + { + Url = null; + return; + } + Url = _visitQueue.Dequeue(); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + public void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + _client.Dispose(); + } + + _disposed = true; + } + } +} diff --git a/dotnet/sitemap/SiteMapLibrary/SiteMapLibrary.csproj b/dotnet/sitemap/SiteMapLibrary/SiteMapLibrary.csproj new file mode 100644 index 0000000..06e82b0 --- /dev/null +++ b/dotnet/sitemap/SiteMapLibrary/SiteMapLibrary.csproj @@ -0,0 +1,10 @@ + + + + net6.0 + enable + enable + SiteMap + + + diff --git a/dotnet/sitemap/SiteMapLibrary/XmlManager.cs b/dotnet/sitemap/SiteMapLibrary/XmlManager.cs new file mode 100644 index 0000000..300dd97 --- /dev/null +++ b/dotnet/sitemap/SiteMapLibrary/XmlManager.cs @@ -0,0 +1,59 @@ +using System.Xml; + +namespace SiteMapLibrary; + +public class XmlManager +{ + private XmlDocument XmlDocument { get; set; } + private XmlDeclaration XmlDeclaration { get; set; } + private XmlElement XmlUrlset { get; set; } + private string Path { get; set; } + + public XmlManager(string path, + string version="1.0", string encoding="utf-8", string standalone="") + { + XmlDocument = new XmlDocument(); + XmlDeclaration = XmlDocument.CreateXmlDeclaration(version, encoding, standalone); + XmlDocument.AppendChild(XmlDeclaration); + XmlUrlset = XmlDocument.CreateElement("urlset"); + XmlDocument.AppendChild(XmlUrlset); + Path = path; + } + + ~XmlManager() + { + Save(); + } + + public void AddUrl(string url) + { + XmlElement newUrl = XmlDocument.CreateElement("url"); + XmlUrlset.AppendChild(newUrl); + XmlElement newLoc = XmlDocument.CreateElement("loc"); + newLoc.InnerText = url; + newUrl.AppendChild(newLoc); + var lastmod = XmlDocument.CreateElement("lastmod"); + lastmod.InnerText = DateTime.Now.Year.ToString() + + '-' + DateTime.Now.Month.ToString() + + '-' + DateTime.Now.Day; + newUrl.AppendChild(lastmod); + var changeFreq = XmlDocument.CreateElement("changefreq"); + changeFreq.InnerText = "daily"; + newUrl.AppendChild(changeFreq); + var priority = XmlDocument.CreateElement("priority"); + priority.InnerText = "0.5"; + newUrl.AppendChild(priority); + } + + public void Save() + { + if (Path == "Console.Out") + { + XmlDocument.Save(Console.Out); + } + else + { + XmlDocument.Save(Path); + } + } +} diff --git a/dotnet/sitemap/sitemap.sln b/dotnet/sitemap/sitemap.sln new file mode 100644 index 0000000..8221e29 --- /dev/null +++ b/dotnet/sitemap/sitemap.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30114.105 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SiteMapLibrary", "SiteMapLibrary\SiteMapLibrary.csproj", "{0BDEDA94-7BBF-4F82-9A14-131C4F8A3330}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleApp", "ConsoleApp\ConsoleApp.csproj", "{600802AC-C872-4115-BFE6-DA2AE7138F9C}" +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 + {0BDEDA94-7BBF-4F82-9A14-131C4F8A3330}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0BDEDA94-7BBF-4F82-9A14-131C4F8A3330}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0BDEDA94-7BBF-4F82-9A14-131C4F8A3330}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0BDEDA94-7BBF-4F82-9A14-131C4F8A3330}.Release|Any CPU.Build.0 = Release|Any CPU + {600802AC-C872-4115-BFE6-DA2AE7138F9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {600802AC-C872-4115-BFE6-DA2AE7138F9C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {600802AC-C872-4115-BFE6-DA2AE7138F9C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {600802AC-C872-4115-BFE6-DA2AE7138F9C}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/dotnet/testing/.gitignore b/dotnet/testing/.gitignore new file mode 100644 index 0000000..a72f3dd --- /dev/null +++ b/dotnet/testing/.gitignore @@ -0,0 +1,454 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET +project.lock.json +project.fragment.lock.json +artifacts/ + +# Tye +.tye/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +## +## Visual studio for Mac +## + + +# globs +Makefile.in +*.userprefs +*.usertasks +config.make +config.status +aclocal.m4 +install-sh +autom4te.cache/ +*.tar.gz +tarballs/ +test-results/ + +# Mac bundle stuff +*.dmg +*.app + +# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# JetBrains Rider +.idea/ +*.sln.iml + +## +## Visual Studio Code +## +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json diff --git a/dotnet/testing/KlipsConsole/KlipsConsole.csproj b/dotnet/testing/KlipsConsole/KlipsConsole.csproj new file mode 100644 index 0000000..529a638 --- /dev/null +++ b/dotnet/testing/KlipsConsole/KlipsConsole.csproj @@ -0,0 +1,13 @@ + + + + + + + Exe + net6.0 + enable + enable + + + diff --git a/dotnet/testing/KlipsConsole/Program.cs b/dotnet/testing/KlipsConsole/Program.cs new file mode 100644 index 0000000..e0d0a49 --- /dev/null +++ b/dotnet/testing/KlipsConsole/Program.cs @@ -0,0 +1,14 @@ +using KlipsLibrary; + +Klips.SayHello(); + +int[] arr = { 2, 4, 6, 2, 1, 44, 10 }; +Klips.Print(arr); + +Console.WriteLine(); +HashSet set = new HashSet(){ "hi", "my", "name", "is", "shaun" }; +Klips.Print(set); +Console.WriteLine(); + +// Test other C# containers +Klips.TestContainers(); diff --git a/dotnet/testing/KlipsLibrary.Test/KlipsLibrary.Test.csproj b/dotnet/testing/KlipsLibrary.Test/KlipsLibrary.Test.csproj new file mode 100644 index 0000000..b559b32 --- /dev/null +++ b/dotnet/testing/KlipsLibrary.Test/KlipsLibrary.Test.csproj @@ -0,0 +1,27 @@ + + + + net6.0 + enable + + false + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/dotnet/testing/KlipsLibrary.Test/UnitTest1.cs b/dotnet/testing/KlipsLibrary.Test/UnitTest1.cs new file mode 100644 index 0000000..d7c92a4 --- /dev/null +++ b/dotnet/testing/KlipsLibrary.Test/UnitTest1.cs @@ -0,0 +1,22 @@ +using Xunit; + +namespace KlipsLibrary.Test; + +public class UnitTest1 +{ + [Fact] + public void DogTest() + { + var dog = new Dog("Buford", "Woof"); + Assert.Equal("Buford", dog.Name); + dog.Speak(); + } + + [Fact] + public void HumanTest() + { + var human = new Human("Shaun", "Hi"); + Assert.Equal("Shaun", human.Name); + human.Speak(); + } +} \ No newline at end of file diff --git a/dotnet/testing/KlipsLibrary/Animal.cs b/dotnet/testing/KlipsLibrary/Animal.cs new file mode 100644 index 0000000..a31bdb2 --- /dev/null +++ b/dotnet/testing/KlipsLibrary/Animal.cs @@ -0,0 +1,70 @@ +using System.Collections; +using KlipsLibrary; +namespace KlipsLibrary; +public class Solution { + public int NumUniqueEmails(string[] emails) + { + HashSet sent = new(); + foreach (var email in emails) + { + var domain = email.Substring(email.IndexOf("@"), email.Length); + var to = email.Substring(0, email.IndexOf("@")); + if (to.Contains(".")) to = to.Replace(".", ""); + to = to.Remove(to.IndexOf("+")); + Console.Write("{0} at {1}", to, domain); + sent.Add(to + "@" + domain); + } + + return sent.Count; + } +} +public abstract class Animal +{ + public Animal(string n, string p) + { + this.Name = n; + this.Phrase = p; + } + + public abstract void Speak(); + + private string name; + public string Name { get; set; } + private string phrase; + public string Phrase { get; set; } +} + +public class Dog : Animal +{ + public Dog(string n, string p) : base(n, p) { } + + public override void Speak() + { + Console.WriteLine("{0} (Dog): {1}", Name, Phrase); + } +} + +public class Human : Animal +{ + public Human(string n, string p) : base(n, p) { } + + public override void Speak() + { + Console.WriteLine("{0} (Human): {1}", Name, Phrase); + } +} + +public class Teacher : Human, IComparable, ICloneable +{ + public Teacher(string n, string p) : base(n, p) { } + + public int CompareTo(object? obj) + { + throw new NotImplementedException(); + } + + public object Clone() + { + throw new NotImplementedException(); + } +} diff --git a/dotnet/testing/KlipsLibrary/Bag.cs b/dotnet/testing/KlipsLibrary/Bag.cs new file mode 100644 index 0000000..e54a5c4 --- /dev/null +++ b/dotnet/testing/KlipsLibrary/Bag.cs @@ -0,0 +1,74 @@ +namespace KlipsLibrary; + +public struct Item : IEquatable +{ + public Item(double v, int q, string name) + { + Value = v; + Qty = q; + Name = name; + } + + public Item(double v, string name) + { + Value = v; + Qty = 1; + Name = name; + } + + public string Name { get; set; } + public double Value { get; set; } + public int Qty { get; set; } + + public static bool operator ==(Item a, Item b) + { + if (((object)a) == null || ((object)b) == null) return Object.Equals(a, b); + return a.Equals(b); + } + + public static bool operator !=(Item a, Item b) + { + return !(a == b); + } + + public bool Equals(Item other) + { + return Name == other.Name && Value == other.Value; + } + + public override bool Equals(object? obj) + { + return obj is Item other && Equals(other); + } + + public override int GetHashCode() + { + return HashCode.Combine(Name, Value); + } +} + +public class Bag +{ + public Bag() + { + contents = new List(); + maxCarry = 10; + } + private List contents; + private int maxCarry; + + public bool AddItem(Item i) + { + if (contents.Count >= maxCarry) return false; + contents.Add(i); + return true; + } + + public Item? TakeItem(Item i) + { + var found = contents.Find((Item inBag) => inBag == i); + if (found == default(Item)) return null; + contents.Remove(found); + return found; + } +} diff --git a/dotnet/testing/KlipsLibrary/Fruit.cs b/dotnet/testing/KlipsLibrary/Fruit.cs new file mode 100644 index 0000000..8851334 --- /dev/null +++ b/dotnet/testing/KlipsLibrary/Fruit.cs @@ -0,0 +1,19 @@ +using System.Collections.ObjectModel; + +namespace KlipsLibrary; + +public class Fruit +{ + public Fruit() + { + Name = "Default"; + } + + public Fruit(string name) + { + Name = name; + } + public string Name { get; set; } +} + +public class Fruits : Collection { } diff --git a/dotnet/testing/KlipsLibrary/InitOrder.cs b/dotnet/testing/KlipsLibrary/InitOrder.cs new file mode 100644 index 0000000..581a9fb --- /dev/null +++ b/dotnet/testing/KlipsLibrary/InitOrder.cs @@ -0,0 +1,75 @@ +namespace KlipsLibrary; + +public class A +{ + public A() + { + Console.WriteLine("A default constructor was called"); + Val = 0; + } + public A(int v) + { + Console.WriteLine("A parameterized constructor was called"); + Val = v; + } + private int val; + public int Val + { + // No setter; We can only initialize on construction + init + { + Console.WriteLine($"A.val was initialized: {val}"); + val = value; + } + } +} + +public class B : A +{ + public B() + { + Console.WriteLine("B default constructor was called"); + BVal = 0; + } + public B(int bv) + { + Console.WriteLine("B parameterized constructor was called"); + BVal = bv; + } + + private int bVal; + + public int BVal + { + init + { + bVal = value; + Console.WriteLine($"B.bVal was initialized: {bVal}"); + } + } +} + +public class C : B +{ + public C() + { + Console.WriteLine("C default constructor was called"); + CVal = 0; + } + public C(int cv) + { + Console.WriteLine("C parameterized constructor was called"); + CVal = cv; + } + + private int cVal; + + public int CVal + { + init + { + cVal = value; + Console.WriteLine($"C.cVal was initialized: {cVal}"); + } + } +} \ No newline at end of file diff --git a/dotnet/testing/KlipsLibrary/Klips.cs b/dotnet/testing/KlipsLibrary/Klips.cs new file mode 100644 index 0000000..be83a4d --- /dev/null +++ b/dotnet/testing/KlipsLibrary/Klips.cs @@ -0,0 +1,281 @@ +using System.Collections; +using System.Linq.Expressions; + +namespace KlipsLibrary; + +public class Klips +{ + public static void SayHello() + { + Console.WriteLine("Hello, C# library!"); + } + + public static void TestInput() + { + string formattingString = "Captured {0} input: {1}\n"; + + Console.Write("\nInput a character, then press enter: "); + int ascii = Console.Read(); + char ch = Convert.ToChar(ascii); + Console.Write(formattingString, "character", ch); + Console.ReadLine(); // Discard any left over input + + Console.Write("\nPress a key: "); + ConsoleKeyInfo key = Console.ReadKey(); + Console.Write("\n" + formattingString, "key", key.KeyChar); + + Console.Write("\nEnter a line: "); + string? line = Console.ReadLine(); + Console.Write(formattingString, "line", line); + } + + public static void PrintEnum(IEnumerable obj) + { + foreach (var i in obj) + { + Console.Write("{0}, ", i); + } + } + + public static void PrintInfo(IEnumerable c) + { + Console.WriteLine("Type: {0}", c.GetType().ToString()); + } + + public static void Print(IEnumerable obj) + { + Console.WriteLine(); + PrintInfo(obj); + PrintEnum(obj); + } + + // Doesn't work because we can't resolve passed object name + // + Limited to parameter object name; ref causes errors + public static void PrintName(T obj) + { + Expression> expression = () => obj!; + string name = ((expression.Body as MemberExpression)!).Member.Name; + Console.WriteLine("{0}", name); + } + + public static void MakeGarbage(int count) + { + for (int i = 0; i < count; i++) + { + var v = new Version(); + } + } + + public static void TestContainers() + { + int[] test = { 1, 2, 3, 4, 5 }; + Klips.Print(test); + + var arr = new int[5]; + for (int i = 1; i < 6; i++) + { + arr[i-1] = i; + } + Klips.Print(arr); + + var list = new List(); + for (int i = 1; i < 6; i++) + { + list.Add(i); + } + Klips.Print(list); + + list = list.Concat(arr).ToList(); + Klips.Print(list); + + list.Sort(); + Klips.Print(list); + + var dict = new Dictionary() + { + [4] = "four", + [2] = "two", + [1] = "one", + [3] = "three", + [5] = "five", + }; + Klips.Print(dict); + for (int i = 1; i <= 5; i++) + { + Console.Write("\n{0}", dict[i]); + } + + var sortedDict = new SortedDictionary(dict); + Klips.Print(sortedDict); + + var hashset = new HashSet{3, 1, 4, 2}; + Klips.Print(hashset); + + var sortedSet = new SortedSet(hashset); + Klips.Print(sortedSet); + + var sortedList = new SortedList(sortedDict); + Console.WriteLine("Type: {0}", sortedList.GetType().ToString()); + foreach (DictionaryEntry item in sortedList) + { + Console.WriteLine("Key: {0} Value: {1}", item.Key.ToString(), item.Value); + } + // Klips.Print(sortedList); + + var q = new Queue(dict.Values); + Klips.Print(q); + + var llist = new LinkedList(dict.Keys); + Klips.Print(llist); + + } + + public static void TestGC() + { + Klips.MakeGarbage(1000000); + // GC.Collect(); + Console.Write("\nHeap memory: {0}\nAllocated heap memory: {1}", + GC.GetGCMemoryInfo().HeapSizeBytes.ToString(), GC.GetTotalMemory(false).ToString()); + for (int i = 0; i < 3; i++) + { + Console.Write("\nGeneration {0} collection count: {1}", i, GC.CollectionCount(i).ToString()); + } + } + + public static void TestStrings() + { + Console.Write("\n\nWhat time is it?\n{0}", DateTime.Now.ToString()); + + var lit = @" + hi + how + ""are"" you? \this\is\a\literal + "; + Console.WriteLine(lit); + + string[] @foreach = {@"\this\is\new\a\test\n", "Not verbatim\nBut still literal"}; + foreach (string s in @foreach) + { + Console.WriteLine(@s); + } + + string a = "This is my string!"; + Console.WriteLine($"This is my rifle; {a, 30}"); + Console.WriteLine($"This is {{my}} rifle; {a, -30}"); + var b = $"This {{is}} my rifle; {a}"; + Console.WriteLine(b); + Console.WriteLine($"Conditional formatting result: {(b.Length == 0 ? "Empty" : "Not empty")}"); + var pi = Math.PI; + Console.WriteLine($"{pi:F3}, {pi:F10}, {DateTime.Now:d}, {DateTime.Now:f}, {DateTime.Now.ToLocalTime():h:mm:ss tt zz}"); + + string fmt = "This is pi: {0}\nThis is the date: {1}\nThis is also pi: {0:F6}"; + Console.WriteLine(fmt, Math.PI, DateTime.Now); + } + + public static void TestLambdas() + { + // Both of these lambdas are of the same type; Func where int is the value returned + var getLen = (string s) => s.Length; + Func funcLen = (string s) => s.Length; + Console.WriteLine("Length: {0}", getLen("Hello").ToString()); + Console.WriteLine("Length: {0}", funcLen("Hello").ToString()); + + var isEqual = (string a, string b) => a == b; + Console.WriteLine(isEqual("Test", "Test")); + Func funcIsEqual = (string a, string b) => a == b; + Console.WriteLine(funcIsEqual("Test", "Test")); + + // These two lamdas are both of type Action, as they do not return a result + var statement = (string s) => + { + var arr = s.ToCharArray(); + Array.Reverse(arr); + Console.WriteLine($"\"{s}\" reversed: {new string(arr)}"); + }; + Action actionReverse = (string s) => + { + var arr = s.ToCharArray(); + Array.Reverse(arr); + Console.WriteLine($"\"{s}\" reversed: {new string(arr)}"); + }; + + // This lamda is a Func as it take a string parameter and returns a string as a result + Func revString = (string s) => + { + var revArr = s.ToCharArray(); + Array.Reverse(revArr); + return new string(revArr); + }; + string testS = "Racecar"; + Console.WriteLine($"{testS} reversed: {revString(testS)}"); + + statement("Test"); + } + + public static void TestShape() + { + var shape = new Square(); + shape.Print(); + var shapeRef = shape; + shapeRef.Height = 20; + shape.Print(); + + var box = new List(); + box.Add(new Shape(5, 5)); + box.Add(new Square()); + box.Last().Width = 8; // Access the last element we added to the List, set its width to 8 + box.Add(new Cube()); + box.Add(new Rectangle()); + box.Add(new Rect()); + // Use a lambda to find a Cube, get a reference to it; If we found a Cube, set its depth to 5 + if (box.Find((Shape s) => s.GetType() == typeof(Cube)) is Cube cubeRef) cubeRef.Depth = 5; + foreach (var s in box) s.Print(); // Print all the Shapes + + var cub = new Cube(); + var sqr = cub as Square; + if (sqr != null) sqr.Print(); + + Console.WriteLine("Testing upcast"); + var c = new Cube(); + c.Print(); + if (c is Square cubeSquare) + { + // Why is Shape's Print() not called?; cubeShape.Print calls Square.Print() instead + var cubeShape = cubeSquare as Shape; + if (cubeShape != null) cubeShape.Print(); + } + } + + public static void TestBag() + { + var bag = new Bag(); + Item wrench = new Item(1.5, 1, "wrench"); // Create a wrench using Item ctor + var spanner = wrench; // Copy wrench to a new item + spanner.Name = "spanner"; + spanner.Value = 5.0; + spanner.Qty = 2; + Item socket = new Item(2.5, 5, "socket"); // Create a new item using ctor + bag.AddItem(wrench); + bag.AddItem(spanner); + bag.AddItem(socket); + Item? bagSpanner = bag.TakeItem(spanner); + Console.WriteLine(Object.ReferenceEquals(bagSpanner, spanner)); + Item? noSpanner = bag.TakeItem(spanner); + Console.WriteLine(noSpanner == null); + } + + public static void TestFruits() + { + Fruits fruits = new Fruits() { new Fruit(), new Fruit("Apple"), new Fruit("Orange")}; + foreach (Fruit f in fruits) + { + Console.WriteLine(f.Name); + } + } + + public static void TestInitOrder() + { + var aClass = new C(5) {CVal = 10}; + } + +} \ No newline at end of file diff --git a/dotnet/testing/KlipsLibrary/KlipsLibrary.csproj b/dotnet/testing/KlipsLibrary/KlipsLibrary.csproj new file mode 100644 index 0000000..bafd05b --- /dev/null +++ b/dotnet/testing/KlipsLibrary/KlipsLibrary.csproj @@ -0,0 +1,9 @@ + + + + net6.0 + enable + enable + + + diff --git a/dotnet/testing/KlipsLibrary/Shape.cs b/dotnet/testing/KlipsLibrary/Shape.cs new file mode 100644 index 0000000..e71aadf --- /dev/null +++ b/dotnet/testing/KlipsLibrary/Shape.cs @@ -0,0 +1,98 @@ +namespace KlipsLibrary; + +public class Shape +{ + public Shape(int y, int x) + { + Y = y; + X = x; + Name = this.ToString(); + } + + public Shape() + { + X = 0; + Y = 0; + Name = this.ToString(); + } + + // Shape position; Private setter, public getter + public int X { get; private set; } + public int Y { get; private set; } + public string Name { get; private set; } + + // Auto-implemented properties may use a default value initializer + public virtual int Width { get; set; } = 1; + public virtual int Height { get; set; } = 2; + + public virtual void Print() + { + Console.WriteLine($"{Name} WxH is {Width}x{Height} at position ({X},{Y})"); + } +} + +public class Square : Shape +{ + // Set default value on encapsulated value for non auto-implemented properties + private int width = 10; + private int height = 10; + + // We can override properties just as we can functions + // + Height and Width properties can no longer set default values + public override int Height + { + // Can use expressions for getters / setters + get => height; + set => width = height = value; + } + + public override int Width + { + get => width; + set + { + // Same setter as Height, just within a block of statements + width = value; + height = value; + } + } + + public override void Print() + { + Console.WriteLine("Printing Square info..."); + base.Print(); // Will use Square's getter / setter to print private int width, height + } +} + +public class Cube : Square +{ + // Add new properties or encapsulated values as needed + private int depth = 10; + public int Depth + { + get => depth; + set => depth = value; + } + // Implement a `new` Print() function which acts as a new stand-alone implementaton + public new void Print() + { + Console.WriteLine("Printing Cube info..."); + Console.WriteLine($"{Name} WxHxD is {Width}x{Height}x{Depth} at position ({X},{Y})"); + } +} + +public class Rectangle : Shape +{ + // Classes that inherit from Rectangle can not override Print + public sealed override void Print() + { + Console.WriteLine("Printing sealed Rectangle info..."); + base.Print(); + } +} + +public class Rect : Rectangle +{ + // Rect can't override Print(), since its base class declared it as `sealed` +} + diff --git a/dotnet/testing/README.md b/dotnet/testing/README.md new file mode 100644 index 0000000..c34973f --- /dev/null +++ b/dotnet/testing/README.md @@ -0,0 +1,5 @@ + +This is a testing project I created while first learning C#. +There's no objective or goal, aside from playing with different features of C# and NuGet packages. +Essentially, this is a scrap project that I code in while learning new C# things. +Since this is just for learning, I won't bother explaining how to use things here; It's very possible this entire project could disappear one day. diff --git a/dotnet/testing/dotnet-klips.sln b/dotnet/testing/dotnet-klips.sln new file mode 100644 index 0000000..4fe9063 --- /dev/null +++ b/dotnet/testing/dotnet-klips.sln @@ -0,0 +1,34 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30114.105 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KlipsLibrary", "KlipsLibrary\KlipsLibrary.csproj", "{58CB87C5-7D98-43F9-8AD8-C73766C06E51}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KlipsLibrary.Test", "KlipsLibrary.Test\KlipsLibrary.Test.csproj", "{3576A541-1B1E-42A1-A348-B0CAA3B5BC5A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KlipsConsole", "KlipsConsole\KlipsConsole.csproj", "{A1E88D77-4A17-47BF-BED4-CEB1821FB627}" +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 + {58CB87C5-7D98-43F9-8AD8-C73766C06E51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {58CB87C5-7D98-43F9-8AD8-C73766C06E51}.Debug|Any CPU.Build.0 = Debug|Any CPU + {58CB87C5-7D98-43F9-8AD8-C73766C06E51}.Release|Any CPU.ActiveCfg = Release|Any CPU + {58CB87C5-7D98-43F9-8AD8-C73766C06E51}.Release|Any CPU.Build.0 = Release|Any CPU + {3576A541-1B1E-42A1-A348-B0CAA3B5BC5A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3576A541-1B1E-42A1-A348-B0CAA3B5BC5A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3576A541-1B1E-42A1-A348-B0CAA3B5BC5A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3576A541-1B1E-42A1-A348-B0CAA3B5BC5A}.Release|Any CPU.Build.0 = Release|Any CPU + {A1E88D77-4A17-47BF-BED4-CEB1821FB627}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1E88D77-4A17-47BF-BED4-CEB1821FB627}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1E88D77-4A17-47BF-BED4-CEB1821FB627}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1E88D77-4A17-47BF-BED4-CEB1821FB627}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal