chore: cleanup — untrack binaries, consolidate Go dirs, dedupe tools

- Untrack and delete compiled binaries (tarsum, gosoft.exe, rust uniq/uniq2);
  ignore build outputs (dist/, bin/, *.exe, *.test, .ruff_cache/)
- Merge tools/go/ and projects/go-tools/go/ into projects/go-tools/<name>/
- Fix goipgrep .gitignore: bare 'ipgrep' pattern was ignoring cmd/ipgrep/,
  so the main entrypoint was never tracked; now anchored to /ipgrep
- Archive duplicate implementations to archive/experimental/{rust,go}/
  (uniq, between, tarsum rewrites); canonical versions stay in tools/
- Update README tool catalog to match new layout

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
tobias
2026-06-10 13:42:45 +02:00
parent 401b3e1781
commit 1cbf8afb4a
39 changed files with 449 additions and 27 deletions
@@ -0,0 +1,140 @@
package main
import (
"bufio"
"flag"
"fmt"
"log"
"os"
"regexp"
"strconv"
"strings"
)
func printUsage() {
fmt.Fprintf(os.Stderr, "Usage: %s [options] <range specification> [file]\n", os.Args[0])
fmt.Fprintln(os.Stderr, "Options:")
flag.PrintDefaults()
fmt.Fprintln(os.Stderr, "\nRange specification examples:")
fmt.Fprintln(os.Stderr, "10,20 Reads lines 10 to 20 from the input")
fmt.Fprintln(os.Stderr, "15:+5 Reads 5 lines starting from line 15")
fmt.Fprintln(os.Stderr, "3 Reads from line 3 to the end of the file")
fmt.Fprintln(os.Stderr, "+2 Reads the first 2 lines")
fmt.Fprintln(os.Stderr, "Please ensure you input valid range specifications and file paths.")
os.Exit(1)
}
func parseArgs(args []string) (int, int, string, error) {
var filename string
var start, length, end int
found := false
// Concatenate all arguments to a single string for easier parsing
joinedArgs := strings.Join(args, " ")
// Regular expression to match ranges and numbers
rangeRegex := regexp.MustCompile(`((\d+)?([ :,;-]))(\+)?(\d+)`)
range2Regex := regexp.MustCompile(`((\d+)([ :,;-]))`)
range3Regex := regexp.MustCompile(`(\+)?(\d+)`)
matches := rangeRegex.FindStringSubmatch(joinedArgs)
if matches != nil {
//check if start was defined
if matches[2] != "" {
start, _ = strconv.Atoi(matches[2]) // Convert start line to integer
} else {
start = 1
}
if matches[4] == "+" { // Check if it's a relative length
length, _ = strconv.Atoi(matches[5]) // Convert length to integer
end = start + length - 1
} else {
end, _ = strconv.Atoi(matches[5]) // Convert end line to integer
}
// Remove the matched part from the arguments
joinedArgs = strings.Replace(joinedArgs, matches[0], "", 1)
found = true
} else {
matches = range2Regex.FindStringSubmatch(joinedArgs)
if matches != nil {
start, _ = strconv.Atoi(matches[2]) // Convert start line to integer
end = -1
// Remove the matched part from the arguments
joinedArgs = strings.Replace(joinedArgs, matches[0], "", 1)
found = true
} else {
matches = range3Regex.FindStringSubmatch(joinedArgs)
if matches != nil {
if matches[1] == "+" { // Check if it's a relative length
length, _ := strconv.Atoi(matches[2]) // Convert length to integer
start = 1
end = start + length - 1
} else { // Otherwise convert it to an absolute length
start, _ = strconv.Atoi(matches[2]) // Convert start line to integer
end = -1
// Remove the matched part from the arguments
}
joinedArgs = strings.Replace(joinedArgs, matches[0], "", 1)
found = true
} else {
found = false
printUsage()
}
}
}
// Clean up and identify the filename, if present
joinedArgs = strings.TrimSpace(joinedArgs)
if joinedArgs != "" && !found {
// If we didn't find numbers, interpret the remaining as filename
filename = joinedArgs
} else if joinedArgs != "" {
// Otherwise, interpret any non-empty remaining part as filename
filename = joinedArgs
}
if !found {
return 0, 0, "", fmt.Errorf("no valid range or line number found")
}
return start, end, filename, nil
}
func main() {
flag.Parse()
args := flag.Args()
if len(args) < 1 {
printUsage()
os.Exit(1)
}
startIndex, endIndex, filename, err := parseArgs(args)
if err != nil {
fmt.Fprintf(os.Stderr, "Error parsing arguments: %v\n", err)
os.Exit(1)
}
// Detemine the input source (file or stdin)
inputSource := os.Stdin
if filename != "" {
inputSource, err = os.Open(filename)
if err != nil {
log.Fatalf("Failed to open file: %v\n", err)
}
defer inputSource.Close()
}
scanner := bufio.NewScanner(inputSource)
//print all lines from line buner start to linenumber end unless linenumber end is -1
for i := 1; scanner.Scan(); i++ {
if startIndex <= i && (i <= endIndex || endIndex == -1) {
fmt.Println(scanner.Text())
}
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
}
+75
View File
@@ -0,0 +1,75 @@
package main
import (
"bufio"
"flag"
"fmt"
"hash/fnv"
"log"
"os"
)
func hashLine(s string) uint32 {
hasher := fnv.New32a()
hasher.Write([]byte(s))
return hasher.Sum32()
}
func main() {
// Define command line flags
reverse := flag.Bool("d", false, "Print only lines that appear more than once.")
help := flag.Bool("h", false, "Display help and usage information.")
flag.Usage = func() {
fmt.Fprintf(flag.CommandLine.Output(), "Usage of %s:\n", os.Args[0])
fmt.Println("This program reads from a file or standard input, deduplicates lines, and outputs the results.")
fmt.Println("Options:")
flag.PrintDefaults()
fmt.Println("Example usage:")
fmt.Println("\t", os.Args[0], "[options] [filename]")
fmt.Println("\t", os.Args[0], "-d filename # Only print duplicates")
fmt.Println("\t", "cat /some/text/file |", os.Args[0], "# Read from standard input")
}
flag.Parse()
// Check for help flag
if *help {
flag.Usage()
os.Exit(0)
}
// Detemine the input source (file or stdin)
inputSource := os.Stdin
var err error
if flag.NArg() > 0 {
inputSource, err = os.Open(flag.Args()[0])
if err != nil {
log.Fatalf("Failed to open file: %v\n", err)
}
defer inputSource.Close()
}
seenLines := make(map[uint32]int)
scanner := bufio.NewScanner(inputSource)
//Readin lines
for scanner.Scan() {
line := scanner.Text()
hash := hashLine(line)
seenLines[hash]++
if *reverse {
// Print only lines that appear more than once
if seenLines[hash] > 1 {
fmt.Println(line)
}
} else {
// Normal mode, print only unique lines
if seenLines[hash] == 1 {
fmt.Println(line)
}
}
}
//Check for errors during scanning
if err := scanner.Err(); err != nil {
log.Fatalf("Failed to read input: %v\n", err)
}
}
+13
View File
@@ -0,0 +1,13 @@
module tarsum
go 1.24.4
require (
github.com/mitchellh/go-homedir v1.1.0
github.com/spf13/cobra v1.9.1
)
require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/spf13/pflag v1.0.6 // indirect
)
+12
View File
@@ -0,0 +1,12 @@
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+138
View File
@@ -0,0 +1,138 @@
package main
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"hash"
"io"
"os"
"github.com/spf13/cobra"
)
// TarSum represents a tar checksum calculator
type TarSum struct {
hasher hash.Hash
}
// NewTarSum creates a new TarSum instance
func NewTarSum() *TarSum {
return &TarSum{
hasher: sha256.New(),
}
}
// Update updates the hash with data
func (ts *TarSum) Update(data []byte) {
ts.hasher.Write(data)
}
// Sum returns the final checksum as a hex string
func (ts *TarSum) Sum() string {
return hex.EncodeToString(ts.hasher.Sum(nil))
}
// CalculateTarSum calculates the checksum of a tar file
func CalculateTarSum(filePath string) (string, error) {
tarSum := NewTarSum()
file, err := os.Open(filePath)
if err != nil {
return "", fmt.Errorf("failed to open file: %w", err)
}
defer file.Close()
// Read the entire tar file
data, err := io.ReadAll(file)
if err != nil {
return "", fmt.Errorf("failed to read file: %w", err)
}
tarSum.Update(data)
return tarSum.Sum(), nil
}
// CalculateTarSumSorted calculates checksum with sorted entries
func CalculateTarSumSorted(filePath string) (string, error) {
tarSum := NewTarSum()
file, err := os.Open(filePath)
if err != nil {
return "", fmt.Errorf("failed to open file: %w", err)
}
defer file.Close()
// Read the entire tar file
data, err := io.ReadAll(file)
if err != nil {
return "", fmt.Errorf("failed to read file: %w", err)
}
tarSum.Update(data)
return tarSum.Sum(), nil
}
// CalculateTarSumWithExclusions calculates checksum with excluded paths
func CalculateTarSumWithExclusions(filePath string, excludes []string) (string, error) {
tarSum := NewTarSum()
file, err := os.Open(filePath)
if err != nil {
return "", fmt.Errorf("failed to open file: %w", err)
}
defer file.Close()
// Read the entire tar file
data, err := io.ReadAll(file)
if err != nil {
return "", fmt.Errorf("failed to read file: %w", err)
}
tarSum.Update(data)
return tarSum.Sum(), nil
}
func main() {
var excludes []string
var sorted bool
var rootCmd = &cobra.Command{
Use: "tarsum [file]",
Short: "Calculate SHA256 checksum of tar files",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
filePath := args[0]
// Check if file exists
if _, err := os.Stat(filePath); os.IsNotExist(err) {
fmt.Fprintf(os.Stderr, "Error: file %s does not exist\n", filePath)
os.Exit(1)
}
var checksum string
var err error
if sorted {
checksum, err = CalculateTarSumSorted(filePath)
} else {
checksum, err = CalculateTarSum(filePath)
}
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
fmt.Println(checksum)
},
}
rootCmd.Flags().StringSliceVarP(&excludes, "exclude", "e", []string{}, "Exclude paths from checksum calculation")
rootCmd.Flags().BoolVar(&sorted, "sorted", false, "Sort entries before calculating checksum")
if err := rootCmd.Execute(); err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
}
+55
View File
@@ -0,0 +1,55 @@
use std::env;
use std::fs::File;
use std::io::{self, BufRead, BufReader};
fn main() -> io::Result<()> {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
eprintln!("Usage: {} start-end [count] [file...]", args[0]);
std::process::exit(1);
}
let range = &args[1];
let (start, mut end) = if let Some(dash_pos) = range.find('-') {
(range[..dash_pos].parse().unwrap(), range[dash_pos + 1..].parse().unwrap())
} else {
(range.parse().unwrap(), 0)
};
let mut count = 1;
let mut files_start = 2;
if end == 0 {
if args.len() > 2 {
if let Ok(c) = args[2].parse::<usize>() {
count = c;
files_start = 3;
}
}
end = start + count - 1;
}
if args.len() > files_start {
for filename in &args[files_start..] {
let file = File::open(filename)?;
let reader = BufReader::new(file);
process_lines(reader, start, end)?;
}
} else {
// No files provided, read from stdin
let stdin = io::stdin();
let reader = stdin.lock();
process_lines(reader, start, end)?;
}
Ok(())
}
fn process_lines<R: BufRead>(reader: R, start: usize, end: usize) -> io::Result<()> {
reader.lines()
.enumerate()
.filter_map(|(i, line)| if i + 1 >= start && i + 1 <= end { line.ok() } else { None })
.for_each(|line| println!("{}", line));
Ok(())
}
+41
View File
@@ -0,0 +1,41 @@
use std::env;
use std::fs::File;
use std::io::{self, BufRead, BufReader};
use std::collections::HashSet;
use std::hash::{Hash, Hasher};
use std::collections::hash_map::DefaultHasher;
fn main() -> io::Result<()> {
let args: Vec<String> = env::args().collect();
if args.len() > 1 {
for filename in &args[1..] {
let file = File::open(filename)?;
let reader = BufReader::new(file);
remove_duplicates(reader)?;
}
} else {
// No files provided, read from stdin
let stdin = io::stdin();
let reader = stdin.lock();
remove_duplicates(reader)?;
}
Ok(())
}
fn remove_duplicates<R: BufRead>(reader: R) -> io::Result<()> {
let mut seen_hashes = HashSet::new();
for line in reader.lines() {
let line = line?;
let mut hasher = DefaultHasher::new();
line.hash(&mut hasher);
let hash = hasher.finish();
if seen_hashes.insert(hash) {
println!("{}", line);
}
}
Ok(())
}
+36
View File
@@ -0,0 +1,36 @@
use std::collections::HashSet;
use std::hash::{Hash, Hasher};
use std::collections::hash_map::DefaultHasher;
struct HashOnlySet {
set: HashSet<u64>,
}
impl HashOnlySet {
fn new() -> HashOnlySet {
HashOnlySet { set: HashSet::new() }
}
fn insert<T: Hash>(&mut self, item: &T) -> bool {
let hash = Self::hash_item(item);
self.set.insert(hash)
}
fn contains<T: Hash>(&self, item: &T) -> bool {
let hash = Self::hash_item(item);
self.set.contains(&hash)
}
fn hash_item<T: Hash>(item: &T) -> u64 {
let mut hasher = DefaultHasher::new();
item.hash(&mut hasher);
hasher.finish()
}
}
fn main() {
let mut set = HashOnlySet::new();
set.insert(&"Hello, world!");
println!("Contains 'Hello, world!': {}", set.contains(&"Hello, world!"));
println!("Contains 'Goodbye, world!': {}", set.contains(&"Goodbye, world!"));
}