summaryrefslogtreecommitdiff
path: root/rg.c
diff options
context:
space:
mode:
authorbryan <bryan@veltar.systems>2026-08-26 15:16:23 +0200
committerbryan <bryan@veltar.systems>2026-08-26 15:16:23 +0200
commit22ade138c4e83b7c0b0c702c38c0ca00a9948d4b (patch)
treef78768a69dded978be14aa4290b198903b27f4c0 /rg.c
parentdfce501af03595a8fb5083fbc43b48d729ae9551 (diff)
downloadrg-22ade138c4e83b7c0b0c702c38c0ca00a9948d4b.tar.gz
rg-22ade138c4e83b7c0b0c702c38c0ca00a9948d4b.zip
Replace the placeholder with a suckless C rg.
rg prints n cryptographically random strings of length l, matching rg.veltar.systems. Bytes come from getrandom(2) on Linux and rand_s on Windows. make selects clang and .exe when OS=Windows_NT.
Diffstat (limited to 'rg.c')
-rw-r--r--rg.c185
1 files changed, 185 insertions, 0 deletions
diff --git a/rg.c b/rg.c
new file mode 100644
index 0000000..9191956
--- /dev/null
+++ b/rg.c
@@ -0,0 +1,185 @@
+/* See LICENSE file for copyright and license details. */
+#ifdef __linux__
+#ifndef _DEFAULT_SOURCE
+#define _DEFAULT_SOURCE
+#endif
+#endif
+#ifdef _WIN32
+#ifndef _CRT_RAND_S
+#define _CRT_RAND_S
+#endif
+#endif
+
+#include <errno.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef _WIN32
+#include <unistd.h>
+#include <sys/random.h>
+#endif
+
+#include "arg.h"
+#include "config.h"
+
+char *argv0;
+
+static unsigned char rbuf[256];
+static size_t rpos = sizeof rbuf;
+
+static void
+die(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
+ fputc(' ', stderr);
+ perror(NULL);
+ } else {
+ fputc('\n', stderr);
+ }
+
+ exit(1);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: %s [-v] [-n n] [-l l]\n", argv0 ? argv0 : "rg");
+ exit(1);
+}
+
+static int
+fillrand(unsigned char *buf, size_t n)
+{
+#ifdef _WIN32
+ size_t i;
+ unsigned int r;
+ int k;
+
+ for (i = 0; i < n; ) {
+ if (rand_s(&r) != 0)
+ return -1;
+ for (k = 0; k < 4 && i < n; k++)
+ buf[i++] = (unsigned char)(r >> (8 * k));
+ }
+ return 0;
+#else
+ size_t off;
+ ssize_t r;
+
+ off = 0;
+ while (off < n) {
+ r = getrandom(buf + off, n - off, 0);
+ if (r < 0) {
+ if (errno == EINTR)
+ continue;
+ return -1;
+ }
+ if (r == 0)
+ return -1;
+ off += (size_t)r;
+ }
+ return 0;
+#endif
+}
+
+static unsigned char
+randbyte(void)
+{
+ if (rpos >= sizeof rbuf) {
+ if (fillrand(rbuf, sizeof rbuf) < 0)
+ die("rg: rng:");
+ rpos = 0;
+ }
+ return rbuf[rpos++];
+}
+
+static uint32_t
+randu32(void)
+{
+ return ((uint32_t)randbyte() << 24) |
+ ((uint32_t)randbyte() << 16) |
+ ((uint32_t)randbyte() << 8) |
+ (uint32_t)randbyte();
+}
+
+static uint32_t
+randbelow(uint32_t n)
+{
+ uint32_t x, min;
+
+ if (n <= 1)
+ return 0;
+ /* reject [0, 2^32 % n) so x % n is unbiased */
+ min = (uint32_t)(-n) % n;
+ do {
+ x = randu32();
+ } while (x < min);
+ return x % n;
+}
+
+static int
+eint(const char *s)
+{
+ char *end;
+ long v;
+
+ if (s == NULL || s[0] == '\0')
+ usage();
+ errno = 0;
+ v = strtol(s, &end, 10);
+ if (errno || end == s || *end != '\0' || v < INT_MIN || v > INT_MAX)
+ die("rg: invalid number '%s'", s);
+ return (int)v;
+}
+
+int
+main(int argc, char *argv[])
+{
+ int i, j, n, l, alen;
+
+ n = defn;
+ l = defl;
+
+ ARGBEGIN {
+ case 'n':
+ n = eint(EARGF(usage()));
+ break;
+ case 'l':
+ l = eint(EARGF(usage()));
+ break;
+ case 'v':
+ puts("rg-" VERSION);
+ return 0;
+ default:
+ usage();
+ } ARGEND
+
+ if (argc)
+ usage();
+ if (n < minn || n > maxn)
+ die("rg: n must be %d..%d", minn, maxn);
+ if (l < minl || l > maxl)
+ die("rg: l must be %d..%d", minl, maxl);
+
+ alen = (int)strlen(alphabet);
+ if (alen < 1)
+ die("rg: empty alphabet");
+
+ for (i = 0; i < n; i++) {
+ for (j = 0; j < l; j++)
+ putchar(alphabet[randbelow((uint32_t)alen)]);
+ putchar('\n');
+ }
+
+ return 0;
+}