n_perr(): restore errno as applicable
[s-mailx.git] / auxlily.c
blobb83afc3634c816b5d76a333547896545aa4016b8
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[42];
161 a_aux_rand->a._j = a_aux_rand->a._dat[84];
162 /* ..but be on the safe side */
163 if(UICMP(z, gr, ==, sizeof(a_aux_rand->a._dat)))
164 break;
165 n_msleep(250, FAL0);
168 # else
169 if((u.fd = open("/dev/urandom", O_RDONLY)) != -1){
170 bool_t ok;
172 ok = (sizeof(a_aux_rand->a._dat) == (size_t)read(u.fd, a_aux_rand->a._dat,
173 sizeof(a_aux_rand->a._dat)));
174 close(u.fd);
176 a_aux_rand->a._i = a_aux_rand->a._dat[42];
177 a_aux_rand->a._j = a_aux_rand->a._dat[84];
178 if(ok)
179 goto jleave;
182 for(seed = (uintptr_t)a_aux_rand & UI32_MAX, rnd = 21; rnd != 0; --rnd){
183 for(u.i = n_NELEM(a_aux_rand->b32); u.i-- != 0;){
184 ui32_t t, k;
186 # ifdef HAVE_CLOCK_GETTIME
187 clock_gettime(CLOCK_REALTIME, &ts);
188 t = (ui32_t)ts.tv_nsec;
189 # else
190 gettimeofday(&ts, NULL);
191 t = (ui32_t)ts.tv_usec;
192 # endif
193 if(rnd & 1)
194 t = (t >> 16) | (t << 16);
195 a_aux_rand->b32[u.i] ^= a_aux_rand_weak(seed ^ t);
196 a_aux_rand->b32[t % n_NELEM(a_aux_rand->b32)] ^= seed;
197 if(rnd == 7 || rnd == 17)
198 a_aux_rand->b32[u.i] ^= a_aux_rand_weak(seed ^ (ui32_t)ts.tv_sec);
199 k = a_aux_rand->b32[u.i] % n_NELEM(a_aux_rand->b32);
200 a_aux_rand->b32[k] ^= a_aux_rand->b32[u.i];
201 seed ^= a_aux_rand_weak(a_aux_rand->b32[k]);
202 if((rnd & 3) == 3)
203 seed ^= nextprime(seed);
207 for(u.i = 5 * sizeof(a_aux_rand->b8); u.i != 0; --u.i)
208 a_aux_rand_get8();
209 jleave:
210 # endif /* !HAVE_GETRANDOM */
211 NYD2_LEAVE;
214 SINLINE ui8_t
215 a_aux_rand_get8(void){
216 ui8_t si, sj;
218 si = a_aux_rand->a._dat[++a_aux_rand->a._i];
219 sj = a_aux_rand->a._dat[a_aux_rand->a._j += si];
220 a_aux_rand->a._dat[a_aux_rand->a._i] = sj;
221 a_aux_rand->a._dat[a_aux_rand->a._j] = si;
222 return a_aux_rand->a._dat[(ui8_t)(si + sj)];
225 # ifndef HAVE_GETRANDOM
226 static ui32_t
227 a_aux_rand_weak(ui32_t seed){
228 /* From "Random number generators: good ones are hard to find",
229 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
230 * October 1988, p. 1195.
231 * (In fact: FreeBSD 4.7, /usr/src/lib/libc/stdlib/random.c.) */
232 ui32_t hi;
234 if(seed == 0)
235 seed = 123459876;
236 hi = seed / 127773;
237 seed %= 127773;
238 seed = (seed * 16807) - (hi * 2836);
239 if((si32_t)seed < 0)
240 seed += SI32_MAX;
241 return seed;
243 # endif /* HAVE_GETRANDOM */
244 #endif /* !HAVE_POSIX_RANDOM */
246 FL size_t
247 n_screensize(void){
248 char const *cp;
249 uiz_t rv;
250 NYD2_ENTER;
252 if((cp = ok_vlook(screen)) != NULL){
253 n_idec_uiz_cp(&rv, cp, 0, NULL);
254 if(rv == 0)
255 rv = n_scrnheight;
256 }else
257 rv = n_scrnheight;
259 if(rv > 2)
260 rv -= 2;
261 NYD2_LEAVE;
262 return rv;
265 FL char const *
266 n_pager_get(char const **env_addon){
267 char const *rv;
268 NYD_ENTER;
270 rv = ok_vlook(PAGER);
272 if(env_addon != NULL){
273 *env_addon = NULL;
274 /* Update the manual upon any changes:
275 * *colour-pager*, $PAGER */
276 if(strstr(rv, "less") != NULL){
277 if(getenv("LESS") == NULL)
278 *env_addon =
279 #ifdef HAVE_TERMCAP
280 (n_psonce & n_PSO_TERMCAP_CA_MODE) ? "LESS=Ri"
281 : !(n_psonce & n_PSO_TERMCAP_DISABLE) ? "LESS=FRi" :
282 #endif
283 "LESS=FRXi";
284 }else if(strstr(rv, "lv") != NULL){
285 if(getenv("LV") == NULL)
286 *env_addon = "LV=-c";
289 NYD_LEAVE;
290 return rv;
293 FL void
294 page_or_print(FILE *fp, size_t lines)
296 int c;
297 char const *cp;
298 NYD_ENTER;
300 fflush_rewind(fp);
302 if (n_source_may_yield_control() && (cp = ok_vlook(crt)) != NULL) {
303 size_t rows;
305 if(*cp == '\0')
306 rows = (size_t)n_scrnheight;
307 else
308 n_idec_uiz_cp(&rows, cp, 0, NULL);
310 if (rows > 0 && lines == 0) {
311 while ((c = getc(fp)) != EOF)
312 if (c == '\n' && ++lines >= rows)
313 break;
314 really_rewind(fp);
317 if (lines >= rows) {
318 char const *env_add[2], *pager;
320 pager = n_pager_get(&env_add[0]);
321 env_add[1] = NULL;
322 run_command(pager, NULL, fileno(fp), COMMAND_FD_PASS, NULL,NULL,NULL,
323 env_add);
324 goto jleave;
328 while ((c = getc(fp)) != EOF)
329 putc(c, n_stdout);
330 jleave:
331 NYD_LEAVE;
334 FL enum protocol
335 which_protocol(char const *name) /* XXX (->URL (yet auxlily.c)) */
337 struct stat st;
338 char const *cp;
339 char *np;
340 size_t sz;
341 enum protocol rv = PROTO_UNKNOWN;
342 NYD_ENTER;
344 temporary_protocol_ext = NULL;
346 if (name[0] == '%' && name[1] == ':')
347 name += 2;
348 for (cp = name; *cp && *cp != ':'; cp++)
349 if (!alnumchar(*cp))
350 goto jfile;
352 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
353 if (!strncmp(name, "pop3://", 7)) {
354 #ifdef HAVE_POP3
355 rv = PROTO_POP3;
356 #else
357 n_err(_("No POP3 support compiled in\n"));
358 #endif
359 } else if (!strncmp(name, "pop3s://", 8)) {
360 #if defined HAVE_POP3 && defined HAVE_SSL
361 rv = PROTO_POP3;
362 #else
363 # ifndef HAVE_POP3
364 n_err(_("No POP3 support compiled in\n"));
365 # endif
366 # ifndef HAVE_SSL
367 n_err(_("No SSL support compiled in\n"));
368 # endif
369 #endif
371 goto jleave;
374 /* TODO This is the de facto maildir code and thus belongs into there!
375 * TODO and: we should have maildir:// and mbox:// pseudo-protos, instead of
376 * TODO or (more likely) in addition to *newfolders*) */
377 jfile:
378 rv = PROTO_FILE;
379 np = ac_alloc((sz = strlen(name)) + 4 +1);
380 memcpy(np, name, sz + 1);
381 if (!stat(name, &st)) {
382 if (S_ISDIR(st.st_mode) &&
383 (memcpy(np+sz, "/tmp", 5), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
384 (memcpy(np+sz, "/new", 5), !stat(np, &st) && S_ISDIR(st.st_mode)) &&
385 (memcpy(np+sz, "/cur", 5), !stat(np, &st) && S_ISDIR(st.st_mode)))
386 rv = PROTO_MAILDIR;
387 } else {
388 if ((memcpy(np+sz, cp=".gz", 4), !stat(np, &st) && S_ISREG(st.st_mode)) ||
389 (memcpy(np+sz, cp=".xz",4), !stat(np,&st) && S_ISREG(st.st_mode)) ||
390 (memcpy(np+sz, cp=".bz2",5), !stat(np, &st) && S_ISREG(st.st_mode)))
391 temporary_protocol_ext = cp;
392 else if ((cp = ok_vlook(newfolders)) != NULL &&
393 !asccasecmp(cp, "maildir"))
394 rv = PROTO_MAILDIR;
396 ac_free(np);
397 jleave:
398 NYD_LEAVE;
399 return rv;
402 FL char *
403 n_c_to_hex_base16(char store[3], char c){
404 static char const itoa16[] = "0123456789ABCDEF";
405 NYD2_ENTER;
407 store[2] = '\0';
408 store[1] = itoa16[(ui8_t)c & 0x0F];
409 c = ((ui8_t)c >> 4) & 0x0F;
410 store[0] = itoa16[(ui8_t)c];
411 NYD2_LEAVE;
412 return store;
415 FL si32_t
416 n_c_from_hex_base16(char const hex[2]){
417 static ui8_t const atoi16[] = {
418 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x30-0x37 */
419 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x38-0x3F */
420 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, /* 0x40-0x47 */
421 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x48-0x4f */
422 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x50-0x57 */
423 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x58-0x5f */
424 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF /* 0x60-0x67 */
426 ui8_t i1, i2;
427 si32_t rv;
428 NYD2_ENTER;
430 if ((i1 = (ui8_t)hex[0] - '0') >= n_NELEM(atoi16) ||
431 (i2 = (ui8_t)hex[1] - '0') >= n_NELEM(atoi16))
432 goto jerr;
433 i1 = atoi16[i1];
434 i2 = atoi16[i2];
435 if ((i1 | i2) & 0xF0u)
436 goto jerr;
437 rv = i1;
438 rv <<= 4;
439 rv += i2;
440 jleave:
441 NYD2_LEAVE;
442 return rv;
443 jerr:
444 rv = -1;
445 goto jleave;
448 FL enum n_idec_state
449 n_idec_buf(void *resp, char const *cbuf, uiz_t clen, ui8_t base,
450 enum n_idec_mode idm, char const **endptr_or_null){
451 /* XXX Brute simple and */
452 ui8_t currc;
453 ui64_t res, cut;
454 enum n_idec_state rv;
455 NYD_ENTER;
457 idm &= n__IDEC_MODE_MASK;
458 rv = n_IDEC_STATE_NONE | idm;
459 res = 0;
461 if(clen == UIZ_MAX){
462 if(*cbuf == '\0')
463 goto jeinval;
464 }else if(clen == 0)
465 goto jeinval;
467 /* Leading WS */
468 while(spacechar(*cbuf))
469 if(*++cbuf == '\0' || --clen == 0)
470 goto jeinval;
472 /* Check sign */
473 switch(*cbuf){
474 case '-':
475 rv |= n_IDEC_STATE_SEEN_MINUS;
476 /* FALLTHROUGH */
477 case '+':
478 if(*++cbuf == '\0' || --clen == 0)
479 goto jeinval;
480 break;
483 /* Base detection/skip */
484 if(*cbuf != '0'){
485 if(base == 0)
486 base = 10;
487 /* Character must be valid for base */
488 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
489 if(currc >= base)
490 goto jeinval;
491 }else{
492 /* 0 always valid as is, fallback base 10 */
493 if(*++cbuf == '\0' || --clen == 0)
494 goto jleave;
496 /* Base "detection" */
497 if(base == 0 || base == 2 || base == 16){
498 switch(*cbuf){
499 case 'x':
500 case 'X':
501 if((base & 2) == 0){
502 base = 0x10;
503 goto jprefix_skip;
505 break;
506 case 'b':
507 case 'B':
508 if((base & 16) == 0){
509 base = 2; /* 0b10 */
510 /* Char after prefix must be valid */
511 jprefix_skip:
512 if(*++cbuf == '\0' || --clen == 0)
513 goto jeinval;
515 /* Character must be valid for base, invalid otherwise */
516 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
517 if(currc >= base)
518 goto jeinval;
520 break;
521 default:
522 if(base == 0)
523 base = 010;
524 break;
528 /* Character must be valid for base, EBASE otherwise */
529 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
530 if(currc >= base)
531 goto jebase;
534 for(cut = a_aux_idec_cutlimit[base - 2];;){
535 if(res >= cut){
536 if(res == cut){
537 res *= base;
538 if(res > UI64_MAX - currc)
539 goto jeover;
540 res += currc;
541 }else
542 goto jeover;
543 }else{
544 res *= base;
545 res += currc;
548 if(*++cbuf == '\0' || --clen == 0)
549 break;
551 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
552 if(currc >= base)
553 goto jebase;
556 jleave:
558 ui64_t uimask;
560 switch(rv & n__IDEC_MODE_LIMIT_MASK){
561 case n_IDEC_MODE_LIMIT_8BIT: uimask = UI8_MAX; break;
562 case n_IDEC_MODE_LIMIT_16BIT: uimask = UI16_MAX; break;
563 case n_IDEC_MODE_LIMIT_32BIT: uimask = UI32_MAX; break;
564 default: uimask = UI64_MAX; break;
566 if(rv & n_IDEC_MODE_SIGNED_TYPE)
567 uimask >>= 1;
569 if(res & ~uimask){
570 if((rv & (n_IDEC_MODE_SIGNED_TYPE | n_IDEC_STATE_SEEN_MINUS)
571 ) == (n_IDEC_MODE_SIGNED_TYPE | n_IDEC_STATE_SEEN_MINUS)){
572 if(res > uimask + 1){
573 res = uimask << 1;
574 res &= ~uimask;
575 }else{
576 res = -res;
577 break;
579 }else
580 res = uimask;
581 if(!(rv & n_IDEC_MODE_LIMIT_NOERROR))
582 rv |= n_IDEC_STATE_EOVERFLOW;
583 }else if(rv & n_IDEC_STATE_SEEN_MINUS)
584 res = -res;
585 }while(0);
587 switch(rv & n__IDEC_MODE_LIMIT_MASK){
588 case n_IDEC_MODE_LIMIT_8BIT:
589 if(rv & n_IDEC_MODE_SIGNED_TYPE)
590 *(si8_t*)resp = (si8_t)res;
591 else
592 *(ui8_t*)resp = (ui8_t)res;
593 break;
594 case n_IDEC_MODE_LIMIT_16BIT:
595 if(rv & n_IDEC_MODE_SIGNED_TYPE)
596 *(si16_t*)resp = (si16_t)res;
597 else
598 *(ui16_t*)resp = (ui16_t)res;
599 break;
600 case n_IDEC_MODE_LIMIT_32BIT:
601 if(rv & n_IDEC_MODE_SIGNED_TYPE)
602 *(si32_t*)resp = (si32_t)res;
603 else
604 *(ui32_t*)resp = (ui32_t)res;
605 break;
606 default:
607 if(rv & n_IDEC_MODE_SIGNED_TYPE)
608 *(si64_t*)resp = (si64_t)res;
609 else
610 *(ui64_t*)resp = (ui64_t)res;
611 break;
614 if(endptr_or_null != NULL)
615 *endptr_or_null = cbuf;
616 if(*cbuf == '\0' || clen == 0)
617 rv |= n_IDEC_STATE_CONSUMED;
618 NYD_LEAVE;
619 return rv;
621 jeinval:
622 rv |= n_IDEC_STATE_EINVAL;
623 goto j_maxval;
624 jebase:
625 /* Not a base error for terminator and whitespace! */
626 if(*cbuf != '\0' && !spacechar(*cbuf))
627 rv |= n_IDEC_STATE_EBASE;
628 goto jleave;
630 jeover:
631 /* Overflow error: consume input until bad character or length out */
632 for(;;){
633 if(*++cbuf == '\0' || --clen == 0)
634 break;
635 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
636 if(currc >= base)
637 break;
640 rv |= n_IDEC_STATE_EOVERFLOW;
641 j_maxval:
642 if(rv & n_IDEC_MODE_SIGNED_TYPE)
643 res = (rv & n_IDEC_STATE_SEEN_MINUS) ? (ui64_t)SI64_MIN
644 : (ui64_t)SI64_MAX;
645 else
646 res = UI64_MAX;
647 rv &= ~n_IDEC_STATE_SEEN_MINUS;
648 goto jleave;
651 FL ui32_t
652 torek_hash(char const *name)
654 /* Chris Torek's hash.
655 * NOTE: need to change *at least* mk-okey-map.pl when changing the
656 * algorithm!! */
657 ui32_t h = 0;
658 NYD_ENTER;
660 while (*name != '\0') {
661 h *= 33;
662 h += *name++;
664 NYD_LEAVE;
665 return h;
668 FL ui32_t
669 torek_ihashn(char const *dat, size_t len){
670 /* See torek_hash() */
671 char c;
672 ui32_t h;
673 NYD_ENTER;
675 for(h = 0; len > 0 && (c = *dat++) != '\0'; --len)
676 h = (h * 33) + lowerconv(c);
677 NYD_LEAVE;
678 return h;
681 FL ui32_t
682 nextprime(ui32_t n)
684 static ui32_t const primes[] = {
685 5, 11, 23, 47, 97, 157, 283,
686 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
687 131071, 262139, 524287, 1048573, 2097143, 4194301,
688 8388593, 16777213, 33554393, 67108859, 134217689,
689 268435399, 536870909, 1073741789, 2147483647
692 ui32_t i, mprime;
693 NYD_ENTER;
695 i = (n < primes[n_NELEM(primes) / 4] ? 0
696 : (n < primes[n_NELEM(primes) / 2] ? n_NELEM(primes) / 4
697 : n_NELEM(primes) / 2));
699 if ((mprime = primes[i]) > n)
700 break;
701 while (++i < n_NELEM(primes));
702 if (i == n_NELEM(primes) && mprime < n)
703 mprime = n;
704 NYD_LEAVE;
705 return mprime;
708 FL char const *
709 n_getdeadletter(void){
710 char const *cp_base, *cp;
711 NYD_ENTER;
713 cp_base = NULL;
714 jredo:
715 cp = fexpand(ok_vlook(DEAD), FEXP_LOCAL | FEXP_NSHELL);
716 if(cp == NULL || strlen(cp) >= PATH_MAX){
717 if(cp_base == NULL){
718 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
719 VAL_DEAD, n_shexp_quote_cp(cp, FAL0));
720 ok_vclear(DEAD);
721 goto jredo;
722 }else{
723 cp = savecatsep(ok_vlook(TMPDIR), '/', VAL_DEAD_BASENAME);
724 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp);
727 NYD_LEAVE;
728 return cp;
731 FL char *
732 nodename(int mayoverride)
734 static char *sys_hostname, *hostname; /* XXX free-at-exit */
736 struct utsname ut;
737 char *hn;
738 #ifdef HAVE_SOCKETS
739 # ifdef HAVE_GETADDRINFO
740 struct addrinfo hints, *res;
741 # else
742 struct hostent *hent;
743 # endif
744 #endif
745 NYD_ENTER;
747 if (mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0') {
749 } else if ((hn = sys_hostname) == NULL) {
750 uname(&ut);
751 hn = ut.nodename;
752 #ifdef HAVE_SOCKETS
753 # ifdef HAVE_GETADDRINFO
754 memset(&hints, 0, sizeof hints);
755 hints.ai_family = AF_UNSPEC;
756 hints.ai_flags = AI_CANONNAME;
757 if (getaddrinfo(hn, NULL, &hints, &res) == 0) {
758 if (res->ai_canonname != NULL) {
759 size_t l = strlen(res->ai_canonname) +1;
761 hn = ac_alloc(l);
762 memcpy(hn, res->ai_canonname, l);
764 freeaddrinfo(res);
766 # else
767 hent = gethostbyname(hn);
768 if (hent != NULL)
769 hn = hent->h_name;
770 # endif
771 #endif
772 sys_hostname = sstrdup(hn);
773 #if defined HAVE_SOCKETS && defined HAVE_GETADDRINFO
774 if (hn != ut.nodename)
775 ac_free(hn);
776 #endif
777 hn = sys_hostname;
780 if (hostname != NULL && hostname != sys_hostname)
781 free(hostname);
782 hostname = sstrdup(hn);
783 NYD_LEAVE;
784 return hostname;
787 FL char *
788 getrandstring(size_t length){
789 struct str b64;
790 char *data;
791 size_t i;
792 NYD_ENTER;
794 #ifndef HAVE_POSIX_RANDOM
795 if(a_aux_rand == NULL)
796 a_aux_rand_init();
797 #endif
799 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
800 * with PAD stripped is still longer than what the user requests, easy way */
801 data = n_lofi_alloc(i = length + 3);
803 #ifndef HAVE_POSIX_RANDOM
804 while(i-- > 0)
805 data[i] = (char)a_aux_rand_get8();
806 #else
807 { char *cp;
809 for(cp = data; i > 0;){
810 union {ui32_t i4; char c[4];} r;
811 size_t j;
813 r.i4 = (ui32_t)arc4random();
814 switch((j = i & 3)){
815 case 0: cp[3] = r.c[3]; j = 4;
816 case 3: cp[2] = r.c[2];
817 case 2: cp[1] = r.c[1];
818 default: cp[0] = r.c[0]; break;
820 cp += j;
821 i -= j;
824 #endif
826 assert(length + 3 < UIZ_MAX / 4);
827 b64_encode_buf(&b64, data, length + 3,
828 B64_SALLOC | B64_RFC4648URL | B64_NOPAD);
829 ac_free(data);
831 assert(b64.l >= length);
832 b64.s[length] = '\0';
833 NYD_LEAVE;
834 return b64.s;
837 FL si8_t
838 boolify(char const *inbuf, uiz_t inlen, si8_t emptyrv)
840 si8_t rv;
841 NYD_ENTER;
843 assert(inlen == 0 || inbuf != NULL);
845 if (inlen == UIZ_MAX)
846 inlen = strlen(inbuf);
848 if (inlen == 0)
849 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
850 else {
851 if ((inlen == 1 && (*inbuf == '1' || *inbuf == 'y' || *inbuf == 'Y')) ||
852 !ascncasecmp(inbuf, "true", inlen) ||
853 !ascncasecmp(inbuf, "yes", inlen) ||
854 !ascncasecmp(inbuf, "on", inlen))
855 rv = 1;
856 else if ((inlen == 1 &&
857 (*inbuf == '0' || *inbuf == 'n' || *inbuf == 'N')) ||
858 !ascncasecmp(inbuf, "false", inlen) ||
859 !ascncasecmp(inbuf, "no", inlen) ||
860 !ascncasecmp(inbuf, "off", inlen))
861 rv = 0;
862 else {
863 ui64_t ib;
865 if((n_idec_buf(&ib, inbuf, inlen, 0, 0, NULL
866 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
867 ) != n_IDEC_STATE_CONSUMED)
868 rv = -1;
869 else
870 rv = (ib != 0);
873 NYD_LEAVE;
874 return rv;
877 FL si8_t
878 quadify(char const *inbuf, uiz_t inlen, char const *prompt, si8_t emptyrv)
880 si8_t rv;
881 NYD_ENTER;
883 assert(inlen == 0 || inbuf != NULL);
885 if (inlen == UIZ_MAX)
886 inlen = strlen(inbuf);
888 if (inlen == 0)
889 rv = (emptyrv >= 0) ? (emptyrv == 0 ? 0 : 1) : -1;
890 else if ((rv = boolify(inbuf, inlen, -1)) < 0 &&
891 !ascncasecmp(inbuf, "ask-", 4) &&
892 (rv = boolify(inbuf + 4, inlen - 4, -1)) >= 0 &&
893 (n_psonce & n_PSO_INTERACTIVE))
894 rv = getapproval(prompt, rv);
895 NYD_LEAVE;
896 return rv;
899 FL bool_t
900 n_is_all_or_aster(char const *name){
901 bool_t rv;
902 NYD_ENTER;
904 rv = ((name[0] == '*' && name[1] == '\0') || !asccasecmp(name, "all"));
905 NYD_LEAVE;
906 return rv;
909 FL time_t
910 n_time_epoch(void)
912 #ifdef HAVE_CLOCK_GETTIME
913 struct timespec ts;
914 #elif defined HAVE_GETTIMEOFDAY
915 struct timeval ts;
916 #endif
917 time_t rv;
918 char const *cp;
919 NYD2_ENTER;
921 if((cp = ok_vlook(SOURCE_DATE_EPOCH)) != NULL){
922 ui64_t tib;
924 (void)/* XXX ?? posnum= */n_idec_ui64_cp(&tib, cp, 0, NULL);
925 rv = (time_t)tib;
926 goto jleave;
929 #ifdef HAVE_CLOCK_GETTIME
930 clock_gettime(CLOCK_REALTIME, &ts);
931 rv = (time_t)ts.tv_sec;
932 #elif defined HAVE_GETTIMEOFDAY
933 gettimeofday(&ts, NULL);
934 rv = (time_t)ts.tv_sec;
935 #else
936 rv = time(NULL);
937 #endif
938 jleave:
939 NYD2_LEAVE;
940 return rv;
943 FL void
944 time_current_update(struct time_current *tc, bool_t full_update)
946 NYD_ENTER;
947 tc->tc_time = n_time_epoch();
948 if (full_update) {
949 memcpy(&tc->tc_gm, gmtime(&tc->tc_time), sizeof tc->tc_gm);
950 memcpy(&tc->tc_local, localtime(&tc->tc_time), sizeof tc->tc_local);
951 sstpcpy(tc->tc_ctime, ctime(&tc->tc_time));
953 NYD_LEAVE;
956 FL uiz_t
957 n_msleep(uiz_t millis, bool_t ignint){
958 uiz_t rv;
959 NYD2_ENTER;
961 #ifdef HAVE_NANOSLEEP
962 /* C99 */{
963 struct timespec ts, trem;
964 int i;
966 ts.tv_sec = millis / 1000;
967 ts.tv_nsec = (millis %= 1000) * 1000 * 1000;
969 while((i = nanosleep(&ts, &trem)) != 0 && ignint)
970 ts = trem;
971 rv = (i == 0) ? 0 : (trem.tv_sec * 1000) + (trem.tv_nsec / (1000 * 1000));
974 #elif defined HAVE_SLEEP
975 if((millis /= 1000) == 0)
976 millis = 1;
977 while((rv = sleep((unsigned int)millis)) != 0 && ignint)
978 millis = rv;
979 #else
980 # error Configuration should have detected a function for sleeping.
981 #endif
983 NYD2_LEAVE;
984 return rv;
987 FL void
988 n_err(char const *format, ...){
989 va_list ap;
990 NYD2_ENTER;
992 va_start(ap, format);
993 #ifdef HAVE_ERRORS
994 if(n_psonce & n_PSO_INTERACTIVE)
995 n_verr(format, ap);
996 else
997 #endif
999 size_t len;
1000 bool_t doname, doflush;
1002 doflush = FAL0;
1003 while(*format == '\n'){
1004 doflush = TRU1;
1005 putc('\n', n_stderr);
1006 ++format;
1009 if((doname = doflush))
1010 a_aux_err_linelen = 0;
1012 if((len = strlen(format)) > 0){
1013 if(doname || a_aux_err_linelen == 0)
1014 fputs(VAL_UAGENT ": ", n_stderr);
1015 vfprintf(n_stderr, format, ap);
1017 /* C99 */{
1018 size_t i = len;
1020 if(format[--len] == '\n'){
1021 a_aux_err_linelen = (i -= ++len);
1022 break;
1024 ++a_aux_err_linelen;
1025 }while(len > 0);
1029 if(doflush)
1030 fflush(n_stderr);
1032 va_end(ap);
1033 NYD2_LEAVE;
1036 FL void
1037 n_verr(char const *format, va_list ap){
1038 #ifdef HAVE_ERRORS
1039 struct a_aux_err_node *enp;
1040 #endif
1041 bool_t doname, doflush;
1042 size_t len;
1043 NYD2_ENTER;
1045 doflush = FAL0;
1046 while(*format == '\n'){
1047 doflush = TRU1;
1048 putc('\n', n_stderr);
1049 ++format;
1052 if((doname = doflush)){
1053 a_aux_err_linelen = 0;
1054 #ifdef HAVE_ERRORS
1055 if(n_psonce & n_PSO_INTERACTIVE){
1056 if((enp = a_aux_err_tail) != NULL &&
1057 (enp->ae_str.s_len > 0 &&
1058 enp->ae_str.s_dat[enp->ae_str.s_len - 1] != '\n'))
1059 n_string_push_c(&enp->ae_str, '\n');
1061 #endif
1064 if((len = strlen(format)) == 0)
1065 goto jleave;
1066 #ifdef HAVE_ERRORS
1067 n_pstate |= n_PS_ERRORS_PROMPT;
1068 #endif
1070 if(doname || a_aux_err_linelen == 0)
1071 fputs(VAL_UAGENT ": ", n_stderr);
1073 /* C99 */{
1074 size_t i = len;
1076 if(format[--len] == '\n'){
1077 a_aux_err_linelen = (i -= ++len);
1078 break;
1080 ++a_aux_err_linelen;
1081 }while(len > 0);
1084 #ifdef HAVE_ERRORS
1085 if(!(n_psonce & n_PSO_INTERACTIVE))
1086 #endif
1087 vfprintf(n_stderr, format, ap);
1088 #ifdef HAVE_ERRORS
1089 else{
1090 int imax, i;
1091 n_LCTAV(ERRORS_MAX > 3);
1093 /* Link it into the `errors' message ring */
1094 if((enp = a_aux_err_tail) == NULL){
1095 jcreat:
1096 enp = smalloc(sizeof *enp);
1097 enp->ae_next = NULL;
1098 n_string_creat(&enp->ae_str);
1099 if(a_aux_err_tail != NULL)
1100 a_aux_err_tail->ae_next = enp;
1101 else
1102 a_aux_err_head = enp;
1103 a_aux_err_tail = enp;
1104 ++a_aux_err_cnt;
1105 }else if(doname ||
1106 (enp->ae_str.s_len > 0 &&
1107 enp->ae_str.s_dat[enp->ae_str.s_len - 1] == '\n')){
1108 if(a_aux_err_cnt < ERRORS_MAX)
1109 goto jcreat;
1111 a_aux_err_head = (enp = a_aux_err_head)->ae_next;
1112 a_aux_err_tail->ae_next = enp;
1113 a_aux_err_tail = enp;
1114 enp->ae_next = NULL;
1115 n_string_trunc(&enp->ae_str, 0);
1118 # ifdef HAVE_N_VA_COPY
1119 imax = 64;
1120 # else
1121 imax = n_MIN(LINESIZE, 1024);
1122 # endif
1123 for(i = imax;; imax = ++i /* xxx could wrap, maybe */){
1124 # ifdef HAVE_N_VA_COPY
1125 va_list vac;
1127 n_va_copy(vac, ap);
1128 # else
1129 # define vac ap
1130 # endif
1132 n_string_resize(&enp->ae_str, (len = enp->ae_str.s_len) + (size_t)i);
1133 i = vsnprintf(&enp->ae_str.s_dat[len], (size_t)i, format, vac);
1134 # ifdef HAVE_N_VA_COPY
1135 va_end(vac);
1136 # else
1137 # undef vac
1138 # endif
1139 if(i <= 0)
1140 goto jleave;
1141 if(UICMP(z, i, >=, imax)){
1142 # ifdef HAVE_N_VA_COPY
1143 /* XXX Check overflow for upcoming LEN+++i! */
1144 n_string_trunc(&enp->ae_str, len);
1145 continue;
1146 # else
1147 i = (int)strlen(&enp->ae_str.s_dat[len]);
1148 # endif
1150 break;
1152 n_string_trunc(&enp->ae_str, len + (size_t)i);
1154 fwrite(&enp->ae_str.s_dat[len], 1, (size_t)i, n_stderr);
1156 #endif /* HAVE_ERRORS */
1158 jleave:
1159 if(doflush)
1160 fflush(n_stderr);
1161 NYD2_LEAVE;
1164 FL void
1165 n_err_sighdl(char const *format, ...){ /* TODO sigsafe; obsolete! */
1166 va_list ap;
1167 NYD_X;
1169 va_start(ap, format);
1170 vfprintf(n_stderr, format, ap);
1171 va_end(ap);
1172 fflush(n_stderr);
1175 FL void
1176 n_perr(char const *msg, int errval){
1177 int e;
1178 char const *fmt;
1179 NYD2_ENTER;
1181 if(msg == NULL){
1182 fmt = "%s%s\n";
1183 msg = n_empty;
1184 }else
1185 fmt = "%s: %s\n";
1187 e = (errval == 0) ? errno : errval;
1188 n_err(fmt, msg, strerror(e));
1189 if(errval == 0)
1190 errno = e;
1191 NYD2_LEAVE;
1194 FL void
1195 n_alert(char const *format, ...){
1196 va_list ap;
1197 NYD2_ENTER;
1199 n_err(a_aux_err_linelen > 0 ? _("\nAlert: ") : _("Alert: "));
1201 va_start(ap, format);
1202 n_verr(format, ap);
1203 va_end(ap);
1205 n_err("\n");
1206 NYD2_LEAVE;
1209 FL void
1210 n_panic(char const *format, ...){
1211 va_list ap;
1212 NYD2_ENTER;
1214 if(a_aux_err_linelen > 0){
1215 putc('\n', n_stderr);
1216 a_aux_err_linelen = 0;
1218 fprintf(n_stderr, VAL_UAGENT ": Panic: ");
1220 va_start(ap, format);
1221 vfprintf(n_stderr, format, ap);
1222 va_end(ap);
1224 putc('\n', n_stderr);
1225 fflush(n_stderr);
1226 NYD2_LEAVE;
1227 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1230 #ifdef HAVE_ERRORS
1231 FL int
1232 c_errors(void *v){
1233 char **argv = v;
1234 struct a_aux_err_node *enp;
1235 NYD_ENTER;
1237 if(*argv == NULL)
1238 goto jlist;
1239 if(argv[1] != NULL)
1240 goto jerr;
1241 if(!asccasecmp(*argv, "show"))
1242 goto jlist;
1243 if(!asccasecmp(*argv, "clear"))
1244 goto jclear;
1245 jerr:
1246 fprintf(n_stderr,
1247 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1248 v = NULL;
1249 jleave:
1250 NYD_LEAVE;
1251 return (v == NULL) ? !STOP : !OKAY; /* xxx 1:bad 0:good -- do some */
1253 jlist:{
1254 FILE *fp;
1255 size_t i;
1257 if(a_aux_err_head == NULL){
1258 fprintf(n_stderr, _("The error ring is empty\n"));
1259 goto jleave;
1262 if((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
1263 NULL){
1264 fprintf(n_stderr, _("tmpfile"));
1265 v = NULL;
1266 goto jleave;
1269 for(i = 0, enp = a_aux_err_head; enp != NULL; enp = enp->ae_next)
1270 fprintf(fp, "%4" PRIuZ ". %s", ++i, n_string_cp(&enp->ae_str));
1271 /* We don't know whether last string ended with NL; be simple XXX */
1272 putc('\n', fp);
1274 page_or_print(fp, 0);
1275 Fclose(fp);
1277 /* FALLTHRU */
1279 jclear:
1280 a_aux_err_tail = NULL;
1281 a_aux_err_cnt = a_aux_err_cnt_noted = 0;
1282 a_aux_err_linelen = 0;
1283 while((enp = a_aux_err_head) != NULL){
1284 a_aux_err_head = enp->ae_next;
1285 n_string_gut(&enp->ae_str);
1286 free(enp);
1288 goto jleave;
1290 #endif /* HAVE_ERRORS */
1292 #ifdef HAVE_REGEX
1293 FL char const *
1294 n_regex_err_to_str(const regex_t *rep, int e){
1295 char *cp;
1296 size_t i;
1297 NYD2_ENTER;
1299 i = regerror(e, rep, NULL, 0) +1;
1300 cp = salloc(i);
1301 regerror(e, rep, cp, i);
1302 NYD2_LEAVE;
1303 return cp;
1305 #endif
1307 /* s-it-mode */