Compare commits
13 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c51f0da8e5 | ||
![]() |
6d400e3751 | ||
![]() |
d9a082bc3f | ||
![]() |
fba807a958 | ||
![]() |
8bab3d0426 | ||
![]() |
80c62d827a | ||
![]() |
e641e02478 | ||
![]() |
1f58feb64e | ||
![]() |
a5a9df01b4 | ||
![]() |
d81f75dab2 | ||
![]() |
519ca9dd3e | ||
![]() |
d101a700aa | ||
![]() |
446cb7be90 |
@ -4,11 +4,11 @@
|
|||||||
|
|
||||||
Automated system-wide installation:
|
Automated system-wide installation:
|
||||||
```
|
```
|
||||||
curl -fsSL https://gitea.jde.nz/public/dehydrate/releases/download/latest/install.sh | bash
|
curl -fsSL https://gitea.jde.nz/public/dehydrate/releases/download/latest/install.sh | sudo bash
|
||||||
```
|
```
|
||||||
To download just the dehydrate executable:
|
To download just the dehydrate executable:
|
||||||
```
|
```
|
||||||
curl -fsSL -o dehydrate https://gitea.jde.nz/public/dehydrate/releases/download/latest/dehydrate.amd64 && chmod a+x bb64
|
curl -fsSL -o dehydrate https://gitea.jde.nz/public/dehydrate/releases/download/latest/dehydrate.amd64 && chmod a+x dehydrate
|
||||||
```
|
```
|
||||||
|
|
||||||
## How it Works
|
## How it Works
|
||||||
|
@ -1 +1 @@
|
|||||||
static const char *VERSION = "4";
|
static const char *VERSION = "11";
|
||||||
|
36
install.sh
36
install.sh
@ -3,24 +3,23 @@ set -e
|
|||||||
|
|
||||||
PROJECT="dehydrate"
|
PROJECT="dehydrate"
|
||||||
|
|
||||||
|
# RUN AS ROOT
|
||||||
|
if [[ $EUID -ne 0 ]]; then
|
||||||
|
echo "This script must be run as root" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
# 0. see if we were passed a folder to install to
|
# 0. see if we were passed a folder to install to
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
INSTALL_DIR="$1"
|
INSTALL_DIR="$1"
|
||||||
if [[ -z "$INSTALL_DIR" ]]; then
|
if [[ -z "$INSTALL_DIR" ]]; then
|
||||||
INSTALL_DIR="/usr/local/bin"
|
INSTALL_DIR="/usr/local/bin"
|
||||||
else
|
else
|
||||||
echo "Installing $PROJECT to $INSTALL_DIR"
|
|
||||||
if [[ ! -d "$INSTALL_DIR" ]]; then
|
if [[ ! -d "$INSTALL_DIR" ]]; then
|
||||||
mkdir -p "$INSTALL_DIR"
|
mkdir -p "$INSTALL_DIR"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
echo "Installing $PROJECT to $INSTALL_DIR"
|
||||||
# 0. see if we were passed a user to chown to
|
|
||||||
# -----------------------------------------------------------------------------
|
|
||||||
CHOWN_USER="$2"
|
|
||||||
if [[ -z "$CHOWN_USER" ]]; then
|
|
||||||
CHOWN_USER=$(id -u)
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 1. Determine architecture
|
# 1. Determine architecture
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
@ -35,27 +34,18 @@ else
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 3. Download the appropriate binary to a temp directory
|
# 3. Download the appropriate binary
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
TMPDIR=$(mktemp -d)
|
|
||||||
trap 'rm -rf "$TMPDIR"' EXIT
|
|
||||||
URL="https://gitea.jde.nz/public/$PROJECT/releases/download/latest/$BIN"
|
|
||||||
echo "Downloading $BIN from $URL..."
|
|
||||||
|
|
||||||
curl -fsSL -o "$TMPDIR/$PROJECT" "$URL"
|
URL="https://gitea.jde.nz/public/$PROJECT/releases/download/latest/$BIN"
|
||||||
|
echo "Downloading $BIN from $URL to $TMPDIR..."
|
||||||
|
|
||||||
|
curl -fsSL -o "$INSTALL_DIR/$PROJECT" "$URL"
|
||||||
|
|
||||||
# 4. Make it executable
|
# 4. Make it executable
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
chmod +x "$TMPDIR/$PROJECT"
|
chmod +x "$INSTALL_DIR/$PROJECT"
|
||||||
|
|
||||||
# 5. Move to /usr/local/bin
|
|
||||||
# -----------------------------------------------------------------------------
|
|
||||||
docker run --rm -v "$TMPDIR:/tmp" -v "$INSTALL_DIR:/target" alpine sh -c "cp /tmp/$PROJECT /target/$PROJECT; chown $CHOWN_USER /target/$PROJECT"
|
|
||||||
rm "$TMPDIR/$PROJECT"
|
|
||||||
|
|
||||||
# 6. Print success message
|
# 6. Print success message
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
echo "$PROJECT installed successfully to $INSTALL_DIR/$PROJECT (arch $ARCH)"
|
echo "$PROJECT installed successfully to $INSTALL_DIR/$PROJECT (arch $ARCH)"
|
||||||
echo " "
|
|
||||||
echo "Update $PROJECT in future with:"
|
|
||||||
echo " $PROJECT -u"
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#include "argparse.hpp"
|
#include "argparse.hpp"
|
||||||
#include "version.hpp"
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
static const std::string HELP_TEXT = R"(
|
static const std::string HELP_TEXT = R"(
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "xxhash.hpp"
|
#include "xxhash.hpp"
|
||||||
|
#include <sys/stat.h> // For file permissions
|
||||||
|
#include <cstring> // For strlen
|
||||||
|
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
@ -24,6 +26,118 @@ static uint64_t fnv1a_64(const void* data, size_t len) {
|
|||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Base64 encoding function - no dependencies
|
||||||
|
static std::string base64_encode(const unsigned char* data, size_t len) {
|
||||||
|
const char* base64_chars =
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||||
|
std::string result;
|
||||||
|
result.reserve((len + 2) / 3 * 4); // Reserve space for the full encoded size
|
||||||
|
|
||||||
|
int val = 0, valb = -6;
|
||||||
|
for (size_t i = 0; i < len; i++) {
|
||||||
|
val = (val << 8) + data[i];
|
||||||
|
valb += 8;
|
||||||
|
while (valb >= 0) {
|
||||||
|
result.push_back(base64_chars[(val >> valb) & 0x3F]);
|
||||||
|
valb -= 6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valb > -6) {
|
||||||
|
result.push_back(base64_chars[((val << 8) >> (valb + 8)) & 0x3F]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add padding
|
||||||
|
while (result.size() % 4) {
|
||||||
|
result.push_back('=');
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to output the _recreate_file_ utility function and Base64 decoder
|
||||||
|
static void output_recreate_file_utility(std::ofstream& cpp) {
|
||||||
|
cpp << R"cpp(
|
||||||
|
// Base64 decoding function - no dependencies
|
||||||
|
static void base64_decode(const char* encoded_data, size_t encoded_len, unsigned char* output, size_t* output_len) {
|
||||||
|
const char* base64_chars =
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||||
|
size_t out_pos = 0;
|
||||||
|
int val = 0, valb = -8;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < encoded_len; i++) {
|
||||||
|
char c = encoded_data[i];
|
||||||
|
if (c == '=') break;
|
||||||
|
|
||||||
|
// Find position in base64_chars
|
||||||
|
const char* pos = strchr(base64_chars, c);
|
||||||
|
if (pos == nullptr) continue; // Skip invalid characters
|
||||||
|
|
||||||
|
val = (val << 6) + static_cast<int>(pos - base64_chars);
|
||||||
|
valb += 6;
|
||||||
|
if (valb >= 0) {
|
||||||
|
output[out_pos++] = static_cast<unsigned char>((val >> valb) & 0xFF);
|
||||||
|
valb -= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*output_len = out_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utility function to recreate a file with proper permissions
|
||||||
|
static bool _recreate_file_(const std::filesystem::path& outpath, uint64_t file_hash, std::filesystem::perms file_perms, const unsigned char* filedata, size_t filedata_len) {
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
bool needs_write = false;
|
||||||
|
|
||||||
|
// Check if file exists and has correct hash
|
||||||
|
if (fs::exists(outpath)) {
|
||||||
|
// Check content hash
|
||||||
|
std::ifstream in(outpath, std::ios::binary);
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << in.rdbuf();
|
||||||
|
std::string data = oss.str();
|
||||||
|
uint64_t existing_hash = fnv1a_64(data.data(), data.size());
|
||||||
|
needs_write = existing_hash != file_hash;
|
||||||
|
} else {
|
||||||
|
needs_write = true; // File doesn't exist, need to create it
|
||||||
|
}
|
||||||
|
|
||||||
|
bool needs_permission_update = true;
|
||||||
|
if (!needs_write) { // we always update permissions if the file is written or changed. Othewise we check.
|
||||||
|
fs::perms current_perms = fs::status(outpath).permissions();
|
||||||
|
needs_permission_update = current_perms != file_perms;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needs_write) {
|
||||||
|
bool existed = fs::exists(outpath);
|
||||||
|
|
||||||
|
fs::create_directories(outpath.parent_path());
|
||||||
|
std::ofstream out(outpath, std::ios::binary);
|
||||||
|
out.write(reinterpret_cast<const char*>(filedata), filedata_len);
|
||||||
|
out.close();
|
||||||
|
// Set the file permissions
|
||||||
|
fs::permissions(outpath, file_perms);
|
||||||
|
|
||||||
|
if (!existed) {
|
||||||
|
std::cout << "[dehydrate] " << outpath.filename() << ": created\n";
|
||||||
|
} else {
|
||||||
|
std::cout << "[dehydrate] " << outpath.filename() << ": updated (hash changed)\n";
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needs_permission_update) {
|
||||||
|
// Update only permissions
|
||||||
|
fs::permissions(outpath, file_perms);
|
||||||
|
std::cout << "[dehydrate] " << outpath.filename() << ": updated (permissions changed)\n";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
)cpp";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void generate_file_code(const std::string& source, const std::string& destfolder, bool silent) {
|
void generate_file_code(const std::string& source, const std::string& destfolder, bool silent) {
|
||||||
fs::path src(source);
|
fs::path src(source);
|
||||||
@ -31,12 +145,17 @@ void generate_file_code(const std::string& source, const std::string& destfolder
|
|||||||
std::string ns = "recreate_" + sanitize(src.stem().string());
|
std::string ns = "recreate_" + sanitize(src.stem().string());
|
||||||
std::string cppname = "_" + src.stem().string() + ".cpp";
|
std::string cppname = "_" + src.stem().string() + ".cpp";
|
||||||
std::string hppname = "_" + src.stem().string() + ".hpp";
|
std::string hppname = "_" + src.stem().string() + ".hpp";
|
||||||
|
std::string bothname = "_" + src.stem().string() + ".{cpp,hpp}";
|
||||||
fs::create_directories(dest);
|
fs::create_directories(dest);
|
||||||
std::ifstream in(source, std::ios::binary);
|
std::ifstream in(source, std::ios::binary);
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
oss << in.rdbuf();
|
oss << in.rdbuf();
|
||||||
std::string filedata = oss.str();
|
std::string filedata = oss.str();
|
||||||
uint64_t hash = fnv1a_64(filedata.data(), filedata.size());
|
uint64_t hash = fnv1a_64(filedata.data(), filedata.size());
|
||||||
|
|
||||||
|
// Get source file permissions
|
||||||
|
fs::perms src_perms = fs::status(src).permissions();
|
||||||
|
|
||||||
// Write HPP
|
// Write HPP
|
||||||
std::ofstream hpp(dest / hppname);
|
std::ofstream hpp(dest / hppname);
|
||||||
hpp << "#pragma once\n#include <string>\nnamespace " << ns << " {\nbool recreate_file(std::string destination_folder);\n}\n";
|
hpp << "#pragma once\n#include <string>\nnamespace " << ns << " {\nbool recreate_file(std::string destination_folder);\n}\n";
|
||||||
@ -46,7 +165,7 @@ void generate_file_code(const std::string& source, const std::string& destfolder
|
|||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iomanip>
|
#include <cstring>
|
||||||
|
|
||||||
// Tiny dependency-free FNV-1a 64-bit hash
|
// Tiny dependency-free FNV-1a 64-bit hash
|
||||||
static uint64_t fnv1a_64(const void* data, size_t len) {
|
static uint64_t fnv1a_64(const void* data, size_t len) {
|
||||||
@ -59,47 +178,51 @@ static uint64_t fnv1a_64(const void* data, size_t len) {
|
|||||||
)cpp";
|
)cpp";
|
||||||
cpp << "#include \"" << hppname << "\"\n";
|
cpp << "#include \"" << hppname << "\"\n";
|
||||||
cpp << "namespace " << ns << " {\n";
|
cpp << "namespace " << ns << " {\n";
|
||||||
// Embed file data
|
|
||||||
cpp << "static const unsigned char filedata[] = {";
|
// Output the recreate_file utility function
|
||||||
for (size_t i = 0; i < filedata.size(); ++i) {
|
output_recreate_file_utility(cpp);
|
||||||
if (i % 16 == 0) cpp << "\n ";
|
|
||||||
cpp << "0x" << std::hex << std::setw(2) << std::setfill('0') << (int)(unsigned char)filedata[i];
|
// Write recreate_file function with embedded file data
|
||||||
if (i + 1 != filedata.size()) cpp << ", ";
|
|
||||||
}
|
|
||||||
cpp << "\n};\n";
|
|
||||||
// Reset to decimal format
|
|
||||||
cpp << std::dec;
|
|
||||||
cpp << "static const size_t filedata_len = " << filedata.size() << ";\n";
|
|
||||||
cpp << "static uint64_t file_hash = " << hash << "U;\n";
|
|
||||||
cpp << R"cpp(
|
cpp << R"cpp(
|
||||||
bool recreate_file(std::string destination_folder) {
|
bool recreate_file(std::string destination_folder) {
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
fs::path outpath = fs::path(destination_folder) / ")cpp" << src.filename().string() << R"cpp(";
|
fs::path outpath = fs::path(destination_folder) / ")cpp" << src.filename().string() << R"cpp(";
|
||||||
uint64_t existing_hash;
|
|
||||||
if (fs::exists(outpath)) {
|
|
||||||
std::ifstream in(outpath, std::ios::binary);
|
|
||||||
std::ostringstream oss;
|
|
||||||
oss << in.rdbuf();
|
|
||||||
std::string data = oss.str();
|
|
||||||
existing_hash = fnv1a_64(data.data(), data.size());
|
|
||||||
}
|
|
||||||
bool needs_write = !fs::exists(outpath) || existing_hash != file_hash;
|
|
||||||
if (needs_write) {
|
|
||||||
std::ofstream out(outpath, std::ios::binary);
|
|
||||||
out.write(reinterpret_cast<const char*>(filedata), filedata_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!fs::exists(outpath)) {
|
// File data embedded as Base64
|
||||||
std::cout << "[dehydrate] " << outpath.filename() << ": created\n";
|
static const char filedata_base64[] = )cpp";
|
||||||
} else if (needs_write) {
|
|
||||||
std::cout << "[dehydrate] " << outpath.filename() << ": updated (hash changed)\n";
|
// Encode the file data to Base64
|
||||||
|
std::string base64 = base64_encode(reinterpret_cast<const unsigned char*>(filedata.data()), filedata.size());
|
||||||
|
|
||||||
|
// Split into 76-character chunks for readability
|
||||||
|
const size_t line_length = 76;
|
||||||
|
for (size_t i = 0; i < base64.length(); i += line_length) {
|
||||||
|
if (i > 0) cpp << "\n ";
|
||||||
|
cpp << "\"" << base64.substr(i, std::min(line_length, base64.length() - i)) << "\"";
|
||||||
|
if (i + line_length < base64.length()) cpp << "\\";
|
||||||
}
|
}
|
||||||
return needs_write;
|
cpp << ";\n\n";
|
||||||
}
|
|
||||||
)cpp";
|
// Decode Base64 at runtime
|
||||||
|
cpp << " // Decode Base64 data\n";
|
||||||
|
cpp << " size_t decoded_size = (strlen(filedata_base64) * 3) / 4;\n";
|
||||||
|
cpp << " unsigned char* decoded_data = new unsigned char[decoded_size];\n";
|
||||||
|
cpp << " size_t actual_size;\n";
|
||||||
|
cpp << " base64_decode(filedata_base64, strlen(filedata_base64), decoded_data, &actual_size);\n\n";
|
||||||
|
|
||||||
|
// Call _recreate_file_ with the decoded data
|
||||||
|
cpp << " bool result = _recreate_file_(outpath, " << hash << "ULL, "
|
||||||
|
<< "std::filesystem::perms(" << static_cast<unsigned>(src_perms) << "), "
|
||||||
|
<< "decoded_data, actual_size);\n";
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
cpp << " delete[] decoded_data;\n";
|
||||||
|
cpp << " return result;\n";
|
||||||
cpp << "}\n";
|
cpp << "}\n";
|
||||||
|
cpp << "}\n";
|
||||||
|
|
||||||
if (!silent) {
|
if (!silent) {
|
||||||
std::cout << "[dehydrate] Generated: " << (dest / cppname) << ", " << (dest / hppname) << std::endl;
|
std::cout << "[dehydrate] Generated: " << (dest / bothname) << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,6 +241,7 @@ void generate_folder_code(const std::string& source, const std::string& destfold
|
|||||||
std::string ns = "recreate_" + sanitize(src.stem().string());
|
std::string ns = "recreate_" + sanitize(src.stem().string());
|
||||||
std::string cppname = "_" + src.stem().string() + ".cpp";
|
std::string cppname = "_" + src.stem().string() + ".cpp";
|
||||||
std::string hppname = "_" + src.stem().string() + ".hpp";
|
std::string hppname = "_" + src.stem().string() + ".hpp";
|
||||||
|
std::string bothname = "_" + src.stem().string() + ".{cpp,hpp}";
|
||||||
fs::create_directories(dest);
|
fs::create_directories(dest);
|
||||||
// Collect all files
|
// Collect all files
|
||||||
std::vector<fs::path> files;
|
std::vector<fs::path> files;
|
||||||
@ -152,9 +276,7 @@ namespace )hpp" << ns << R"hpp( {
|
|||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iomanip>
|
#include <cstring>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
@ -181,67 +303,70 @@ static uint64_t fnv1a_64(const void* data, size_t len) {
|
|||||||
|
|
||||||
)cpp";
|
)cpp";
|
||||||
|
|
||||||
// Embed all files
|
// Output the recreate_file utility function
|
||||||
|
output_recreate_file_utility(cpp);
|
||||||
|
|
||||||
|
// Start writing recreate_tree - we'll embed file data directly in function
|
||||||
|
cpp << R"cpp(
|
||||||
|
bool recreate_tree(std::string destination_folder) {
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
bool any_written = false;
|
||||||
|
)cpp";
|
||||||
|
|
||||||
|
// Process each file
|
||||||
for (const auto& file : files) {
|
for (const auto& file : files) {
|
||||||
std::ifstream in(file, std::ios::binary);
|
std::ifstream in(file, std::ios::binary);
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
oss << in.rdbuf();
|
oss << in.rdbuf();
|
||||||
std::string filedata = oss.str();
|
std::string filedata = oss.str();
|
||||||
uint64_t hash = fnv1a_64(filedata.data(), filedata.size());
|
uint64_t hash = fnv1a_64(filedata.data(), filedata.size());
|
||||||
|
fs::perms file_perms = fs::status(file).permissions();
|
||||||
std::string rel = fs::relative(file, src).string();
|
std::string rel = fs::relative(file, src).string();
|
||||||
std::string var = sanitize(rel);
|
std::string var = sanitize(rel);
|
||||||
cpp << "static const unsigned char data_" << var << "[] = {";
|
|
||||||
for (size_t i = 0; i < filedata.size(); ++i) {
|
// Start a scope to limit data's lifetime
|
||||||
if (i % 16 == 0) cpp << "\n ";
|
cpp << " {\n";
|
||||||
cpp << "0x" << std::hex << std::setw(2) << std::setfill('0') << (int)(unsigned char)filedata[i];
|
cpp << " // File: " << rel << "\n";
|
||||||
if (i + 1 != filedata.size()) cpp << ", ";
|
cpp << " fs::path outpath = fs::path(destination_folder) / \"" << rel << "\";\n";
|
||||||
|
|
||||||
|
// Embed file data as Base64
|
||||||
|
cpp << " static const char filedata_base64[] = ";
|
||||||
|
|
||||||
|
// Encode the file data to Base64
|
||||||
|
std::string base64 = base64_encode(reinterpret_cast<const unsigned char*>(filedata.data()), filedata.size());
|
||||||
|
|
||||||
|
// Split into 76-character chunks for readability
|
||||||
|
const size_t line_length = 76;
|
||||||
|
for (size_t i = 0; i < base64.length(); i += line_length) {
|
||||||
|
if (i > 0) cpp << "\n ";
|
||||||
|
cpp << "\"" << base64.substr(i, std::min(line_length, base64.length() - i)) << "\"";
|
||||||
|
if (i + line_length < base64.length()) cpp << "\\";
|
||||||
}
|
}
|
||||||
cpp << "\n};\n";
|
cpp << ";\n\n";
|
||||||
// Reset to decimal format
|
|
||||||
cpp << std::dec;
|
// Decode Base64 at runtime
|
||||||
cpp << "static const size_t len_" << var << " = " << filedata.size() << ";\n";
|
cpp << " // Decode Base64 data\n";
|
||||||
cpp << "static uint64_t hash_" << var << " = " << hash << "U;\n";
|
cpp << " size_t decoded_size = (strlen(filedata_base64) * 3) / 4;\n";
|
||||||
cpp << "static const char* rel_" << var << " = \"" << rel << "\";\n";
|
cpp << " unsigned char* decoded_data = new unsigned char[decoded_size];\n";
|
||||||
|
cpp << " size_t actual_size;\n";
|
||||||
|
cpp << " base64_decode(filedata_base64, strlen(filedata_base64), decoded_data, &actual_size);\n\n";
|
||||||
|
|
||||||
|
// Call _recreate_file_ with the decoded data
|
||||||
|
cpp << " bool file_written = _recreate_file_(outpath, "
|
||||||
|
<< hash << "ULL, std::filesystem::perms(" << static_cast<unsigned>(file_perms) << "), "
|
||||||
|
<< "decoded_data, actual_size);\n";
|
||||||
|
|
||||||
|
// Clean up and update flag
|
||||||
|
cpp << " delete[] decoded_data;\n";
|
||||||
|
cpp << " any_written = any_written || file_written;\n";
|
||||||
|
cpp << " }\n"; // Close scope to free memory
|
||||||
}
|
}
|
||||||
// Write recreate_tree using heredoc style
|
|
||||||
cpp << R"cpp(
|
cpp << " return any_written;\n";
|
||||||
bool recreate_tree(std::string destination_folder) {
|
|
||||||
namespace fs = std::filesystem;
|
|
||||||
bool any_written = false;
|
|
||||||
)cpp";
|
|
||||||
for (const auto& file : files) {
|
|
||||||
std::string rel = fs::relative(file, src).string();
|
|
||||||
std::string var = sanitize(rel);
|
|
||||||
cpp << R"cpp( {
|
|
||||||
fs::path outpath = fs::path(destination_folder) / )cpp" << "rel_" << var << R"cpp(;
|
|
||||||
fs::create_directories(outpath.parent_path());
|
|
||||||
uint64_t existing_hash;
|
|
||||||
if (fs::exists(outpath)) {
|
|
||||||
std::ifstream in(outpath, std::ios::binary);
|
|
||||||
std::ostringstream oss; oss << in.rdbuf();
|
|
||||||
std::string data = oss.str();
|
|
||||||
existing_hash = fnv1a_64(data.data(), data.size());
|
|
||||||
}
|
|
||||||
bool needs_write = !fs::exists(outpath) || existing_hash != )cpp" << "hash_" << var << R"cpp(;
|
|
||||||
if (needs_write) {
|
|
||||||
std::ofstream out(outpath, std::ios::binary);
|
|
||||||
out.write(reinterpret_cast<const char*>()cpp" << "data_" << var << R"cpp(), )cpp" << "len_" << var << R"cpp();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!fs::exists(outpath)) {
|
|
||||||
std::cout << "[dehydrate] " << outpath.filename() << ": created\n";
|
|
||||||
} else if (needs_write) {
|
|
||||||
std::cout << "[dehydrate] " << outpath.filename() << ": updated (hash changed)\n";
|
|
||||||
}
|
|
||||||
any_written = any_written || needs_write;
|
|
||||||
}
|
|
||||||
)cpp";
|
|
||||||
}
|
|
||||||
cpp << R"cpp( return any_written;
|
|
||||||
}
|
|
||||||
)cpp";
|
|
||||||
cpp << "}\n";
|
cpp << "}\n";
|
||||||
|
cpp << "}\n";
|
||||||
|
|
||||||
if (!silent) {
|
if (!silent) {
|
||||||
std::cout << "[dehydrate] Generated: " << (dest / cppname) << ", " << (dest / hppname) << std::endl;
|
std::cout << "[dehydrate] Generated: " << (dest / bothname) << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
2
test.sh
2
test.sh
@ -38,7 +38,7 @@ $TEST_DIR/testexe
|
|||||||
|
|
||||||
# third time just two files changed
|
# third time just two files changed
|
||||||
echo "----------------------------------------"
|
echo "----------------------------------------"
|
||||||
echo "Should be two changes: generator.cpp and hash.cpp"
|
echo "Should see: hash.cpp - updated, and generator.cpp - created."
|
||||||
rm $TEST_DIR/temp/generator.cpp
|
rm $TEST_DIR/temp/generator.cpp
|
||||||
echo "whee!" >> $TEST_DIR/temp/hash.cpp
|
echo "whee!" >> $TEST_DIR/temp/hash.cpp
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user