Finish phonebook command and add to install targets.
This commit is contained in:
parent
30c586d37d
commit
e3c95964b3
10
Arena.cc
10
Arena.cc
|
@ -123,25 +123,27 @@ Arena::Open(const char *path)
|
|||
|
||||
|
||||
int
|
||||
Arena::Create(const char *path, size_t fileSize, mode_t mode)
|
||||
Arena::Create(const char *path, size_t fileSize)
|
||||
{
|
||||
FILE *fHandle = nullptr;
|
||||
int newFileDes = 0;
|
||||
|
||||
if (this->size > 0) {
|
||||
this->Destroy();
|
||||
}
|
||||
|
||||
newFileDes = open(path, O_WRONLY | O_CREAT | O_TRUNC, mode);
|
||||
if (newFileDes == -1) {
|
||||
|
||||
fHandle = fopen(path, "w");
|
||||
if (fHandle == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
newFileDes = fileno(fHandle);
|
||||
|
||||
if (ftruncate(newFileDes, fileSize) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
close(newFileDes);
|
||||
|
||||
return this->Open(path);
|
||||
}
|
||||
#elif defined(__WIN64__) || defined(__WIN32__) || defined(WIN32)
|
||||
|
|
16
Arena.h
16
Arena.h
|
@ -35,15 +35,6 @@
|
|||
|
||||
namespace klib {
|
||||
|
||||
/// DefaultFileMode is a sane set of default permissions that can be used for a
|
||||
/// new Arena.
|
||||
#if defined(__linux__)
|
||||
static constexpr mode_t DefaultFileMode = 0644;
|
||||
#elif defined(__WIN64__) || defined(__WIN32__) || defined(WIN32)
|
||||
static constexpr DWORD DefaultFileMode =
|
||||
(FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE);
|
||||
#endif
|
||||
|
||||
|
||||
/// \enum ArenaType
|
||||
///
|
||||
|
@ -126,10 +117,9 @@ public:
|
|||
///
|
||||
/// \param path The path to the file that should be created.
|
||||
/// \param fileSize The size of the file to create.
|
||||
/// \param mode The permissions to load.
|
||||
/// \return Returns 0 on success and -1 on error.
|
||||
#if defined(__linux__)
|
||||
int Create(const char *path, size_t fileSize, mode_t mode);
|
||||
int Create(const char *path, size_t fileSize);
|
||||
#elif defined(__WIN64__) || defined(__WIN32__) || defined(WIN32)
|
||||
|
||||
int Create(const char *path, size_t fileSize, DWORD mode);
|
||||
|
@ -144,11 +134,7 @@ public:
|
|||
///
|
||||
/// \param path The path to the file to be loaded.
|
||||
/// \return Returns 0 on success and -1 on error.
|
||||
#if defined(__linux__)
|
||||
int Open(const char *path);
|
||||
#elif defined(__WIN64__) || defined(__WIN32__) || defined(WIN32)
|
||||
int Open(const char *path);
|
||||
#endif
|
||||
|
||||
/// NewCursor returns a pointer to the start of the memory in the arena.
|
||||
///
|
||||
|
|
|
@ -34,9 +34,12 @@ set(SOURCE_FILES
|
|||
Dictionary.cc
|
||||
Exceptions.cpp
|
||||
Test.cc
|
||||
TLV.cc)
|
||||
TLV.cc
|
||||
Commander.cpp
|
||||
Commander.h)
|
||||
|
||||
add_library(klib STATIC ${SOURCE_FILES} ${HEADER_FILES})
|
||||
|
||||
add_executable(phonebook phonebook.cpp)
|
||||
target_link_libraries(phonebook klib)
|
||||
|
||||
|
@ -67,8 +70,9 @@ add_custom_target(cloc
|
|||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
|
||||
|
||||
configure_file(klib.pc.in klib.pc @ONLY)
|
||||
install(TARGETS klib LIBRARY DESTINATION ${PREFIX}/lib)
|
||||
install(FILES ${HEADER_FILES} DESTINATION include/{klib})
|
||||
install(TARGETS klib LIBRARY DESTINATION lib)
|
||||
install(TARGETS phonebook RUNTIME DESTINATION bin)
|
||||
install(FILES ${HEADER_FILES} DESTINATION include/klib)
|
||||
install(FILES klibConfig.cmake DESTINATION share/klib/cmake)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/klib.pc DESTINATION lib/pkgconfig)
|
||||
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html DESTINATION share/doc/klib)
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
///
|
||||
/// \file Commander.cpp
|
||||
/// \author kyle
|
||||
/// \created 10/10/23
|
||||
/// \brief
|
||||
/// \section COPYRIGHT
|
||||
/// \section COPYRIGHT
|
||||
/// 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 DESCRIPTION
|
||||
|
||||
#include <iostream>
|
||||
#include "Commander.h"
|
||||
|
||||
namespace klib {
|
||||
|
||||
|
||||
Subcommand::Status
|
||||
Subcommand::Run(int argc, char **argv)
|
||||
{
|
||||
if (argc < this->args) {
|
||||
std::cerr << "[!] " << this->command << " expects ";
|
||||
std::cerr << this->args << " args, but was given ";
|
||||
std::cerr << argc << " args.\n";
|
||||
return Subcommand::Status::NotEnoughArgs;
|
||||
}
|
||||
if (this->fn(argc, argv)) {
|
||||
return Subcommand::Status::OK;
|
||||
}
|
||||
|
||||
return Subcommand::Status::Failed;
|
||||
}
|
||||
|
||||
Commander::Commander()
|
||||
: cmap()
|
||||
{
|
||||
this->cmap.clear();
|
||||
}
|
||||
|
||||
bool
|
||||
Commander::Register(Subcommand scmd)
|
||||
{
|
||||
if (this->cmap.count(scmd.Name()) > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto *pScmd = new Subcommand(scmd);
|
||||
this->cmap[scmd.Name()] = pScmd;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Subcommand::Status
|
||||
Commander::Run(std::string command, int argc, char **argv)
|
||||
{
|
||||
if (this->cmap.count(command) != 1) {
|
||||
return Subcommand::Status::CommandNotRegistered;
|
||||
}
|
||||
|
||||
auto scmd = this->cmap[command];
|
||||
return scmd->Run(argc, argv);
|
||||
}
|
||||
|
||||
|
||||
} // klib
|
|
@ -0,0 +1,59 @@
|
|||
///
|
||||
/// \file Commander.h
|
||||
/// \author kyle
|
||||
/// \created 2023-10-10
|
||||
/// \brief Subprogram tooling.
|
||||
|
||||
#include <map>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
#ifndef KLIB_COMMANDER_H
|
||||
#define KLIB_COMMANDER_H
|
||||
|
||||
namespace klib {
|
||||
|
||||
|
||||
typedef std::function<bool(int, char **)> CommanderFunc;
|
||||
|
||||
|
||||
class Subcommand {
|
||||
public:
|
||||
enum class Status : uint8_t {
|
||||
OK = 0,
|
||||
NotEnoughArgs = 1,
|
||||
Failed = 2,
|
||||
CommandNotRegistered = 3,
|
||||
};
|
||||
|
||||
Subcommand(std::string name, int argc, CommanderFunc func)
|
||||
: fn(func), args(argc), command(name)
|
||||
{}
|
||||
|
||||
std::string Name() { return this->command; }
|
||||
Status Run(int argc, char **argv);
|
||||
private:
|
||||
CommanderFunc fn;
|
||||
int args;
|
||||
std::string command;
|
||||
};
|
||||
|
||||
/// Commander does this.
|
||||
///
|
||||
/// Longer description...
|
||||
class Commander {
|
||||
public:
|
||||
Commander();
|
||||
|
||||
bool Register(Subcommand scmd);
|
||||
Subcommand::Status Run(std::string command, int argc, char **argv);
|
||||
private:
|
||||
std::map<std::string, Subcommand *> cmap;
|
||||
};
|
||||
|
||||
} // klib
|
||||
|
||||
|
||||
#endif //KLIB_COMMANDER_H
|
|
@ -64,7 +64,7 @@ Dictionary::Set(const char *key, uint8_t klen, const char *val, uint8_t vlen)
|
|||
}
|
||||
|
||||
|
||||
|
||||
/// seek searches the Dictionary for the key.
|
||||
uint8_t *
|
||||
Dictionary::seek(const char *key, uint8_t klen)
|
||||
{
|
||||
|
@ -94,6 +94,21 @@ Dictionary::Contains(const char *key, uint8_t klen)
|
|||
}
|
||||
|
||||
|
||||
bool
|
||||
Dictionary::Delete(const char *key, uint8_t klen)
|
||||
{
|
||||
auto *cursor = this->seek(key, klen);
|
||||
|
||||
if (cursor == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TLV::DeleteRecord(this->arena, cursor);
|
||||
TLV::DeleteRecord(this->arena, cursor);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Dictionary::spaceAvailable(uint8_t klen, uint8_t vlen)
|
||||
{
|
||||
|
@ -123,7 +138,6 @@ operator<<(std::ostream &os, const Dictionary &dictionary)
|
|||
TLV::Record rec;
|
||||
|
||||
TLV::ReadFromMemory(rec, cursor);
|
||||
os << "Dictionary KV pairs" << std::endl;
|
||||
if (rec.Tag == TLV::TAG_EMPTY) {
|
||||
os << "\t(NONE)" << std::endl;
|
||||
return os;
|
||||
|
|
|
@ -91,6 +91,13 @@ public:
|
|||
/// \return True if the key is in the Dictionary, otherwise false.
|
||||
bool Contains(const char *key, uint8_t klen);
|
||||
|
||||
/// Delete removes the key from the Dictionary.
|
||||
///
|
||||
/// \param key The key to look up.
|
||||
/// \param klen The length of the key.
|
||||
/// \return True if the key was removed, otherwise false.
|
||||
bool Delete(const char *key, uint8_t klen);
|
||||
|
||||
|
||||
/// DumpToFile is a wrapper aorund a call to Arena::Write on the
|
||||
/// underlying Arena.
|
||||
|
@ -99,7 +106,7 @@ public:
|
|||
/// \return 0 on success, -1 on failure.
|
||||
int DumpToFile(const char *path);
|
||||
|
||||
/// operator<< writes the key pairs to the output stream.
|
||||
/// operator<< writes the key pairs phonto the output stream.
|
||||
///
|
||||
/// \param os The output stream to write to.
|
||||
/// \param dictionary The dictionary to write out.
|
||||
|
|
|
@ -45,7 +45,7 @@ main(int argc, const char *argv[])
|
|||
std::cout << "TESTPROG: " << argv[0] << std::endl;
|
||||
|
||||
#if defined(__linux__)
|
||||
if (arena.Create(ARENA_FILE, ARENA_SIZE, 0644) == -1) {
|
||||
if (arena.Create(ARENA_FILE, ARENA_SIZE) == -1) {
|
||||
abort();
|
||||
}
|
||||
#else
|
||||
|
|
130
phonebook.cpp
130
phonebook.cpp
|
@ -7,10 +7,96 @@
|
|||
using namespace std;
|
||||
|
||||
#include "Arena.h"
|
||||
#include "Commander.h"
|
||||
#include "Dictionary.h"
|
||||
using namespace klib;
|
||||
|
||||
static const char *defaultPhonebook = "pb.dat";
|
||||
static char *pbFile = (char *)defaultPhonebook;
|
||||
static Arena arena;
|
||||
static Dictionary pb(arena);
|
||||
|
||||
|
||||
static bool
|
||||
listFiles(int argc, char **argv)
|
||||
{
|
||||
(void)argc; (void)argv;
|
||||
cout << "[+] keys in '" << pbFile << "':\n";
|
||||
cout << pb;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
newPhonebook(int argc, char **argv)
|
||||
{
|
||||
(void)argc;
|
||||
|
||||
auto size = std::stoul(string(argv[0]));
|
||||
cout << "[+] create new " << size << "B phonebook '" << pbFile << "'\n";
|
||||
|
||||
return arena.Create(pbFile, size) == 0;
|
||||
}
|
||||
|
||||
static bool
|
||||
delKey(int argc, char **argv)
|
||||
{
|
||||
(void)argc;
|
||||
string key = argv[0];
|
||||
|
||||
cout << "[+] deleting key '" << key << "'\n";
|
||||
return pb.Delete(key.c_str(), key.size());
|
||||
}
|
||||
|
||||
static bool
|
||||
hasKey(int argc, char **argv)
|
||||
{
|
||||
(void)argc;
|
||||
string key = argv[0];
|
||||
|
||||
cout << "[+] looking up '" << key << "': ";
|
||||
if (pb.Contains(key.c_str(), key.size())) {
|
||||
cout << "found\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
cout << "not found\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
getKey(int argc, char **argv)
|
||||
{
|
||||
(void)argc;
|
||||
TLV::Record rec;
|
||||
auto key = string(argv[0]);
|
||||
|
||||
cout << "[+] key '" << key << "' ";
|
||||
if (!pb.Lookup(key.c_str(), key.size(), rec)) {
|
||||
cout << "not found\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
cout << "-> " << rec.Val << "\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
putKey(int argc, char **argv)
|
||||
{
|
||||
(void)argc;
|
||||
auto key = string(argv[0]);
|
||||
auto val = string(argv[1]);
|
||||
|
||||
cout << "[+] setting '" << key << "' -> '" << val << "': ";
|
||||
if (pb.Set(key.c_str(), key.size(), val.c_str(), val.size()) != 0) {
|
||||
cout << "failed\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
cout << "set\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
usage(ostream &os, int exc)
|
||||
|
@ -19,9 +105,10 @@ usage(ostream &os, int exc)
|
|||
os << "\nThe default filename is pb.dat.\n\n";
|
||||
os << "Usage:\n";
|
||||
os << "\tphonebook [-f file] list\n";
|
||||
os << "\tphonebook [-f file] new size\n";
|
||||
os << "\tphonebook [-f file] del key\n";
|
||||
os << "\tphonebook [-f file] has key\n";
|
||||
os << "\tphonebook [-f file] get key value\n";
|
||||
os << "\tphonebook [-f file] get key\n";
|
||||
os << "\tphonebook [-f file] put key value\n";
|
||||
os << "\n";
|
||||
|
||||
|
@ -31,12 +118,9 @@ usage(ostream &os, int exc)
|
|||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
Arena arena;
|
||||
Dictionary pb(arena);
|
||||
char *pbFile = (char *)defaultPhonebook;
|
||||
int optind = 1;
|
||||
|
||||
for (optind; optind < argc; optind++) {
|
||||
for (optind = 1; optind < argc; optind++) {
|
||||
auto arg = string(argv[optind]);
|
||||
if (arg[0] != '-') break;
|
||||
if (arg == "-h") usage(cout, 0);
|
||||
|
@ -53,18 +137,40 @@ main(int argc, char *argv[])
|
|||
usage(cout, 0);
|
||||
}
|
||||
|
||||
auto command = string(argv[optind++]);
|
||||
Commander commander;
|
||||
|
||||
commander.Register(Subcommand("list", 0, listFiles));
|
||||
commander.Register(Subcommand("new", 1, newPhonebook));
|
||||
commander.Register(Subcommand("del", 1, delKey));
|
||||
commander.Register(Subcommand("has", 1, hasKey));
|
||||
commander.Register(Subcommand("get", 1, getKey));
|
||||
commander.Register(Subcommand("put", 2, putKey));
|
||||
|
||||
if (command != "new") {
|
||||
cout << "[+] loading phonebook from " << pbFile << "\n";
|
||||
if (arena.Open(pbFile) != 0) {
|
||||
cerr << "Failed to open " << pbFile << "\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
auto command = string(argv[optind++]);
|
||||
|
||||
if (command == "list") {
|
||||
cout << pb << "\n";
|
||||
} else if (command == "del") {
|
||||
|
||||
}
|
||||
|
||||
auto result = commander.Run(command, argc-optind, argv+optind);
|
||||
switch (result) {
|
||||
case Subcommand::Status::OK:
|
||||
std::cout << "[+] OK\n";
|
||||
break;
|
||||
case Subcommand::Status::NotEnoughArgs:
|
||||
usage(cerr, 1);
|
||||
break;
|
||||
case Subcommand::Status::Failed:
|
||||
cerr << "[!] '"<< command << "' failed\n";
|
||||
break;
|
||||
case Subcommand::Status::CommandNotRegistered:
|
||||
cerr << "[!] '" << command << "' not registered.\n";
|
||||
usage(cerr, 1);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
|
@ -99,7 +99,7 @@ main(int argc, const char *argv[])
|
|||
|
||||
Arena arenaFile;
|
||||
|
||||
if (-1 == arenaFile.Create(ARENA_FILE, ARENA_SIZE, 0644)) {
|
||||
if (-1 == arenaFile.Create(ARENA_FILE, ARENA_SIZE)) {
|
||||
abort();
|
||||
} else if (!runSuite(arenaFile, "arenaFile")) {
|
||||
abort();
|
||||
|
|
Loading…
Reference in New Issue