util  Check-in [42ea2c7296]

Overview
Comment:add mkpw
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 42ea2c7296fcf7fbdfb520435719ffece5a2b9d6db8baf6affc8e0521c1d84a6
User & Date: lexi on 2019-07-19 11:34:42
Other Links: manifest | tags
Context
2019-07-19
11:47
switch from syscall to getrandom check-in: 6b3b3fa87f user: lexi tags: trunk
11:34
add mkpw check-in: 42ea2c7296 user: lexi tags: trunk
06:37
fix dumb bugs check-in: e3d4aa2fb2 user: lexi tags: trunk
Changes

Added clib/iaia.c version [b9f828e90b].

            1  +/* [ʞ] iaia.c
            2  + *  ~ lexi hale <lexi@hale.su>
            3  + *  # include "iaia.c"
            4  + *  # define _IAIA_FN_{ATOI,ITOA,ASCTOI,ITOASC}
            5  + *  : typedef iaia_word_type,
            6  + *            iaia_error_type
            7  + *  : enum iaia_e_domain,
            8  + *         iaia_e_base,
            9  + *         iaia_e_overflow
           10  + *  ? arbitrary-base integer conversion functions
           11  + */
           12  +
           13  +#ifndef _IAIA_FN_ATOI
           14  +#	define _IAIA_FN_ATOI atoi
           15  +#endif
           16  +#ifndef _IAIA_FN_ITOA
           17  +#	define _IAIA_FN_ITOA itoa
           18  +#endif
           19  +#ifndef _IAIA_FN_ASCTOI
           20  +#	define _IAIA_FN_ASCTOI asctoi
           21  +#endif
           22  +#ifndef _IAIA_FN_ITOASC
           23  +#	define _IAIA_FN_ITOASC itoasc
           24  +#endif
           25  +
           26  +enum /* constants */ {
           27  +
           28  +	/* ascii address space */
           29  +	numspace        = (0x39 - 0x30) + 1, /* 10 */
           30  +	alphaspace      = (0x5a - 0x41) + 1, /* 26 */
           31  +	smallalphaspace = (0x7a - 0x61) + 1, /* 26 */
           32  +
           33  +	/* base representations */
           34  +	imaxbase = numspace + alphaspace,    /* 36 */
           35  +	maxbase = imaxbase + smallalphaspace /* 62 */
           36  +};
           37  +
           38  +/* -- string to integer converters -- */
           39  +
           40  +iaia_error_type _IAIA_FN_ASCTOI(const char* s, iaia_word_type* ret) {
           41  +	iaia_word_type val = 0;
           42  +	enum { base = 128 };
           43  +
           44  +	for (;*s!=null;++s) {
           45  +		uint8_t v = *s;
           46  +		if (v > base) return iaia_e_domain;
           47  +
           48  +		val *= base;
           49  +		val += v;
           50  +	}
           51  +
           52  +	*ret = val;
           53  +	return ok;
           54  +}
           55  +
           56  +iaia_error_type _IAIA_FN_ATOI(iaia_word_type base, const char* s, iaia_word_type* ret) {
           57  +	/* s must be a null-terminated ASCII numeral string */
           58  +	if (base > maxbase) return iaia_e_base;
           59  +
           60  +	/* override the default base if it's a basèd literal */
           61  +	if (s[0] == '@' || base == 0) return _IAIA_FN_ASCTOI(s + (s[0]=='@'),ret);
           62  +	else if (s[0] == '0' && s[1] == 'x') base = 16, s += 2;
           63  +	else if (s[0] == '0' && s[1] == 'd') base = 10, s += 2;
           64  +	else if (s[0] == '0' && s[1] == 'b') base =  2, s += 2;
           65  +	else if (s[0] == '0' && s[1] == 't') base =  3, s += 2;
           66  +	else if (s[0] == '0')                base =  8, s += 1;
           67  +
           68  +	bool insens = (base <= imaxbase);
           69  +	iaia_word_type val = 0;
           70  +
           71  +	for (;*s!=null;++s) {
           72  +		uint8_t v = *s;
           73  +		if(v >= 0x30 && v <= 0x39) v -= 0x30; else {
           74  +			if(v >= 0x61 && v <= 0x7a) {
           75  +				if (insens) v -= 0x20; else {
           76  +					v = numspace + alphaspace + (v - 0x61);
           77  +					goto checkval;
           78  +				}
           79  +			}
           80  +			if(v >= 0x41 && v <= 0x5a) v = numspace + (v - 0x41);
           81  +				else return iaia_e_domain;
           82  +		}
           83  +		checkval: if (v >= base) return iaia_e_domain;
           84  +
           85  +		val *= base;
           86  +		val += v;
           87  +	}
           88  +
           89  +	*ret = val;
           90  +	return iaia_e_ok;
           91  +}
           92  +
           93  +
           94  +/* -- integer to string converters -- */
           95  +
           96  +/* needed for efficiency's sake, but really sucky -
           97  + * this table needs to be kept in sync with the
           98  + * itoa algorithm by hand. unfortunately, given C's
           99  + * abject lack of metaprogramming, we have to do this
          100  + * by hand. */
          101  +const char iaia_ref_table[] = /* numerals[10] */ "0123456789"
          102  +	/* bigalpha[26] */ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
          103  +	/* smallalpha[26] */ "abcdefghijklmnopqrstuvwxyz";
          104  +_Static_assert (sizeof iaia_ref_table - 1 == maxbase);
          105  +
          106  +iaia_error_type _IAIA_FN_ITOASC(iaia_word_type val, const char* buf_start, char* buf_end, char** newbuf) {
          107  +	char* ptr = buf_end;
          108  +
          109  +	*ptr-- = 0;
          110  +	while(val > 0) {
          111  +		if (ptr < buf_start) return iaia_e_overflow;
          112  +		iaia_word_type rem = val % 128;
          113  +		val /= 128;
          114  +		*ptr-- = (char)rem;
          115  +	}
          116  +
          117  +	if (newbuf != null) *newbuf = ptr + 1;
          118  +	return ok;
          119  +}
          120  +
          121  +iaia_error_type _IAIA_FN_ITOA(iaia_word_type base, iaia_word_type val, const char* buf_start,
          122  +		char* buf_end, char** newbuf, bool lowercase) {
          123  +
          124  +	char* ptr = buf_end;
          125  +
          126  +	if (base > maxbase) return iaia_e_base;
          127  +	if (base == 0) return _IAIA_FN_ITOASC(val, buf_start, buf_end, newbuf);
          128  +
          129  +	*ptr-- = 0;
          130  +	if (val == 0) *ptr-- = '0';
          131  +	else while(val > 0) {
          132  +		if (ptr < buf_start) return iaia_e_overflow;
          133  +		iaia_word_type rem = val % base;
          134  +		val /= base;
          135  +		char out = iaia_ref_table[rem];
          136  +		if (lowercase && base <= imaxbase)
          137  +			if (out >= 'A' && out <= 'Z')
          138  +				out += ('a' - 'A');
          139  +		*ptr-- = out;
          140  +	}
          141  +
          142  +	if (newbuf != null) *newbuf = ptr + 1;
          143  +	return iaia_e_ok;
          144  +}
          145  +

Added clib/tbl.c version [92768be859].

            1  +/* [ʞ] tbl.c
            2  + *  ~ lexi hale <lexi@hale.su>
            3  + *  ? simple table-scanning function to make
            4  + *    parsing easier */
            5  +
            6  +struct tblrow { tbl_word_type val; const char* str; };
            7  +
            8  +tbl_error_type tblget(size_t stacksz, const struct tblrow* haystack, const char* needle, tbl_word_type* val) {
            9  +	for (size_t i = 0; i<stacksz; ++i) {
           10  +		if (strcmp(haystack[i].str, needle) == ok) {
           11  +			*val = haystack[i].val;
           12  +			return tbl_ok;
           13  +		}
           14  +	}
           15  +	return tbl_error;
           16  +}

Added mkpw.c version [a0f03f1e22].

            1  +/* [ʞ] mkpw.c - make password
            2  + *  ~ lexi hale <lexi@hale.su>
            3  + *  © AGPLv3
            4  + *  $ cc -O4 mkpw.c -omkpw [-D_CLIPBOARD]
            5  + *    - D_CLIPBOARD enables mkpw to automatically
            6  + *      copy new passwords to the clipboard. it
            7  + *      does this by attempting to execute a sequence
            8  + *      of binaries, and then writing the password
            9  + *      to STDIN of the binary that succeeds.
           10  + *  ? generates passwords
           11  + *  → mkpw is unlikely to be portable to non-POSIX
           12  + *    systems, but should run fine on Linux as well
           13  + *    as BSDs with getrandom() support.
           14  + */
           15  +
           16  +#include <unistd.h>
           17  +#include <sys/random.h>
           18  +#include <stddef.h>
           19  +#include <stdint.h>
           20  +#include <string.h>
           21  +#define sz(a) ( sizeof (a) / sizeof (a) [0] )
           22  +#define say(x) (write(2, (x), sizeof (x)))
           23  +
           24  +#ifdef _CLIPBOARD
           25  +#	include <sys/types.h>
           26  +#	include <pwd.h>
           27  +#	include <stdlib.h>
           28  +#	define _cl_opt o('n',nocopy,copy = false)
           29  +#else
           30  +#	define _cl_opt
           31  +#endif
           32  + 
           33  +#define options \
           34  +	o('l',lower,mode = lower) \
           35  +	o('m',mix,mode = mix) \
           36  +	o('u',upper,mode = upper)\
           37  +	_cl_opt
           38  +
           39  +enum /* constants */ {
           40  +	null = 0, true = 1, false = 0
           41  +};
           42  +
           43  +typedef enum bad {
           44  +	ok = 0,
           45  +	fail = 1,
           46  +	bad_user,
           47  +	bad_option,
           48  +	bad_syntax,
           49  +	bad_usage = 64, /* fleabsd idiom */
           50  +} bad;
           51  +
           52  +typedef _Bool bool;
           53  +typedef unsigned long long iaia_word_type;
           54  +typedef bad iaia_error_type;
           55  +enum /* iaia errors */ {
           56  +	iaia_e_ok = ok,
           57  +	iaia_e_base = fail,
           58  +	iaia_e_domain = fail,
           59  +	iaia_e_overflow = fail,
           60  +};
           61  +#define _IAIA_FN_ATOI katoi
           62  +#include "clib/iaia.c"
           63  +
           64  +typedef enum {
           65  +	tbl_ok = ok, tbl_error = fail
           66  +} tbl_error_type;
           67  +typedef unsigned char tbl_word_type;
           68  +#include "clib/tbl.c"
           69  +
           70  +enum args {
           71  +#define o(short, long, code) arg_##long,
           72  +	options
           73  +#undef o
           74  +};
           75  +
           76  +struct tblrow argtbl[] = {
           77  +#define o(short, long, code) {arg_##long, #long},
           78  +	options
           79  +#undef o
           80  +};
           81  +
           82  +#define str_ucase "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
           83  +#define str_lcase "abcdefghijklmnopqrstuvwxyz"
           84  +#define str_num "0123456789" 
           85  +const char* reftbl = str_num str_ucase str_lcase;
           86  +const char* reftbl_lcase = str_num str_lcase;
           87  +
           88  +#ifdef _CLIPBOARD
           89  +char* const* cbd_cmds[] = {
           90  +	/* NOTE: these commands must be specified in order of
           91  +	 * most- to least-specific. more than one utility may
           92  +	 * be present on a given system, so we need to make sure
           93  +	 * the right one is called. */
           94  +	(char* const[]){"termux-clipboard-set", null},
           95  +	(char* const[]){"xsel", "-bi", null},
           96  +	/* TODO: allow command to be specified by env var */
           97  +	null
           98  +};
           99  +#endif
          100  +int main(int argc, const char** argv) {
          101  +	if (argc == 0) return -1;
          102  +	if (argc == 1) { 
          103  +		size_t namelen = strlen(argv[0]);
          104  +		say("\x1b[1musage:\x1b[m ");
          105  +		write(2, argv[0], namelen);
          106  +		say(" [-lmu"
          107  +#		ifdef _CLIPBOARD		
          108  +			"n"
          109  +#		endif
          110  +		"] [--lower] [--mix] [--upper]"
          111  +#		ifdef _CLIPBOARD		
          112  +			" [--nocopy]"
          113  +#		endif
          114  +		" <length> [<quantity>]");
          115  +		return bad_usage;
          116  +	}
          117  +
          118  +	enum { upper, mix, lower } mode = lower;
          119  +	unsigned long long len, q = 1;
          120  +	bool gotlen = false;
          121  +	bool gotq = false;
          122  +
          123  +#	ifdef _CLIPBOARD
          124  +		bool copy = true;
          125  +#	endif
          126  +	for (const char** arg = argv; *arg != null; ++arg) {
          127  +		if ((*arg)[0] == '-') {
          128  +			if ((*arg)[1] == '-') { /* long opt */
          129  +				unsigned char a;
          130  +				if (tblget(sz(argtbl), argtbl, *arg + 2, &a) == ok) switch (a) {
          131  +#					define o(short, long, code) case arg_##long:code;break;
          132  +						options
          133  +#					undef o
          134  +				} else {
          135  +					return bad_option;
          136  +				}
          137  +			} else { /* short opt */
          138  +				for(const char* ptr = (*arg) + 1; *ptr != 0; ++ptr) {
          139  +					switch(*ptr) {
          140  +#						define o(short, long, code) case short:code;break;
          141  + 		   					options
          142  +#						undef o
          143  +						default: return bad_option;
          144  +					}
          145  +				}
          146  +			}
          147  +		} else {
          148  +			if (gotq) return bad_syntax;
          149  +			else if (gotlen) {
          150  +				if (katoi(10, *arg, &q) == ok) {
          151  +					gotq = true;
          152  +				}
          153  +			} else if(katoi(10, *arg, &len) == ok) {
          154  +				gotlen = true;
          155  +			}
          156  +		}
          157  +	}
          158  +	if (!gotlen) return bad_syntax;
          159  +#	ifdef _CLIPBOARD
          160  +	if (geteuid() == 0 && copy) {
          161  +		/* on a sane system, what we'd do is hike up the process
          162  +		 * tree til we found a non-root user. alas, this is UNIX. */
          163  +		const char* realuser = getenv("SUDO_USER");
          164  +		if (realuser == null) realuser = "nobody";
          165  +
          166  +		say("\x1b[1;33mwarning:\x1b[m you are running \x1b[4m");
          167  +			size_t namelen = strlen(argv[0]);
          168  +			write(2,argv[0],namelen);
          169  +		say("\x1b[24m as \x1b[1mroot\x1b[m! dropping to \x1b[1m");
          170  +			write(2,realuser,strlen(realuser));
          171  +			setenv("USER", realuser, true);
          172  +		say("\x1b[m to prevent malicious behavior\n");
          173  +
          174  +		struct passwd* nobody = getpwnam(realuser);
          175  +		if (nobody == null) {
          176  +			say("\x1b[1;31mfatal:\x1b[m could not get UID to drop privileges; bailing");
          177  +			return bad_user;
          178  +		} else {
          179  +			setenv("HOME", nobody -> pw_dir, true);
          180  +			setenv("SHELL", "/dev/null", true);
          181  +			setuid(nobody -> pw_uid);
          182  +		}
          183  +
          184  +	}
          185  +#	endif
          186  +	
          187  +	const unsigned char chars = (sizeof str_num - 1) +
          188  +		((mode == upper) ? (sizeof str_ucase - 1) :
          189  +		((mode == lower) ? (sizeof str_lcase - 1) :
          190  +			((sizeof str_ucase - 1) + (sizeof str_lcase - 1))));
          191  +	const char* tbl = (mode == upper) ? reftbl :
          192  +			((mode == lower) ? reftbl_lcase : reftbl);
          193  +
          194  +	char buf[len+1];
          195  +	/* *buf = 0; */
          196  +
          197  +	unsigned char noise[len];
          198  +	for (size_t i = 0; i<q; ++i) {
          199  +		unsigned char* cur = noise;
          200  +		getrandom(noise, len, 0);
          201  +
          202  +		for(char* ptr = buf; ptr < buf + len; ++ptr, ++cur) {
          203  +			*ptr = tbl[*cur % chars]; /* such a waste of entropy… :( */
          204  +		}
          205  +
          206  +		buf[len] = '\n';
          207  +		write(1, buf, len + 1);
          208  +	}
          209  +
          210  +#	ifdef _CLIPBOARD
          211  +		if (copy) {
          212  +			char* const clipboard_env = getenv("mkpw_clipboard_setter");
          213  +			char* const clipboard_env_arg = getenv("mkpw_clipboard_setter_arg");
          214  +			// FIXME: allow multiple args
          215  +			int fds[2];
          216  +			if (pipe(fds) != 0) return 63;
          217  +			if (!fork()) {
          218  +				close(fds[1]);
          219  +				dup2(fds[0], 0);
          220  +				if (clipboard_env != null) {
          221  +					execvp(clipboard_env, (char* const[]){
          222  +							clipboard_env, clipboard_env_arg, null});
          223  +					return -1;
          224  +				} else for(char* const** cmd = cbd_cmds; *cmd != null; ++cmd) {
          225  +					execvp((*cmd)[0], *cmd);
          226  +				}
          227  +				return -1; /* exec failed */
          228  +			} else {
          229  +				close(fds[0]);
          230  +				write(fds[1], buf, len);
          231  +				write(fds[1], "\n", 1);
          232  +				close(fds[1]);
          233  +			}
          234  +		}
          235  +#	endif
          236  +	
          237  +	return ok;
          238  +}

Modified ord.c from [1556b70bdd] to [4cd517ba89].

    34     34   #	define forlibc(x) x
    35     35   #endif
    36     36   #include <stddef.h>
    37     37   #include <stdint.h>
    38     38   #include <string.h>
    39     39   #include <limits.h>
    40     40   #define sz(x) ( sizeof (x) / sizeof (x) [0] )
           41  +
    41     42   
    42     43   enum /* constants */ {
    43     44   	null = 0,
    44         -
    45         -	/* ascii address space */
    46         -	numspace        = (0x39 - 0x30) + 1, /* 10 */
    47         -	alphaspace      = (0x5a - 0x41) + 1, /* 26 */
    48         -	smallalphaspace = (0x7a - 0x61) + 1, /* 26 */
    49         -
    50         -	/* base representations */
    51         -	imaxbase = numspace + alphaspace,    /* 36 */
    52         -	maxbase = imaxbase + smallalphaspace /* 62 */
    53     45   };
    54     46   
    55     47   typedef unsigned long long word;
    56     48   typedef _Bool bool;
    57     49   enum { false = 0, true = 1 };
    58     50   
    59         -typedef struct pair { uint8_t val; const char* str; } pair;
    60     51   
    61     52   #define error_list \
    62     53   	e(domain, "bad argument passed for domain") \
    63     54   	e(find, "could not find key in table") \
    64     55   	e(syntax, "invalid syntax") \
    65     56   	e(base, "that base is out of range") \
    66     57   	e(overflow, "a memory overflow has occurred") \
................................................................................
    69     60   typedef enum bad {
    70     61   	ok = 0, fail = 1,
    71     62   #	define e(name, desc) bad_##name,
    72     63   		error_list
    73     64   #	undef e
    74     65   } bad;
    75     66   
    76         -bad tblget(size_t stacksz, const pair* haystack, const char* needle, uint8_t* val) {
    77         -	for (size_t i = 0; i<stacksz; ++i) {
    78         -		if (strcmp(haystack[i].str, needle) == ok) {
    79         -			*val = haystack[i].val;
    80         -			return ok;
    81         -		}
    82         -	}
    83         -	return bad_find;
    84         -}
           67  +typedef enum {
           68  +	tbl_ok = ok, tbl_error = bad_find
           69  +} tbl_error_type;
           70  +typedef unsigned char tbl_word_type;
           71  +#include "clib/tbl.c"
           72  +typedef struct tblrow pair;
    85     73   
    86     74   enum argument {
    87     75   	arg_to, arg_set, arg_base,
    88     76   
    89     77   	arg_asc,
    90     78   
    91     79   	arg_bin, arg_trn, arg_oct, arg_dec,
................................................................................
   137    125   	{switch_prefix, "-p"}, {switch_prefix, "--prefix"},
   138    126   	{switch_lowercase, "-l"}, {switch_lowercase, "--lowercase"},
   139    127   	{param_prefix, "-m"}, {param_prefix, "--manual-prefix"},
   140    128   
   141    129   	{arg_ebcdic, "ebcdic"},
   142    130   };
   143    131   
   144         -bad asctoi(const char* s, word* ret) {
   145         -	word val = 0;
   146         -	enum { base = 128 };
   147         -
   148         -	for (;*s!=null;++s) {
   149         -		uint8_t v = *s;
   150         -		if (v > base) return bad_domain;
   151         -
   152         -		val *= base;
   153         -		val += v;
   154         -	}
   155         -
   156         -	*ret = val;
   157         -	return ok;
   158         -}
   159         -
   160         -bad atoi(word base, const char* s, word* ret) {
   161         -	/* s must be a null-terminated ASCII numeral string */
   162         -	if (base > maxbase) return bad_base;
   163         -
   164         -	/* override the default base if it's a basèd literal */
   165         -	if (s[0] == '@' || base == 0) return asctoi(s + (s[0]=='@'),ret);
   166         -	else if (s[0] == '0' && s[1] == 'x') base = 16, s += 2;
   167         -	else if (s[0] == '0' && s[1] == 'd') base = 10, s += 2;
   168         -	else if (s[0] == '0' && s[1] == 'b') base =  2, s += 2;
   169         -	else if (s[0] == '0' && s[1] == 't') base =  3, s += 2;
   170         -	else if (s[0] == '0')                base =  8, s += 1;
   171         -
   172         -	bool insens = (base <= imaxbase);
   173         -	word val = 0;
   174         -
   175         -	for (;*s!=null;++s) {
   176         -		uint8_t v = *s;
   177         -		if(v >= 0x30 && v <= 0x39) v -= 0x30; else {
   178         -			if(v >= 0x61 && v <= 0x7a) {
   179         -				if (insens) v -= 0x20; else {
   180         -					v = numspace + alphaspace + (v - 0x61);
   181         -					goto checkval;
   182         -				}
   183         -			}
   184         -			if(v >= 0x41 && v <= 0x5a) v = numspace + (v - 0x41);
   185         -				else return bad_domain;
   186         -		}
   187         -		checkval: if (v >= base) return bad_domain;
   188         -
   189         -		val *= base;
   190         -		val += v;
   191         -	}
   192         -
   193         -	*ret = val;
   194         -	return ok;
   195         -}
   196         -
   197         -/* needed for efficiency's sake, but really sucky -
   198         - * this table needs to be kept in sync with the
   199         - * itoa algorithm by hand. unfortunately, given C's
   200         - * abject lack of metaprogramming, we have to do this
   201         - * by hand. */
   202         -const char baseref[] = /* numerals[10] */ "0123456789"
   203         -	/* bigalpha[26] */ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
   204         -	/* smallalpha[26] */ "abcdefghijklmnopqrstuvwxyz";
   205         -_Static_assert (sizeof baseref - 1 == maxbase);
   206         -
   207         -bad itoasc(word val, const char* buf_start, char* buf_end, char** newbuf) {
   208         -	char* ptr = buf_end;
   209         -
   210         -	*ptr-- = 0;
   211         -	while(val > 0) {
   212         -		if (ptr < buf_start) return bad_overflow;
   213         -		word rem = val % 128;
   214         -		val /= 128;
   215         -		*ptr-- = (char)rem;
   216         -	}
   217         -
   218         -	if (newbuf != null) *newbuf = ptr + 1;
   219         -	return ok;
   220         -}
   221         -
   222         -bool lowercase = false;
   223         -bad itoa(word base, word val, const char* buf_start,
   224         -		char* buf_end, char** newbuf) {
   225         -
   226         -	char* ptr = buf_end;
   227         -
   228         -	if (base > maxbase) return bad_base;
   229         -	if (base == 0) return itoasc(val, buf_start, buf_end, newbuf);
   230         -
   231         -	*ptr-- = 0;
   232         -	if (val == 0) *ptr-- = '0';
   233         -	else while(val > 0) {
   234         -		if (ptr < buf_start) return bad_overflow;
   235         -		word rem = val % base;
   236         -		val /= base;
   237         -		char out = baseref[rem];
   238         -		if (lowercase && base <= imaxbase)
   239         -			if (out >= 'A' && out <= 'Z')
   240         -				out += ('a' - 'A');
   241         -		*ptr-- = out;
   242         -	}
   243         -
   244         -	if (newbuf != null) *newbuf = ptr + 1;
   245         -	return ok;
   246         -}
          132  +/* import the conversion utilities */
          133  +typedef bad iaia_error_type;
          134  +typedef word iaia_word_type;
          135  +enum /* iaia synonyms */ {
          136  +	iaia_e_ok = ok,
          137  +	iaia_e_domain = bad_domain,
          138  +	iaia_e_base = bad_base,
          139  +	iaia_e_overflow = bad_overflow,
          140  +};
          141  +#include "clib/iaia.c"
   247    142   
   248    143   bad run(const int argc, const char** argv) {
   249    144   #	ifndef _POSIX_IO
   250    145   		/* fuck your buffering, it only ever makes
   251    146   		 * things worse */
   252    147   		setvbuf(stdout,null,_IONBF);
   253    148   #	endif
................................................................................
   260    155   	const char** invalp = in_vals;
   261    156   	const char* pfxstr;
   262    157   	forposix(size_t pfxstrlen);
   263    158   
   264    159   	
   265    160   	bool raw = false;
   266    161   	bool prefix = false;
          162  +	bool lowercase = false;
   267    163   
   268    164   	for (const char** arg = argv + 1; *arg != null; ++arg) {
   269    165   		uint8_t tblval;
   270    166   		if (*arg[0] == '%') { ++ *arg; goto number; } else
   271    167   		if (!raw && (tblget(sz(argtbl),argtbl, *arg, &tblval) == ok)) {
   272    168   			enum argument symbol = (enum argument) tblval;
   273    169   			switch (symbol) {
................................................................................
   339    235   	char* ptr = (buf + bufmax) - 1;
   340    236   	forposix(char* lastptr = ptr);
   341    237   
   342    238   	for (const char** s = in_vals; *s != null; ++s) {
   343    239   		word val;
   344    240   		bad e = atoi(base[set_in], *s, &val);
   345    241   		if (e == ok) {
   346         -			bad e = itoa(base[set_out], val, buf, ptr, &ptr);
          242  +			bad e = itoa(base[set_out], val, buf, ptr, &ptr, lowercase);
   347    243   
   348    244   			if (prefix) {
   349    245   				if (pfxstr != null) { print(pfxstrlen, pfxstr); }
   350    246   				else if (base[set_out] < sz(prefixes)) {
   351    247   					print((size_t)prefixes[base[set_out]][0],
   352    248   							prefixes[base[set_out]] + 1);
   353    249   				}