Overview
Comment: | add mkpw |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
42ea2c7296fcf7fbdfb520435719ffec |
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 }