Cleanup code and docs; add missing header.

- The source and header files should have standard comment headers.
- Windows support has been removed, as I've decomissioned my Windows
  desktop in favour of a Linux desktop.
- Commander.h wasn't being added to the install directory.
This commit is contained in:
Kyle Isom 2023-10-16 02:18:09 -07:00
parent 0fff669b40
commit 407ee1c85d
33 changed files with 988 additions and 404 deletions

View File

@ -1,2 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module classpath="CMake" type="CPP_MODULE" version="4" />
<module classpath="CMake" type="CPP_MODULE" version="4">
<component name="FacetManager">
<facet type="Python" name="Python facet">
<configuration sdkName="Python 3.10 (scsl)" />
</facet>
</component>
</module>

View File

@ -1,4 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Black">
<option name="sdkName" value="Python 3.10 (scsl)" />
</component>
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
</project>

View File

@ -1,19 +1,37 @@
#include <cassert>
///
/// \file Arena.cc
/// \author K. Isom <kyle@imap.cc>
/// \date 2023-10-06
/// \brief Memory management using an arena.
///
/// Copyright 2023 K. Isom <kyle@imap.cc>
///
/// Permission to use, copy, modify, and/or distribute this software for
/// any purpose with or without fee is hereby granted, provided that
/// the above copyright notice and this permission notice appear in all /// copies.
///
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
/// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
/// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
/// OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
/// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
/// PERFORMANCE OF THIS SOFTWARE.
///
#include <cstdio>
#include <cstdlib>
#include <cstring>
#if defined(__posix__) || defined(__linux__) || defined(__APPLE__)
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define PROT_RW (PROT_WRITE|PROT_READ)
#elif defined(__WIN64__) || defined(__WIN32__) || defined(WIN32)
#include "WinHelpers.h"
#pragma comment(lib, "advapi32.lib")
#endif
#include <ios>
@ -246,7 +264,7 @@ Arena::Destroy()
case ArenaType::Static:
break;
case ArenaType::Alloc:
delete this->store;
delete[] this->store;
break;
#if defined(__posix__) || defined(__linux__) || defined(__APPLE__)
case ArenaType::MemoryMapped:

26
Arena.h
View File

@ -1,11 +1,26 @@
///
/// \file Arena.h
/// \author K. Isom
/// \author K. Isom <kyle@imap.cc>
/// \date 2023-10-06
/// \brief Memory management using an arena.
///
/// Arena defines a memory management backend for pre-allocating memory.
///
/// Copyright 2023 K. Isom <kyle@imap.cc>
///
/// Permission to use, copy, modify, and/or distribute this software for
/// any purpose with or without fee is hereby granted, provided that
/// the above copyright notice and this permission notice appear in all /// copies.
///
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
/// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
/// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
/// OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
/// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
/// PERFORMANCE OF THIS SOFTWARE.
///
/// \section PLATFORM SUPPORT
///
/// Arena will build on the major platforms, but memory-mapped files are only
@ -17,10 +32,10 @@
#define KIMODEM_ARENA_H
#include <iostream>
#include <sys/stat.h>
#include <cstddef>
#include <cstdint>
#include <iostream>
#include <sys/stat.h>
#include "Exceptions.h"
@ -112,12 +127,7 @@ public:
/// \param path The path to the file that should be created.
/// \param fileSize The size of the file to create.
/// \return Returns 0 on success and -1 on error.
#if defined(__posix__) || defined(__linux__) || defined(__APPLE__)
int Create(const char *path, size_t fileSize);
#elif defined(__WIN64__) || defined(__WIN32__) || defined(WIN32)
int Create(const char *path, size_t fileSize);
#endif
/// Open reads a file into the arena; the file must already exist. On
/// Unix-based platforms, the arena will be backed by a memory via

View File

@ -2,14 +2,28 @@
/// \file Buffer.cc
/// \author K. Isom <kyle@imap.cc>
/// \date 2023-10-09
/// \brief Buffer implements basic line buffers.
///
/// Copyright 2023 K. Isom <kyle@imap.cc>
///
/// Permission to use, copy, modify, and/or distribute this software for
/// any purpose with or without fee is hereby granted, provided that
/// the above copyright notice and this permission notice appear in all /// copies.
///
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
/// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
/// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
/// OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
/// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
/// PERFORMANCE OF THIS SOFTWARE.
///
#include <cassert>
#include <cstring>
#include <ios>
#include <iostream>
#include <iomanip>
#include "Buffer.h"

View File

@ -8,12 +8,27 @@
/// editing. It allocates memory in powers of two, and will grow or shrink
/// as needed.
///
/// Copyright 2023 K. Isom <kyle@imap.cc>
///
/// Permission to use, copy, modify, and/or distribute this software for
/// any purpose with or without fee is hereby granted, provided that
/// the above copyright notice and this permission notice appear in all /// copies.
///
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
/// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
/// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
/// OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
/// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
/// PERFORMANCE OF THIS SOFTWARE.
///
#ifndef KGE_BUFFER_H
#define KGE_BUFFER_H
#include <iostream>
#include <cstdint>
#include <iostream>
namespace scsl {

View File

@ -1,64 +1,61 @@
cmake_minimum_required(VERSION 3.22)
project(scsl LANGUAGES CXX
VERSION 0.1.1
VERSION 0.2.2
DESCRIPTION "Shimmering Clarity Standard Library")
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_VERBOSE_MAKEFILES TRUE)
set(VERBOSE YES)
if (MSVC)
add_compile_options("/W4" "$<$<CONFIG:RELEASE>:/O2>")
# compile options:
# -Wall Default to all errors.
# -Wextra And a few extra.
# -Werror And require them to be fixed to build.
# -Wno-unused-function This is a library. Not every function is used here.
# -Wno-unused-parameter Some functions have parameters defined for compatibility,
# and aren't used in the implementation.
add_compile_options(
"-static"
"-Wall"
"-Wextra"
"-Werror"
"-Wno-unused-function"
"-Wno-unused-parameter"
"-g"
"$<$<CONFIG:RELEASE>:-O2>"
)
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
add_compile_options("-stdlib=libc++")
else ()
# compile options:
# -Wall Default to all errors.
# -Wextra And a few extra.
# -Werror And require them to be fixed to build.
# -Wno-unused-function This is a library. Not every function is used here.
# -Wno-unused-parameter Some functions have parameters defined for compatibility,
# and aren't used in the implementation.
add_compile_options(
"-static"
"-Wall"
"-Wextra"
"-Werror"
"-Wno-unused-function"
"-Wno-unused-parameter"
"$<$<CONFIG:RELEASE>:-O2>"
)
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
add_compile_options("-stdlib=libc++")
else ()
# nothing special for gcc at the moment
endif ()
# nothing special for gcc at the moment
endif ()
add_compile_definitions(SCSL_DESKTOP_BUILD)
add_compile_definitions(SCSL_VERSION=${PROJECT_VERSION})
set(HEADER_FILES scsl.h
Arena.h
Buffer.h
Commander.h
Dictionary.h
Exceptions.h
Flag.h
StringUtil.h
TLV.h
Test.h
WinHelpers.h)
)
set(SOURCE_FILES
Arena.cc
Buffer.cc
Commander.cc
Commander.h
Dictionary.cc
Exceptions.cc
Flag.cc
StringUtil.cc
TLV.cc
Test.cc
WinHelpers.cc)
)
if (APPLE)
add_library(scsl
@ -119,6 +116,6 @@ install(FILES ${HEADER_FILES} DESTINATION include/scsl)
install(FILES scslConfig.cmake DESTINATION share/scsl/cmake)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/scsl.pc DESTINATION lib/pkgconfig)
include(CMakePack.txt)
include(CMakeDocs.txt)
include(cmake/packaging.cmake)
include(cmake/docs.cmake)
endif()

View File

@ -1,12 +1,30 @@
///
/// \file Commander.cc
/// \author kyle
/// \author K. Isom <kyle@imap.cc>
/// \date 2023-10-10
/// \brief Subprogram tooling.
///
/// Copyright 2023 K. Isom <kyle@imap.cc>
///
/// Permission to use, copy, modify, and/or distribute this software for
/// any purpose with or without fee is hereby granted, provided that
/// the above copyright notice and this permission notice appear in all /// copies.
///
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
/// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
/// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
/// OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
/// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
/// PERFORMANCE OF THIS SOFTWARE.
///
#include <iostream>
#include "Commander.h"
namespace scsl {
@ -57,4 +75,4 @@ Commander::Run(std::string command, int argc, char **argv)
}
} // scsl
} // namespace scsl

View File

@ -12,9 +12,24 @@
/// $ some_tool subcommand args...
/// ```
///
/// Copyright 2023 K. Isom <kyle@imap.cc>
///
/// Permission to use, copy, modify, and/or distribute this software for
/// any purpose with or without fee is hereby granted, provided that
/// the above copyright notice and this permission notice appear in all /// copies.
///
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
/// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
/// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
/// OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
/// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
/// PERFORMANCE OF THIS SOFTWARE.
///
#include <map>
#include <functional>
#include <map>
#include <string>
#include <vector>
@ -28,7 +43,7 @@ namespace scsl {
/// CommanderFunc describes a function that can be run in Commander.
///
/// It expects an argument count and a list of arguments.
typedef std::function<bool(int, char **)> CommanderFunc;
using CommanderFunc = std::function<bool (int, char **)>;
/// Subcommands are the individual commands for the program. A Subcommand
@ -104,7 +119,8 @@ private:
std::map<std::string, Subcommand *> cmap;
};
} // scsl
} // namespace scsl
#endif //SCSL_COMMANDER_H

View File

@ -1,5 +1,28 @@
#include <cstring>
///
/// \file Dictionary.cc
/// \author K.Isom <kyle@imap.cc>
/// \date 2023-10-05
/// \brief Key-value store built on top of Arena and TLV.
///
/// Copyright 2023 K. Isom <kyle@imap.cc>
///
/// Permission to use, copy, modify, and/or distribute this software for
/// any purpose with or without fee is hereby granted, provided that
/// the above copyright notice and this permission notice appear in all /// copies.
///
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
/// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
/// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
/// OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
/// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
/// PERFORMANCE OF THIS SOFTWARE.
///
#include <cstdlib>
#include <cstring>
#include "Dictionary.h"
#if defined(SCSL_DESKTOP_BUILD)

View File

@ -1,7 +1,24 @@
///
/// \file scsl.h
/// \author kyle
/// \date 2023-10-06
/// \file Dictionary.h
/// \author kyle (kyle@imap.cc)
/// \date 2023-10-12
/// \brief Key-value store built on top of Arena and TLV.
///
///
/// Copyright 2023 K. Isom <kyle@imap.cc>
///
/// Permission to use, copy, modify, and/or distribute this software for
/// any purpose with or without fee is hereby granted, provided that
/// the above copyright notice and this permission notice appear in all /// copies.
///
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
/// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
/// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
/// OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
/// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
/// PERFORMANCE OF THIS SOFTWARE.
///
@ -9,6 +26,8 @@
#define SCSL_DICTIONARY_H
#include <cstdint>
#include "Arena.h"
#include "TLV.h"

View File

@ -1,6 +1,24 @@
//
// Created by kyle on 2023-10-10.
//
///
/// \file Exceptions.cc
/// \author K. Isom <kyle@imap.cc>
/// \date 2023-10-10
/// \brief Custom exceptions used in writing test programs.
///
/// Copyright 2023 K. Isom <kyle@imap.cc>
///
/// Permission to use, copy, modify, and/or distribute this software for
/// any purpose with or without fee is hereby granted, provided that
/// the above copyright notice and this permission notice appear in all /// copies.
///
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
/// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
/// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
/// OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
/// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
/// PERFORMANCE OF THIS SOFTWARE.
///
#include "Exceptions.h"

View File

@ -1,7 +1,24 @@
//
// Created by kyle on 2023-10-10.
//
///
/// \file Exceptions.h
/// \author K. Isom <kyle@imap.cc>
/// \date 2023-10-10
/// \brief Custom exceptions for use in SCSL used in writing test programs.
///
/// Copyright 2023 K. Isom <kyle@imap.cc>
///
/// Permission to use, copy, modify, and/or distribute this software for
/// any purpose with or without fee is hereby granted, provided that
/// the above copyright notice and this permission notice appear in all /// copies.
///
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
/// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
/// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
/// OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
/// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
/// PERFORMANCE OF THIS SOFTWARE.
///
#ifndef SCSL_EXCEPTIONS_H
#define SCSL_EXCEPTIONS_H
@ -9,6 +26,7 @@
#include <exception>
#include <string>
namespace scsl {

146
Flag.cc
View File

@ -1,11 +1,9 @@
///
/// \file Flag.h
/// \author kyle
/// \created 2023-10-12
/// \file Flag.cc
/// \author K. Isom <kyle@imap.cc>
/// \date 2023-10-12
/// \brief Flag defines a command-line flag parser.
///
/// \section COPYRIGHT
///
/// Copyright 2023 K. Isom <kyle@imap.cc>
///
/// Permission to use, copy, modify, and/or distribute this software for
@ -23,9 +21,7 @@
///
#include <cstdint>
#include <functional>
#include <map>
#include <iostream>
#include <regex>
#include <vector>
@ -36,11 +32,11 @@
namespace scsl {
static const std::regex isFlag("^--?[a-zA-Z0-9][a-zA-Z0-9-_]*$",
static const std::regex isFlag("^--?[a-zA-Z0-9][a-zA-Z0-9-_]*$",
std::regex_constants::nosubs);
std::string
ParseStatusToString(ParseStatus status)
Flags::ParseStatusToString(ParseStatus status)
{
switch (status) {
case ParseStatus::OK:
@ -58,22 +54,22 @@ ParseStatusToString(ParseStatus status)
Flag *
NewFlag(FlagType fType, std::string fName, std::string fDescription)
NewFlag(std::string fName, FlagType fType, std::string fDescription)
{
auto flag = new Flag;
flag->Type = fType;
flag->WasSet = false;
flag->Name = fName;
flag->Type = fType;
flag->WasSet = false;
flag->Name = fName;
flag->Description = fDescription;
flag->Value = FlagValue{};
flag->Value = FlagValue{};
return flag;
}
Flags::Flags(std::string fName)
: name(fName), description("")
: name(fName), description("")
{
}
@ -84,10 +80,21 @@ Flags::Flags(std::string fName, std::string fDescription)
}
Flags::~Flags()
{
for (auto flag : this->flags) {
if (flag.second->Type == FlagType::String) {
delete flag.second->Value.s;
}
delete flag.second;
}
}
bool
Flags::Register(std::string fName, FlagType fType, std::string fDescription)
{
if (!std::regex_search(fName, isFlag)) {
if (!std::regex_search(fName, isFlag) || fName == "-h") {
return false;
}
@ -95,7 +102,7 @@ Flags::Register(std::string fName, FlagType fType, std::string fDescription)
return false;
}
auto flag = NewFlag(fType, fName, fDescription);
auto flag = NewFlag(fName, fType, fDescription);
this->flags[fName] = flag;
return true;
}
@ -191,12 +198,10 @@ Flags::ValueOf(std::string fName, FlagValue &value)
}
ParseStatus
Flags::ParseStatus
Flags::parseArg(int argc, char **argv, int &index)
{
std::string arg(argv[index]);
std::string arg(argv[index]);
U::S::TrimWhitespace(arg);
index++;
@ -205,29 +210,36 @@ Flags::parseArg(int argc, char **argv, int &index)
}
if (this->flags.count(arg) == 0) {
if (arg == "-h" || arg == "--help") {
Usage(std::cout, 0);
}
return ParseStatus::NotRegistered;
}
auto flag = flags[arg];
if ((flag->Type != FlagType::Boolean) && index == argc) {
return ParseStatus::NotEnoughArgs;
}
switch (flag->Type) {
case FlagType::Boolean:
flag->WasSet = true;
flag->WasSet = true;
flag->Value.b = true;
return ParseStatus::OK;
case FlagType::Integer:
flag->WasSet = true;
flag->WasSet = true;
flag->Value.i = std::stoi(argv[++index], 0, 0);
return ParseStatus::OK;
case FlagType::UnsignedInteger:
flag->WasSet = true;
flag->WasSet = true;
flag->Value.u = static_cast<unsigned int>(std::stoi(argv[index++], 0, 0));
return ParseStatus::OK;
case FlagType::String:
flag->WasSet = true;
flag->WasSet = true;
flag->Value.s = new std::string(argv[index++]);
return ParseStatus::OK;
case FlagType::SizeT:
flag->WasSet = true;
flag->WasSet = true;
flag->Value.size = static_cast<size_t>(std::stoull(argv[index++]));
return ParseStatus::OK;
default:
@ -242,10 +254,13 @@ Flags::parseArg(int argc, char **argv, int &index)
}
ParseStatus
Flags::Parse(int argc, char **argv)
Flags::ParseStatus
Flags::Parse(int argc, char **argv, bool skipFirst)
{
int index = 1;
int index = 0;
if (skipFirst) {
index = 1;
}
while (index != argc) {
auto result = this->parseArg(argc, argv, index);
@ -277,6 +292,55 @@ Flags::Parse(int argc, char **argv)
}
void
Flags::Usage(std::ostream &os, int exitCode)
{
os << this->name << ":\t";
auto indent = this->name.size() + 7;
U::S::WriteTabIndented(os, description, 72 - indent, indent / 8, false);
os << "\n\n";
for (const auto &pair : this->flags) {
auto argLine = "\t" + pair.first;
switch (pair.second->Type) {
case FlagType::Boolean:
argLine += "\t\t";
break;
case FlagType::Integer:
argLine += "int\t\t";
break;
case FlagType::UnsignedInteger:
argLine += "uint\t\t";
break;
case FlagType::SizeT:
argLine += "size_t\t";
break;
case FlagType::String:
argLine += "string\t";
break;
case FlagType::Unknown:
// fallthrough
default:
#ifdef SCSL_NOEXCEPT
abort();
#else
throw std::runtime_error("unhandled parsing error - this is a bug");
#endif
break;
}
os << argLine;
indent = argLine.size();
U::S::WriteTabIndented(os, pair.second->Description,
72-indent, (indent/8)+2, false);
}
os << "\n";
exit(exitCode);
}
size_t
Flags::NumArgs()
{
@ -291,6 +355,17 @@ Flags::Args()
}
std::string
Flags::Arg(size_t i)
{
if (i >= this->args.size()) {
throw std::out_of_range("index is out of range");
}
return this->args[i];
}
Flag *
Flags::checkGetArg(std::string fName, FlagType eType)
{
@ -314,7 +389,7 @@ Flags::checkGetArg(std::string fName, FlagType eType)
bool
Flags::GetBool(std::string fName, bool &flagValue)
{
auto flag = this->checkGetArg(fName, FlagType::Boolean);
auto flag = this->checkGetArg(fName, FlagType::Boolean);
flagValue = flag->Value.b;
return flag->WasSet;
@ -324,7 +399,7 @@ Flags::GetBool(std::string fName, bool &flagValue)
bool
Flags::GetInteger(std::string fName, int &flagValue)
{
auto flag = this->checkGetArg(fName, FlagType::Integer);
auto flag = this->checkGetArg(fName, FlagType::Integer);
flagValue = flag->Value.i;
return flag->WasSet;
@ -334,18 +409,17 @@ Flags::GetInteger(std::string fName, int &flagValue)
bool
Flags::GetUnsignedInteger(std::string fName, unsigned int &flagValue)
{
auto flag = this->checkGetArg(fName, FlagType::UnsignedInteger);
auto flag = this->checkGetArg(fName, FlagType::UnsignedInteger);
flagValue = flag->Value.u;
return flag->WasSet;
}
bool
Flags::GetSizeT(std::string fName, std::size_t &flagValue)
{
auto flag = this->checkGetArg(fName, FlagType::SizeT);
auto flag = this->checkGetArg(fName, FlagType::SizeT);
flagValue = flag->Value.size;
return flag->WasSet;
@ -355,7 +429,7 @@ Flags::GetSizeT(std::string fName, std::size_t &flagValue)
bool
Flags::GetString(std::string fName, std::string &flagValue)
{
auto flag = this->checkGetArg(fName, FlagType::String);
auto flag = this->checkGetArg(fName, FlagType::String);
if (flag->Value.s == nullptr) {
return false;

281
Flag.h
View File

@ -1,11 +1,9 @@
///
/// \file Flag.h
/// \author kyle
/// \created 2023-10-12
/// \author K. Isom <kyle@imap.cc>
/// \date 2023-10-12
/// \brief Flag declares a command-line flag parser.
///
/// \section COPYRIGHT
///
/// Copyright 2023 K. Isom <kyle@imap.cc>
///
/// Permission to use, copy, modify, and/or distribute this software for
@ -35,90 +33,297 @@ namespace scsl {
/// FlagType indicates the value held in a FlagValue.
///
/// \todo When C++17 support is more common, switch to `std::variant`.
enum class FlagType : uint8_t {
Unknown = 0,
Boolean = 1,
Unknown = 0, ///< Unsupported value type.
Boolean = 1, ///< bool
Integer = 2, ///< int32_t
UnsignedInteger = 3, ///< uint32_t
SizeT = 4, ///< size_t
String = 5,
String = 5, ///< std::string
};
enum class ParseStatus : uint8_t {
Unknown = 0,
OK = 1,
EndOfFlags = 2,
NotRegistered = 3,
NotEnoughArgs = 4,
};
std::string
ParseStatusToString(ParseStatus status);
/// FlagValue holds the value of a command line flag.
typedef union {
unsigned int u;
int i;
std::size_t size;
std::string *s;
bool b;
unsigned int u; ///< FlagType::UnsignedInteger
int i; ///< FlagType::Integer
std::size_t size; ///< FlagType::SizeT
std::string *s; ///< FlagType::String
bool b; ///< FlagType::Boolean
} FlagValue;
/// Flag describes an individual command-line flag.
typedef struct {
FlagType Type;
bool WasSet;
std::string Name;
std::string Description;
FlagValue Value;
FlagType Type; ///< The type of the value in the flag.
bool WasSet; ///< The flag was set on the command-line.
std::string Name; ///< The name of the flag.
std::string Description; ///< A description of the flag.
FlagValue Value; ///< The flag's value.
} Flag;
Flag *
NewFlag(FlagType fType, std::string fName, std::string fDescription);
/// NewFlag is a helper function for constructing a new flag.
///
/// \param fName The name of the flag.
/// \param fType The type of the flag.
/// \param fDescription A description of the flag.
/// \return A pointer to a flag.
Flag *NewFlag(std::string fName, FlagType fType, std::string fDescription);
/// Flags provides a basic facility for processing command line flags.
///
/// Any remaining arguments after the args are added to the parser as
/// arguments that can be accessed with NumArgs, Args, and Arg.
///
/// \note The parser automatically handles the -h and --help flags by
/// calling Usage(std::cout, 0). The user can override this flag
/// and handle providing help on their own.
///
/// \details
///
/// Arguments are named with their leading dash, e.g. "-f". For example,
///
/// ```c++
/// flags.Register("-f", FlagType::String, "Path to a configuration file.");
/// ```
/// \section Usage
///
/// A short example program is below:
///
/// int
/// main(int argc, char *argv[])
/// {
/// std::string server = "service.example.com";
/// unsigned int port = 1234;
///
/// auto flags = new scsl::Flags("example-client",
/// "This interacts with the example.com service.");
/// flags->Register("-p", port, "server port");
/// flags->Register("-s", server, "hostname to connect to");
///
/// auto status = flags->Parse(argc, argv);
/// if (status != ParseStatus::OK) {
/// std::cerr << "failed to parse flags: "
/// << scsl::Flags::ParseStatusToString(status)
/// << "\n";
/// exit(1);
/// }
///
/// auto wasSet = flags->GetString("-s", server);
/// if (wasSet) {
/// std::cout << "hostname override: " << server << "\n";
/// }
///
/// wasSet = flags->GetUnsignedInteger("-p", port);
/// if (wasSet) {
/// std::cout << "port override: " << port << "\n";
/// }
///
/// std::cout << "connecting to " << server << ":" << port << "\n";
/// for (size_t i = 0; i < flags.NumArgs(); i++) {
/// std::cout << "\tExecuting command " << flags.Arg(i) << "\n";
/// }
/// return 0;
/// }
///
class Flags {
public:
/// ParseStatus describes the result of parsing command-line
/// arguments.
enum class ParseStatus : uint8_t {
/// An unknown parsing error occurred. This is a bug,
/// and users should never see this.
Unknown = 0,
/// Parsing succeeded.
OK = 1,
/// This is an internal status marking the end of
/// command line flags.
EndOfFlags = 2,
/// The command line flag provided isn't registered.
NotRegistered = 3,
/// Not enough arguments were provided to a flag that
/// takes an argument. For example, if "-p" expects
/// a number, and the program was called with just
/// `./program -p`, this error will be reported.
NotEnoughArgs = 4,
};
/// ParseStatusToString returns a string message describing the
/// result of parsing command line args.
static std::string ParseStatusToString(ParseStatus status);
/// Create a new flags parser for the named program.
///
/// \param fName The program name,
Flags(std::string fName);
/// Create a new flags parser for the named program.
///
/// \param fName The program name.
/// \param fDescription A short description of the program.
Flags(std::string fName, std::string fDescription);
~Flags();
/// Register a new command line flag.
///
/// \param fName The name of the flag, including a leading dash.
/// \param fType The type of the argument to parse.
/// \param fDescription A description of the flag.
/// \return True if the flag was registered, false if the flag could
/// not be registered (e.g. a duplicate flag was registered).
bool Register(std::string fName,
FlagType fType,
std::string fDescription);
/// Register a new boolean command line flag with a default value.
///
/// \note For booleans, it only makes sense to pass a false default
/// value, as there is no way to set a boolean flag to false.
/// This form is provided for compatibility with the other
/// variants on this method.
///
/// \param fName The name of the flag, including a leading dash.
/// \param defaultValue The default value for the flag.
/// \param fDescription A short description of the flag.
/// \return True if the flag was registered, false if the flag could
/// not be registered (e.g. a duplicate flag was registered).
bool Register(std::string fName,
bool defaultValue,
std::string fDescription);
/// Register a new integer command line flag with a default value.
///
/// \param fName The name of the flag, including a leading dash.
/// \param defaultValue The default value for the flag.
/// \param fDescription A short description of the flag.
/// \return True if the flag was registered, false if the flag could
/// not be registered (e.g. a duplicate flag was registered).
bool Register(std::string fName,
int defaultValue,
std::string fDescription);
/// Register a new unsigned integer command line flag with a default
/// value.
///
/// \param fName The name of the flag, including a leading dash.
/// \param defaultValue The default value for the flag.
/// \param fDescription A short description of the flag.
/// \return True if the flag was registered, false if the flag could
/// not be registered (e.g. a duplicate flag was registered).
bool Register(std::string fName,
unsigned int defaultValue,
std::string fDescription);
/// Register a new size_t command line flag with a default value.
///
/// \param fName The name of the flag, including a leading dash.
/// \param defaultValue The default value for the flag.
/// \param fDescription A short description of the flag.
/// \return True if the flag was registered, false if the flag could
/// not be registered (e.g. a duplicate flag was registered).
bool Register(std::string fName,
size_t defaultValue,
std::string fDescription);
/// Register a new string command line flag with a default value.
///
/// \param fName The name of the flag, including a leading dash.
/// \param defaultValue The default value for the flag.
/// \param fDescription A short description of the flag.
/// \return True if the flag was registered, false if the flag could
/// not be registered (e.g. a duplicate flag was registered).
bool Register(std::string fName,
std::string defaultValue,
std::string fDescription);
/// Return the number of registered flags.
size_t Size();
/// Lookup a flag.
///
/// \param fName The flag name.
/// \return A pointer to flag if registered, or nullptr if the flag
/// wasn't registered.
Flag *Lookup(std::string fName);
/// ValueOf returns the value of the flag in the
bool ValueOf(std::string fName, FlagValue &value);
ParseStatus Parse(int argc, char **argv);
/// Process a list of arguments into flags.
///
/// \param argc The number of arguments.
/// \param argv The arguments passed to the program.
/// \param skipFirst Flags expects to receive arguments directly
/// from those passed to `main`, and defaults to skipping
/// the first argument. Set to false if this is not the
/// case.
/// \return
ParseStatus Parse(int argc, char **argv, bool skipFirst=true);
/// Print a usage message to the output stream.
void Usage(std::ostream &os, int exitCode);
/// Return the number of arguments processed. These are any
/// remaining elements in argv that are not flags.
size_t NumArgs();
/// Return the arguments as a vector.
std::vector<std::string> Args();
std::string Arg(int index);
/// Return a particular argument.
///
/// \param index The argument number to extract.
/// \return The argument at index i. If the index is greater than
/// the number of arguments present, an out_of_range
/// exception is thrown.
std::string Arg(size_t index);
/// Retrieve the state of a boolean flag.
///
/// \param fName The flag to look up.
/// \param flagValue The value to store.
/// \return True if the value was set, or false if the value isn't
/// a boolean or if it wasn't set.
bool GetBool(std::string fName, bool &flagValue);
bool GetUnsignedInteger(std::string fName, unsigned int &flagValue);
bool GetInteger(std::string fName, int &flagValue);
bool GetString(std::string fName, std::string &flagValue);
bool GetSizeT(std::string fName, std::size_t &flagValue);
/// Retrieve the value of an unsigned integer flag.
///
/// \param fName The flag to look up.
/// \param flagValue The value to store.
/// \return True if the value was set, or false if the value isn't
/// an unsigned integer or if it wasn't set.
bool GetUnsignedInteger(std::string fName, unsigned int &flagValue);
/// Retrieve the value of an integer flag.
///
/// \param fName The flag to look up.
/// \param flagValue The value to store.
/// \return True if the value was set, or false if the value isn't
/// an integer or if it wasn't set.
bool GetInteger(std::string fName, int &flagValue);
/// Retrieve the value of a string flag.
///
/// \param fName The flag to look up.
/// \param flagValue The value to store.
/// \return True if the value was set, or false if the value isn't
/// a string or if it wasn't set.
bool GetString(std::string fName, std::string &flagValue);
/// Retrieve the value of a size_t flag.
///
/// \param fName The flag to look up.
/// \param flagValue The value to store.
/// \return True if the value was set, or false if the value isn't
/// a size_t or if it wasn't set.
bool GetSizeT(std::string fName, std::size_t &flagValue);
private:
ParseStatus parseArg(int argc, char **argv, int &index);

13
LICENSE Normal file
View File

@ -0,0 +1,13 @@
Copyright 2023 K. Isom <kyle@imap.cc>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@ -1,39 +1,59 @@
TARGET := klib.a
TESTS := tlv_test dictionary_test
HEADERS := $(wildcard *.h)
SOURCES := $(wildcard *.cc)
OBJS := Arena.o Dictionary.o TLV.o
HEADERS := scsl.h \
Arena.h \
Buffer.h \
Commander.h \
Dictionary.h \
Exceptions.h \
Flag.h \
StringUtil.h \
Test.h \
TLV.h \
WinHelpers.h
SOURCES := Arena.cc \
Buffer.cc \
Commander.cc \
Dictionary.cc \
Exceptions.cc \
Flag.cc \
StringUtil.cc \
Test.cc \
TLV.cc \
WinHelpers.cc
BUILD := DEBUG
OBJS := $(patsubst %.cc,%.o,$(SOURCES))
LIBS := libscsl.a
TARGETS := $(LIBS) phonebook
TESTS := bufferTest dictionaryTest flagTest tlvTest
CXX := clang++
CXXFLAGS := -g -std=c++14 -Werror -Wall -DSCSL_DESKTOP_BUILD
CXXFLAGS := -std=c++14 -Werror -Wall -Wextra -DSCSL_DESKTOP_BUILD \
-DSCSL_BUILD_TYPE=${BUILD}
ifeq ($(BUILD),DEBUG)
CXXFLAGS += -g -fsanitize=address
else
CXXFLAGS += -O2
endif
.PHONY: all
all: $(TARGET) $(TESTS) tags run-tests
all: $(TARGETS) $(TESTS) tags run-tests
tags: $(HEADERS) $(SOURCES)
ctags $(HEADERS) $(SOURCES)
$(TARGET): $(OBJS)
libscsl.a: $(OBJS)
$(AR) rcs $@ $(OBJS)
tlv_test: tlvTest.o $(TARGET)
$(CXX) -o $@ $(CXXFLAGS) tlvTest.o $(TARGET)
dictionary_test: dictionaryTest.o $(TARGET)
$(CXX) -o $@ $(CXXFLAGS) dictionaryTest.o $(TARGET)
.PHONY: print-%
print-%: ; @echo '$(subst ','\'',$*=$($*))'
klib.a: $(OBJS)
%.o: %.cc
$(CXX) -o $@ -c $(CXXFLAGS) $<
.PHONY: clean
clean:
# build outputs
rm -f $(TARGET) $(TESTS) *.o
rm -f $(TARGETS) $(TESTS) *.o
# test miscellaneous
rm -f core core.* tags arena_test.bin
@ -45,3 +65,22 @@ run-tests: $(TESTS)
echo "./$${testbin}" ; \
./$${testbin}; \
done
phonebook: phonebook.o $(LIBS)
$(CXX) -o $@ $(CXXFLAGS) $@.o $(LIBS)
bufferTest: bufferTest.o $(LIBS)
$(CXX) -o $@ $(CXXFLAGS) $@.o $(LIBS)
dictionaryTest: dictionaryTest.o $(LIBS)
$(CXX) -o $@ $(CXXFLAGS) $@.o $(LIBS)
flagTest: flagTest.o $(LIBS)
$(CXX) -o $@ $(CXXFLAGS) $@.o $(LIBS)
tlvTest: tlvTest.o $(LIBS)
$(CXX) -o $@ $(CXXFLAGS) $@.o $(LIBS)
%.o: %.cc
$(CXX) -o $@ -c $(CXXFLAGS) $<

27
README.rst Normal file
View File

@ -0,0 +1,27 @@
scsl : The Shimmering Clarity Standard C++ Library
==================================================
scsl is a collection of software I found myself needing to use repeatedly.
Full `Doxygen documentation`_ is available.
.. _Doxygen documentation: https://docs.shimmering-clarity.net/scsl/
License
-------
Copyright 2023 K. Isom <kyle@imap.cc>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@ -1,3 +1,26 @@
///
/// \file StringUtil.cc
/// \author K. Isom <kyle@imap.cc>
/// \date 2023-10-12
/// \brief Utilities for working with strings.
///
/// Copyright 2023 K. Isom <kyle@imap.cc>
///
/// Permission to use, copy, modify, and/or distribute this software for
/// any purpose with or without fee is hereby granted, provided that
/// the above copyright notice and this permission notice appear in all /// copies.
///
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
/// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
/// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
/// OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
/// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
/// PERFORMANCE OF THIS SOFTWARE.
///
#include <cassert>
#include <iostream>
#include <sstream>
@ -12,22 +35,21 @@ namespace U {
namespace S {
/*
std::vector<std::string>
SplitKeyValuePair(std::string line, std::string delimiter)
{
std::string key;
std::string val;
auto pair = SplitN(line, delimiter, 2);
auto pos = line.find(delimiter);
if (pos == std::string::npos) {
key = line;
val = "";
} else {
key = line.substr(0, pos);
val = line.substr(pos + 1, line.size() - pos - 2);
if (pair.size() == 0) {
return {"", ""};
} else if (pair.size() == 1) {
return {pair[0], ""};
}
assert(pair.size() == 2);
auto key = pair[0];
auto val = pair[1];
TrimWhitespace(key);
TrimWhitespace(val);
return {key, val};
@ -37,23 +59,11 @@ SplitKeyValuePair(std::string line, std::string delimiter)
std::vector<std::string>
SplitKeyValuePair(std::string line, char delimiter)
{
std::string key;
std::string val;
std::string sDelim;
auto pos = line.find(delimiter);
if (pos == std::string::npos) {
key = line;
val = "";
} else {
key = line.substr(0, pos);
val = line.substr(pos + 1, line.size() - pos - 2);
}
TrimWhitespace(key);
TrimWhitespace(val);
return {key, val};
sDelim.push_back(delimiter);
return SplitKeyValuePair(line, sDelim);
}
*/
void
@ -132,6 +142,66 @@ SplitN(std::string s, std::string delim, size_t maxCount)
}
std::vector<std::string>
WrapText(std::string line, size_t lineLength)
{
std::vector<std::string> wrapped;
auto parts = SplitN(line, " ", 0);
for (auto &part: parts) {
TrimWhitespace(part);
}
std::string wLine;
for (auto word: parts) {
if (word.size() == 0) {
continue;
}
if ((wLine.size() + word.size() + 1) > lineLength) {
wrapped.push_back(wLine);
wLine.clear();
}
if (wLine.size() > 0) {
wLine += " ";
}
wLine += word;
}
if (wLine.size() > 0) {
wrapped.push_back(wLine);
}
return wrapped;
}
void
WriteTabIndented(std::ostream &os, std::vector<std::string> lines,
int tabStop, bool indentFirst)
{
std::string indent(tabStop, '\t');
for (size_t i = 0; i < lines.size(); i++) {
if (i > 0 || indentFirst) {
os << indent;
}
os << lines[i] << "\n";
}
}
void
WriteTabIndented(std::ostream &os, std::string line, size_t maxLength,
int tabStop, bool indentFirst)
{
auto lines = WrapText(line, maxLength);
WriteTabIndented(os, lines, tabStop, indentFirst);
}
std::ostream &
VectorToString(std::ostream &os, const std::vector<std::string> &svec)
{
@ -141,7 +211,7 @@ VectorToString(std::ostream &os, const std::vector<std::string> &svec)
os << "{";
for (size_t i = 0; i < svec.size(); i++) {
if (i > 0) os << ", ";
if (i > 0) { os << ", "; }
os << svec[i];
}
@ -153,7 +223,7 @@ VectorToString(std::ostream &os, const std::vector<std::string> &svec)
std::string
VectorToString(const std::vector<std::string> &svec)
{
std::stringstream ss;
std::stringstream ss;
VectorToString(ss, svec);
return ss.str();

View File

@ -1,10 +1,9 @@
///
/// \file StringUtil.h
/// \author kyle (kyle@midgard)
/// \created 2023-10-14
/// \brief StringUtil contains string utilities.
/// \author K. Isom <kyle@imap.cc>
/// \date 2023-10-14
/// \brief Utilities for working with strings.
///
/// \section COPYRIGHT
/// Copyright 2023 K. Isom <kyle@imap.cc>
///
/// Permission to use, copy, modify, and/or distribute this software for
@ -85,17 +84,44 @@ std::vector<std::string> SplitKeyValuePair(std::string line, char delimiter);
/// Split a string into parts based on the delimiter.
///
/// \param s The string that should be split.
/// \param delimiter The string that delimits the parts of the string.
/// \param maxCount The maximum number of parts to split. If 0, there is no limit
/// to the number of parts.
/// \param maxCount The maximum number of parts to split. If 0, there is no
/// limit to the number of parts.
/// \return A vector containing all the parts of the string.
std::vector<std::string> SplitN(std::string, std::string delimiter, size_t maxCount=0);
//std::vector<std::string> SplitN(std::string, char delimiter, size_t size_t maxCount=0);
/// WrapText is a very simple wrapping function that breaks the line into
/// lines of at most lineLength characters. It does this by breaking the
/// line into individual words (splitting on whitespace).
std::vector<std::string> WrapText(std::string line, size_t lineLength);
/// Write out a vector of lines indented with tabs.
///
/// \param os The output stream to write to.
/// \param lines The lines of text to write.
/// \param tabStop The number of tabs to indent.
/// \param indentFirst Whether the first line should be indented.
void WriteTabIndented(std::ostream &os, std::vector<std::string> lines,
int tabStop, bool indentFirst);
/// Wrap a line, then output it to a stream.
///
/// \param os The output stream to write to.
/// \param line The line to wrap and output.
/// \param maxLength The maximum length of each section of text.
/// \param tabStop The number of tabs to indent.
/// \param indentFirst Whether the first line should be indented.
void WriteTabIndented(std::ostream &os, std::string line, size_t maxLength,
int tabStop, bool indentFirst);
/// Return a string represention of a string vector in the form [size]{"foo", "bar", ...}.
/// Write a string vector to the output stream in the same format as
/// VectorToString.
std::ostream &VectorToString(std::ostream &os, const std::vector<std::string> &svec);
/// Return a string represention of a string vector in the form
/// [size]{"foo", "bar", ...}.
std::string VectorToString(const std::vector<std::string> &svec);

36
TLV.cc
View File

@ -1,9 +1,35 @@
///
/// \file TLV.cc
/// \author K. Isom <kyle@imap.cc>
/// \date 2023-10-06
/// \brief Tag-Length-Value records built on Arena.
///
/// Copyright 2023 K. Isom <kyle@imap.cc>
///
/// Permission to use, copy, modify, and/or distribute this software for
/// any purpose with or without fee is hereby granted, provided that
/// the above copyright notice and this permission notice appear in all /// copies.
///
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
/// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
/// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
/// OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
/// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
/// PERFORMANCE OF THIS SOFTWARE.
///
#include <cassert>
#include <cstring>
#include "TLV.h"
using namespace scsl;
/// REC_SIZE calculates the total length of a TLV record, including the
/// two byte header.
#define REC_SIZE(x) ((std::size_t)x.Len + 2)
@ -90,6 +116,7 @@ FindTag(Arena &arena, uint8_t *cursor, Record &rec)
{
cursor = LocateTag(arena, cursor, rec);
if (rec.Tag != TAG_EMPTY) {
std::cout << "skipping record\n";
cursor = SkipRecord(rec, cursor);
}
@ -102,13 +129,22 @@ LocateTag(Arena &arena, uint8_t *cursor, Record &rec)
{
uint8_t tag, len;
if (!arena.CursorInArena(cursor)) {
cursor = nullptr;
}
if (cursor == nullptr) {
std::cout << "move cursor to arena start\n";
cursor = arena.Start();
}
while ((tag = cursor[0]) != rec.Tag) {
assert(arena.CursorInArena(cursor));
std::cout << "cursor is in arena\n";
len = cursor[1];
std::cout << "record length" << len << "\n";
if (!spaceAvailable(arena, cursor, len)) {
std::cout << "no space available\n";
return nullptr;
}
cursor += len;

3
TLV.h
View File

@ -14,8 +14,8 @@
#ifndef KIMODEM_TLV_H
#define KIMODEM_TLV_H
#include <cstdint>
#include <array>
#include <cstdint>
#include "Arena.h"
@ -23,6 +23,7 @@
namespace scsl {
namespace TLV {
#ifndef TLV_MAX_LEN
static constexpr size_t TLV_MAX_LEN = 253;
#endif

25
Test.cc
View File

@ -1,11 +1,28 @@
//
// Created by kyle on 2023-10-09.
//
///
/// \file Test.cc
/// \author K. Isom <kyle@imap.cc>
/// \date 2023-10-09
/// \brief Tooling to assist in building test programs..
///
/// Copyright 2023 K. Isom <kyle@imap.cc>
///
/// Permission to use, copy, modify, and/or distribute this software for
/// any purpose with or without fee is hereby granted, provided that
/// the above copyright notice and this permission notice appear in all /// copies.
///
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
/// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
/// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
/// OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
/// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
/// PERFORMANCE OF THIS SOFTWARE.
///
#include "Exceptions.h"
#include "Test.h"
#include <cassert>
#include <iostream>
#include <sstream>

19
Test.h
View File

@ -2,11 +2,28 @@
/// \file Test.h
/// \author K. Isom <kyle@imap.cc>
/// \date 2023-10-09
/// \brief Test.h implements basic testing tools.
/// \brief Tooling to assist in building test programs..
///
/// Copyright 2023 K. Isom <kyle@imap.cc>
///
/// Permission to use, copy, modify, and/or distribute this software for
/// any purpose with or without fee is hereby granted, provided that
/// the above copyright notice and this permission notice appear in all /// copies.
///
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
/// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
/// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
/// OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
/// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
/// PERFORMANCE OF THIS SOFTWARE.
///
#ifndef SCSL_TEST_H
#define SCSL_TEST_H
#include <string>

View File

@ -1,142 +0,0 @@
//
// Created by kyle on 2023-10-10.
//
#if defined(__WIN64__) || defined(__WIN32__) || defined(WIN32)
#include "WinHelpers.h"
namespace scsl {
namespace Windows {
int
DisplayWinError(LPTSTR lpszFunction, HANDLE handle)
{
// Retrieve the system error message for the last-error code
DWORD dw = GetLastError();
#ifndef NDEBUG
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL);
// Display the error message and exit the process
lpDisplayBuf = (LPVOID) LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR) lpMsgBuf) +
lstrlen((LPCTSTR) lpszFunction) +
40) * sizeof(TCHAR));
StringCchPrintf((LPTSTR) lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
lpszFunction, dw, lpMsgBuf);
MessageBox(NULL, (LPCTSTR) lpDisplayBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
#endif
if ((handle != NULL) && (handle != INVALID_HANDLE_VALUE)) {
CloseHandle(handle);
}
return dw;
}
BOOL SetPrivilege(
HANDLE hToken, // access token handle
LPCTSTR lpszPrivilege, // name of privilege to enable/disable
BOOL bEnablePrivilege // to enable or disable privilege
)
{
TOKEN_PRIVILEGES tp;
LUID luid;
if (!LookupPrivilegeValue(
NULL, // lookup privilege on local system
lpszPrivilege, // privilege to lookup
&luid)) // receives LUID of privilege
{
printf("LookupPrivilegeValue error: %u\n", GetLastError());
return FALSE;
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
if (bEnablePrivilege)
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
else
tp.Privileges[0].Attributes = 0;
// Enable the privilege or disable all privileges.
if (!AdjustTokenPrivileges(
hToken,
FALSE,
&tp,
sizeof(TOKEN_PRIVILEGES),
(PTOKEN_PRIVILEGES) NULL,
(PDWORD) NULL)) {
printf("AdjustTokenPrivileges error: %u\n", GetLastError());
return FALSE;
}
if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) {
printf("The token does not have the specified privilege. \n");
return FALSE;
}
return TRUE;
}
HANDLE
CreateFileWindows(const char *path)
{
HANDLE fHandle;
return CreateFileA(
(LPSTR) path,
(GENERIC_READ | GENERIC_WRITE),
(FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE),
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
}
int
CreateFixedSizeFile(const char *path, size_t size)
{
_LARGE_INTEGER fileSize;
fileSize.QuadPart = size;
HANDLE fHandle = CreateFileWindows(path);
if (SetFilePointerEx(fHandle, fileSize, nullptr, FILE_BEGIN) != TRUE) {
return DisplayWinError("SetFilePointerEx", fHandle);
}
if (SetEndOfFile(fHandle) != TRUE) {
return DisplayWinError("SetEndOfFile", fHandle);
}
CloseHandle(fHandle);
return 0;
}
} // namespace Windows
} // namespace scsl
#endif

View File

@ -1,39 +0,0 @@
//
// Created by kyle on 2023-10-10.
//
#ifndef SCSL_WINHELPERS_H
#define SCSL_WINHELPERS_H
#if defined(__WIN64__) || defined(__WIN32__) || defined(WIN32)
#include <Windows.h>
#include <winbase.h>
#include <fileapi.h>
#include <strsafe.h>
namespace scsl {
namespace Windows {
int DisplayWinError(LPTSTR lpszFunction, HANDLE handle);
BOOL SetPrivilege(
HANDLE hToken, // access token handle
LPCTSTR lpszPrivilege, // name of privilege to enable/disable
BOOL bEnablePrivilege // to enable or disable privilege
);
HANDLE CreateFileWindows(const char *path);
int CreateFixedSizeFile(const char *path, size_t size);
} // namespace Windows
} // namespace scsl
#endif // Windows-only guards.
#endif //SCSL_WINHELPERS_H

View File

@ -4,7 +4,7 @@ find_package(Doxygen)
if (${DOXYGEN_FOUND})
set(DOXYGEN_GENERATE_MAN YES)
set(DOXYGEN_GENERATE_LATEX YES)
#set(DOXYGEN_EXTRACT_ALL YES)
set(DOXYGEN_EXTRACT_ALL YES)
message(STATUS "Doxygen found, building docs.")
doxygen_add_docs(scsl_docs

View File

@ -7,6 +7,9 @@ set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR})
set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})
set(CPACK_PACKAGE_FILE_NAME
${PROJECT_NAME}-${PROJECT_VERSION}-${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_ARCH}${CMAKE_HOST_SYSTEM_PROCESSOR})
# Debian settings
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Shimmering Clarity")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "The Shimmering Clarity standard C++ library")
@ -35,4 +38,3 @@ set(CPACK_SOURCE_IGNORE_FILES
include (CPack)
add_custom_target(package_docs DEPENDS SCSL_docs package package_source)

View File

@ -20,18 +20,19 @@ main(int argc, char *argv[])
int testInteger = 0;
std::string testString;
auto flags = new Flags("flag_test", "this is a test of the flag functionality.");
auto flags = new Flags("flag_test", "this is a test of the flag functionality. This line is particularly long to make sure the wrapping works.");
flags->Register("-b", FlagType::Boolean, "test boolean");
flags->Register("-s", FlagType::String, "test string");
flags->Register("-u", (unsigned int)42, "test unsigned integer");
flags->Register("-u", (unsigned int)42, "test unsigned integer with a long description line. This should trigger multiline text-wrapping.");
flags->Register("-i", -42, "test integer");
flags->Register("-size", FlagType::SizeT, "test size_t");
TestAssert(flags->Size() == 5, "flags weren't registered");
auto status = flags->Parse(argc, argv);
if (status != ParseStatus::OK) {
std::cerr << "failed to parse flags: " << ParseStatusToString(status) << "\n";
if (status != Flags::ParseStatus::OK) {
std::cerr << "failed to parse flags: "
<< Flags::ParseStatusToString(status) << "\n";
exit(1);
}
@ -55,5 +56,6 @@ main(int argc, char *argv[])
std::cout << "(string) test flag was set: " << wasSet << "\n";
std::cout << "(string) test flag value: " << testString << "\n";
delete flags;
return 0;
}

View File

@ -1,6 +1,24 @@
//
// Created by kyle on 2023-10-10.
//
///
/// \file phonebook.cc
/// \author K. Isom <kyle@imap.cc>
/// \date 2023-10-10
/// \brief Commandline tools for interacting with dictionary data file.
///
/// Copyright 2023 K. Isom <kyle@imap.cc>
///
/// Permission to use, copy, modify, and/or distribute this software for
/// any purpose with or without fee is hereby granted, provided that
/// the above copyright notice and this permission notice appear in all /// copies.
///
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
/// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
/// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
/// OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
/// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
/// PERFORMANCE OF THIS SOFTWARE.
///
#include <iostream>
#include <string>
@ -9,6 +27,7 @@ using namespace std;
#include "Arena.h"
#include "Commander.h"
#include "Dictionary.h"
#include "Flag.h"
using namespace scsl;
static const char *defaultPhonebook = "pb.dat";

22
scsl.h
View File

@ -1,10 +1,13 @@
///
/// \file scsl.h
/// \author kyle
/// \created 2023-10-10
/// \author kyle (kyle@imap.cc)
/// \date 2023-10-10
/// \brief scsl is my collection of C++ data structures and code.
///
/// scsl.h is a utility header that includes all of SCSL.
///
/// \section COPYRIGHT
///
/// Copyright 2023 K. Isom <kyle@imap.cc>
///
/// Permission to use, copy, modify, and/or distribute this software for
@ -25,12 +28,15 @@
#define SCSL_SCSL_H
#include <klib/Arena.h>
#include <klib/Buffer.h>
#include <klib/Dictionary.h>
#include <klib/Exceptions.h>
#include <klib/TLV.h>
#include <klib/Test.h>
#include <scsl/Arena.h>
#include <scsl/Buffer.h>
#include <scsl/Commander.h>
#include <scsl/Dictionary.h>
#include <scsl/Exceptions.h>
#include <scsl/Flag.h>
#include <scsl/StringUtil.h>
#include <scsl/TLV.h>
#include <scsl/Test.h>
/// scsl is the top-level namespace containing all the code in this library.

View File

@ -1,10 +1,9 @@
///
/// \file stringutil_test.cc
/// \author kyle
/// \created 10/14/23
/// \date 10/14/23
/// \brief Ensure the stringutil functions work.
///
/// \section COPYRIGHT
/// Copyright 2023 K. Isom <kyle@imap.cc>
///
/// Permission to use, copy, modify, and/or distribute this software for
@ -27,24 +26,26 @@
#include "StringUtil.h"
#include "Test.h"
using namespace scsl;
static void
TestTrimming(std::string line, std::string lExpected, std::string rExpected, std::string expected)
{
std::string result;
std::string message;
std::string result;
std::string message;
result = U::S::TrimLeadingWhitespaceDup(line);
result = U::S::TrimLeadingWhitespaceDup(line);
message = "TrimLeadingDup(\"" + line + "\"): '" + result + "'";
TestAssert(result == lExpected, message);
result = U::S::TrimTrailingWhitespaceDup(line);
result = U::S::TrimTrailingWhitespaceDup(line);
message = "TrimTrailingDup(\"" + line + "\"): '" + result + "'";
TestAssert(result == rExpected, message);
result = U::S::TrimWhitespaceDup(line);
result = U::S::TrimWhitespaceDup(line);
message = "TrimDup(\"" + line + "\"): '" + result + "'";
TestAssert(result == expected, message);
@ -68,7 +69,7 @@ TestTrimming(std::string line, std::string lExpected, std::string rExpected, std
static std::string
vec2string(std::vector<std::string> v)
{
std::stringstream ss;
std::stringstream ss;
ss << "(";
ss << v.size();
@ -76,7 +77,7 @@ vec2string(std::vector<std::string> v)
ss << "{";
for (size_t i = 0; i < v.size(); i++) {
if (i > 0) ss << ", ";
if (i > 0) { ss << ", "; }
ss << v[i];
}
@ -100,6 +101,36 @@ TestSplit(std::string line, std::string delim, size_t maxCount, std::vector<std:
}
static void
TestWrapping()
{
std::string testLine = "A much longer line, something that can be tested with WrapText. ";
testLine += "Does it handle puncuation? I hope so.";
std::vector<std::string> expected{
"A much longer",
"line, something",
"that can be",
"tested with",
"WrapText. Does",
"it handle",
"puncuation? I",
"hope so.",
};
auto wrapped = U::S::WrapText(testLine, 16);
TestAssert(wrapped.size() == expected.size(),
U::S::VectorToString(wrapped) + " != " + U::S::VectorToString(expected));
for (size_t i = 0; i < wrapped.size(); i++) {
TestAssert(wrapped[i] == expected[i],
"\"" + wrapped[i] + "\" != \"" + expected[i] + "\"");
}
U::S::WriteTabIndented(std::cout, wrapped, 4, true);
}
int
main()
{
@ -116,4 +147,6 @@ main()
std::vector<std::string>{"abc:def:ghij:klm"});
TestSplit("abc::def:ghi", ":", 0,
std::vector<std::string>{"abc", "", "def", "ghi"});
TestWrapping();
}

View File

@ -48,13 +48,16 @@ tlvTestSuite(Arena &backend)
assert(cursor != nullptr);
assert(cmpRecord(rec3, rec4));
std::cout << "\tSetRecord 1\n";
TLV::SetRecord(rec4, 3, TEST_STRLEN3, TEST_STR3);
assert(TLV::WriteToMemory(backend, nullptr, rec4));
std::cout << "FindTag 3\n";
rec4.Tag = 2;
cursor = TLV::FindTag(backend, nullptr, rec4);
assert(cursor != nullptr);
std::cout << "DeleteRecord\n";
TLV::DeleteRecord(backend, cursor);
assert(cursor[0] == 3);
}