diff options
| author | bryan <bryan@veltar.systems> | 2026-08-26 15:16:23 +0200 |
|---|---|---|
| committer | bryan <bryan@veltar.systems> | 2026-08-26 15:16:23 +0200 |
| commit | 22ade138c4e83b7c0b0c702c38c0ca00a9948d4b (patch) | |
| tree | f78768a69dded978be14aa4290b198903b27f4c0 | |
| parent | dfce501af03595a8fb5083fbc43b48d729ae9551 (diff) | |
| download | rg-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.
| -rw-r--r-- | .gitattributes | 5 | ||||
| -rw-r--r-- | .gitignore | 46 | ||||
| -rw-r--r-- | LICENSE | 21 | ||||
| -rw-r--r-- | Makefile | 51 | ||||
| -rw-r--r-- | README | 73 | ||||
| -rw-r--r-- | README.md | 1 | ||||
| -rw-r--r-- | arg.h | 50 | ||||
| -rw-r--r-- | config.def.h | 16 | ||||
| -rw-r--r-- | config.mk | 29 | ||||
| -rw-r--r-- | rg.1 | 55 | ||||
| -rw-r--r-- | rg.c | 185 |
11 files changed, 531 insertions, 1 deletions
diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..b5e1bde --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +* text=auto eol=lf +*.c text eol=lf +*.h text eol=lf +*.1 text eol=lf +Makefile text eol=lf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..263bcd6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,46 @@ +# local suckless config (copied from config.def.h by make) +config.h + +# dist +*.tar +*.tar.gz +rg-[0-9]* + +# compiled / binaries +rg +rg.exe +*.o +*.obj +*.a +*.so +*.dll +*.out +a.out +core +core.* + +# editors +*~ +*.bak +*.tmp +*.swp +*.swo +*.orig +*.rej + +# OS +.DS_Store +Thumbs.db +ehthumbs.db +Desktop.ini + +# IDEs +.vscode/ +.idea/ +*.code-workspace + +# agent / session local files +.grok/ +.claude/ +.cursor/ +*.log @@ -0,0 +1,21 @@ +MIT/X Consortium License + +© 2026 bryan <bryan@veltar.systems> + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..19ecfb6 --- /dev/null +++ b/Makefile @@ -0,0 +1,51 @@ +# See LICENSE file for copyright and license details. +# rg - random string generator + +include config.mk + +SRC = rg.c +OBJ = ${SRC:.c=.o} + +all: options rg${EXE} + +options: + @echo rg build options: + @echo "CFLAGS = ${CFLAGS}" + @echo "LDFLAGS = ${LDFLAGS}" + @echo "CC = ${CC}" + +config.h: + cp config.def.h $@ + +.c.o: + ${CC} -c ${CFLAGS} $< + +${OBJ}: arg.h config.h config.mk + +rg${EXE}: ${OBJ} + ${CC} -o $@ ${OBJ} ${LDFLAGS} + +clean: + rm -f rg rg.exe ${OBJ} rg-${VERSION}.tar.gz + +dist: clean + mkdir -p rg-${VERSION} + cp -R LICENSE Makefile README arg.h config.def.h config.mk \ + rg.c rg.1 rg-${VERSION} + tar -cf - rg-${VERSION} | gzip -c > rg-${VERSION}.tar.gz + rm -rf rg-${VERSION} + +install: all + mkdir -p ${DESTDIR}${PREFIX}/bin + cp -f rg${EXE} ${DESTDIR}${PREFIX}/bin + chmod 755 ${DESTDIR}${PREFIX}/bin/rg${EXE} + mkdir -p ${DESTDIR}${MANPREFIX}/man1 + sed "s/VERSION/${VERSION}/g" < rg.1 > ${DESTDIR}${MANPREFIX}/man1/rg.1 + chmod 644 ${DESTDIR}${MANPREFIX}/man1/rg.1 + +uninstall: + rm -f ${DESTDIR}${PREFIX}/bin/rg \ + ${DESTDIR}${PREFIX}/bin/rg.exe \ + ${DESTDIR}${MANPREFIX}/man1/rg.1 + +.PHONY: all options clean dist install uninstall @@ -0,0 +1,73 @@ +rg - random string generator +============================ +rg prints cryptographically random alphanumeric strings. It is a +suckless rewrite of rg.veltar.systems as a small C program that +builds on Linux and Windows. + + rg [-v] [-n n] [-l l] + +n number of strings (default 6, min 1, max 1000) +l length of each string (default 10, min 1, max 256) + + +Requirements +------------ +A C99 compiler. + +Linux: libc with getrandom(2) (glibc 2.25+). +Windows: a compiler with rand_s (MSVC, clang targeting MSVC, or +MinGW-w64). + + +Installation +------------ +Edit config.mk to match your local setup. Edit config.def.h for the +alphabet and limits. + +Linux: + + make clean install + +Windows (GNU make + clang, from this tree): + + make + +The Windows bits of config.mk are selected when OS=Windows_NT. +Otherwise set CC, EXE=.exe, and CPPFLAGS by hand as commented there. + + +Running without make +-------------------- + cp config.def.h config.h + +Linux: + + cc -std=c99 -D_DEFAULT_SOURCE -o rg rg.c + +Windows (clang / MSVC CRT): + + clang -std=c99 -D_CRT_RAND_S -o rg.exe rg.c + +Windows (MinGW-w64): + + gcc -std=c99 -D_CRT_RAND_S -o rg.exe rg.c + + +Usage +----- + rg + rg -n 6 -l 10 + rg -n 1 -l 32 + + +Configuration +------------- +Copy config.def.h to config.h (make does this) and edit. Do not +edit rg.c for configuration. + + +Remarks +------- +The original page at rg.veltar.systems used n % alphabet.length on +raw 32-bit draws, which is slightly biased. This program rejects +values in the incomplete last bucket so each symbol is equiprobable. diff --git a/README.md b/README.md deleted file mode 100644 index 149d672..0000000 --- a/README.md +++ /dev/null @@ -1 +0,0 @@ -# rg @@ -0,0 +1,50 @@ +/* + * Copy me if you can. + * by 20h + */ + +#ifndef ARG_H__ +#define ARG_H__ + +extern char *argv0; + +/* use main(int argc, char *argv[]) */ +#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\ + argv[0] && argv[0][0] == '-'\ + && argv[0][1];\ + argc--, argv++) {\ + char argc_;\ + char **argv_;\ + int brk_;\ + if (argv[0][1] == '-' && argv[0][2] == '\0') {\ + argv++;\ + argc--;\ + break;\ + }\ + int i_;\ + for (i_ = 1, brk_ = 0, argv_ = argv;\ + argv[0][i_] && !brk_;\ + i_++) {\ + if (argv_ != argv)\ + break;\ + argc_ = argv[0][i_];\ + switch (argc_) + +#define ARGEND }\ + } + +#define ARGC() argc_ + +#define EARGF(x) ((argv[0][i_+1] == '\0' && argv[1] == NULL)?\ + ((x), abort(), (char *)0) :\ + (brk_ = 1, (argv[0][i_+1] != '\0')?\ + (&argv[0][i_+1]) :\ + (argc--, argv++, argv[0]))) + +#define ARGF() ((argv[0][i_+1] == '\0' && argv[1] == NULL)?\ + (char *)0 :\ + (brk_ = 1, (argv[0][i_+1] != '\0')?\ + (&argv[0][i_+1]) :\ + (argc--, argv++, argv[0]))) + +#endif diff --git a/config.def.h b/config.def.h new file mode 100644 index 0000000..3747be5 --- /dev/null +++ b/config.def.h @@ -0,0 +1,16 @@ +/* See LICENSE file for copyright and license details. */ + +#ifndef VERSION +#define VERSION "1.0" +#endif + +/* alphabet drawn from; change and re-copy to config.h */ +static const char alphabet[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + +static const int defn = 6; /* default number of strings */ +static const int defl = 10; /* default length of each string */ +static const int minn = 1; +static const int maxn = 1000; +static const int minl = 1; +static const int maxl = 256; diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..845b457 --- /dev/null +++ b/config.mk @@ -0,0 +1,29 @@ +# rg version +VERSION = 1.0 + +# Customize below to fit your system + +# paths +PREFIX = /usr/local +MANPREFIX = ${PREFIX}/share/man + +# compiler +CC = cc +EXE = +LIBS = + +# Windows (MinGW-w64): uncomment +# CC = gcc +# EXE = .exe + +# Windows (clang targeting MSVC CRT): uncomment +# CC = clang +# EXE = .exe + +ifeq ($(OS),Windows_NT) +CC = clang +EXE = .exe +endif + +CFLAGS = -std=c99 -pedantic -Wall -Wextra -Os +LDFLAGS = ${LIBS} @@ -0,0 +1,55 @@ +.TH RG 1 rg-VERSION +.SH NAME +rg \- generate random strings +.SH SYNOPSIS +.B rg +.RB [ \-v ] +.RB [ \-n +.IR n ] +.RB [ \-l +.IR l ] +.SH DESCRIPTION +.B rg +prints +.I n +cryptographically random strings of length +.I l +drawn from a configurable alphabet. Defaults match +rg.veltar.systems: +.I n += 6, +.I l += 10, alphabet A–Z a–z 0–9. +.P +Bytes come from the OS CSPRNG +.RI ( getrandom +on Linux, +.I rand_s +on Windows). Incomplete last buckets of 2^32 are rejected so each +symbol is equiprobable. +.SH OPTIONS +.TP +.BI \-n " n" +Number of strings. Default 6. Range 1..1000. +.TP +.BI \-l " l" +Length of each string. Default 10. Range 1..256. +.TP +.B \-v +Print version and exit. +.SH CONFIGURATION +The alphabet, defaults, and limits live in +.IR config.h , +copied from +.I config.def.h +by +.BR make . +Edit +.I config.def.h +and rebuild. Do not edit +.I rg.c +for configuration. +.SH SEE ALSO +.IR getrandom (2) +.SH AUTHORS +See LICENSE for copyright details. @@ -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; +} |
