Skip to content

Commit

Permalink
Added logging feature plus some minor changes (#18)
Browse files Browse the repository at this point in the history
* ✨ Added colors support for windows

Also add a new error if the file doesn't exist

* 🔨 Added windows flags

* ✨ Added new error code for file not found

* ✏️ Typo

* ✏️ Add semicolon

* ✨ Added logging

During certain events, it will now log to a file in ~/.hexdump/logs/ instead of stdout
  • Loading branch information
KeithBrown39423 committed Jul 23, 2023
1 parent dc559b9 commit 5a130cf
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 34 deletions.
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
CXX := g++

SRCDIR := src
BINDIR := bin

SRCEXT := cpp
SOURCES := $(shell find $(SRCDIR) -type f -name *.$(SRCEXT))
Expand All @@ -8,7 +10,7 @@ INC := -I lib -I include

build:
mkdir -p bin
$(CXX) $(CXXFLAGS) $(INC) $(SOURCES) -o bin/hexdump
$(CXX) $(CXXFLAGS) $(INC) $(SOURCES) -o $(BINDIR)/hexdump

.PHONY: clean
clean:
Expand Down
1 change: 1 addition & 0 deletions include/hexdump_errno.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@
#define HEX_EIO 2 // I/O error
#define HEX_EFBIG 3 // File too big
#define HEX_EFEMPTY 4 // File is empty
#define HEX_ENOENT 5 // File does not exist

#endif
152 changes: 119 additions & 33 deletions src/hexdump.cpp
Original file line number Diff line number Diff line change
@@ -1,40 +1,66 @@
#include <hexdump_errno.h>

#include <ctime>
#include <cxxopts.hpp>
#include <filesystem>
#include <iterator>
#include <fstream>
#include <iostream>
#include <iterator>
#include <string>
#include <vector>

// Check windows
#ifdef _WIN32
#include <windows.h>
#endif

using namespace cxxopts;

using std::string;
namespace fs = std::filesystem;

const string hexdump_version = "v1.1.0";
const unsigned int max_file_size = 0xFFFFFFFF;
const size_t max_file_size = 0xFFFFFFFF;
const size_t max_log_count = 128;

const string error_header = "\x1b[1;38;2;255;0;0mError: \x1b[0m";
string binary_name;

string error_header = "\x1b[1;38;2;255;0;0mError: \x1b[0m";

string ansi_reset = "\x1b[0m";
string offset_color = "\x1b[38;2;0;144;255m";
string ascii_color = "\x1b[38;2;0;144;48m";

bool output_color = true;
enum log_level {
LOG_INFO,
LOG_WARN,
LOG_ERROR
};

const string enum_vals[] = {
"INFO",
"WARN",
"ERROR"
};

ParseResult initialize_options(int argc, char** argv);
string int_to_hex(int value, int width);
void outputHexLine(std::ostream& output, std::vector<unsigned char> buffer, size_t offset, size_t size, int ascii);
void log(string message, log_level level = LOG_INFO, bool output = false);

int main(int argc, char** argv) {
ParseResult result = initialize_options(argc, argv);
const string filename = result["file"].as<string>();

if (!fs::exists(filename)) {
std::cerr << error_header << "File '" + filename + "' does not exist";
return HEX_ENOENT;
}

std::ifstream input_stream(filename, std::ios::binary | std::ios::in);

if (!input_stream.is_open()) {
std::cerr << error_header << "Could not open file '" << filename << "'" << std::endl;
std::cerr << error_header << "Failed to open file '" << filename << "'" << std::endl;
return HEX_EIO;
}

Expand All @@ -43,8 +69,8 @@ int main(int argc, char** argv) {
input_stream.seekg(0, input_stream.beg);

if (file_size > max_file_size) {
std::cerr << error_header << "File is too big" << std::endl;
return HEX_EFBIG;
std::cerr << error_header << "File size is too large" << std::endl;
return HEX_EFBIG;
} else if (file_size == 0) {
std::cerr << error_header << "File is empty" << std::endl;
return HEX_EFEMPTY;
Expand All @@ -56,14 +82,8 @@ int main(int argc, char** argv) {
buffer.push_back(byte);
}

if (!output_color) {
ansi_reset = "";
offset_color = "";
ascii_color = "";
}

std::stringstream output = std::stringstream();

output << offset_color
<< " Offset: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F ";
if (result.count("ascii")) {
Expand All @@ -81,7 +101,6 @@ int main(int argc, char** argv) {
output_stream.open(output_filename, std::ios::out);
output_stream << output.str() << std::endl;
output_stream.close();

std::cout << "Wrote output to " << output_filename << std::endl;
} else {
std::cout << output.str() << std::endl;
Expand All @@ -91,7 +110,7 @@ int main(int argc, char** argv) {
}

ParseResult initialize_options(int argc, char** argv) {
string binary_name = std::filesystem::path(argv[0]).filename().string();
binary_name = fs::path(argv[0]).filename().string();

Options options(binary_name, "A simple hexdump utility\n");

Expand Down Expand Up @@ -122,26 +141,39 @@ ParseResult initialize_options(int argc, char** argv) {
std::cout << "Hexdump " << hexdump_version << std::endl;
exit(EXIT_SUCCESS);
}

if (!result.count("file")) {
std::cout << error_header << "No file specified\n"
<< "\n"
<< "Usage: " << binary_name << " [options...] <file>\n"
<< "Try `" << binary_name << " --help` for more information." << std::endl;
exit(HEX_EMISARG);
}

if ((result.count("output-color") &&
result["output-color"].as<string>() == "false") ||
result.count("output")
) {
output_color = false;
result["output-color"].as<string>() == "false") ||
(result.count("output") && !(result.count("output-color") &&
result["output-color"].as<string>() == "true"))) {
ansi_reset = "";
offset_color = "";
ascii_color = "";
error_header = "Error: ";
}
#ifdef _WIN32
else {
DWORD dwMode = 0;
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
GetConsoleMode(hConsole, &dwMode);
DWORD dwNewMode = dwMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING;
SetConsoleMode(hConsole, dwNewMode);
GetConsoleMode(hConsole, &dwMode);

if (dwMode == dwNewMode) {
log("Enabled VT100 escape codes", LOG_INFO)
} else {
log("Failed to enable VT100 escape codes", LOG_WARN);
exit(128);
}
}
#endif

if ((result.count("output-color") &&
result["output-color"].as<string>() == "true")
) {
output_color = true;
if (!result.count("file")) {
std::cerr << error_header << "Missing required argument: file\nUsage: "
<< binary_name << " [options...] <file>\nTry `" << binary_name
<< " --help` for more information." << std::endl;

exit(HEX_EMISARG);
}

return result;
Expand Down Expand Up @@ -182,3 +214,57 @@ void outputHexLine(std::ostream& output, std::vector<unsigned char> buffer, size
}
output << ansi_reset << "\n";
}

string timestamp() {
time_t timer;
char buffer[26];
struct tm* tm_info;

timer = time(NULL);
tm_info = localtime(&timer);

strftime(buffer, 26, "%Y-%m-%d, %H:%M:%S", tm_info);

return string(buffer);
}

void log(string message, log_level level, bool output) {
string home_directory;
#ifdef _WIN32
home_directory = std::getenv("HOMEDRIVE") + std::getenv("HOMEPATH");
#else
home_directory = std::getenv("HOME");
#endif
fs::path log_path = fs::path(home_directory)
.append("." + binary_name);

if (!fs::exists(log_path)) {
fs::create_directory(log_path);
}

log_path = log_path.append("logs");

if (!fs::exists(log_path)) {
fs::create_directory(log_path);
}

if (!fs::is_directory(log_path)) {
// TODO: Implement failsafe if log_path cannot be created
}

fs::path log = log_path.append(binary_name + ".log");

string log_message = "[" + timestamp() + "] [" + enum_vals[level] + "]: " + message;

std::ofstream log_stream = std::ofstream(log, std::ios::out | std::ios::app);
log_stream << log_message << std::endl;
log_stream.close();

if (output) {
if (level == LOG_ERROR) {
std::cerr << error_header << message << std::endl;
} else {
std::cout << message << std::endl;
}
}
}

0 comments on commit 5a130cf

Please sign in to comment.