Merge branch 'topic/digmsg'
[s-mailx.git] / auxlily.c
blob5ff85e6e20c09b565fa214f2d7666970181de3a7
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 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
6 * SPDX-License-Identifier: BSD-3-Clause
7 */
8 /*
9 * Copyright (c) 1980, 1993
10 * The Regents of the University of California. All rights reserved.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
36 #undef n_FILE
37 #define n_FILE auxlily
39 #ifndef HAVE_AMALGAMATION
40 # include "nail.h"
41 #endif
43 #include <sys/utsname.h>
45 #ifdef HAVE_SOCKETS
46 # ifdef HAVE_GETADDRINFO
47 # include <sys/socket.h>
48 # endif
50 # include <netdb.h>
51 #endif
53 #ifdef HAVE_NL_LANGINFO
54 # include <langinfo.h>
55 #endif
56 #ifdef HAVE_SETLOCALE
57 # include <locale.h>
58 #endif
60 #if HAVE_RANDOM == n_RANDOM_IMPL_GETRANDOM
61 # include n_RANDOM_GETRANDOM_H
62 #endif
64 #ifdef HAVE_IDNA
65 # if HAVE_IDNA == n_IDNA_IMPL_LIBIDN2
66 # include <idn2.h>
67 # elif HAVE_IDNA == n_IDNA_IMPL_LIBIDN
68 # include <idna.h>
69 # include <idn-free.h>
70 # elif HAVE_IDNA == n_IDNA_IMPL_IDNKIT
71 # include <idn/api.h>
72 # endif
73 #endif
75 #if HAVE_RANDOM != n_RANDOM_IMPL_ARC4 && HAVE_RANDOM != n_RANDOM_IMPL_TLS
76 union rand_state{
77 struct rand_arc4{
78 ui8_t _dat[256];
79 ui8_t _i;
80 ui8_t _j;
81 ui8_t __pad[6];
82 } a;
83 ui8_t b8[sizeof(struct rand_arc4)];
84 ui32_t b32[sizeof(struct rand_arc4) / sizeof(ui32_t)];
86 #endif
88 #ifdef HAVE_ERRORS
89 struct a_aux_err_node{
90 struct a_aux_err_node *ae_next;
91 struct n_string ae_str;
93 #endif
95 struct a_aux_err_map{
96 ui32_t aem_hash; /* Hash of name */
97 ui32_t aem_nameoff; /* Into a_aux_err_names[] */
98 ui32_t aem_docoff; /* Into a_aux_err docs[] (if HAVE_DOCSTRINGS) */
99 si32_t aem_err_no; /* The OS error value for this one */
102 /* IDEC: byte to integer value lookup table */
103 static ui8_t const a_aux_idec_atoi[256] = {
104 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
105 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
106 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
107 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
108 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x01,
109 0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0xFF,0xFF,
110 0xFF,0xFF,0xFF,0xFF,0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,
111 0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,
112 0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,
113 0x23,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0A,0x0B,0x0C,
114 0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,
115 0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,
116 0x21,0x22,0x23,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
117 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
118 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
119 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
120 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
121 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
122 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
123 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
124 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
125 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
126 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
127 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
128 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
129 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
132 /* IDEC: avoid divisions for cutlimit calculation (indexed by base-2) */
133 #define a_X(X) (UI64_MAX / (X))
134 static ui64_t const a_aux_idec_cutlimit[35] = {
135 a_X( 2), a_X( 3), a_X( 4), a_X( 5), a_X( 6), a_X( 7), a_X( 8),
136 a_X( 9), a_X(10), a_X(11), a_X(12), a_X(13), a_X(14), a_X(15),
137 a_X(16), a_X(17), a_X(18), a_X(19), a_X(20), a_X(21), a_X(22),
138 a_X(23), a_X(24), a_X(25), a_X(26), a_X(27), a_X(28), a_X(29),
139 a_X(30), a_X(31), a_X(32), a_X(33), a_X(34), a_X(35), a_X(36)
141 #undef a_X
143 /* IENC: is power-of-two table, and if, shift (indexed by base-2) */
144 static ui8_t const a_aux_ienc_shifts[35] = {
145 1, 0, 2, 0, 0, 0, 3, 0, /* 2 .. 9 */
146 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, /* 10 .. 19 */
147 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 20 .. 29 */
148 0, 0, 5, 0, 0, 0, 0 /* 30 .. 36 */
151 /* IENC: integer to byte lookup tables */
152 static char const a_aux_ienc_itoa_upper[36] =
153 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
154 static char const a_aux_ienc_itoa_lower[36] =
155 "0123456789abcdefghijklmnopqrstuvwxyz";
157 /* Include the constant make-errors.sh output */
158 #include <gen-errors.h>
160 /* And these things come from mk-config.h (config-time make-errors.sh output) */
161 static n__ERR_NUMBER_TYPE const a_aux_err_no2mapoff[][2] = {
162 #undef a_X
163 #define a_X(N,I) {N,I},
164 n__ERR_NUMBER_TO_MAPOFF
165 #undef a_X
168 #if HAVE_RANDOM != n_RANDOM_IMPL_ARC4 && HAVE_RANDOM != n_RANDOM_IMPL_TLS
169 static union rand_state *a_aux_rand;
170 #endif
172 /* Error ring, for `errors' */
173 #ifdef HAVE_ERRORS
174 static struct a_aux_err_node *a_aux_err_head, *a_aux_err_tail;
175 static size_t a_aux_err_cnt, a_aux_err_cnt_noted;
176 #endif
177 static size_t a_aux_err_linelen;
179 /* Our ARC4 random generator with its completely unacademical pseudo
180 * initialization (shall /dev/urandom fail) */
181 #if HAVE_RANDOM != n_RANDOM_IMPL_ARC4 && HAVE_RANDOM != n_RANDOM_IMPL_TLS
182 static void a_aux_rand_init(void);
183 n_INLINE ui8_t a_aux_rand_get8(void);
184 static ui32_t a_aux_rand_weak(ui32_t seed);
185 #endif
187 /* Find the descriptive mapping of an error number, or _ERR_INVAL */
188 static struct a_aux_err_map const *a_aux_err_map_from_no(si32_t eno);
190 #if HAVE_RANDOM != n_RANDOM_IMPL_ARC4 && HAVE_RANDOM != n_RANDOM_IMPL_TLS
191 static void
192 a_aux_rand_init(void){
193 # ifdef HAVE_CLOCK_GETTIME
194 struct timespec ts;
195 # else
196 struct timeval ts;
197 # endif
198 union {int fd; size_t i;} u;
199 ui32_t seed, rnd;
200 NYD2_ENTER;
202 a_aux_rand = n_alloc(sizeof *a_aux_rand);
204 # if HAVE_RANDOM == n_RANDOM_IMPL_GETRANDOM
205 /* getrandom(2) guarantees 256 without n_ERR_INTR..
206 * However, support sequential reading to avoid possible hangs that have
207 * been reported on the ML (2017-08-22, s-nail/s-mailx freezes when
208 * HAVE_GETRANDOM is #defined) */
209 n_LCTA(sizeof(a_aux_rand->a._dat) <= 256,
210 "Buffer too large to be served without n_ERR_INTR error");
211 n_LCTA(sizeof(a_aux_rand->a._dat) >= 256,
212 "Buffer too small to serve used array indices");
213 /* C99 */{
214 size_t o, i;
216 for(o = 0, i = sizeof a_aux_rand->a._dat;;){
217 ssize_t gr;
219 gr = n_RANDOM_GETRANDOM_FUN(&a_aux_rand->a._dat[o], i);
220 if(gr == -1 && n_err_no == n_ERR_NOSYS)
221 break;
222 a_aux_rand->a._i = a_aux_rand->a._dat[a_aux_rand->a._dat[1] ^
223 a_aux_rand->a._dat[84]];
224 a_aux_rand->a._j = a_aux_rand->a._dat[a_aux_rand->a._dat[65] ^
225 a_aux_rand->a._dat[42]];
226 /* ..but be on the safe side */
227 if(gr > 0){
228 i -= (size_t)gr;
229 if(i == 0)
230 goto jleave;
231 o += (size_t)gr;
233 n_err(_("Not enough entropy for the PseudoRandomNumberGenerator, "
234 "waiting a bit\n"));
235 n_msleep(250, FAL0);
239 # elif HAVE_RANDOM == n_RANDOM_IMPL_URANDOM
240 if((u.fd = open("/dev/urandom", O_RDONLY)) != -1){
241 bool_t ok;
243 ok = (sizeof(a_aux_rand->a._dat) == (size_t)read(u.fd, a_aux_rand->a._dat,
244 sizeof(a_aux_rand->a._dat)));
245 close(u.fd);
247 a_aux_rand->a._i = a_aux_rand->a._dat[a_aux_rand->a._dat[1] ^
248 a_aux_rand->a._dat[84]];
249 a_aux_rand->a._j = a_aux_rand->a._dat[a_aux_rand->a._dat[65] ^
250 a_aux_rand->a._dat[42]];
251 if(ok)
252 goto jleave;
254 # elif HAVE_RANDOM != n_RANDOM_IMPL_BUILTIN
255 # error a_aux_rand_init(): the value of HAVE_RANDOM is not supported
256 # endif
258 /* As a fallback, a homebrew seed */
259 if(n_poption & n_PO_D_V)
260 n_err(_("P(seudo)R(andomNumber)G(enerator): creating homebrew seed\n"));
261 for(seed = (uintptr_t)a_aux_rand & UI32_MAX, rnd = 21; rnd != 0; --rnd){
262 for(u.i = n_NELEM(a_aux_rand->b32); u.i-- != 0;){
263 ui32_t t, k;
265 # ifdef HAVE_CLOCK_GETTIME
266 clock_gettime(CLOCK_REALTIME, &ts);
267 t = (ui32_t)ts.tv_nsec;
268 # else
269 gettimeofday(&ts, NULL);
270 t = (ui32_t)ts.tv_usec;
271 # endif
272 if(rnd & 1)
273 t = (t >> 16) | (t << 16);
274 a_aux_rand->b32[u.i] ^= a_aux_rand_weak(seed ^ t);
275 a_aux_rand->b32[t % n_NELEM(a_aux_rand->b32)] ^= seed;
276 if(rnd == 7 || rnd == 17)
277 a_aux_rand->b32[u.i] ^= a_aux_rand_weak(seed ^ (ui32_t)ts.tv_sec);
278 k = a_aux_rand->b32[u.i] % n_NELEM(a_aux_rand->b32);
279 a_aux_rand->b32[k] ^= a_aux_rand->b32[u.i];
280 seed ^= a_aux_rand_weak(a_aux_rand->b32[k]);
281 if((rnd & 3) == 3)
282 seed ^= n_prime_next(seed);
286 for(u.i = 5 * sizeof(a_aux_rand->b8); u.i != 0; --u.i)
287 a_aux_rand_get8();
288 jleave:
289 NYD2_LEAVE;
292 n_INLINE ui8_t
293 a_aux_rand_get8(void){
294 ui8_t si, sj;
296 si = a_aux_rand->a._dat[++a_aux_rand->a._i];
297 sj = a_aux_rand->a._dat[a_aux_rand->a._j += si];
298 a_aux_rand->a._dat[a_aux_rand->a._i] = sj;
299 a_aux_rand->a._dat[a_aux_rand->a._j] = si;
300 return a_aux_rand->a._dat[(ui8_t)(si + sj)];
303 static ui32_t
304 a_aux_rand_weak(ui32_t seed){
305 /* From "Random number generators: good ones are hard to find",
306 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
307 * October 1988, p. 1195.
308 * (In fact: FreeBSD 4.7, /usr/src/lib/libc/stdlib/random.c.) */
309 ui32_t hi;
311 if(seed == 0)
312 seed = 123459876;
313 hi = seed / 127773;
314 seed %= 127773;
315 seed = (seed * 16807) - (hi * 2836);
316 if((si32_t)seed < 0)
317 seed += SI32_MAX;
318 return seed;
320 #endif /* HAVE_RANDOM != IMPL_ARC4 != IMPL_TLS */
322 static struct a_aux_err_map const *
323 a_aux_err_map_from_no(si32_t eno){
324 si32_t ecmp;
325 size_t asz;
326 n__ERR_NUMBER_TYPE const (*adat)[2], (*tmp)[2];
327 struct a_aux_err_map const *aemp;
328 NYD2_ENTER;
330 aemp = &a_aux_err_map[n__ERR_NUMBER_VOIDOFF];
332 if(UICMP(z, n_ABS(eno), <=, (n__ERR_NUMBER_TYPE)-1)){
333 for(adat = a_aux_err_no2mapoff, asz = n_NELEM(a_aux_err_no2mapoff);
334 asz != 0; asz >>= 1){
335 tmp = &adat[asz >> 1];
336 if((ecmp = (si32_t)((n__ERR_NUMBER_TYPE)eno - (*tmp)[0])) == 0){
337 aemp = &a_aux_err_map[(*tmp)[1]];
338 break;
340 if(ecmp > 0){
341 adat = &tmp[1];
342 --asz;
346 NYD2_LEAVE;
347 return aemp;
350 FL void
351 n_locale_init(void){
352 NYD2_ENTER;
354 n_psonce &= ~(n_PSO_UNICODE | n_PSO_ENC_MBSTATE);
356 #ifndef HAVE_SETLOCALE
357 n_mb_cur_max = 1;
358 #else
359 setlocale(LC_ALL, n_empty);
360 n_mb_cur_max = MB_CUR_MAX;
361 # ifdef HAVE_NL_LANGINFO
362 /* C99 */{
363 char const *cp;
365 if((cp = nl_langinfo(CODESET)) != NULL)
366 /* (Will log during startup if user set that via -S) */
367 ok_vset(ttycharset, cp);
369 # endif /* HAVE_SETLOCALE */
371 # ifdef HAVE_C90AMEND1
372 if(n_mb_cur_max > 1){
373 # ifdef HAVE_ALWAYS_UNICODE_LOCALE
374 n_psonce |= n_PSO_UNICODE;
375 # else
376 wchar_t wc;
377 if(mbtowc(&wc, "\303\266", 2) == 2 && wc == 0xF6 &&
378 mbtowc(&wc, "\342\202\254", 3) == 3 && wc == 0x20AC)
379 n_psonce |= n_PSO_UNICODE;
380 /* Reset possibly messed up state; luckily this also gives us an
381 * indication whether the encoding has locking shift state sequences */
382 if(mbtowc(&wc, NULL, n_mb_cur_max))
383 n_psonce |= n_PSO_ENC_MBSTATE;
384 # endif
386 # endif
387 #endif /* HAVE_C90AMEND1 */
388 NYD2_LEAVE;
391 FL size_t
392 n_screensize(void){
393 char const *cp;
394 uiz_t rv;
395 NYD2_ENTER;
397 if((cp = ok_vlook(screen)) != NULL){
398 n_idec_uiz_cp(&rv, cp, 0, NULL);
399 if(rv == 0)
400 rv = n_scrnheight;
401 }else
402 rv = n_scrnheight;
404 if(rv > 2)
405 rv -= 2;
406 NYD2_LEAVE;
407 return rv;
410 FL char const *
411 n_pager_get(char const **env_addon){
412 char const *rv;
413 NYD_ENTER;
415 rv = ok_vlook(PAGER);
417 if(env_addon != NULL){
418 *env_addon = NULL;
419 /* Update the manual upon any changes:
420 * *colour-pager*, $PAGER */
421 if(strstr(rv, "less") != NULL){
422 if(getenv("LESS") == NULL)
423 *env_addon = "LESS=RXi";
424 }else if(strstr(rv, "lv") != NULL){
425 if(getenv("LV") == NULL)
426 *env_addon = "LV=-c";
429 NYD_LEAVE;
430 return rv;
433 FL void
434 page_or_print(FILE *fp, size_t lines)
436 int c;
437 char const *cp;
438 NYD_ENTER;
440 fflush_rewind(fp);
442 if (n_go_may_yield_control() && (cp = ok_vlook(crt)) != NULL) {
443 size_t rows;
445 if(*cp == '\0')
446 rows = (size_t)n_scrnheight;
447 else
448 n_idec_uiz_cp(&rows, cp, 0, NULL);
450 if (rows > 0 && lines == 0) {
451 while ((c = getc(fp)) != EOF)
452 if (c == '\n' && ++lines >= rows)
453 break;
454 really_rewind(fp);
457 if (lines >= rows) {
458 char const *env_add[2], *pager;
460 pager = n_pager_get(&env_add[0]);
461 env_add[1] = NULL;
462 n_child_run(pager, NULL, fileno(fp), n_CHILD_FD_PASS, NULL,NULL,NULL,
463 env_add, NULL);
464 goto jleave;
468 while ((c = getc(fp)) != EOF)
469 putc(c, n_stdout);
470 jleave:
471 NYD_LEAVE;
474 FL enum protocol
475 which_protocol(char const *name, bool_t check_stat, bool_t try_hooks,
476 char const **adjusted_or_null)
478 /* TODO This which_protocol() sickness should be URL::new()->protocol() */
479 char const *cp, *orig_name;
480 enum protocol rv = PROTO_UNKNOWN;
481 NYD_ENTER;
483 if(name[0] == '%' && name[1] == ':')
484 name += 2;
485 orig_name = name;
487 for (cp = name; *cp && *cp != ':'; cp++)
488 if (!alnumchar(*cp))
489 goto jfile;
491 if(cp[0] == ':' && cp[1] == '/' && cp[2] == '/'){
492 if(!strncmp(name, "file", sizeof("file") -1) ||
493 !strncmp(name, "mbox", sizeof("mbox") -1))
494 rv = PROTO_FILE;
495 else if(!strncmp(name, "maildir", sizeof("maildir") -1)){
496 #ifdef HAVE_MAILDIR
497 rv = PROTO_MAILDIR;
498 #else
499 n_err(_("No Maildir directory support compiled in\n"));
500 #endif
501 }else if(!strncmp(name, "pop3", sizeof("pop3") -1)){
502 #ifdef HAVE_POP3
503 rv = PROTO_POP3;
504 #else
505 n_err(_("No POP3 support compiled in\n"));
506 #endif
507 }else if(!strncmp(name, "pop3s", sizeof("pop3s") -1)){
508 #if defined HAVE_POP3 && defined HAVE_TLS
509 rv = PROTO_POP3;
510 #else
511 n_err(_("No POP3S support compiled in\n"));
512 #endif
513 }else if(!strncmp(name, "imap", sizeof("imap") -1)){
514 #ifdef HAVE_IMAP
515 rv = PROTO_IMAP;
516 #else
517 n_err(_("No IMAP support compiled in\n"));
518 #endif
519 }else if(!strncmp(name, "imaps", sizeof("imaps") -1)){
520 #if defined HAVE_IMAP && defined HAVE_TLS
521 rv = PROTO_IMAP;
522 #else
523 n_err(_("No IMAPS support compiled in\n"));
524 #endif
526 orig_name = &cp[3];
527 goto jleave;
530 jfile:
531 rv = PROTO_FILE;
533 if(check_stat || try_hooks){
534 struct n_file_type ft;
535 struct stat stb;
536 char *np;
537 size_t sz;
539 np = n_lofi_alloc((sz = strlen(name)) + 4 +1);
540 memcpy(np, name, sz + 1);
542 if(!stat(name, &stb)){
543 if(S_ISDIR(stb.st_mode)
544 #ifdef HAVE_MAILDIR
545 && (memcpy(&np[sz], "/tmp", 5),
546 !stat(np, &stb) && S_ISDIR(stb.st_mode)) &&
547 (memcpy(&np[sz], "/new", 5),
548 !stat(np, &stb) && S_ISDIR(stb.st_mode)) &&
549 (memcpy(&np[sz], "/cur", 5),
550 !stat(np, &stb) && S_ISDIR(stb.st_mode))
551 #endif
553 #ifdef HAVE_MAILDIR
554 rv = PROTO_MAILDIR;
555 #else
556 rv = PROTO_UNKNOWN;
557 #endif
559 }else if(try_hooks && n_filetype_trial(&ft, name))
560 orig_name = savecatsep(name, '.', ft.ft_ext_dat);
561 else if((cp = ok_vlook(newfolders)) != NULL &&
562 !asccasecmp(cp, "maildir")){
563 #ifdef HAVE_MAILDIR
564 rv = PROTO_MAILDIR;
565 #else
566 n_err(_("*newfolders*: no Maildir directory support compiled in\n"));
567 #endif
570 n_lofi_free(np);
572 jleave:
573 if(adjusted_or_null != NULL)
574 *adjusted_or_null = orig_name;
575 NYD_LEAVE;
576 return rv;
579 FL char *
580 n_c_to_hex_base16(char store[3], char c){
581 static char const itoa16[] = "0123456789ABCDEF";
582 NYD2_ENTER;
584 store[2] = '\0';
585 store[1] = itoa16[(ui8_t)c & 0x0F];
586 c = ((ui8_t)c >> 4) & 0x0F;
587 store[0] = itoa16[(ui8_t)c];
588 NYD2_LEAVE;
589 return store;
592 FL si32_t
593 n_c_from_hex_base16(char const hex[2]){
594 static ui8_t const atoi16[] = {
595 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x30-0x37 */
596 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x38-0x3F */
597 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, /* 0x40-0x47 */
598 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x48-0x4f */
599 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x50-0x57 */
600 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x58-0x5f */
601 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF /* 0x60-0x67 */
603 ui8_t i1, i2;
604 si32_t rv;
605 NYD2_ENTER;
607 if ((i1 = (ui8_t)hex[0] - '0') >= n_NELEM(atoi16) ||
608 (i2 = (ui8_t)hex[1] - '0') >= n_NELEM(atoi16))
609 goto jerr;
610 i1 = atoi16[i1];
611 i2 = atoi16[i2];
612 if ((i1 | i2) & 0xF0u)
613 goto jerr;
614 rv = i1;
615 rv <<= 4;
616 rv += i2;
617 jleave:
618 NYD2_LEAVE;
619 return rv;
620 jerr:
621 rv = -1;
622 goto jleave;
625 FL enum n_idec_state
626 n_idec_buf(void *resp, char const *cbuf, uiz_t clen, ui8_t base,
627 enum n_idec_mode idm, char const **endptr_or_null){
628 ui8_t currc;
629 ui64_t res, cut;
630 enum n_idec_state rv;
631 NYD_ENTER;
633 idm &= n__IDEC_MODE_MASK;
634 rv = n_IDEC_STATE_NONE | idm;
635 res = 0;
637 if(clen == UIZ_MAX){
638 if(*cbuf == '\0')
639 goto jeinval;
640 }else if(clen == 0)
641 goto jeinval;
643 assert(base != 1 && base <= 36);
644 /*if(base == 1 || base > 36)
645 * goto jeinval;*/
647 /* Leading WS */
648 while(spacechar(*cbuf))
649 if(*++cbuf == '\0' || --clen == 0)
650 goto jeinval;
652 /* Check sign */
653 switch(*cbuf){
654 case '-':
655 rv |= n_IDEC_STATE_SEEN_MINUS;
656 /* FALLTHROUGH */
657 case '+':
658 if(*++cbuf == '\0' || --clen == 0)
659 goto jeinval;
660 break;
663 /* Base detection/skip */
664 if(*cbuf != '0'){
665 if(base == 0){
666 base = 10;
668 /* Support BASE#number prefix, where BASE is decimal 2-36 */
669 if(clen > 2){
670 char c1, c2, c3;
672 if(((c1 = cbuf[0]) >= '0' && c1 <= '9') &&
673 (((c2 = cbuf[1]) == '#') ||
674 (c2 >= '0' && c2 <= '9' && clen > 3 && cbuf[2] == '#'))){
675 base = a_aux_idec_atoi[(ui8_t)c1];
676 if(c2 == '#')
677 c3 = cbuf[2];
678 else{
679 c3 = cbuf[3];
680 base *= 10; /* xxx Inline atoi decimal base */
681 base += a_aux_idec_atoi[(ui8_t)c2];
684 /* We do not interpret this as BASE#number at all if either we
685 * did not get a valid base or if the first char is not valid
686 * according to base, to comply to the latest interpretion of
687 * "prefix", see comment for standard prefixes below */
688 if(base < 2 || base > 36 || a_aux_idec_atoi[(ui8_t)c3] >= base)
689 base = 10;
690 else if(c2 == '#')
691 clen -= 2, cbuf += 2;
692 else
693 clen -= 3, cbuf += 3;
698 /* Character must be valid for base */
699 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
700 if(currc >= base)
701 goto jeinval;
702 }else{
703 /* 0 always valid as is, fallback base 10 */
704 if(*++cbuf == '\0' || --clen == 0)
705 goto jleave;
707 /* Base "detection" */
708 if(base == 0 || base == 2 || base == 16){
709 switch(*cbuf){
710 case 'x':
711 case 'X':
712 if((base & 2) == 0){
713 base = 0x10;
714 goto jprefix_skip;
716 break;
717 case 'b':
718 case 'B':
719 if((base & 16) == 0){
720 base = 2; /* 0b10 */
721 /* Char after prefix must be valid. However, after some error
722 * in the tor software all libraries (which had to) turned to
723 * an interpretation of the C standard which says that the
724 * prefix may optionally precede an otherwise valid sequence,
725 * which means that "0x" is not a STATE_INVAL error but gives
726 * a "0" result with a "STATE_BASE" error and a rest of "x" */
727 jprefix_skip:
728 #if 1
729 if(clen > 1 && a_aux_idec_atoi[(ui8_t)cbuf[1]] < base)
730 --clen, ++cbuf;
731 #else
732 if(*++cbuf == '\0' || --clen == 0)
733 goto jeinval;
735 /* Character must be valid for base, invalid otherwise */
736 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
737 if(currc >= base)
738 goto jeinval;
739 #endif
741 break;
742 default:
743 if(base == 0)
744 base = 010;
745 break;
749 /* Character must be valid for base, _EBASE otherwise */
750 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
751 if(currc >= base)
752 goto jebase;
755 for(cut = a_aux_idec_cutlimit[base - 2];;){
756 if(res >= cut){
757 if(res == cut){
758 res *= base;
759 if(res > UI64_MAX - currc)
760 goto jeover;
761 res += currc;
762 }else
763 goto jeover;
764 }else{
765 res *= base;
766 res += currc;
769 if(*++cbuf == '\0' || --clen == 0)
770 break;
772 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
773 if(currc >= base)
774 goto jebase;
777 jleave:
779 ui64_t uimask;
781 switch(rv & n__IDEC_MODE_LIMIT_MASK){
782 case n_IDEC_MODE_LIMIT_8BIT: uimask = UI8_MAX; break;
783 case n_IDEC_MODE_LIMIT_16BIT: uimask = UI16_MAX; break;
784 case n_IDEC_MODE_LIMIT_32BIT: uimask = UI32_MAX; break;
785 default: uimask = UI64_MAX; break;
787 if((rv & n_IDEC_MODE_SIGNED_TYPE) &&
788 (!(rv & n_IDEC_MODE_POW2BASE_UNSIGNED) || !n_ISPOW2(base)))
789 uimask >>= 1;
791 if(res & ~uimask){
792 /* XXX never entered unless _SIGNED_TYPE! */
793 if((rv & (n_IDEC_MODE_SIGNED_TYPE | n_IDEC_STATE_SEEN_MINUS)
794 ) == (n_IDEC_MODE_SIGNED_TYPE | n_IDEC_STATE_SEEN_MINUS)){
795 if(res > uimask + 1){
796 res = uimask << 1;
797 res &= ~uimask;
798 }else{
799 res = -res;
800 break;
802 }else
803 res = uimask;
804 if(!(rv & n_IDEC_MODE_LIMIT_NOERROR))
805 rv |= n_IDEC_STATE_EOVERFLOW;
806 }else if(rv & n_IDEC_STATE_SEEN_MINUS)
807 res = -res;
808 }while(0);
810 switch(rv & n__IDEC_MODE_LIMIT_MASK){
811 case n_IDEC_MODE_LIMIT_8BIT:
812 if(rv & n_IDEC_MODE_SIGNED_TYPE)
813 *(si8_t*)resp = (si8_t)res;
814 else
815 *(ui8_t*)resp = (ui8_t)res;
816 break;
817 case n_IDEC_MODE_LIMIT_16BIT:
818 if(rv & n_IDEC_MODE_SIGNED_TYPE)
819 *(si16_t*)resp = (si16_t)res;
820 else
821 *(ui16_t*)resp = (ui16_t)res;
822 break;
823 case n_IDEC_MODE_LIMIT_32BIT:
824 if(rv & n_IDEC_MODE_SIGNED_TYPE)
825 *(si32_t*)resp = (si32_t)res;
826 else
827 *(ui32_t*)resp = (ui32_t)res;
828 break;
829 default:
830 if(rv & n_IDEC_MODE_SIGNED_TYPE)
831 *(si64_t*)resp = (si64_t)res;
832 else
833 *(ui64_t*)resp = (ui64_t)res;
834 break;
837 if(endptr_or_null != NULL)
838 *endptr_or_null = cbuf;
839 if(*cbuf == '\0' || clen == 0)
840 rv |= n_IDEC_STATE_CONSUMED;
841 NYD_LEAVE;
842 return rv;
844 jeinval:
845 rv |= n_IDEC_STATE_EINVAL;
846 goto j_maxval;
847 jebase:
848 /* Not a base error for terminator and whitespace! */
849 if(*cbuf != '\0' && !spacechar(*cbuf))
850 rv |= n_IDEC_STATE_EBASE;
851 goto jleave;
853 jeover:
854 /* Overflow error: consume input until bad character or length out */
855 for(;;){
856 if(*++cbuf == '\0' || --clen == 0)
857 break;
858 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
859 if(currc >= base)
860 break;
863 rv |= n_IDEC_STATE_EOVERFLOW;
864 j_maxval:
865 if(rv & n_IDEC_MODE_SIGNED_TYPE)
866 res = (rv & n_IDEC_STATE_SEEN_MINUS) ? (ui64_t)SI64_MIN
867 : (ui64_t)SI64_MAX;
868 else
869 res = UI64_MAX;
870 rv &= ~n_IDEC_STATE_SEEN_MINUS;
871 goto jleave;
874 FL char *
875 n_ienc_buf(char cbuf[n_IENC_BUFFER_SIZE], ui64_t value, ui8_t base,
876 enum n_ienc_mode iem){
877 enum{a_ISNEG = 1u<<n__IENC_MODE_SHIFT};
879 ui8_t shiftmodu;
880 char const *itoa;
881 char *rv;
882 NYD_ENTER;
884 iem &= n__IENC_MODE_MASK;
886 assert(base != 1 && base <= 36);
887 /*if(base == 1 || base > 36){
888 * rv = NULL;
889 * goto jleave;
890 *}*/
892 rv = &cbuf[n_IENC_BUFFER_SIZE];
893 *--rv = '\0';
894 itoa = (iem & n_IENC_MODE_LOWERCASE) ? a_aux_ienc_itoa_lower
895 : a_aux_ienc_itoa_upper;
897 if((si64_t)value < 0){
898 iem |= a_ISNEG;
899 if(iem & n_IENC_MODE_SIGNED_TYPE){
900 /* self->is_negative = TRU1; */
901 value = -value;
905 if((shiftmodu = a_aux_ienc_shifts[base - 2]) != 0){
906 --base; /* convert to mask */
908 *--rv = itoa[value & base];
909 value >>= shiftmodu;
910 }while(value != 0);
912 if(!(iem & n_IENC_MODE_NO_PREFIX)){
913 /* self->before_prefix = cp; */
914 if(shiftmodu == 4)
915 *--rv = 'x';
916 else if(shiftmodu == 1)
917 *--rv = 'b';
918 else if(shiftmodu != 3){
919 ++base; /* Reconvert from mask */
920 goto jnumber_sign_prefix;
922 *--rv = '0';
924 }else{
926 shiftmodu = value % base;
927 value /= base;
928 *--rv = itoa[shiftmodu];
929 }while(value != 0);
931 if(!(iem & n_IENC_MODE_NO_PREFIX) && base != 10){
932 jnumber_sign_prefix:
933 value = base;
934 base = 10;
935 *--rv = '#';
937 shiftmodu = value % base;
938 value /= base;
939 *--rv = itoa[shiftmodu];
940 }while(value != 0);
943 if(iem & n_IENC_MODE_SIGNED_TYPE){
944 char c;
946 if(iem & a_ISNEG)
947 c = '-';
948 else if(iem & n_IENC_MODE_SIGNED_PLUS)
949 c = '+';
950 else if(iem & n_IENC_MODE_SIGNED_SPACE)
951 c = ' ';
952 else
953 c = '\0';
955 if(c != '\0')
956 *--rv = c;
959 NYD_LEAVE;
960 return rv;
963 FL ui32_t
964 n_torek_hash(char const *name){
965 /* Chris Torek's hash */
966 char c;
967 ui32_t h;
968 NYD2_ENTER;
970 for(h = 0; (c = *name++) != '\0';)
971 h = (h * 33) + c;
972 NYD2_LEAVE;
973 return h;
976 FL ui32_t
977 n_torek_ihashn(char const *dat, size_t len){
978 /* See n_torek_hash() */
979 char c;
980 ui32_t h;
981 NYD2_ENTER;
983 if(len == UIZ_MAX)
984 for(h = 0; (c = *dat++) != '\0';)
985 h = (h * 33) + lowerconv(c);
986 else
987 for(h = 0; len > 0; --len){
988 c = *dat++;
989 h = (h * 33) + lowerconv(c);
991 NYD2_LEAVE;
992 return h;
995 FL ui32_t
996 n_prime_next(ui32_t n){
997 static ui32_t const primes[] = {
998 5, 11, 23, 47, 97, 157, 283,
999 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
1000 131071, 262139, 524287, 1048573, 2097143, 4194301,
1001 8388593, 16777213, 33554393, 67108859, 134217689,
1002 268435399, 536870909, 1073741789, 2147483647
1004 ui32_t i, mprime;
1005 NYD2_ENTER;
1007 i = (n < primes[n_NELEM(primes) / 4] ? 0
1008 : (n < primes[n_NELEM(primes) / 2] ? n_NELEM(primes) / 4
1009 : n_NELEM(primes) / 2));
1011 do if((mprime = primes[i]) > n)
1012 break;
1013 while(++i < n_NELEM(primes));
1015 if(i == n_NELEM(primes) && mprime < n)
1016 mprime = n;
1017 NYD2_LEAVE;
1018 return mprime;
1021 FL char const *
1022 n_getdeadletter(void){
1023 char const *cp;
1024 bool_t bla;
1025 NYD_ENTER;
1027 bla = FAL0;
1028 jredo:
1029 cp = fexpand(ok_vlook(DEAD), FEXP_LOCAL | FEXP_NSHELL);
1030 if(cp == NULL || strlen(cp) >= PATH_MAX){
1031 if(!bla){
1032 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
1033 VAL_DEAD, n_shexp_quote_cp((cp == NULL ? n_empty : cp), FAL0));
1034 ok_vclear(DEAD);
1035 bla = TRU1;
1036 goto jredo;
1037 }else{
1038 cp = savecatsep(ok_vlook(TMPDIR), '/', VAL_DEAD_BASENAME);
1039 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp);
1042 NYD_LEAVE;
1043 return cp;
1046 FL char *
1047 n_nodename(bool_t mayoverride){
1048 static char *sys_hostname, *hostname; /* XXX free-at-exit */
1050 struct utsname ut;
1051 char *hn;
1052 #ifdef HAVE_SOCKETS
1053 # ifdef HAVE_GETADDRINFO
1054 struct addrinfo hints, *res;
1055 # else
1056 struct hostent *hent;
1057 # endif
1058 #endif
1059 NYD2_ENTER;
1061 if(n_psonce & n_PSO_REPRODUCIBLE)
1062 hn = n_UNCONST(n_reproducible_name);
1063 else if(mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0'){
1065 }else if((hn = sys_hostname) == NULL){
1066 bool_t lofi;
1068 lofi = FAL0;
1069 uname(&ut);
1070 hn = ut.nodename;
1072 #ifdef HAVE_SOCKETS
1073 # ifdef HAVE_GETADDRINFO
1074 memset(&hints, 0, sizeof hints);
1075 hints.ai_family = AF_UNSPEC;
1076 hints.ai_flags = AI_CANONNAME;
1077 if(getaddrinfo(hn, NULL, &hints, &res) == 0){
1078 if(res->ai_canonname != NULL){
1079 size_t l;
1081 l = strlen(res->ai_canonname) +1;
1082 hn = n_lofi_alloc(l);
1083 lofi = TRU1;
1084 memcpy(hn, res->ai_canonname, l);
1086 freeaddrinfo(res);
1088 # else
1089 hent = gethostbyname(hn);
1090 if(hent != NULL)
1091 hn = hent->h_name;
1092 # endif
1093 #endif /* HAVE_SOCKETS */
1095 #ifdef HAVE_IDNA
1096 /* C99 */{
1097 struct n_string cnv;
1099 n_string_creat(&cnv);
1100 if(!n_idna_to_ascii(&cnv, hn, UIZ_MAX))
1101 n_panic(_("The system hostname is invalid, "
1102 "IDNA conversion failed: %s\n"),
1103 n_shexp_quote_cp(hn, FAL0));
1104 sys_hostname = n_string_cp(&cnv);
1105 n_string_drop_ownership(&cnv);
1106 /*n_string_gut(&cnv);*/
1108 #else
1109 sys_hostname = sstrdup(hn);
1110 #endif
1112 if(lofi)
1113 n_lofi_free(hn);
1114 hn = sys_hostname;
1117 if(hostname != NULL && hostname != sys_hostname)
1118 n_free(hostname);
1119 hostname = sstrdup(hn);
1120 NYD2_LEAVE;
1121 return hostname;
1124 #ifdef HAVE_IDNA
1125 FL bool_t
1126 n_idna_to_ascii(struct n_string *out, char const *ibuf, size_t ilen){
1127 char *idna_utf8;
1128 bool_t lofi, rv;
1129 NYD_ENTER;
1131 if(ilen == UIZ_MAX)
1132 ilen = strlen(ibuf);
1134 lofi = FAL0;
1136 if((rv = (ilen == 0)))
1137 goto jleave;
1138 if(ibuf[ilen] != '\0'){
1139 lofi = TRU1;
1140 idna_utf8 = n_lofi_alloc(ilen +1);
1141 memcpy(idna_utf8, ibuf, ilen);
1142 idna_utf8[ilen] = '\0';
1143 ibuf = idna_utf8;
1145 ilen = 0;
1147 # ifndef HAVE_ALWAYS_UNICODE_LOCALE
1148 if(n_psonce & n_PSO_UNICODE)
1149 # endif
1150 idna_utf8 = n_UNCONST(ibuf);
1151 # ifndef HAVE_ALWAYS_UNICODE_LOCALE
1152 else if((idna_utf8 = n_iconv_onetime_cp(n_ICONV_NONE, "utf-8",
1153 ok_vlook(ttycharset), ibuf)) == NULL)
1154 goto jleave;
1155 # endif
1157 # if HAVE_IDNA == n_IDNA_IMPL_LIBIDN2
1158 /* C99 */{
1159 char *idna_ascii;
1160 int f, rc;
1162 f = IDN2_NONTRANSITIONAL;
1163 jidn2_redo:
1164 if((rc = idn2_to_ascii_8z(idna_utf8, &idna_ascii, f)) == IDN2_OK){
1165 out = n_string_assign_cp(out, idna_ascii);
1166 idn2_free(idna_ascii);
1167 rv = TRU1;
1168 ilen = out->s_len;
1169 }else if(rc == IDN2_DISALLOWED && f != IDN2_TRANSITIONAL){
1170 f = IDN2_TRANSITIONAL;
1171 goto jidn2_redo;
1175 # elif HAVE_IDNA == n_IDNA_IMPL_LIBIDN
1176 /* C99 */{
1177 char *idna_ascii;
1179 if(idna_to_ascii_8z(idna_utf8, &idna_ascii, 0) == IDNA_SUCCESS){
1180 out = n_string_assign_cp(out, idna_ascii);
1181 idn_free(idna_ascii);
1182 rv = TRU1;
1183 ilen = out->s_len;
1187 # elif HAVE_IDNA == n_IDNA_IMPL_IDNKIT
1188 ilen = strlen(idna_utf8);
1189 jredo:
1190 switch(idn_encodename(
1191 /* LOCALCONV changed meaning in v2 and is no longer available for
1192 * encoding. This makes sense, bu */
1194 # ifdef IDN_UNICODECONV /* v2 */
1195 IDN_ENCODE_APP & ~IDN_UNICODECONV
1196 # else
1197 IDN_DELIMMAP | IDN_LOCALMAP | IDN_NAMEPREP | IDN_IDNCONV |
1198 IDN_LENCHECK | IDN_ASCCHECK
1199 # endif
1200 ), idna_utf8,
1201 n_string_resize(n_string_trunc(out, 0), ilen)->s_dat, ilen)){
1202 case idn_buffer_overflow:
1203 ilen += HOST_NAME_MAX +1;
1204 goto jredo;
1205 case idn_success:
1206 rv = TRU1;
1207 ilen = strlen(out->s_dat);
1208 break;
1209 default:
1210 ilen = 0;
1211 break;
1214 # else
1215 # error Unknown HAVE_IDNA
1216 # endif
1217 jleave:
1218 if(lofi)
1219 n_lofi_free(n_UNCONST(ibuf));
1220 out = n_string_trunc(out, ilen);
1221 NYD_LEAVE;
1222 return rv;
1224 #endif /* HAVE_IDNA */
1226 FL char *
1227 n_random_create_buf(char *dat, size_t len, ui32_t *reprocnt_or_null){
1228 struct str b64;
1229 char *indat, *cp, *oudat;
1230 size_t i, inlen, oulen;
1231 NYD_ENTER;
1233 if(!(n_psonce & n_PSO_RANDOM_INIT)){
1234 n_psonce |= n_PSO_RANDOM_INIT;
1236 if(n_poption & n_PO_D_V){
1237 char const *prngn;
1239 #if HAVE_RANDOM == n_RANDOM_IMPL_ARC4
1240 prngn = "arc4random";
1241 #elif HAVE_RANDOM == n_RANDOM_IMPL_TLS
1242 prngn = "*TLS RAND_*";
1243 #elif HAVE_RANDOM == n_RANDOM_IMPL_GETRANDOM
1244 prngn = "getrandom(2/3) + builtin ARC4";
1245 #elif HAVE_RANDOM == n_RANDOM_IMPL_URANDOM
1246 prngn = "/dev/urandom + builtin ARC4";
1247 #elif HAVE_RANDOM == n_RANDOM_IMPL_BUILTIN
1248 prngn = "builtin ARC4";
1249 #else
1250 # error n_random_create_buf(): the value of HAVE_RANDOM is not supported
1251 #endif
1252 n_err(_("P(seudo)R(andomNumber)G(enerator): %s\n"), prngn);
1255 #if HAVE_RANDOM != n_RANDOM_IMPL_ARC4 && HAVE_RANDOM != n_RANDOM_IMPL_TLS
1256 a_aux_rand_init();
1257 #endif
1260 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
1261 * with PAD stripped is still longer than what the user requests, easy way.
1262 * The relation of base64 is fixed 3 in = 4 out, and we do not want to
1263 * include the base64 PAD characters in our random string: give some pad */
1264 i = len;
1265 if((inlen = i % 3) != 0)
1266 i += 3 - inlen;
1267 jinc1:
1268 inlen = i >> 2;
1269 oulen = inlen << 2;
1270 if(oulen < len){
1271 i += 3;
1272 goto jinc1;
1274 inlen = inlen + (inlen << 1);
1276 indat = n_lofi_alloc(inlen +1);
1278 if(!(n_psonce & n_PSO_REPRODUCIBLE) || reprocnt_or_null == NULL){
1279 #if HAVE_RANDOM == n_RANDOM_IMPL_TLS
1280 n_tls_rand_bytes(indat, inlen);
1281 #elif HAVE_RANDOM != n_RANDOM_IMPL_ARC4
1282 for(i = inlen; i-- > 0;)
1283 indat[i] = (char)a_aux_rand_get8();
1284 #else
1285 for(cp = indat, i = inlen; i > 0;){
1286 union {ui32_t i4; char c[4];} r;
1287 size_t j;
1289 r.i4 = (ui32_t)arc4random();
1290 switch((j = i & 3)){
1291 case 0: cp[3] = r.c[3]; j = 4; /* FALLTHRU */
1292 case 3: cp[2] = r.c[2]; /* FALLTHRU */
1293 case 2: cp[1] = r.c[1]; /* FALLTHRU */
1294 default: cp[0] = r.c[0]; break;
1296 cp += j;
1297 i -= j;
1299 #endif
1300 }else{
1301 for(cp = indat, i = inlen; i > 0;){
1302 union {ui32_t i4; char c[4];} r;
1303 size_t j;
1305 r.i4 = ++*reprocnt_or_null;
1306 if(n_psonce & n_PSO_BIG_ENDIAN){ /* TODO BSWAP */
1307 char x;
1309 x = r.c[0];
1310 r.c[0] = r.c[3];
1311 r.c[3] = x;
1312 x = r.c[1];
1313 r.c[1] = r.c[2];
1314 r.c[2] = x;
1316 switch((j = i & 3)){
1317 case 0: cp[3] = r.c[3]; j = 4; /* FALLTHRU */
1318 case 3: cp[2] = r.c[2]; /* FALLTHRU */
1319 case 2: cp[1] = r.c[1]; /* FALLTHRU */
1320 default: cp[0] = r.c[0]; break;
1322 cp += j;
1323 i -= j;
1327 oudat = (len >= oulen) ? dat : n_lofi_alloc(oulen +1);
1328 b64.s = oudat;
1329 b64_encode_buf(&b64, indat, inlen, B64_BUF | B64_RFC4648URL | B64_NOPAD);
1330 assert(b64.l >= len);
1331 memcpy(dat, b64.s, len);
1332 dat[len] = '\0';
1333 if(oudat != dat)
1334 n_lofi_free(oudat);
1336 n_lofi_free(indat);
1338 NYD_LEAVE;
1339 return dat;
1342 FL char *
1343 n_random_create_cp(size_t len, ui32_t *reprocnt_or_null){
1344 char *dat;
1345 NYD_ENTER;
1347 dat = n_autorec_alloc(len +1);
1348 dat = n_random_create_buf(dat, len, reprocnt_or_null);
1349 NYD_LEAVE;
1350 return dat;
1353 FL bool_t
1354 n_boolify(char const *inbuf, uiz_t inlen, bool_t emptyrv){
1355 bool_t rv;
1356 NYD2_ENTER;
1357 assert(inlen == 0 || inbuf != NULL);
1359 if(inlen == UIZ_MAX)
1360 inlen = strlen(inbuf);
1362 if(inlen == 0)
1363 rv = (emptyrv >= FAL0) ? (emptyrv == FAL0 ? FAL0 : TRU1) : TRU2;
1364 else{
1365 if((inlen == 1 && (*inbuf == '1' || *inbuf == 'y' || *inbuf == 'Y')) ||
1366 !ascncasecmp(inbuf, "true", inlen) ||
1367 !ascncasecmp(inbuf, "yes", inlen) ||
1368 !ascncasecmp(inbuf, "on", inlen))
1369 rv = TRU1;
1370 else if((inlen == 1 &&
1371 (*inbuf == '0' || *inbuf == 'n' || *inbuf == 'N')) ||
1372 !ascncasecmp(inbuf, "false", inlen) ||
1373 !ascncasecmp(inbuf, "no", inlen) ||
1374 !ascncasecmp(inbuf, "off", inlen))
1375 rv = FAL0;
1376 else{
1377 ui64_t ib;
1379 if((n_idec_buf(&ib, inbuf, inlen, 0, 0, NULL
1380 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
1381 ) != n_IDEC_STATE_CONSUMED)
1382 rv = TRUM1;
1383 else
1384 rv = (ib != 0);
1387 NYD2_LEAVE;
1388 return rv;
1391 FL bool_t
1392 n_quadify(char const *inbuf, uiz_t inlen, char const *prompt, bool_t emptyrv){
1393 bool_t rv;
1394 NYD2_ENTER;
1395 assert(inlen == 0 || inbuf != NULL);
1397 if(inlen == UIZ_MAX)
1398 inlen = strlen(inbuf);
1400 if(inlen == 0)
1401 rv = (emptyrv >= FAL0) ? (emptyrv == FAL0 ? FAL0 : TRU1) : TRU2;
1402 else if((rv = n_boolify(inbuf, inlen, emptyrv)) < FAL0 &&
1403 !ascncasecmp(inbuf, "ask-", 4) &&
1404 (rv = n_boolify(&inbuf[4], inlen - 4, emptyrv)) >= FAL0 &&
1405 (n_psonce & n_PSO_INTERACTIVE) && !(n_pstate & n_PS_ROBOT))
1406 rv = getapproval(prompt, rv);
1407 NYD2_LEAVE;
1408 return rv;
1411 FL bool_t
1412 n_is_all_or_aster(char const *name){
1413 bool_t rv;
1414 NYD2_ENTER;
1416 rv = ((name[0] == '*' && name[1] == '\0') || !asccasecmp(name, "all"));
1417 NYD2_LEAVE;
1418 return rv;
1421 FL struct n_timespec const *
1422 n_time_now(bool_t force_update){ /* TODO event loop update IF cmd requests! */
1423 static struct n_timespec ts_now;
1424 NYD2_ENTER;
1426 if(n_UNLIKELY((n_psonce & n_PSO_REPRODUCIBLE) != 0)){
1427 /* Guaranteed 32-bit posnum TODO SOURCE_DATE_EPOCH should be 64-bit! */
1428 (void)n_idec_si64_cp(&ts_now.ts_sec, ok_vlook(SOURCE_DATE_EPOCH), 0,NULL);
1429 ts_now.ts_nsec = 0;
1430 }else if(force_update || ts_now.ts_sec == 0){
1431 #ifdef HAVE_CLOCK_GETTIME
1432 struct timespec ts;
1434 clock_gettime(CLOCK_REALTIME, &ts);
1435 ts_now.ts_sec = (si64_t)ts.tv_sec;
1436 ts_now.ts_nsec = (siz_t)ts.tv_nsec;
1437 #elif defined HAVE_GETTIMEOFDAY
1438 struct timeval tv;
1440 gettimeofday(&tv, NULL);
1441 ts_now.ts_sec = (si64_t)tv.tv_sec;
1442 ts_now.ts_nsec = (siz_t)tv.tv_usec * 1000;
1443 #else
1444 ts_now.ts_sec = (si64_t)time(NULL);
1445 ts_now.ts_nsec = 0;
1446 #endif
1449 /* Just in case.. */
1450 if(n_UNLIKELY(ts_now.ts_sec < 0))
1451 ts_now.ts_sec = 0;
1452 NYD2_LEAVE;
1453 return &ts_now;
1456 FL void
1457 time_current_update(struct time_current *tc, bool_t full_update){
1458 NYD_ENTER;
1459 tc->tc_time = (time_t)n_time_now(TRU1)->ts_sec;
1461 if(full_update){
1462 char *cp;
1463 struct tm *tmp;
1464 time_t t;
1466 t = tc->tc_time;
1467 jredo:
1468 if((tmp = gmtime(&t)) == NULL){
1469 t = 0;
1470 goto jredo;
1472 memcpy(&tc->tc_gm, tmp, sizeof tc->tc_gm);
1473 if((tmp = localtime(&t)) == NULL){
1474 t = 0;
1475 goto jredo;
1477 memcpy(&tc->tc_local, tmp, sizeof tc->tc_local);
1478 cp = sstpcpy(tc->tc_ctime, n_time_ctime((si64_t)tc->tc_time, tmp));
1479 *cp++ = '\n';
1480 *cp = '\0';
1481 assert(PTR2SIZE(++cp - tc->tc_ctime) < sizeof(tc->tc_ctime));
1483 NYD_LEAVE;
1486 FL char *
1487 n_time_ctime(si64_t secsepoch, struct tm const *localtime_or_nil){/* TODO err*/
1488 /* Problem is that secsepoch may be invalid for representation of ctime(3),
1489 * which indeed is asctime(localtime(t)); musl libc says for asctime(3):
1490 * ISO C requires us to use the above format string,
1491 * even if it will not fit in the buffer. Thus asctime_r
1492 * is _supposed_ to crash if the fields in tm are too large.
1493 * We follow this behavior and crash "gracefully" to warn
1494 * application developers that they may not be so lucky
1495 * on other implementations (e.g. stack smashing..).
1496 * So we need to do it on our own or the libc may kill us */
1497 static char buf[32]; /* TODO static buffer (-> datetime_to_format()) */
1499 si32_t y, md, th, tm, ts;
1500 char const *wdn, *mn;
1501 struct tm const *tmp;
1502 NYD_ENTER;
1504 if((tmp = localtime_or_nil) == NULL){
1505 time_t t;
1507 t = (time_t)secsepoch;
1508 jredo:
1509 if((tmp = localtime(&t)) == NULL){
1510 /* TODO error log */
1511 t = 0;
1512 goto jredo;
1516 if(n_UNLIKELY((y = tmp->tm_year) < 0 || y >= 9999/*SI32_MAX*/ - 1900)){
1517 y = 1970;
1518 wdn = n_weekday_names[4];
1519 mn = n_month_names[0];
1520 md = 1;
1521 th = tm = ts = 0;
1522 }else{
1523 y += 1900;
1524 wdn = (tmp->tm_wday >= 0 && tmp->tm_wday <= 6)
1525 ? n_weekday_names[tmp->tm_wday] : n_qm;
1526 mn = (tmp->tm_mon >= 0 && tmp->tm_mon <= 11)
1527 ? n_month_names[tmp->tm_mon] : n_qm;
1529 if((md = tmp->tm_mday) < 1 || md > 31)
1530 md = 1;
1532 if((th = tmp->tm_hour) < 0 || th > 23)
1533 th = 0;
1534 if((tm = tmp->tm_min) < 0 || tm > 59)
1535 tm = 0;
1536 if((ts = tmp->tm_sec) < 0 || ts > 60)
1537 ts = 0;
1540 (void)snprintf(buf, sizeof buf, "%3s %3s%3d %.2d:%.2d:%.2d %d",
1541 wdn, mn, md, th, tm, ts, y);
1542 NYD_LEAVE;
1543 return buf;
1546 FL uiz_t
1547 n_msleep(uiz_t millis, bool_t ignint){
1548 uiz_t rv;
1549 NYD2_ENTER;
1551 #ifdef HAVE_NANOSLEEP
1552 /* C99 */{
1553 struct timespec ts, trem;
1554 int i;
1556 ts.tv_sec = millis / 1000;
1557 ts.tv_nsec = (millis %= 1000) * 1000 * 1000;
1559 while((i = nanosleep(&ts, &trem)) != 0 && ignint)
1560 ts = trem;
1561 rv = (i == 0) ? 0 : (trem.tv_sec * 1000) + (trem.tv_nsec / (1000 * 1000));
1564 #elif defined HAVE_SLEEP
1565 if((millis /= 1000) == 0)
1566 millis = 1;
1567 while((rv = sleep((unsigned int)millis)) != 0 && ignint)
1568 millis = rv;
1569 #else
1570 # error Configuration should have detected a function for sleeping.
1571 #endif
1573 NYD2_LEAVE;
1574 return rv;
1577 FL void
1578 n_err(char const *format, ...){
1579 va_list ap;
1580 NYD2_ENTER;
1582 va_start(ap, format);
1583 #ifdef HAVE_ERRORS
1584 if(n_psonce & n_PSO_INTERACTIVE)
1585 n_verr(format, ap);
1586 else
1587 #endif
1589 size_t len;
1590 bool_t doname;
1592 doname = FAL0;
1594 while(*format == '\n'){
1595 doname = TRU1;
1596 putc('\n', n_stderr);
1597 ++format;
1600 if(doname)
1601 a_aux_err_linelen = 0;
1603 if((len = strlen(format)) > 0){
1604 if(doname || a_aux_err_linelen == 0){
1605 char const *cp;
1607 if(*(cp = ok_vlook(log_prefix)) != '\0')
1608 fputs(cp, n_stderr);
1610 vfprintf(n_stderr, format, ap);
1612 /* C99 */{
1613 size_t i = len;
1615 if(format[--len] == '\n'){
1616 a_aux_err_linelen = (i -= ++len);
1617 break;
1619 ++a_aux_err_linelen;
1620 }while(len > 0);
1624 fflush(n_stderr);
1626 va_end(ap);
1627 NYD2_LEAVE;
1630 FL void
1631 n_verr(char const *format, va_list ap){
1632 #ifdef HAVE_ERRORS
1633 struct a_aux_err_node *enp;
1634 #endif
1635 bool_t doname;
1636 size_t len;
1637 NYD2_ENTER;
1639 doname = FAL0;
1641 while(*format == '\n'){
1642 putc('\n', n_stderr);
1643 doname = TRU1;
1644 ++format;
1647 if(doname){
1648 a_aux_err_linelen = 0;
1649 #ifdef HAVE_ERRORS
1650 if(n_psonce & n_PSO_INTERACTIVE){
1651 if((enp = a_aux_err_tail) != NULL &&
1652 (enp->ae_str.s_len > 0 &&
1653 enp->ae_str.s_dat[enp->ae_str.s_len - 1] != '\n'))
1654 n_string_push_c(&enp->ae_str, '\n');
1656 #endif
1659 if((len = strlen(format)) == 0)
1660 goto jleave;
1661 #ifdef HAVE_ERRORS
1662 n_pstate |= n_PS_ERRORS_PROMPT;
1663 #endif
1665 if(doname || a_aux_err_linelen == 0){
1666 char const *cp;
1668 if(*(cp = ok_vlook(log_prefix)) != '\0')
1669 fputs(cp, n_stderr);
1672 /* C99 */{
1673 size_t i = len;
1675 if(format[--len] == '\n'){
1676 a_aux_err_linelen = (i -= ++len);
1677 break;
1679 ++a_aux_err_linelen;
1680 }while(len > 0);
1683 #ifdef HAVE_ERRORS
1684 if(!(n_psonce & n_PSO_INTERACTIVE))
1685 #endif
1686 vfprintf(n_stderr, format, ap);
1687 #ifdef HAVE_ERRORS
1688 else{
1689 int imax, i;
1690 n_LCTAV(ERRORS_MAX > 3);
1692 /* Link it into the `errors' message ring */
1693 if((enp = a_aux_err_tail) == NULL){
1694 jcreat:
1695 enp = n_alloc(sizeof *enp);
1696 enp->ae_next = NULL;
1697 n_string_creat(&enp->ae_str);
1698 if(a_aux_err_tail != NULL)
1699 a_aux_err_tail->ae_next = enp;
1700 else
1701 a_aux_err_head = enp;
1702 a_aux_err_tail = enp;
1703 ++a_aux_err_cnt;
1704 }else if(doname ||
1705 (enp->ae_str.s_len > 0 &&
1706 enp->ae_str.s_dat[enp->ae_str.s_len - 1] == '\n')){
1707 if(a_aux_err_cnt < ERRORS_MAX)
1708 goto jcreat;
1710 a_aux_err_head = (enp = a_aux_err_head)->ae_next;
1711 a_aux_err_tail->ae_next = enp;
1712 a_aux_err_tail = enp;
1713 enp->ae_next = NULL;
1714 n_string_trunc(&enp->ae_str, 0);
1717 # ifdef HAVE_N_VA_COPY
1718 imax = 64;
1719 # else
1720 imax = n_MIN(LINESIZE, 1024);
1721 # endif
1722 for(i = imax;; imax = ++i /* xxx could wrap, maybe */){
1723 # ifdef HAVE_N_VA_COPY
1724 va_list vac;
1726 n_va_copy(vac, ap);
1727 # else
1728 # define vac ap
1729 # endif
1731 n_string_resize(&enp->ae_str, (len = enp->ae_str.s_len) + (size_t)i);
1732 i = vsnprintf(&enp->ae_str.s_dat[len], (size_t)i, format, vac);
1733 # ifdef HAVE_N_VA_COPY
1734 va_end(vac);
1735 # else
1736 # undef vac
1737 # endif
1738 if(i <= 0)
1739 goto jleave;
1740 if(UICMP(z, i, >=, imax)){
1741 # ifdef HAVE_N_VA_COPY
1742 /* XXX Check overflow for upcoming LEN+++i! */
1743 n_string_trunc(&enp->ae_str, len);
1744 continue;
1745 # else
1746 i = (int)strlen(&enp->ae_str.s_dat[len]);
1747 # endif
1749 break;
1751 n_string_trunc(&enp->ae_str, len + (size_t)i);
1753 fwrite(&enp->ae_str.s_dat[len], 1, (size_t)i, n_stderr);
1755 #endif /* HAVE_ERRORS */
1757 jleave:
1758 fflush(n_stderr);
1759 NYD2_LEAVE;
1762 FL void
1763 n_err_sighdl(char const *format, ...){ /* TODO sigsafe; obsolete! */
1764 va_list ap;
1765 NYD_X;
1767 va_start(ap, format);
1768 vfprintf(n_stderr, format, ap);
1769 va_end(ap);
1770 fflush(n_stderr);
1773 FL void
1774 n_perr(char const *msg, int errval){
1775 int e;
1776 char const *fmt;
1777 NYD2_ENTER;
1779 if(msg == NULL){
1780 fmt = "%s%s\n";
1781 msg = n_empty;
1782 }else
1783 fmt = "%s: %s\n";
1785 e = (errval == 0) ? n_err_no : errval;
1786 n_err(fmt, msg, n_err_to_doc(e));
1787 if(errval == 0)
1788 n_err_no = e;
1789 NYD2_LEAVE;
1792 FL void
1793 n_alert(char const *format, ...){
1794 va_list ap;
1795 NYD2_ENTER;
1797 n_err(a_aux_err_linelen > 0 ? _("\nAlert: ") : _("Alert: "));
1799 va_start(ap, format);
1800 n_verr(format, ap);
1801 va_end(ap);
1803 n_err("\n");
1804 NYD2_LEAVE;
1807 FL void
1808 n_panic(char const *format, ...){
1809 va_list ap;
1810 NYD2_ENTER;
1812 if(a_aux_err_linelen > 0){
1813 putc('\n', n_stderr);
1814 a_aux_err_linelen = 0;
1816 fprintf(n_stderr, "%sPanic: ", ok_vlook(log_prefix));
1818 va_start(ap, format);
1819 vfprintf(n_stderr, format, ap);
1820 va_end(ap);
1822 putc('\n', n_stderr);
1823 fflush(n_stderr);
1824 NYD2_LEAVE;
1825 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1828 #ifdef HAVE_ERRORS
1829 FL int
1830 c_errors(void *v){
1831 char **argv = v;
1832 struct a_aux_err_node *enp;
1833 NYD_ENTER;
1835 if(*argv == NULL)
1836 goto jlist;
1837 if(argv[1] != NULL)
1838 goto jerr;
1839 if(!asccasecmp(*argv, "show"))
1840 goto jlist;
1841 if(!asccasecmp(*argv, "clear"))
1842 goto jclear;
1843 jerr:
1844 fprintf(n_stderr,
1845 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1846 v = NULL;
1847 jleave:
1848 NYD_LEAVE;
1849 return (v == NULL) ? !STOP : !OKAY; /* xxx 1:bad 0:good -- do some */
1851 jlist:{
1852 FILE *fp;
1853 size_t i;
1855 if(a_aux_err_head == NULL){
1856 fprintf(n_stderr, _("The error ring is empty\n"));
1857 goto jleave;
1860 if((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
1861 NULL){
1862 fprintf(n_stderr, _("tmpfile"));
1863 v = NULL;
1864 goto jleave;
1867 for(i = 0, enp = a_aux_err_head; enp != NULL; enp = enp->ae_next)
1868 fprintf(fp, "%4" PRIuZ ". %s", ++i, n_string_cp(&enp->ae_str));
1869 /* We don't know whether last string ended with NL; be simple XXX */
1870 putc('\n', fp);
1872 page_or_print(fp, 0);
1873 Fclose(fp);
1875 /* FALLTHRU */
1877 jclear:
1878 a_aux_err_tail = NULL;
1879 a_aux_err_cnt = a_aux_err_cnt_noted = 0;
1880 a_aux_err_linelen = 0;
1881 while((enp = a_aux_err_head) != NULL){
1882 a_aux_err_head = enp->ae_next;
1883 n_string_gut(&enp->ae_str);
1884 n_free(enp);
1886 goto jleave;
1888 #endif /* HAVE_ERRORS */
1890 FL char const *
1891 n_err_to_doc(si32_t eno){
1892 char const *rv;
1893 struct a_aux_err_map const *aemp;
1894 NYD2_ENTER;
1896 aemp = a_aux_err_map_from_no(eno);
1897 #ifdef HAVE_DOCSTRINGS
1898 rv = &a_aux_err_docs[aemp->aem_docoff];
1899 #else
1900 rv = &a_aux_err_names[aemp->aem_nameoff];
1901 #endif
1902 NYD2_LEAVE;
1903 return rv;
1906 FL char const *
1907 n_err_to_name(si32_t eno){
1908 char const *rv;
1909 struct a_aux_err_map const *aemp;
1910 NYD2_ENTER;
1912 aemp = a_aux_err_map_from_no(eno);
1913 rv = &a_aux_err_names[aemp->aem_nameoff];
1914 NYD2_LEAVE;
1915 return rv;
1918 FL si32_t
1919 n_err_from_name(char const *name){
1920 struct a_aux_err_map const *aemp;
1921 ui32_t hash, i, j, x;
1922 si32_t rv;
1923 NYD2_ENTER;
1925 hash = n_torek_hash(name);
1927 for(i = hash % a_AUX_ERR_REV_PRIME, j = 0; j <= a_AUX_ERR_REV_LONGEST; ++j){
1928 if((x = a_aux_err_revmap[i]) == a_AUX_ERR_REV_ILL)
1929 break;
1931 aemp = &a_aux_err_map[x];
1932 if(aemp->aem_hash == hash &&
1933 !strcmp(&a_aux_err_names[aemp->aem_nameoff], name)){
1934 rv = aemp->aem_err_no;
1935 goto jleave;
1938 if(++i == a_AUX_ERR_REV_PRIME){
1939 #ifdef a_AUX_ERR_REV_WRAPAROUND
1940 i = 0;
1941 #else
1942 break;
1943 #endif
1947 /* Have not found it. But wait, it could be that the user did, e.g.,
1948 * eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
1949 if((n_idec_si32_cp(&rv, name, 0, NULL) &
1950 (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
1951 ) == n_IDEC_STATE_CONSUMED){
1952 aemp = a_aux_err_map_from_no(rv);
1953 rv = aemp->aem_err_no;
1954 goto jleave;
1957 rv = a_aux_err_map[n__ERR_NUMBER_VOIDOFF].aem_err_no;
1958 jleave:
1959 NYD2_LEAVE;
1960 return rv;
1963 #ifdef HAVE_REGEX
1964 FL char const *
1965 n_regex_err_to_doc(const regex_t *rep, int e){
1966 char *cp;
1967 size_t i;
1968 NYD2_ENTER;
1970 i = regerror(e, rep, NULL, 0) +1;
1971 cp = n_autorec_alloc(i);
1972 regerror(e, rep, cp, i);
1973 NYD2_LEAVE;
1974 return cp;
1976 #endif
1978 /* s-it-mode */