repeat.git


repeat

~ repeat a command n times
git clone https://github.com/djnnvx/repeat.git
log | files | refs | archive | README

commit 30e3423700a35701527a6b68eaa5be8cbcf28933
Author: djnn <[email protected]>
Date: Tue, 16 Sep 2025 21:33:30 +0900

feat: genesis

Diffstat:
A.clang-format | 16++++++++++++++++
A.gitignore | 7+++++++
AMakefile | 84+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AREADME.md | 28++++++++++++++++++++++++++++
Aincludes/repeat.h | 31+++++++++++++++++++++++++++++++
Asources/lib.c | 39+++++++++++++++++++++++++++++++++++++++
Asources/main.c | 26++++++++++++++++++++++++++

7 files changed, 231 insertions(+), 0 deletions(-)

diff --git a/.clang-format b/.clang-format
@@ -0,0 +1,16 @@
+BasedOnStyle: LLVM
+AccessModifierOffset: '-4'
+ColumnLimit: '0'
+FixNamespaceComments: 'false'
+IndentCaseLabels: 'false'
+IndentWidth: '4'
+NamespaceIndentation: All
+TabWidth: '4'
+UseTab: Never
+SortIncludes: true
+IncludeBlocks: Preserve
+IncludeCategories:
+  - Regex:           '^"[[:alnum:]./]+"'       # Any "my_header.h"
+    Priority:        2
+  - Regex:           '<[[:alnum:]./]+>'        # System/Standard libs
+    Priority:        1
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,7 @@
+.cache/
+*.json
+objects/
+*.o
+*gcno
+*gcda
+repeat
diff --git a/Makefile b/Makefile
@@ -0,0 +1,84 @@
+
+NAME		 	= repeat
+CC			 	= clang
+RM			 	= rm -rf
+
+INCLUDES 	 	= -I./includes/
+LINK_FLAG    	=
+
+CFLAGS 			= -Wno-unused-command-line-argument -fPIC
+ifeq ($(MAKECMDGOALS), debug)
+	CFLAGS 			+= -g3 -DDEBUG -Wno-format-security -Wno-visibility -DCLIENTID=DEBUG -Wno-unsafe-buffer-usage
+	CFLAGS 			+= -Wno-unused-macros
+else
+	CFLAGS 		 	+= -Wall -Wextra -Os -Weverything -Wpedantic -Wl,-s -static -Wno-visibility
+	CFLAGS 			+= -flto -m64 -fvisibility=hidden -fvisibility-inlines-hidden -s -Wno-unsafe-buffer-usage
+	CFLAGS 			+= -Wno-padded -Wno-gnu-zero-variadic-macro-arguments -Wno-declaration-after-statement
+endif
+
+
+PATTERN 	 	= .c
+OBJPATTERN  	= .o
+SRC_DIR 		= ./sources
+OBJECT_DIR 		= ./objects
+
+vpath %$(PATTERN) $(SRC_DIR)
+
+MAIN		 	= main.c
+SRC 		 	= lib.c
+
+
+COMBINED		= $(SRC) $(MAIN)
+OBJ 	 		= $(patsubst %$(PATTERN), $(OBJECT_DIR)/%$(OBJPATTERN), $(COMBINED))
+
+
+.PHONY: all
+all: directories $(NAME) ## generate PROD binary (you might have to run make fclean before)
+	@strip -s $(NAME)
+	@echo "   [*** BINARY STRIP DONE ***]"
+
+.PHONY: help
+help:
+	@echo "\033[34mtargets:\033[0m"
+	@perl -nle'print $& if m{^[a-zA-Z_-\d]+:.*?## .*$$}' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-22s\033[0m %s\n", $$1, $$2}'
+
+.PHONY: install
+install: all ## builds and moves binary to /usr/bin/. (might have to run as root)
+	@mv $(NAME) /usr/bin/$(NAME)
+
+
+.PHONY: debug
+debug: directories $(NAME) ## generate DEBUG binary (you might have to run make fclean before)
+	@echo "[ ** DEBUG BINARY COMPILED WITH $(CFLAGS) **]"
+
+
+$(OBJECT_DIR):
+	@mkdir -pv $@
+
+directories: | $(OBJECT_DIR)
+
+$(OBJECT_DIR)/%$(OBJPATTERN) : %$(PATTERN)
+	@$(CC) -o $@ -c $< $(CFLAGS) $(INCLUDES) $(LINK_FLAG)
+	@echo "Compiling $@"
+
+$(NAME): $(OBJ)
+	@$(CC) -o $(NAME) $^ $(CFLAGS) $(INCLUDES) $(LINK_FLAG)
+	@echo "[*** COMPILATION  SUCCESSFUL ***]"
+
+.PHONY: clean
+clean:  ## removes object files
+	@$(RM) objects
+
+.PHONY: fclean
+fclean: clean  ## removes all compilation artifacts
+	@$(RM) $(NAME) mazepa_*
+	@echo "[*** CLEAN ***]"
+
+.PHONY: gen-compile-commands
+gen-compile-commands: clean ## generate compile_commands.json
+	@bear -- make debug
+
+.PHONY: re  ## cleans everything and rebuild PROD binary
+re: fclean all
+
+
diff --git a/README.md b/README.md
@@ -0,0 +1,28 @@
+# repeat
+
+This is an uber-simple command-line tool to repeat a function n times.
+
+It very much sucks in its current version, don't actually use this for serious work.
+The path, shell, ENV handling is almost non-existent. There is no way to specify if you want your commands to continue even if one fails.
+
+All it does is execute one function n times. if it fails, it stops. i just needed this for a random script, but why not share it for those
+who need it.
+
+## Compile
+
+```bash
+$ make
+
+# if you dare
+$ make install
+```
+
+> requires `clang` to be installed, but you can compile it with pretty much anything. it is a _very_ simple program.
+
+## Usage
+
+```bash
+$ repeat 5 'echo "gimme gimme gimme !"'
+```
+
+> yes, you need to wrap your command in single-quotes. Yes, it's fucking stupid. But it's what I need.
diff --git a/includes/repeat.h b/includes/repeat.h
@@ -0,0 +1,31 @@
+
+
+/*
+    djnn.sh ~ repeat
+
+    version 0.1.0
+
+
+    this is an uber-simple CLI function to repeat a command n times.
+    if one of the commands fails, it will stop.
+
+    Example:
+
+        repeat 5 'echo "gimme gimme gimme !"'
+
+*/
+
+#ifndef REPEAT_H
+#define REPEAT_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+/* lib.c */
+int parse_positive_int(char const *);
+int execute_command(char *);
+
+#endif /* REPEAT_H */
diff --git a/sources/lib.c b/sources/lib.c
@@ -0,0 +1,39 @@
+
+#include "repeat.h"
+
+#include <string.h>
+
+int parse_positive_int(char const *s) {
+    int n = -1;
+
+    if (!s)
+        return n;
+    n = atoi(s);
+    return (n > 0) ? n : -1;
+}
+
+int execute_command(char *cmd_args) {
+    int status;
+    pid_t pid = fork();
+
+    if (pid == -1) {
+        perror("repeat: fork failed");
+        return -1;
+    } else if (pid == 0) {
+        char *sh_path = "/bin/sh";
+        char *sh_args[] = {sh_path, "-c", cmd_args, NULL};
+
+        execve(sh_path, sh_args, NULL);
+        perror("repeat: execve failed");
+        _exit(127);
+    } else {
+        waitpid(pid, &status, 0);
+
+        if (WIFEXITED(status)) {
+            return WEXITSTATUS(status);
+        } else if (WIFSIGNALED(status)) {
+            return 128 + WTERMSIG(status);
+        }
+    }
+    return -1; /* Should not be reached */
+}
diff --git a/sources/main.c b/sources/main.c
@@ -0,0 +1,26 @@
+
+#include "repeat.h"
+
+int main(int ac, char **av) {
+    int exit_status = 0;
+
+    if (ac != 3) {
+        perror("repeat: expects 3 arguments");
+        perror("Usage: repeat <number> <command>");
+        exit(1);
+    }
+
+    const int nb_executions = parse_positive_int(av[1]);
+    if (nb_executions < 1) {
+        perror("repeat: expects first argument to be a positive number");
+        exit(1);
+    }
+
+    for (int i = 0; i < nb_executions; ++i) {
+        exit_status = execute_command(av[2]);
+        if (exit_status != 0) {
+            return exit_status;
+        }
+    }
+    return exit_status;
+}