Last active
January 15, 2022 00:02
-
-
Save gkbrk/cbab5c8840bec9f8c415542a1276ebc1 to your computer and use it in GitHub Desktop.
Thanos-snap half of your files out of existence
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define _XOPEN_SOURCE 500 | |
#include <assert.h> | |
#include <dirent.h> | |
#include <stdbool.h> | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <sys/stat.h> | |
#include <sys/types.h> | |
#include <time.h> | |
#include <unistd.h> | |
/* Random number generator */ | |
static uint64_t | |
rotl64(uint64_t n, uint8_t k) | |
{ | |
uint64_t a = n << k; | |
uint64_t b = n >> (64 - k); | |
return a | b; | |
} | |
static uint64_t | |
rxprime64(uint64_t x) | |
{ | |
x *= 7919; | |
x ^= rotl64(x, 7); | |
x *= 7723; | |
x ^= rotl64(x, 11); | |
x *= 7561; | |
x ^= rotl64(x, 13); | |
x *= 7411; | |
x ^= rotl64(x, 17); | |
return x; | |
} | |
static uint64_t rng_state; | |
static void | |
rng_absorb(const void* ptr, size_t n) | |
{ | |
size_t i; | |
const char* buf = ptr; | |
for (i = 0; i < n; i++) | |
rng_state = rxprime64(rng_state ^ buf[i]); | |
} | |
static uint64_t | |
rng_next() | |
{ | |
uint64_t n; | |
rng_state = rxprime64(rng_state); | |
n = rng_state & 0xFFFFFFFF00000000; | |
rng_state = rxprime64(rng_state); | |
n |= (rng_state & 0xFFFFFFFF00000000) >> 32; | |
return n; | |
} | |
static void | |
init_rng() | |
{ | |
time_t t = time(NULL); | |
pid_t pid; | |
rng_absorb(&t, sizeof(t)); | |
pid = getpid(); | |
rng_absorb(&pid, sizeof(pid)); | |
pid = getppid(); | |
rng_absorb(&pid, sizeof(pid)); | |
} | |
/* Dynamic file list */ | |
typedef struct | |
{ | |
size_t count; | |
char** files; | |
} FileList; | |
FileList file_list; | |
void | |
add_file(FileList* fl, const char* path) | |
{ | |
fl->count++; | |
fl->files = realloc(fl->files, fl->count * sizeof(char*)); | |
fl->files[fl->count - 1] = strdup(path); | |
} | |
void | |
shuffle_files(FileList* fl) | |
{ | |
size_t i; | |
size_t j; | |
char* tmp; | |
for (i = fl->count - 1; i > 0; i--) { | |
j = rng_next() % i; | |
tmp = fl->files[i]; | |
fl->files[i] = fl->files[j]; | |
fl->files[j] = tmp; | |
} | |
} | |
bool | |
streq(const char* s1, const char* s2) | |
{ | |
return strcmp(s1, s2) == 0; | |
} | |
size_t | |
path_mode(const char* path) | |
{ | |
struct stat st = { 0 }; | |
int res = stat(path, &st); | |
if (res != 0) { | |
printf("Could not stat file %s\n", path); | |
return 0; | |
} | |
return st.st_mode; | |
} | |
bool | |
is_file(const char* path) | |
{ | |
size_t mode = path_mode(path); | |
return S_ISREG(mode); | |
} | |
bool | |
is_dir(const char* path) | |
{ | |
size_t mode = path_mode(path); | |
return S_ISDIR(mode); | |
} | |
void | |
recursive_ls(const char* path) | |
{ | |
DIR* dir; | |
struct dirent* de; | |
char* filepath; | |
size_t path_len = strlen(path); | |
dir = opendir(path); | |
if (dir == NULL) { | |
printf("Could not open directory %s\n", path); | |
return; | |
} | |
while (true) { | |
de = readdir(dir); | |
if (de == NULL) | |
break; | |
if (streq(de->d_name, ".") || streq(de->d_name, "..")) | |
continue; | |
/* Build a path string, if it's a dir, recurse, else print */ | |
/* +2 comes from the path separator and the terminating null byte. | |
*/ | |
filepath = calloc(1, path_len + strlen(de->d_name) + 2); | |
strcat(filepath, path); | |
strcat(filepath, "/"); | |
strcat(filepath, de->d_name); | |
if (is_dir(filepath)) { | |
recursive_ls(filepath); | |
} else if (is_file(filepath)) { | |
add_file(&file_list, filepath); | |
} | |
free(filepath); | |
} | |
closedir(dir); | |
} | |
int | |
main(int argc, char** argv) | |
{ | |
size_t i; | |
bool really_do_it = false; | |
if (argc == 2 && streq(argv[1], "--really-do-it")) { | |
really_do_it = true; | |
} else { | |
puts("Dry run, pass --really-do-it to do it for real"); | |
} | |
init_rng(); | |
recursive_ls("."); | |
shuffle_files(&file_list); | |
printf("Found %ld files, going to remove %ld of them\n", | |
file_list.count, | |
file_list.count / 2); | |
for (i = 0; i < file_list.count / 2; i++) { | |
printf("The file %s has turned to dust\n", file_list.files[i]); | |
if (really_do_it) | |
unlink(file_list.files[i]); | |
} | |
/* Free all the file paths */ | |
for (i = 0; i < file_list.count; i++) | |
free(file_list.files[i]); | |
free(file_list.files); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment