commit: f2893cbeda686082b8f627016ad89358eefbb314
parent 7efb9b47340165b450ec1671f6b5afb148c3eef4
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date: Sat, 16 Nov 2024 06:26:13 +0100
Add -j option
Diffstat:
M | deblob.1 | 15 | +++++++++------ |
M | main.ha | 61 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
2 files changed, 70 insertions(+), 6 deletions(-)
diff --git a/deblob.1 b/deblob.1
@@ -1,6 +1,6 @@
.\" SPDX-FileCopyrightText: 2019 deblob Authors <https://hacktivis.me/projects/deblob>
.\" SPDX-License-Identifier: BSD-3-Clause
-.Dd 2024-10-31
+.Dd 2024-11-16
.Dt DEBLOB 1
.Os
.Sh NAME
@@ -9,8 +9,9 @@
.Sh SYNOPSIS
.Nm
.Op Fl cn
-.Op Fl e Ar excluded path ...
.Op Fl d Ar working directory
+.Op Fl e Ar excluded path ...
+.Op Fl j Ar file
.Sh DESCRIPTION
The
.Nm
@@ -25,13 +26,15 @@ The options are as follows:
.Bl -tag -width Ds
.It Fl c
Return error if any non-excluded blobs were found.
-.It Fl n
-Scan the files but do not actually delete them.
+.It Fl d Ar working directory
+Directory to be scanned rather than the current working directory on execution.
.It Fl e Ar excluded path
Paths to be excluded from removal, accepts shell-style globbing.
Pass the option multiple times to do multiple exclusions.
-.It Fl d Ar working directory
-Directory to be scanned rather than the current working directory on execution.
+.It Fl j Ar file
+JSON file to log actions taken and metadata into, intended to be used by other programs.
+.It Fl n
+Scan the files but do not actually delete them.
.El
.Sh EXIT STATUS
The
diff --git a/main.ha b/main.ha
@@ -1,6 +1,7 @@
// Copyright © 2019 deblob Authors <https://hacktivis.me/projects/deblob>
// SPDX-License-Identifier: BSD-3-Clause
use bytes;
+use encoding::json;
use endian;
use errors;
use fmt;
@@ -15,6 +16,7 @@ use strings;
let excludes: []str = [];
let noop: bool = false;
let check: bool = false;
+let json: bool = false;
const beam: []u8 = ['F', 'O', 'R', '1']; // Erlang BEAM
const magic: [_](str, []u8) = [
@@ -87,6 +89,7 @@ const jar: []u8 = [0xFE, 0xCA, 0, 0];
const shebang: []u8 = ['#', '!'];
let found: bool = false;
+let json_out: io::handle = 0;
fn id_blob(filename: str) (void | str | fs::error | io::error) = {
static let buffer: [4096]u8 = [0...];
@@ -267,6 +270,33 @@ fn is_excluded(filename: str) bool = {
return false;
};
+fn append_action(action: str, filename: str, format: str) void = {
+ if(!json) return;
+
+ let obj = json::object { ... };
+
+ json::put(&obj, "action", action);
+ defer json::take(&obj, "action");
+ json::put(&obj, "path", filename);
+ defer json::take(&obj, "path");
+ json::put(&obj, "format", format);
+ defer json::take(&obj, "format");
+
+ let obj_s = json::dumpstr(obj);
+ defer free(obj_s);
+
+ static let first_obj: bool = true;
+ if(first_obj)
+ {
+ fmt::fprintf(json_out, "\n\t{}", obj_s)!;
+ first_obj = false;
+ }
+ else
+ {
+ fmt::fprintf(json_out, ",\n\t{}", obj_s)!;
+ };
+};
+
fn check_dir(dirname: str) (void | errors::invalid | io::error) = {
const iter = match (os::iter(dirname)) {
case let iter: *fs::iterator =>
@@ -310,6 +340,7 @@ fn check_dir(dirname: str) (void | errors::invalid | io::error) = {
};
if (is_excluded(filename)) {
+ append_action("ignoring", filename, blob_id);
fmt::printfln("ignoring {}:\t{}", blob_id, filename)!;
continue;
};
@@ -317,10 +348,12 @@ fn check_dir(dirname: str) (void | errors::invalid | io::error) = {
found = true;
if (noop) {
+ append_action("detected", filename, blob_id);
fmt::printfln("detected {}:\t{}", blob_id, filename)!;
continue;
};
+ append_action("removing", filename, blob_id);
fmt::printfln("removing {}:\t{}", blob_id, filename)!;
match (os::remove(filename)) {
@@ -366,12 +399,14 @@ export fn main() void = {
('c', "Return error if any non-excluded blobs were found"),
('e', "NAME", "Exclude filename from removal (defaults to none)"),
('d', "PATH", "Set working directory (default to current dir)"),
+ ('j', "PATH", "JSON output file"),
('n', "No actual removal, only scan and log"),
);
defer getopt::finish(&cmd);
defer free(excludes);
let opt_d = "";
+ let json_out_path = "";
for (let i = 0z; i < len(cmd.opts); i += 1) {
const opt = cmd.opts[i];
@@ -384,11 +419,25 @@ export fn main() void = {
opt_d = opt.1;
case 'n' =>
noop = true;
+ case 'j' =>
+ json = true;
+ json_out_path = opt.1;
case =>
fmt::fatalf("deblob: Unhandled option -{}", opt.0);
};
};
+ if(json_out_path != "")
+ {
+ json_out = match (os::create(json_out_path, fs::mode::USER_RW | fs::mode::GROUP_R | fs::mode::OTHER_R)) {
+ case let f: io::file =>
+ yield f;
+ case let e: fs::error =>
+ fmt::fatalf("deblob: error: Failed creating/opening file '{}' for JSON output: {}", json_out_path, fs::strerror(e));
+ };
+
+ fmt::fprint(json_out, "[")!;
+ };
if(opt_d != "")
{
@@ -406,6 +455,18 @@ export fn main() void = {
fmt::println(":: Done checking for blobs")!;
+ if(json_out_path != "")
+ {
+ fmt::fprint(json_out, "\n]")!;
+
+ match(io::close(json_out)) {
+ case void =>
+ void;
+ case let e: io::error =>
+ fmt::fatalf("deblob: error: Failed closing JSON output file '{}': {}", json_out_path, io::strerror(e));
+ };
+ };
+
match (ret) {
case void =>
if(check && found) os::exit(2);