ADDED clib/iaia.c Index: clib/iaia.c ================================================================== --- clib/iaia.c +++ clib/iaia.c @@ -0,0 +1,145 @@ +/* [ʞ] iaia.c + * ~ lexi hale + * # include "iaia.c" + * # define _IAIA_FN_{ATOI,ITOA,ASCTOI,ITOASC} + * : typedef iaia_word_type, + * iaia_error_type + * : enum iaia_e_domain, + * iaia_e_base, + * iaia_e_overflow + * ? arbitrary-base integer conversion functions + */ + +#ifndef _IAIA_FN_ATOI +# define _IAIA_FN_ATOI atoi +#endif +#ifndef _IAIA_FN_ITOA +# define _IAIA_FN_ITOA itoa +#endif +#ifndef _IAIA_FN_ASCTOI +# define _IAIA_FN_ASCTOI asctoi +#endif +#ifndef _IAIA_FN_ITOASC +# define _IAIA_FN_ITOASC itoasc +#endif + +enum /* constants */ { + + /* ascii address space */ + numspace = (0x39 - 0x30) + 1, /* 10 */ + alphaspace = (0x5a - 0x41) + 1, /* 26 */ + smallalphaspace = (0x7a - 0x61) + 1, /* 26 */ + + /* base representations */ + imaxbase = numspace + alphaspace, /* 36 */ + maxbase = imaxbase + smallalphaspace /* 62 */ +}; + +/* -- string to integer converters -- */ + +iaia_error_type _IAIA_FN_ASCTOI(const char* s, iaia_word_type* ret) { + iaia_word_type val = 0; + enum { base = 128 }; + + for (;*s!=null;++s) { + uint8_t v = *s; + if (v > base) return iaia_e_domain; + + val *= base; + val += v; + } + + *ret = val; + return ok; +} + +iaia_error_type _IAIA_FN_ATOI(iaia_word_type base, const char* s, iaia_word_type* ret) { + /* s must be a null-terminated ASCII numeral string */ + if (base > maxbase) return iaia_e_base; + + /* override the default base if it's a basèd literal */ + if (s[0] == '@' || base == 0) return _IAIA_FN_ASCTOI(s + (s[0]=='@'),ret); + else if (s[0] == '0' && s[1] == 'x') base = 16, s += 2; + else if (s[0] == '0' && s[1] == 'd') base = 10, s += 2; + else if (s[0] == '0' && s[1] == 'b') base = 2, s += 2; + else if (s[0] == '0' && s[1] == 't') base = 3, s += 2; + else if (s[0] == '0') base = 8, s += 1; + + bool insens = (base <= imaxbase); + iaia_word_type val = 0; + + for (;*s!=null;++s) { + uint8_t v = *s; + if(v >= 0x30 && v <= 0x39) v -= 0x30; else { + if(v >= 0x61 && v <= 0x7a) { + if (insens) v -= 0x20; else { + v = numspace + alphaspace + (v - 0x61); + goto checkval; + } + } + if(v >= 0x41 && v <= 0x5a) v = numspace + (v - 0x41); + else return iaia_e_domain; + } + checkval: if (v >= base) return iaia_e_domain; + + val *= base; + val += v; + } + + *ret = val; + return iaia_e_ok; +} + + +/* -- integer to string converters -- */ + +/* needed for efficiency's sake, but really sucky - + * this table needs to be kept in sync with the + * itoa algorithm by hand. unfortunately, given C's + * abject lack of metaprogramming, we have to do this + * by hand. */ +const char iaia_ref_table[] = /* numerals[10] */ "0123456789" + /* bigalpha[26] */ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + /* smallalpha[26] */ "abcdefghijklmnopqrstuvwxyz"; +_Static_assert (sizeof iaia_ref_table - 1 == maxbase); + +iaia_error_type _IAIA_FN_ITOASC(iaia_word_type val, const char* buf_start, char* buf_end, char** newbuf) { + char* ptr = buf_end; + + *ptr-- = 0; + while(val > 0) { + if (ptr < buf_start) return iaia_e_overflow; + iaia_word_type rem = val % 128; + val /= 128; + *ptr-- = (char)rem; + } + + if (newbuf != null) *newbuf = ptr + 1; + return ok; +} + +iaia_error_type _IAIA_FN_ITOA(iaia_word_type base, iaia_word_type val, const char* buf_start, + char* buf_end, char** newbuf, bool lowercase) { + + char* ptr = buf_end; + + if (base > maxbase) return iaia_e_base; + if (base == 0) return _IAIA_FN_ITOASC(val, buf_start, buf_end, newbuf); + + *ptr-- = 0; + if (val == 0) *ptr-- = '0'; + else while(val > 0) { + if (ptr < buf_start) return iaia_e_overflow; + iaia_word_type rem = val % base; + val /= base; + char out = iaia_ref_table[rem]; + if (lowercase && base <= imaxbase) + if (out >= 'A' && out <= 'Z') + out += ('a' - 'A'); + *ptr-- = out; + } + + if (newbuf != null) *newbuf = ptr + 1; + return iaia_e_ok; +} + ADDED clib/tbl.c Index: clib/tbl.c ================================================================== --- clib/tbl.c +++ clib/tbl.c @@ -0,0 +1,16 @@ +/* [ʞ] tbl.c + * ~ lexi hale + * ? simple table-scanning function to make + * parsing easier */ + +struct tblrow { tbl_word_type val; const char* str; }; + +tbl_error_type tblget(size_t stacksz, const struct tblrow* haystack, const char* needle, tbl_word_type* val) { + for (size_t i = 0; i + * © AGPLv3 + * $ cc -O4 mkpw.c -omkpw [-D_CLIPBOARD] + * - D_CLIPBOARD enables mkpw to automatically + * copy new passwords to the clipboard. it + * does this by attempting to execute a sequence + * of binaries, and then writing the password + * to STDIN of the binary that succeeds. + * ? generates passwords + * → mkpw is unlikely to be portable to non-POSIX + * systems, but should run fine on Linux as well + * as BSDs with getrandom() support. + */ + +#include +#include +#include +#include +#include +#define sz(a) ( sizeof (a) / sizeof (a) [0] ) +#define say(x) (write(2, (x), sizeof (x))) + +#ifdef _CLIPBOARD +# include +# include +# include +# define _cl_opt o('n',nocopy,copy = false) +#else +# define _cl_opt +#endif + +#define options \ + o('l',lower,mode = lower) \ + o('m',mix,mode = mix) \ + o('u',upper,mode = upper)\ + _cl_opt + +enum /* constants */ { + null = 0, true = 1, false = 0 +}; + +typedef enum bad { + ok = 0, + fail = 1, + bad_user, + bad_option, + bad_syntax, + bad_usage = 64, /* fleabsd idiom */ +} bad; + +typedef _Bool bool; +typedef unsigned long long iaia_word_type; +typedef bad iaia_error_type; +enum /* iaia errors */ { + iaia_e_ok = ok, + iaia_e_base = fail, + iaia_e_domain = fail, + iaia_e_overflow = fail, +}; +#define _IAIA_FN_ATOI katoi +#include "clib/iaia.c" + +typedef enum { + tbl_ok = ok, tbl_error = fail +} tbl_error_type; +typedef unsigned char tbl_word_type; +#include "clib/tbl.c" + +enum args { +#define o(short, long, code) arg_##long, + options +#undef o +}; + +struct tblrow argtbl[] = { +#define o(short, long, code) {arg_##long, #long}, + options +#undef o +}; + +#define str_ucase "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +#define str_lcase "abcdefghijklmnopqrstuvwxyz" +#define str_num "0123456789" +const char* reftbl = str_num str_ucase str_lcase; +const char* reftbl_lcase = str_num str_lcase; + +#ifdef _CLIPBOARD +char* const* cbd_cmds[] = { + /* NOTE: these commands must be specified in order of + * most- to least-specific. more than one utility may + * be present on a given system, so we need to make sure + * the right one is called. */ + (char* const[]){"termux-clipboard-set", null}, + (char* const[]){"xsel", "-bi", null}, + /* TODO: allow command to be specified by env var */ + null +}; +#endif +int main(int argc, const char** argv) { + if (argc == 0) return -1; + if (argc == 1) { + size_t namelen = strlen(argv[0]); + say("\x1b[1musage:\x1b[m "); + write(2, argv[0], namelen); + say(" [-lmu" +# ifdef _CLIPBOARD + "n" +# endif + "] [--lower] [--mix] [--upper]" +# ifdef _CLIPBOARD + " [--nocopy]" +# endif + " []"); + return bad_usage; + } + + enum { upper, mix, lower } mode = lower; + unsigned long long len, q = 1; + bool gotlen = false; + bool gotq = false; + +# ifdef _CLIPBOARD + bool copy = true; +# endif + for (const char** arg = argv; *arg != null; ++arg) { + if ((*arg)[0] == '-') { + if ((*arg)[1] == '-') { /* long opt */ + unsigned char a; + if (tblget(sz(argtbl), argtbl, *arg + 2, &a) == ok) switch (a) { +# define o(short, long, code) case arg_##long:code;break; + options +# undef o + } else { + return bad_option; + } + } else { /* short opt */ + for(const char* ptr = (*arg) + 1; *ptr != 0; ++ptr) { + switch(*ptr) { +# define o(short, long, code) case short:code;break; + options +# undef o + default: return bad_option; + } + } + } + } else { + if (gotq) return bad_syntax; + else if (gotlen) { + if (katoi(10, *arg, &q) == ok) { + gotq = true; + } + } else if(katoi(10, *arg, &len) == ok) { + gotlen = true; + } + } + } + if (!gotlen) return bad_syntax; +# ifdef _CLIPBOARD + if (geteuid() == 0 && copy) { + /* on a sane system, what we'd do is hike up the process + * tree til we found a non-root user. alas, this is UNIX. */ + const char* realuser = getenv("SUDO_USER"); + if (realuser == null) realuser = "nobody"; + + say("\x1b[1;33mwarning:\x1b[m you are running \x1b[4m"); + size_t namelen = strlen(argv[0]); + write(2,argv[0],namelen); + say("\x1b[24m as \x1b[1mroot\x1b[m! dropping to \x1b[1m"); + write(2,realuser,strlen(realuser)); + setenv("USER", realuser, true); + say("\x1b[m to prevent malicious behavior\n"); + + struct passwd* nobody = getpwnam(realuser); + if (nobody == null) { + say("\x1b[1;31mfatal:\x1b[m could not get UID to drop privileges; bailing"); + return bad_user; + } else { + setenv("HOME", nobody -> pw_dir, true); + setenv("SHELL", "/dev/null", true); + setuid(nobody -> pw_uid); + } + + } +# endif + + const unsigned char chars = (sizeof str_num - 1) + + ((mode == upper) ? (sizeof str_ucase - 1) : + ((mode == lower) ? (sizeof str_lcase - 1) : + ((sizeof str_ucase - 1) + (sizeof str_lcase - 1)))); + const char* tbl = (mode == upper) ? reftbl : + ((mode == lower) ? reftbl_lcase : reftbl); + + char buf[len+1]; + /* *buf = 0; */ + + unsigned char noise[len]; + for (size_t i = 0; i #include #include #include #define sz(x) ( sizeof (x) / sizeof (x) [0] ) + enum /* constants */ { null = 0, - - /* ascii address space */ - numspace = (0x39 - 0x30) + 1, /* 10 */ - alphaspace = (0x5a - 0x41) + 1, /* 26 */ - smallalphaspace = (0x7a - 0x61) + 1, /* 26 */ - - /* base representations */ - imaxbase = numspace + alphaspace, /* 36 */ - maxbase = imaxbase + smallalphaspace /* 62 */ }; typedef unsigned long long word; typedef _Bool bool; enum { false = 0, true = 1 }; -typedef struct pair { uint8_t val; const char* str; } pair; #define error_list \ e(domain, "bad argument passed for domain") \ e(find, "could not find key in table") \ e(syntax, "invalid syntax") \ @@ -71,19 +62,16 @@ # define e(name, desc) bad_##name, error_list # undef e } bad; -bad tblget(size_t stacksz, const pair* haystack, const char* needle, uint8_t* val) { - for (size_t i = 0; i base) return bad_domain; - - val *= base; - val += v; - } - - *ret = val; - return ok; -} - -bad atoi(word base, const char* s, word* ret) { - /* s must be a null-terminated ASCII numeral string */ - if (base > maxbase) return bad_base; - - /* override the default base if it's a basèd literal */ - if (s[0] == '@' || base == 0) return asctoi(s + (s[0]=='@'),ret); - else if (s[0] == '0' && s[1] == 'x') base = 16, s += 2; - else if (s[0] == '0' && s[1] == 'd') base = 10, s += 2; - else if (s[0] == '0' && s[1] == 'b') base = 2, s += 2; - else if (s[0] == '0' && s[1] == 't') base = 3, s += 2; - else if (s[0] == '0') base = 8, s += 1; - - bool insens = (base <= imaxbase); - word val = 0; - - for (;*s!=null;++s) { - uint8_t v = *s; - if(v >= 0x30 && v <= 0x39) v -= 0x30; else { - if(v >= 0x61 && v <= 0x7a) { - if (insens) v -= 0x20; else { - v = numspace + alphaspace + (v - 0x61); - goto checkval; - } - } - if(v >= 0x41 && v <= 0x5a) v = numspace + (v - 0x41); - else return bad_domain; - } - checkval: if (v >= base) return bad_domain; - - val *= base; - val += v; - } - - *ret = val; - return ok; -} - -/* needed for efficiency's sake, but really sucky - - * this table needs to be kept in sync with the - * itoa algorithm by hand. unfortunately, given C's - * abject lack of metaprogramming, we have to do this - * by hand. */ -const char baseref[] = /* numerals[10] */ "0123456789" - /* bigalpha[26] */ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - /* smallalpha[26] */ "abcdefghijklmnopqrstuvwxyz"; -_Static_assert (sizeof baseref - 1 == maxbase); - -bad itoasc(word val, const char* buf_start, char* buf_end, char** newbuf) { - char* ptr = buf_end; - - *ptr-- = 0; - while(val > 0) { - if (ptr < buf_start) return bad_overflow; - word rem = val % 128; - val /= 128; - *ptr-- = (char)rem; - } - - if (newbuf != null) *newbuf = ptr + 1; - return ok; -} - -bool lowercase = false; -bad itoa(word base, word val, const char* buf_start, - char* buf_end, char** newbuf) { - - char* ptr = buf_end; - - if (base > maxbase) return bad_base; - if (base == 0) return itoasc(val, buf_start, buf_end, newbuf); - - *ptr-- = 0; - if (val == 0) *ptr-- = '0'; - else while(val > 0) { - if (ptr < buf_start) return bad_overflow; - word rem = val % base; - val /= base; - char out = baseref[rem]; - if (lowercase && base <= imaxbase) - if (out >= 'A' && out <= 'Z') - out += ('a' - 'A'); - *ptr-- = out; - } - - if (newbuf != null) *newbuf = ptr + 1; - return ok; -} +/* import the conversion utilities */ +typedef bad iaia_error_type; +typedef word iaia_word_type; +enum /* iaia synonyms */ { + iaia_e_ok = ok, + iaia_e_domain = bad_domain, + iaia_e_base = bad_base, + iaia_e_overflow = bad_overflow, +}; +#include "clib/iaia.c" bad run(const int argc, const char** argv) { # ifndef _POSIX_IO /* fuck your buffering, it only ever makes * things worse */ @@ -262,10 +157,11 @@ forposix(size_t pfxstrlen); bool raw = false; bool prefix = false; + bool lowercase = false; for (const char** arg = argv + 1; *arg != null; ++arg) { uint8_t tblval; if (*arg[0] == '%') { ++ *arg; goto number; } else if (!raw && (tblget(sz(argtbl),argtbl, *arg, &tblval) == ok)) { @@ -341,11 +237,11 @@ for (const char** s = in_vals; *s != null; ++s) { word val; bad e = atoi(base[set_in], *s, &val); if (e == ok) { - bad e = itoa(base[set_out], val, buf, ptr, &ptr); + bad e = itoa(base[set_out], val, buf, ptr, &ptr, lowercase); if (prefix) { if (pfxstr != null) { print(pfxstrlen, pfxstr); } else if (base[set_out] < sz(prefixes)) { print((size_t)prefixes[base[set_out]][0],