README: Add "Security record" section
[s-mailx.git] / auxlily.c
blobac017895f9a64adbd8ca854ffaba09351b5faf4d
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Auxiliary functions that don't fit anywhere else.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2017 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
6 */
7 /*
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
35 #undef n_FILE
36 #define n_FILE auxlily
38 #ifndef HAVE_AMALGAMATION
39 # include "nail.h"
40 #endif
42 #include <sys/utsname.h>
44 #ifdef HAVE_GETRANDOM
45 # include HAVE_GETRANDOM_HEADER
46 #endif
48 #ifdef HAVE_SOCKETS
49 # ifdef HAVE_GETADDRINFO
50 # include <sys/socket.h>
51 # endif
53 # include <netdb.h>
54 #endif
56 #ifndef HAVE_POSIX_RANDOM
57 union rand_state{
58 struct rand_arc4{
59 ui8_t _dat[256];
60 ui8_t _i;
61 ui8_t _j;
62 ui8_t __pad[6];
63 } a;
64 ui8_t b8[sizeof(struct rand_arc4)];
65 ui32_t b32[sizeof(struct rand_arc4) / sizeof(ui32_t)];
67 #endif
69 #ifdef HAVE_ERRORS
70 struct a_aux_err_node{
71 struct a_aux_err_node *ae_next;
72 struct n_string ae_str;
74 #endif
76 static ui8_t a_aux_idec_atoi[256] = {
77 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
78 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
79 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
80 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
81 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x01,
82 0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0xFF,0xFF,
83 0xFF,0xFF,0xFF,0xFF,0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,
84 0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,
85 0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,
86 0x23,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0A,0x0B,0x0C,
87 0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,
88 0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,
89 0x21,0x22,0x23,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
90 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
91 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
92 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
93 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
94 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
95 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
96 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
97 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
98 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
99 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
100 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
101 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
102 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
105 #define a_X(X) ((ui64_t)-1 / (X))
106 static ui64_t const a_aux_idec_cutlimit[35] = {
107 a_X( 2), a_X( 3), a_X( 4), a_X( 5), a_X( 6), a_X( 7), a_X( 8),
108 a_X( 9), a_X(10), a_X(11), a_X(12), a_X(13), a_X(14), a_X(15),
109 a_X(16), a_X(17), a_X(18), a_X(19), a_X(20), a_X(21), a_X(22),
110 a_X(23), a_X(24), a_X(25), a_X(26), a_X(27), a_X(28), a_X(29),
111 a_X(30), a_X(31), a_X(32), a_X(33), a_X(34), a_X(35), a_X(36)
113 #undef a_X
115 #ifndef HAVE_POSIX_RANDOM
116 static union rand_state *a_aux_rand;
117 #endif
119 /* Error ring, for `errors' */
120 #ifdef HAVE_ERRORS
121 static struct a_aux_err_node *a_aux_err_head, *a_aux_err_tail;
122 static size_t a_aux_err_cnt, a_aux_err_cnt_noted;
123 #endif
124 static size_t a_aux_err_linelen;
126 /* Our ARC4 random generator with its completely unacademical pseudo
127 * initialization (shall /dev/urandom fail) */
128 #ifndef HAVE_POSIX_RANDOM
129 static void a_aux_rand_init(void);
130 SINLINE ui8_t a_aux_rand_get8(void);
131 # ifndef HAVE_GETRANDOM
132 static ui32_t a_aux_rand_weak(ui32_t seed);
133 # endif
134 #endif
136 #ifndef HAVE_POSIX_RANDOM
137 static void
138 a_aux_rand_init(void){
139 # ifndef HAVE_GETRANDOM
140 # ifdef HAVE_CLOCK_GETTIME
141 struct timespec ts;
142 # else
143 struct timeval ts;
144 # endif
145 union {int fd; size_t i;} u;
146 ui32_t seed, rnd;
147 # endif
148 NYD2_ENTER;
150 a_aux_rand = smalloc(sizeof *a_aux_rand);
152 # ifdef HAVE_GETRANDOM
153 /* getrandom(2) guarantees 256 without EINTR.. */
154 n_LCTA(sizeof(a_aux_rand->a._dat) <= 256,
155 "Buffer to large to be served without EINTR error");
156 for(;;){
157 ssize_t gr;
159 gr = HAVE_GETRANDOM(a_aux_rand->a._dat, sizeof a_aux_rand->a._dat);
160 a_aux_rand->a._i = a_aux_rand->a._dat[a_aux_rand->a._dat[1] ^
161 a_aux_rand->a._dat[84]];
162 a_aux_rand->a._j = a_aux_rand->a._dat[a_aux_rand->a._dat[65] ^
163 a_aux_rand->a._dat[42]];
164 /* ..but be on the safe side */
165 if(UICMP(z, gr, ==, sizeof(a_aux_rand->a._dat)))
166 break;
167 n_msleep(250, FAL0);
170 # else
171 if((u.fd = open("/dev/urandom", O_RDONLY)) != -1){
172 bool_t ok;
174 ok = (sizeof(a_aux_rand->a._dat) == (size_t)read(u.fd, a_aux_rand->a._dat,
175 sizeof(a_aux_rand->a._dat)));
176 close(u.fd);
178 a_aux_rand->a._i = a_aux_rand->a._dat[a_aux_rand->a._dat[1] ^
179 a_aux_rand->a._dat[84]];
180 a_aux_rand->a._j = a_aux_rand->a._dat[a_aux_rand->a._dat[65] ^
181 a_aux_rand->a._dat[42]];
182 if(ok)
183 goto jleave;
186 for(seed = (uintptr_t)a_aux_rand & UI32_MAX, rnd = 21; rnd != 0; --rnd){
187 for(u.i = n_NELEM(a_aux_rand->b32); u.i-- != 0;){
188 ui32_t t, k;
190 # ifdef HAVE_CLOCK_GETTIME
191 clock_gettime(CLOCK_REALTIME, &ts);
192 t = (ui32_t)ts.tv_nsec;
193 # else
194 gettimeofday(&ts, NULL);
195 t = (ui32_t)ts.tv_usec;
196 # endif
197 if(rnd & 1)
198 t = (t >> 16) | (t << 16);
199 a_aux_rand->b32[u.i] ^= a_aux_rand_weak(seed ^ t);
200 a_aux_rand->b32[t % n_NELEM(a_aux_rand->b32)] ^= seed;
201 if(rnd == 7 || rnd == 17)
202 a_aux_rand->b32[u.i] ^= a_aux_rand_weak(seed ^ (ui32_t)ts.tv_sec);
203 k = a_aux_rand->b32[u.i] % n_NELEM(a_aux_rand->b32);
204 a_aux_rand->b32[k] ^= a_aux_rand->b32[u.i];
205 seed ^= a_aux_rand_weak(a_aux_rand->b32[k]);
206 if((rnd & 3) == 3)
207 seed ^= nextprime(seed);
211 for(u.i = 5 * sizeof(a_aux_rand->b8); u.i != 0; --u.i)
212 a_aux_rand_get8();
213 jleave:
214 # endif /* !HAVE_GETRANDOM */
215 NYD2_LEAVE;
218 SINLINE ui8_t
219 a_aux_rand_get8(void){
220 ui8_t si, sj;
222 si = a_aux_rand->a._dat[++a_aux_rand->a._i];
223 sj = a_aux_rand->a._dat[a_aux_rand->a._j += si];
224 a_aux_rand->a._dat[a_aux_rand->a._i] = sj;
225 a_aux_rand->a._dat[a_aux_rand->a._j] = si;
226 return a_aux_rand->a._dat[(ui8_t)(si + sj)];
229 # ifndef HAVE_GETRANDOM
230 static ui32_t
231 a_aux_rand_weak(ui32_t seed){
232 /* From "Random number generators: good ones are hard to find",
233 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
234 * October 1988, p. 1195.
235 * (In fact: FreeBSD 4.7, /usr/src/lib/libc/stdlib/random.c.) */
236 ui32_t hi;
238 if(seed == 0)
239 seed = 123459876;
240 hi = seed / 127773;
241 seed %= 127773;
242 seed = (seed * 16807) - (hi * 2836);
243 if((si32_t)seed < 0)
244 seed += SI32_MAX;
245 return seed;
247 # endif /* HAVE_GETRANDOM */
248 #endif /* !HAVE_POSIX_RANDOM */
250 FL size_t
251 n_screensize(void){
252 char const *cp;
253 uiz_t rv;
254 NYD2_ENTER;
256 if((cp = ok_vlook(screen)) != NULL){
257 n_idec_uiz_cp(&rv, cp, 0, NULL);
258 if(rv == 0)
259 rv = n_scrnheight;
260 }else
261 rv = n_scrnheight;
263 if(rv > 2)
264 rv -= 2;
265 NYD2_LEAVE;
266 return rv;
269 FL char const *
270 n_pager_get(char const **env_addon){
271 char const *rv;
272 NYD_ENTER;
274 rv = ok_vlook(PAGER);
276 if(env_addon != NULL){
277 *env_addon = NULL;
278 /* Update the manual upon any changes:
279 * *colour-pager*, $PAGER */
280 if(strstr(rv, "less") != NULL){
281 if(getenv("LESS") == NULL)
282 *env_addon =
283 #ifdef HAVE_TERMCAP
284 (n_psonce & n_PSO_TERMCAP_CA_MODE) ? "LESS=Ri"
285 : !(n_psonce & n_PSO_TERMCAP_DISABLE) ? "LESS=FRi" :
286 #endif
287 "LESS=FRXi";
288 }else if(strstr(rv, "lv") != NULL){
289 if(getenv("LV") == NULL)
290 *env_addon = "LV=-c";
293 NYD_LEAVE;
294 return rv;
297 FL void
298 page_or_print(FILE *fp, size_t lines)
300 int c;
301 char const *cp;
302 NYD_ENTER;
304 fflush_rewind(fp);
306 if (n_source_may_yield_control() && (cp = ok_vlook(crt)) != NULL) {
307 size_t rows;
309 if(*cp == '\0')
310 rows = (size_t)n_scrnheight;
311 else
312 n_idec_uiz_cp(&rows, cp, 0, NULL);
314 if (rows > 0 && lines == 0) {
315 while ((c = getc(fp)) != EOF)
316 if (c == '\n' && ++lines >= rows)
317 break;
318 really_rewind(fp);
321 if (lines >= rows) {
322 char const *env_add[2], *pager;
324 pager = n_pager_get(&env_add[0]);
325 env_add[1] = NULL;
326 run_command(pager, NULL, fileno(fp), COMMAND_FD_PASS, NULL,NULL,NULL,
327 env_add);
328 goto jleave;
332 while ((c = getc(fp)) != EOF)
333 putc(c, n_stdout);
334 jleave:
335 NYD_LEAVE;
338 FL enum protocol
339 which_protocol(char const *name) /* XXX (->URL (yet auxlily.c)) */
341 struct stat st;
342 char const *cp;
343 char *np;
344 size_t sz;
345 enum protocol rv = PROTO_UNKNOWN;
346 NYD_ENTER;
348 temporary_protocol_ext = NULL;
350 if (name[0] == '%' && name[1] == ':')
351 name += 2;
352 for (cp = name; *cp && *cp != ':'; cp++)
353 if (!alnumchar(*cp))
354 goto jfile;
356 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
357 if (!strncmp(name, "pop3://", 7)) {
358 #ifdef HAVE_POP3
359 rv = PROTO_POP3;
360 #else
361 n_err(_("No POP3 support compiled in\n"));
362 #endif
363 } else if (!strncmp(name, "pop3s://", 8)) {
364 #if defined HAVE_POP3 && defined HAVE_SSL
365 rv = PROTO_POP3;
366 #else
367 # ifndef HAVE_POP3
368 n_err(_("No POP3 support compiled in\n"));
369 # endif
370 # ifndef HAVE_SSL
371 n_err(_("No SSL support compiled in\n"));
372 # endif
373 #endif
375 goto jleave;
378 /* TODO This is the de facto maildir code and thus belongs into there!
379 * TODO and: we should have maildir:// and mbox:// pseudo-protos, instead of
380 * TODO or (more likely) in addition to *newfolders*) */
381 jfile:
382 rv = PROTO_FILE;
383 np = ac_alloc((sz = strlen(name)) + 4 +1);
384 memcpy(np, name, sz + 1);
385 if (!stat(name, &st)) {
386 if (S_ISDIR(st.st_mode) &&
387 (memcpy(np+sz, "/tmp", 5), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
388 (memcpy(np+sz, "/new", 5), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
389 (memcpy(np+sz, "/cur", 5), !stat(np, &st) && S_ISDIR(st.st_mode)))
390 rv = PROTO_MAILDIR;
391 } else {
392 if ((memcpy(np+sz, cp=".gz", 4), !stat(np, &st) && S_ISREG(st.st_mode)) ||
393 (memcpy(np+sz, cp=".xz",4), !stat(np,&st) && S_ISREG(st.st_mode)) ||
394 (memcpy(np+sz, cp=".bz2",5), !stat(np, &st) && S_ISREG(st.st_mode)))
395 temporary_protocol_ext = cp;
396 else if ((cp = ok_vlook(newfolders)) != NULL &&
397 !asccasecmp(cp, "maildir"))
398 rv = PROTO_MAILDIR;
400 ac_free(np);
401 jleave:
402 NYD_LEAVE;
403 return rv;
406 FL char *
407 n_c_to_hex_base16(char store[3], char c){
408 static char const itoa16[] = "0123456789ABCDEF";
409 NYD2_ENTER;
411 store[2] = '\0';
412 store[1] = itoa16[(ui8_t)c & 0x0F];
413 c = ((ui8_t)c >> 4) & 0x0F;
414 store[0] = itoa16[(ui8_t)c];
415 NYD2_LEAVE;
416 return store;
419 FL si32_t
420 n_c_from_hex_base16(char const hex[2]){
421 static ui8_t const atoi16[] = {
422 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x30-0x37 */
423 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x38-0x3F */
424 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, /* 0x40-0x47 */
425 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x48-0x4f */
426 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x50-0x57 */
427 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x58-0x5f */
428 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF /* 0x60-0x67 */
430 ui8_t i1, i2;
431 si32_t rv;
432 NYD2_ENTER;
434 if ((i1 = (ui8_t)hex[0] - '0') >= n_NELEM(atoi16) ||
435 (i2 = (ui8_t)hex[1] - '0') >= n_NELEM(atoi16))
436 goto jerr;
437 i1 = atoi16[i1];
438 i2 = atoi16[i2];
439 if ((i1 | i2) & 0xF0u)
440 goto jerr;
441 rv = i1;
442 rv <<= 4;
443 rv += i2;
444 jleave:
445 NYD2_LEAVE;
446 return rv;
447 jerr:
448 rv = -1;
449 goto jleave;
452 FL enum n_idec_state
453 n_idec_buf(void *resp, char const *cbuf, uiz_t clen, ui8_t base,
454 enum n_idec_mode idm, char const **endptr_or_null){
455 /* XXX Brute simple and */
456 ui8_t currc;
457 ui64_t res, cut;
458 enum n_idec_state rv;
459 NYD_ENTER;
461 idm &= n__IDEC_MODE_MASK;
462 rv = n_IDEC_STATE_NONE | idm;
463 res = 0;
465 if(clen == UIZ_MAX){
466 if(*cbuf == '\0')
467 goto jeinval;
468 }else if(clen == 0)
469 goto jeinval;
471 /* Leading WS */
472 while(spacechar(*cbuf))
473 if(*++cbuf == '\0' || --clen == 0)
474 goto jeinval;
476 /* Check sign */
477 switch(*cbuf){
478 case '-':
479 rv |= n_IDEC_STATE_SEEN_MINUS;
480 /* FALLTHROUGH */
481 case '+':
482 if(*++cbuf == '\0' || --clen == 0)
483 goto jeinval;
484 break;
487 /* Base detection/skip */
488 if(*cbuf != '0'){
489 if(base == 0)
490 base = 10;
491 /* Character must be valid for base */
492 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
493 if(currc >= base)
494 goto jeinval;
495 }else{
496 /* 0 always valid as is, fallback base 10 */
497 if(*++cbuf == '\0' || --clen == 0)
498 goto jleave;
500 /* Base "detection" */
501 if(base == 0 || base == 2 || base == 16){
502 switch(*cbuf){
503 case 'x':
504 case 'X':
505 if((base & 2) == 0){
506 base = 0x10;
507 goto jprefix_skip;
509 break;
510 case 'b':
511 case 'B':
512 if((base & 16) == 0){
513 base = 2; /* 0b10 */
514 /* Char after prefix must be valid */
515 jprefix_skip:
516 if(*++cbuf == '\0' || --clen == 0)
517 goto jeinval;
519 /* Character must be valid for base, invalid otherwise */
520 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
521 if(currc >= base)
522 goto jeinval;
524 break;
525 default:
526 if(base == 0)
527 base = 010;
528 break;
532 /* Character must be valid for base, EBASE otherwise */
533 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
534 if(currc >= base)
535 goto jebase;
538 for(cut = a_aux_idec_cutlimit[base - 2];;){
539 if(res >= cut){
540 if(res == cut){
541 res *= base;
542 if(res > UI64_MAX - currc)
543 goto jeover;
544 res += currc;
545 }else
546 goto jeover;
547 }else{
548 res *= base;
549 res += currc;
552 if(*++cbuf == '\0' || --clen == 0)
553 break;
555 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
556 if(currc >= base)
557 goto jebase;
560 jleave:
562 ui64_t uimask;
564 switch(rv & n__IDEC_MODE_LIMIT_MASK){
565 case n_IDEC_MODE_LIMIT_8BIT: uimask = UI8_MAX; break;
566 case n_IDEC_MODE_LIMIT_16BIT: uimask = UI16_MAX; break;
567 case n_IDEC_MODE_LIMIT_32BIT: uimask = UI32_MAX; break;
568 default: uimask = UI64_MAX; break;
570 if(rv & n_IDEC_MODE_SIGNED_TYPE)
571 uimask >>= 1;
573 if(res & ~uimask){
574 if((rv & (n_IDEC_MODE_SIGNED_TYPE | n_IDEC_STATE_SEEN_MINUS)
575 ) == (n_IDEC_MODE_SIGNED_TYPE | n_IDEC_STATE_SEEN_MINUS)){
576 if(res > uimask + 1){
577 res = uimask << 1;
578 res &= ~uimask;
579 }else{
580 res = -res;
581 break;
583 }else
584 res = uimask;
585 if(!(rv & n_IDEC_MODE_LIMIT_NOERROR))
586 rv |= n_IDEC_STATE_EOVERFLOW;
587 }else if(rv & n_IDEC_STATE_SEEN_MINUS)
588 res = -res;
589 }while(0);
591 switch(rv & n__IDEC_MODE_LIMIT_MASK){
592 case n_IDEC_MODE_LIMIT_8BIT:
593 if(rv & n_IDEC_MODE_SIGNED_TYPE)
594 *(si8_t*)resp = (si8_t)res;
595 else
596 *(ui8_t*)resp = (ui8_t)res;
597 break;
598 case n_IDEC_MODE_LIMIT_16BIT:
599 if(rv & n_IDEC_MODE_SIGNED_TYPE)
600 *(si16_t*)resp = (si16_t)res;
601 else
602 *(ui16_t*)resp = (ui16_t)res;
603 break;
604 case n_IDEC_MODE_LIMIT_32BIT:
605 if(rv & n_IDEC_MODE_SIGNED_TYPE)
606 *(si32_t*)resp = (si32_t)res;
607 else
608 *(ui32_t*)resp = (ui32_t)res;
609 break;
610 default:
611 if(rv & n_IDEC_MODE_SIGNED_TYPE)
612 *(si64_t*)resp = (si64_t)res;
613 else
614 *(ui64_t*)resp = (ui64_t)res;
615 break;
618 if(endptr_or_null != NULL)
619 *endptr_or_null = cbuf;
620 if(*cbuf == '\0' || clen == 0)
621 rv |= n_IDEC_STATE_CONSUMED;
622 NYD_LEAVE;
623 return rv;
625 jeinval:
626 rv |= n_IDEC_STATE_EINVAL;
627 goto j_maxval;
628 jebase:
629 /* Not a base error for terminator and whitespace! */
630 if(*cbuf != '\0' && !spacechar(*cbuf))
631 rv |= n_IDEC_STATE_EBASE;
632 goto jleave;
634 jeover:
635 /* Overflow error: consume input until bad character or length out */
636 for(;;){
637 if(*++cbuf == '\0' || --clen == 0)
638 break;
639 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
640 if(currc >= base)
641 break;
644 rv |= n_IDEC_STATE_EOVERFLOW;
645 j_maxval:
646 if(rv & n_IDEC_MODE_SIGNED_TYPE)
647 res = (rv & n_IDEC_STATE_SEEN_MINUS) ? (ui64_t)SI64_MIN
648 : (ui64_t)SI64_MAX;
649 else
650 res = UI64_MAX;
651 rv &= ~n_IDEC_STATE_SEEN_MINUS;
652 goto jleave;
655 FL ui32_t
656 torek_hash(char const *name)
658 /* Chris Torek's hash.
659 * NOTE: need to change *at least* mk-okey-map.pl when changing the
660 * algorithm!! */
661 ui32_t h = 0;
662 NYD_ENTER;
664 while (*name != '\0') {
665 h *= 33;
666 h += *name++;
668 NYD_LEAVE;
669 return h;
672 FL ui32_t
673 torek_ihashn(char const *dat, size_t len){
674 /* See torek_hash() */
675 char c;
676 ui32_t h;
677 NYD_ENTER;
679 for(h = 0; len > 0 && (c = *dat++) != '\0'; --len)
680 h = (h * 33) + lowerconv(c);
681 NYD_LEAVE;
682 return h;
685 FL ui32_t
686 nextprime(ui32_t n)
688 static ui32_t const primes[] = {
689 5, 11, 23, 47, 97, 157, 283,
690 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
691 131071, 262139, 524287, 1048573, 2097143, 4194301,
692 8388593, 16777213, 33554393, 67108859, 134217689,
693 268435399, 536870909, 1073741789, 2147483647
696 ui32_t i, mprime;
697 NYD_ENTER;
699 i = (n < primes[n_NELEM(primes) / 4] ? 0
700 : (n < primes[n_NELEM(primes) / 2] ? n_NELEM(primes) / 4
701 : n_NELEM(primes) / 2));
703 if ((mprime = primes[i]) > n)
704 break;
705 while (++i < n_NELEM(primes));
706 if (i == n_NELEM(primes) && mprime < n)
707 mprime = n;
708 NYD_LEAVE;
709 return mprime;
712 FL char const *
713 n_getdeadletter(void){
714 char const *cp_base, *cp;
715 NYD_ENTER;
717 cp_base = NULL;
718 jredo:
719 cp = fexpand(ok_vlook(DEAD), FEXP_LOCAL | FEXP_NSHELL);
720 if(cp == NULL || strlen(cp) >= PATH_MAX){
721 if(cp_base == NULL){
722 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
723 VAL_DEAD, n_shexp_quote_cp(cp, FAL0));
724 ok_vclear(DEAD);
725 goto jredo;
726 }else{
727 cp = savecatsep(ok_vlook(TMPDIR), '/', VAL_DEAD_BASENAME);
728 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp);
731 NYD_LEAVE;
732 return cp;
735 FL char *
736 nodename(int mayoverride)
738 static char *sys_hostname, *hostname; /* XXX free-at-exit */
740 struct utsname ut;
741 char *hn;
742 #ifdef HAVE_SOCKETS
743 # ifdef HAVE_GETADDRINFO
744 struct addrinfo hints, *res;
745 # else
746 struct hostent *hent;
747 # endif
748 #endif
749 NYD_ENTER;
751 if (mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0') {
753 } else if ((hn = sys_hostname) == NULL) {
754 uname(&ut);
755 hn = ut.nodename;
756 #ifdef HAVE_SOCKETS
757 # ifdef HAVE_GETADDRINFO
758 memset(&hints, 0, sizeof hints);
759 hints.ai_family = AF_UNSPEC;
760 hints.ai_flags = AI_CANONNAME;
761 if (getaddrinfo(hn, NULL, &hints, &res) == 0) {
762 if (res->ai_canonname != NULL) {
763 size_t l = strlen(res->ai_canonname) +1;
765 hn = ac_alloc(l);
766 memcpy(hn, res->ai_canonname, l);
768 freeaddrinfo(res);
770 # else
771 hent = gethostbyname(hn);
772 if (hent != NULL)
773 hn = hent->h_name;
774 # endif
775 #endif
776 sys_hostname = sstrdup(hn);
777 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
778 if (hn != ut.nodename)
779 ac_free(hn);
780 #endif
781 hn = sys_hostname;
784 if (hostname != NULL && hostname != sys_hostname)
785 free(hostname);
786 hostname = sstrdup(hn);
787 NYD_LEAVE;
788 return hostname;
791 FL char *
792 getrandstring(size_t length){
793 struct str b64;
794 char *data;
795 size_t i;
796 NYD_ENTER;
798 #ifndef HAVE_POSIX_RANDOM
799 if(a_aux_rand == NULL)
800 a_aux_rand_init();
801 #endif
803 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
804 * with PAD stripped is still longer than what the user requests, easy way */
805 data = n_lofi_alloc(i = length + 3);
807 #ifndef HAVE_POSIX_RANDOM
808 while(i-- > 0)
809 data[i] = (char)a_aux_rand_get8();
810 #else
811 { char *cp;
813 for(cp = data; i > 0;){
814 union {ui32_t i4; char c[4];} r;
815 size_t j;
817 r.i4 = (ui32_t)arc4random();
818 switch((j = i & 3)){
819 case 0: cp[3] = r.c[3]; j = 4;
820 case 3: cp[2] = r.c[2];
821 case 2: cp[1] = r.c[1];
822 default: cp[0] = r.c[0]; break;
824 cp += j;
825 i -= j;
828 #endif
830 assert(length + 3 < UIZ_MAX / 4);
831 b64_encode_buf(&b64, data, length + 3,
832 B64_SALLOC | B64_RFC4648URL | B64_NOPAD);
833 ac_free(data);
835 assert(b64.l >= length);
836 b64.s[length] = '\0';
837 NYD_LEAVE;
838 return b64.s;
841 FL si8_t
842 boolify(char const *inbuf, uiz_t inlen, si8_t emptyrv)
844 si8_t rv;
845 NYD_ENTER;
847 assert(inlen == 0 || inbuf != NULL);
849 if (inlen == UIZ_MAX)
850 inlen = strlen(inbuf);
852 if (inlen == 0)
853 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
854 else {
855 if ((inlen == 1 && (*inbuf == '1' || *inbuf == 'y' || *inbuf == 'Y')) ||
856 !ascncasecmp(inbuf, "true", inlen) ||
857 !ascncasecmp(inbuf, "yes", inlen) ||
858 !ascncasecmp(inbuf, "on", inlen))
859 rv = 1;
860 else if ((inlen == 1 &&
861 (*inbuf == '0' || *inbuf == 'n' || *inbuf == 'N')) ||
862 !ascncasecmp(inbuf, "false", inlen) ||
863 !ascncasecmp(inbuf, "no", inlen) ||
864 !ascncasecmp(inbuf, "off", inlen))
865 rv = 0;
866 else {
867 ui64_t ib;
869 if((n_idec_buf(&ib, inbuf, inlen, 0, 0, NULL
870 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
871 ) != n_IDEC_STATE_CONSUMED)
872 rv = -1;
873 else
874 rv = (ib != 0);
877 NYD_LEAVE;
878 return rv;
881 FL si8_t
882 quadify(char const *inbuf, uiz_t inlen, char const *prompt, si8_t emptyrv)
884 si8_t rv;
885 NYD_ENTER;
887 assert(inlen == 0 || inbuf != NULL);
889 if (inlen == UIZ_MAX)
890 inlen = strlen(inbuf);
892 if (inlen == 0)
893 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
894 else if ((rv = boolify(inbuf, inlen, -1)) < 0 &&
895 !ascncasecmp(inbuf, "ask-", 4) &&
896 (rv = boolify(inbuf + 4, inlen - 4, -1)) >= 0 &&
897 (n_psonce & n_PSO_INTERACTIVE))
898 rv = getapproval(prompt, rv);
899 NYD_LEAVE;
900 return rv;
903 FL bool_t
904 n_is_all_or_aster(char const *name){
905 bool_t rv;
906 NYD_ENTER;
908 rv = ((name[0] == '*' && name[1] == '\0') || !asccasecmp(name, "all"));
909 NYD_LEAVE;
910 return rv;
913 FL time_t
914 n_time_epoch(void)
916 #ifdef HAVE_CLOCK_GETTIME
917 struct timespec ts;
918 #elif defined HAVE_GETTIMEOFDAY
919 struct timeval ts;
920 #endif
921 time_t rv;
922 char const *cp;
923 NYD2_ENTER;
925 if((cp = ok_vlook(SOURCE_DATE_EPOCH)) != NULL){
926 ui64_t tib;
928 (void)/* XXX ?? posnum= */n_idec_ui64_cp(&tib, cp, 0, NULL);
929 rv = (time_t)tib;
930 goto jleave;
933 #ifdef HAVE_CLOCK_GETTIME
934 clock_gettime(CLOCK_REALTIME, &ts);
935 rv = (time_t)ts.tv_sec;
936 #elif defined HAVE_GETTIMEOFDAY
937 gettimeofday(&ts, NULL);
938 rv = (time_t)ts.tv_sec;
939 #else
940 rv = time(NULL);
941 #endif
942 jleave:
943 NYD2_LEAVE;
944 return rv;
947 FL void
948 time_current_update(struct time_current *tc, bool_t full_update)
950 NYD_ENTER;
951 tc->tc_time = n_time_epoch();
952 if (full_update) {
953 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
954 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
955 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
957 NYD_LEAVE;
960 FL uiz_t
961 n_msleep(uiz_t millis, bool_t ignint){
962 uiz_t rv;
963 NYD2_ENTER;
965 #ifdef HAVE_NANOSLEEP
966 /* C99 */{
967 struct timespec ts, trem;
968 int i;
970 ts.tv_sec = millis / 1000;
971 ts.tv_nsec = (millis %= 1000) * 1000 * 1000;
973 while((i = nanosleep(&ts, &trem)) != 0 && ignint)
974 ts = trem;
975 rv = (i == 0) ? 0 : (trem.tv_sec * 1000) + (trem.tv_nsec / (1000 * 1000));
978 #elif defined HAVE_SLEEP
979 if((millis /= 1000) == 0)
980 millis = 1;
981 while((rv = sleep((unsigned int)millis)) != 0 && ignint)
982 millis = rv;
983 #else
984 # error Configuration should have detected a function for sleeping.
985 #endif
987 NYD2_LEAVE;
988 return rv;
991 FL void
992 n_err(char const *format, ...){
993 va_list ap;
994 NYD2_ENTER;
996 va_start(ap, format);
997 #ifdef HAVE_ERRORS
998 if(n_psonce & n_PSO_INTERACTIVE)
999 n_verr(format, ap);
1000 else
1001 #endif
1003 size_t len;
1004 bool_t doname, doflush;
1006 doflush = FAL0;
1007 while(*format == '\n'){
1008 doflush = TRU1;
1009 putc('\n', n_stderr);
1010 ++format;
1013 if((doname = doflush))
1014 a_aux_err_linelen = 0;
1016 if((len = strlen(format)) > 0){
1017 if(doname || a_aux_err_linelen == 0)
1018 fputs(VAL_UAGENT ": ", n_stderr);
1019 vfprintf(n_stderr, format, ap);
1021 /* C99 */{
1022 size_t i = len;
1024 if(format[--len] == '\n'){
1025 a_aux_err_linelen = (i -= ++len);
1026 break;
1028 ++a_aux_err_linelen;
1029 }while(len > 0);
1033 if(doflush)
1034 fflush(n_stderr);
1036 va_end(ap);
1037 NYD2_LEAVE;
1040 FL void
1041 n_verr(char const *format, va_list ap){
1042 #ifdef HAVE_ERRORS
1043 struct a_aux_err_node *enp;
1044 #endif
1045 bool_t doname;
1046 size_t len;
1047 NYD2_ENTER;
1049 doname = FAL0;
1051 while(*format == '\n'){
1052 putc('\n', n_stderr);
1053 doname = TRU1;
1054 ++format;
1057 if(doname){
1058 a_aux_err_linelen = 0;
1059 #ifdef HAVE_ERRORS
1060 if(n_psonce & n_PSO_INTERACTIVE){
1061 if((enp = a_aux_err_tail) != NULL &&
1062 (enp->ae_str.s_len > 0 &&
1063 enp->ae_str.s_dat[enp->ae_str.s_len - 1] != '\n'))
1064 n_string_push_c(&enp->ae_str, '\n');
1066 #endif
1069 if((len = strlen(format)) == 0)
1070 goto jleave;
1071 #ifdef HAVE_ERRORS
1072 n_pstate |= n_PS_ERRORS_PROMPT;
1073 #endif
1075 if(doname || a_aux_err_linelen == 0)
1076 fputs(VAL_UAGENT ": ", n_stderr);
1078 /* C99 */{
1079 size_t i = len;
1081 if(format[--len] == '\n'){
1082 a_aux_err_linelen = (i -= ++len);
1083 break;
1085 ++a_aux_err_linelen;
1086 }while(len > 0);
1089 #ifdef HAVE_ERRORS
1090 if(!(n_psonce & n_PSO_INTERACTIVE))
1091 #endif
1092 vfprintf(n_stderr, format, ap);
1093 #ifdef HAVE_ERRORS
1094 else{
1095 int imax, i;
1096 n_LCTAV(ERRORS_MAX > 3);
1098 /* Link it into the `errors' message ring */
1099 if((enp = a_aux_err_tail) == NULL){
1100 jcreat:
1101 enp = smalloc(sizeof *enp);
1102 enp->ae_next = NULL;
1103 n_string_creat(&enp->ae_str);
1104 if(a_aux_err_tail != NULL)
1105 a_aux_err_tail->ae_next = enp;
1106 else
1107 a_aux_err_head = enp;
1108 a_aux_err_tail = enp;
1109 ++a_aux_err_cnt;
1110 }else if(doname ||
1111 (enp->ae_str.s_len > 0 &&
1112 enp->ae_str.s_dat[enp->ae_str.s_len - 1] == '\n')){
1113 if(a_aux_err_cnt < ERRORS_MAX)
1114 goto jcreat;
1116 a_aux_err_head = (enp = a_aux_err_head)->ae_next;
1117 a_aux_err_tail->ae_next = enp;
1118 a_aux_err_tail = enp;
1119 enp->ae_next = NULL;
1120 n_string_trunc(&enp->ae_str, 0);
1123 # ifdef HAVE_N_VA_COPY
1124 imax = 64;
1125 # else
1126 imax = n_MIN(LINESIZE, 1024);
1127 # endif
1128 for(i = imax;; imax = ++i /* xxx could wrap, maybe */){
1129 # ifdef HAVE_N_VA_COPY
1130 va_list vac;
1132 n_va_copy(vac, ap);
1133 # else
1134 # define vac ap
1135 # endif
1137 n_string_resize(&enp->ae_str, (len = enp->ae_str.s_len) + (size_t)i);
1138 i = vsnprintf(&enp->ae_str.s_dat[len], (size_t)i, format, vac);
1139 # ifdef HAVE_N_VA_COPY
1140 va_end(vac);
1141 # else
1142 # undef vac
1143 # endif
1144 if(i <= 0)
1145 goto jleave;
1146 if(UICMP(z, i, >=, imax)){
1147 # ifdef HAVE_N_VA_COPY
1148 /* XXX Check overflow for upcoming LEN+++i! */
1149 n_string_trunc(&enp->ae_str, len);
1150 continue;
1151 # else
1152 i = (int)strlen(&enp->ae_str.s_dat[len]);
1153 # endif
1155 break;
1157 n_string_trunc(&enp->ae_str, len + (size_t)i);
1159 fwrite(&enp->ae_str.s_dat[len], 1, (size_t)i, n_stderr);
1161 #endif /* HAVE_ERRORS */
1163 jleave:
1164 fflush(n_stderr);
1165 NYD2_LEAVE;
1168 FL void
1169 n_err_sighdl(char const *format, ...){ /* TODO sigsafe; obsolete! */
1170 va_list ap;
1171 NYD_X;
1173 va_start(ap, format);
1174 vfprintf(n_stderr, format, ap);
1175 va_end(ap);
1176 fflush(n_stderr);
1179 FL void
1180 n_perr(char const *msg, int errval){
1181 int e;
1182 char const *fmt;
1183 NYD2_ENTER;
1185 if(msg == NULL){
1186 fmt = "%s%s\n";
1187 msg = n_empty;
1188 }else
1189 fmt = "%s: %s\n";
1191 e = (errval == 0) ? errno : errval;
1192 n_err(fmt, msg, strerror(e));
1193 if(errval == 0)
1194 errno = e;
1195 NYD2_LEAVE;
1198 FL void
1199 n_alert(char const *format, ...){
1200 va_list ap;
1201 NYD2_ENTER;
1203 n_err(a_aux_err_linelen > 0 ? _("\nAlert: ") : _("Alert: "));
1205 va_start(ap, format);
1206 n_verr(format, ap);
1207 va_end(ap);
1209 n_err("\n");
1210 NYD2_LEAVE;
1213 FL void
1214 n_panic(char const *format, ...){
1215 va_list ap;
1216 NYD2_ENTER;
1218 if(a_aux_err_linelen > 0){
1219 putc('\n', n_stderr);
1220 a_aux_err_linelen = 0;
1222 fprintf(n_stderr, VAL_UAGENT ": Panic: ");
1224 va_start(ap, format);
1225 vfprintf(n_stderr, format, ap);
1226 va_end(ap);
1228 putc('\n', n_stderr);
1229 fflush(n_stderr);
1230 NYD2_LEAVE;
1231 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1234 #ifdef HAVE_ERRORS
1235 FL int
1236 c_errors(void *v){
1237 char **argv = v;
1238 struct a_aux_err_node *enp;
1239 NYD_ENTER;
1241 if(*argv == NULL)
1242 goto jlist;
1243 if(argv[1] != NULL)
1244 goto jerr;
1245 if(!asccasecmp(*argv, "show"))
1246 goto jlist;
1247 if(!asccasecmp(*argv, "clear"))
1248 goto jclear;
1249 jerr:
1250 fprintf(n_stderr,
1251 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1252 v = NULL;
1253 jleave:
1254 NYD_LEAVE;
1255 return (v == NULL) ? !STOP : !OKAY; /* xxx 1:bad 0:good -- do some */
1257 jlist:{
1258 FILE *fp;
1259 size_t i;
1261 if(a_aux_err_head == NULL){
1262 fprintf(n_stderr, _("The error ring is empty\n"));
1263 goto jleave;
1266 if((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
1267 NULL){
1268 fprintf(n_stderr, _("tmpfile"));
1269 v = NULL;
1270 goto jleave;
1273 for(i = 0, enp = a_aux_err_head; enp != NULL; enp = enp->ae_next)
1274 fprintf(fp, "%4" PRIuZ ". %s", ++i, n_string_cp(&enp->ae_str));
1275 /* We don't know whether last string ended with NL; be simple XXX */
1276 putc('\n', fp);
1278 page_or_print(fp, 0);
1279 Fclose(fp);
1281 /* FALLTHRU */
1283 jclear:
1284 a_aux_err_tail = NULL;
1285 a_aux_err_cnt = a_aux_err_cnt_noted = 0;
1286 a_aux_err_linelen = 0;
1287 while((enp = a_aux_err_head) != NULL){
1288 a_aux_err_head = enp->ae_next;
1289 n_string_gut(&enp->ae_str);
1290 free(enp);
1292 goto jleave;
1294 #endif /* HAVE_ERRORS */
1296 #ifdef HAVE_REGEX
1297 FL char const *
1298 n_regex_err_to_str(const regex_t *rep, int e){
1299 char *cp;
1300 size_t i;
1301 NYD2_ENTER;
1303 i = regerror(e, rep, NULL, 0) +1;
1304 cp = salloc(i);
1305 regerror(e, rep, cp, i);
1306 NYD2_LEAVE;
1307 return cp;
1309 #endif
1311 /* s-it-mode */