a_go_evaluate(): fix un/signed comparison
[s-mailx.git] / auxlily.c
blob7498eda35ba869dd04351adab01ad9f86894f85f
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 */
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_SOCKETS
45 # ifdef HAVE_GETADDRINFO
46 # include <sys/socket.h>
47 # endif
49 # include <netdb.h>
50 #endif
52 #ifdef HAVE_NL_LANGINFO
53 # include <langinfo.h>
54 #endif
55 #ifdef HAVE_SETLOCALE
56 # include <locale.h>
57 #endif
59 #if HAVE_RANDOM == n_RANDOM_IMPL_GETRANDOM
60 # include n_RANDOM_GETRANDOM_H
61 #endif
63 #ifdef HAVE_IDNA
64 # if HAVE_IDNA == n_IDNA_IMPL_LIBIDN2
65 # include <idn2.h>
66 # elif HAVE_IDNA == n_IDNA_IMPL_LIBIDN
67 # include <idna.h>
68 # include <idn-free.h>
69 # elif HAVE_IDNA == n_IDNA_IMPL_IDNKIT
70 # include <idn/api.h>
71 # endif
72 #endif
74 #if HAVE_RANDOM != n_RANDOM_IMPL_ARC4 && HAVE_RANDOM != n_RANDOM_IMPL_TLS
75 union rand_state{
76 struct rand_arc4{
77 ui8_t _dat[256];
78 ui8_t _i;
79 ui8_t _j;
80 ui8_t __pad[6];
81 } a;
82 ui8_t b8[sizeof(struct rand_arc4)];
83 ui32_t b32[sizeof(struct rand_arc4) / sizeof(ui32_t)];
85 #endif
87 #ifdef HAVE_ERRORS
88 struct a_aux_err_node{
89 struct a_aux_err_node *ae_next;
90 struct n_string ae_str;
92 #endif
94 struct a_aux_err_map{
95 ui32_t aem_hash; /* Hash of name */
96 ui32_t aem_nameoff; /* Into a_aux_err_names[] */
97 ui32_t aem_docoff; /* Into a_aux_err docs[] (if HAVE_DOCSTRINGS) */
98 si32_t aem_err_no; /* The OS error value for this one */
101 /* IDEC: byte to integer value lookup table */
102 static ui8_t const a_aux_idec_atoi[256] = {
103 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
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,0x00,0x01,
108 0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0xFF,0xFF,
109 0xFF,0xFF,0xFF,0xFF,0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,
110 0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,
111 0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,
112 0x23,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0A,0x0B,0x0C,
113 0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,
114 0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,
115 0x21,0x22,0x23,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
116 0xFF,0xFF,0xFF,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
131 /* IDEC: avoid divisions for cutlimit calculation (indexed by base-2) */
132 #define a_X(X) (UI64_MAX / (X))
133 static ui64_t const a_aux_idec_cutlimit[35] = {
134 a_X( 2), a_X( 3), a_X( 4), a_X( 5), a_X( 6), a_X( 7), a_X( 8),
135 a_X( 9), a_X(10), a_X(11), a_X(12), a_X(13), a_X(14), a_X(15),
136 a_X(16), a_X(17), a_X(18), a_X(19), a_X(20), a_X(21), a_X(22),
137 a_X(23), a_X(24), a_X(25), a_X(26), a_X(27), a_X(28), a_X(29),
138 a_X(30), a_X(31), a_X(32), a_X(33), a_X(34), a_X(35), a_X(36)
140 #undef a_X
142 /* IENC: is power-of-two table, and if, shift (indexed by base-2) */
143 static ui8_t const a_aux_ienc_shifts[35] = {
144 1, 0, 2, 0, 0, 0, 3, 0, /* 2 .. 9 */
145 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, /* 10 .. 19 */
146 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 20 .. 29 */
147 0, 0, 5, 0, 0, 0, 0 /* 30 .. 36 */
150 /* IENC: integer to byte lookup tables */
151 static char const a_aux_ienc_itoa_upper[36] =
152 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
153 static char const a_aux_ienc_itoa_lower[36] =
154 "0123456789abcdefghijklmnopqrstuvwxyz";
156 /* Include the constant make-errors.sh output */
157 #include <gen-errors.h>
159 /* And these things come from mk-config.h (config-time make-errors.sh output) */
160 static n__ERR_NUMBER_TYPE const a_aux_err_no2mapoff[][2] = {
161 #undef a_X
162 #define a_X(N,I) {N,I},
163 n__ERR_NUMBER_TO_MAPOFF
164 #undef a_X
167 #if HAVE_RANDOM != n_RANDOM_IMPL_ARC4 && HAVE_RANDOM != n_RANDOM_IMPL_TLS
168 static union rand_state *a_aux_rand;
169 #endif
171 /* Error ring, for `errors' */
172 #ifdef HAVE_ERRORS
173 static struct a_aux_err_node *a_aux_err_head, *a_aux_err_tail;
174 static size_t a_aux_err_cnt, a_aux_err_cnt_noted;
175 #endif
176 static size_t a_aux_err_linelen;
178 /* Our ARC4 random generator with its completely unacademical pseudo
179 * initialization (shall /dev/urandom fail) */
180 #if HAVE_RANDOM != n_RANDOM_IMPL_ARC4 && HAVE_RANDOM != n_RANDOM_IMPL_TLS
181 static void a_aux_rand_init(void);
182 n_INLINE ui8_t a_aux_rand_get8(void);
183 static ui32_t a_aux_rand_weak(ui32_t seed);
184 #endif
186 /* Find the descriptive mapping of an error number, or _ERR_INVAL */
187 static struct a_aux_err_map const *a_aux_err_map_from_no(si32_t eno);
189 #if HAVE_RANDOM != n_RANDOM_IMPL_ARC4 && HAVE_RANDOM != n_RANDOM_IMPL_TLS
190 static void
191 a_aux_rand_init(void){
192 # ifdef HAVE_CLOCK_GETTIME
193 struct timespec ts;
194 # else
195 struct timeval ts;
196 # endif
197 union {int fd; size_t i;} u;
198 ui32_t seed, rnd;
199 NYD2_ENTER;
201 a_aux_rand = n_alloc(sizeof *a_aux_rand);
203 # if HAVE_RANDOM == n_RANDOM_IMPL_GETRANDOM
204 /* getrandom(2) guarantees 256 without n_ERR_INTR..
205 * However, support sequential reading to avoid possible hangs that have
206 * been reported on the ML (2017-08-22, s-nail/s-mailx freezes when
207 * HAVE_GETRANDOM is #defined) */
208 n_LCTA(sizeof(a_aux_rand->a._dat) <= 256,
209 "Buffer too large to be served without n_ERR_INTR error");
210 n_LCTA(sizeof(a_aux_rand->a._dat) >= 256,
211 "Buffer too small to serve used array indices");
212 /* C99 */{
213 size_t o, i;
215 for(o = 0, i = sizeof a_aux_rand->a._dat;;){
216 ssize_t gr;
218 gr = n_RANDOM_GETRANDOM_FUN(&a_aux_rand->a._dat[o], i);
219 if(gr == -1 && n_err_no == n_ERR_NOSYS)
220 break;
221 a_aux_rand->a._i = a_aux_rand->a._dat[a_aux_rand->a._dat[1] ^
222 a_aux_rand->a._dat[84]];
223 a_aux_rand->a._j = a_aux_rand->a._dat[a_aux_rand->a._dat[65] ^
224 a_aux_rand->a._dat[42]];
225 /* ..but be on the safe side */
226 if(gr > 0){
227 i -= (size_t)gr;
228 if(i == 0)
229 goto jleave;
230 o += (size_t)gr;
232 n_err(_("Not enough entropy for the PseudoRandomNumberGenerator, "
233 "waiting a bit\n"));
234 n_msleep(250, FAL0);
238 # elif HAVE_RANDOM == n_RANDOM_IMPL_URANDOM
239 if((u.fd = open("/dev/urandom", O_RDONLY)) != -1){
240 bool_t ok;
242 ok = (sizeof(a_aux_rand->a._dat) == (size_t)read(u.fd, a_aux_rand->a._dat,
243 sizeof(a_aux_rand->a._dat)));
244 close(u.fd);
246 a_aux_rand->a._i = a_aux_rand->a._dat[a_aux_rand->a._dat[1] ^
247 a_aux_rand->a._dat[84]];
248 a_aux_rand->a._j = a_aux_rand->a._dat[a_aux_rand->a._dat[65] ^
249 a_aux_rand->a._dat[42]];
250 if(ok)
251 goto jleave;
253 # elif HAVE_RANDOM != n_RANDOM_IMPL_BUILTIN
254 # error a_aux_rand_init(): the value of HAVE_RANDOM is not supported
255 # endif
257 /* As a fallback, a homebrew seed */
258 if(n_poption & n_PO_D_V)
259 n_err(_("P(seudo)R(andomNumber)G(enerator): creating homebrew seed\n"));
260 for(seed = (uintptr_t)a_aux_rand & UI32_MAX, rnd = 21; rnd != 0; --rnd){
261 for(u.i = n_NELEM(a_aux_rand->b32); u.i-- != 0;){
262 ui32_t t, k;
264 # ifdef HAVE_CLOCK_GETTIME
265 clock_gettime(CLOCK_REALTIME, &ts);
266 t = (ui32_t)ts.tv_nsec;
267 # else
268 gettimeofday(&ts, NULL);
269 t = (ui32_t)ts.tv_usec;
270 # endif
271 if(rnd & 1)
272 t = (t >> 16) | (t << 16);
273 a_aux_rand->b32[u.i] ^= a_aux_rand_weak(seed ^ t);
274 a_aux_rand->b32[t % n_NELEM(a_aux_rand->b32)] ^= seed;
275 if(rnd == 7 || rnd == 17)
276 a_aux_rand->b32[u.i] ^= a_aux_rand_weak(seed ^ (ui32_t)ts.tv_sec);
277 k = a_aux_rand->b32[u.i] % n_NELEM(a_aux_rand->b32);
278 a_aux_rand->b32[k] ^= a_aux_rand->b32[u.i];
279 seed ^= a_aux_rand_weak(a_aux_rand->b32[k]);
280 if((rnd & 3) == 3)
281 seed ^= n_prime_next(seed);
285 for(u.i = 5 * sizeof(a_aux_rand->b8); u.i != 0; --u.i)
286 a_aux_rand_get8();
287 jleave:
288 NYD2_LEAVE;
291 n_INLINE ui8_t
292 a_aux_rand_get8(void){
293 ui8_t si, sj;
295 si = a_aux_rand->a._dat[++a_aux_rand->a._i];
296 sj = a_aux_rand->a._dat[a_aux_rand->a._j += si];
297 a_aux_rand->a._dat[a_aux_rand->a._i] = sj;
298 a_aux_rand->a._dat[a_aux_rand->a._j] = si;
299 return a_aux_rand->a._dat[(ui8_t)(si + sj)];
302 static ui32_t
303 a_aux_rand_weak(ui32_t seed){
304 /* From "Random number generators: good ones are hard to find",
305 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
306 * October 1988, p. 1195.
307 * (In fact: FreeBSD 4.7, /usr/src/lib/libc/stdlib/random.c.) */
308 ui32_t hi;
310 if(seed == 0)
311 seed = 123459876;
312 hi = seed / 127773;
313 seed %= 127773;
314 seed = (seed * 16807) - (hi * 2836);
315 if((si32_t)seed < 0)
316 seed += SI32_MAX;
317 return seed;
319 #endif /* HAVE_RANDOM != IMPL_ARC4 != IMPL_TLS */
321 static struct a_aux_err_map const *
322 a_aux_err_map_from_no(si32_t eno){
323 si32_t ecmp;
324 size_t asz;
325 n__ERR_NUMBER_TYPE const (*adat)[2], (*tmp)[2];
326 struct a_aux_err_map const *aemp;
327 NYD2_ENTER;
329 aemp = &a_aux_err_map[n__ERR_NUMBER_VOIDOFF];
331 if(UICMP(z, n_ABS(eno), <=, (n__ERR_NUMBER_TYPE)-1)){
332 for(adat = a_aux_err_no2mapoff, asz = n_NELEM(a_aux_err_no2mapoff);
333 asz != 0; asz >>= 1){
334 tmp = &adat[asz >> 1];
335 if((ecmp = (si32_t)((n__ERR_NUMBER_TYPE)eno - (*tmp)[0])) == 0){
336 aemp = &a_aux_err_map[(*tmp)[1]];
337 break;
339 if(ecmp > 0){
340 adat = &tmp[1];
341 --asz;
345 NYD2_LEAVE;
346 return aemp;
349 FL void
350 n_locale_init(void){
351 NYD2_ENTER;
353 n_psonce &= ~(n_PSO_UNICODE | n_PSO_ENC_MBSTATE);
355 #ifndef HAVE_SETLOCALE
356 n_mb_cur_max = 1;
357 #else
358 setlocale(LC_ALL, n_empty);
359 n_mb_cur_max = MB_CUR_MAX;
360 # ifdef HAVE_NL_LANGINFO
361 /* C99 */{
362 char const *cp;
364 if((cp = nl_langinfo(CODESET)) != NULL)
365 /* (Will log during startup if user set that via -S) */
366 ok_vset(ttycharset, cp);
368 # endif /* HAVE_SETLOCALE */
370 # ifdef HAVE_C90AMEND1
371 if(n_mb_cur_max > 1){
372 # ifdef HAVE_ALWAYS_UNICODE_LOCALE
373 n_psonce |= n_PSO_UNICODE;
374 # else
375 wchar_t wc;
376 if(mbtowc(&wc, "\303\266", 2) == 2 && wc == 0xF6 &&
377 mbtowc(&wc, "\342\202\254", 3) == 3 && wc == 0x20AC)
378 n_psonce |= n_PSO_UNICODE;
379 /* Reset possibly messed up state; luckily this also gives us an
380 * indication whether the encoding has locking shift state sequences */
381 if(mbtowc(&wc, NULL, n_mb_cur_max))
382 n_psonce |= n_PSO_ENC_MBSTATE;
383 # endif
385 # endif
386 #endif /* HAVE_C90AMEND1 */
387 NYD2_LEAVE;
390 FL size_t
391 n_screensize(void){
392 char const *cp;
393 uiz_t rv;
394 NYD2_ENTER;
396 if((cp = ok_vlook(screen)) != NULL){
397 n_idec_uiz_cp(&rv, cp, 0, NULL);
398 if(rv == 0)
399 rv = n_scrnheight;
400 }else
401 rv = n_scrnheight;
403 if(rv > 2)
404 rv -= 2;
405 NYD2_LEAVE;
406 return rv;
409 FL char const *
410 n_pager_get(char const **env_addon){
411 char const *rv;
412 NYD_ENTER;
414 rv = ok_vlook(PAGER);
416 if(env_addon != NULL){
417 *env_addon = NULL;
418 /* Update the manual upon any changes:
419 * *colour-pager*, $PAGER */
420 if(strstr(rv, "less") != NULL){
421 if(getenv("LESS") == NULL)
422 *env_addon = "LESS=RXi";
423 }else if(strstr(rv, "lv") != NULL){
424 if(getenv("LV") == NULL)
425 *env_addon = "LV=-c";
428 NYD_LEAVE;
429 return rv;
432 FL void
433 page_or_print(FILE *fp, size_t lines)
435 int c;
436 char const *cp;
437 NYD_ENTER;
439 fflush_rewind(fp);
441 if (n_go_may_yield_control() && (cp = ok_vlook(crt)) != NULL) {
442 size_t rows;
444 if(*cp == '\0')
445 rows = (size_t)n_scrnheight;
446 else
447 n_idec_uiz_cp(&rows, cp, 0, NULL);
449 if (rows > 0 && lines == 0) {
450 while ((c = getc(fp)) != EOF)
451 if (c == '\n' && ++lines >= rows)
452 break;
453 really_rewind(fp);
456 if (lines >= rows) {
457 char const *env_add[2], *pager;
459 pager = n_pager_get(&env_add[0]);
460 env_add[1] = NULL;
461 n_child_run(pager, NULL, fileno(fp), n_CHILD_FD_PASS, NULL,NULL,NULL,
462 env_add, NULL);
463 goto jleave;
467 while ((c = getc(fp)) != EOF)
468 putc(c, n_stdout);
469 jleave:
470 NYD_LEAVE;
473 FL enum protocol
474 which_protocol(char const *name, bool_t check_stat, bool_t try_hooks,
475 char const **adjusted_or_null)
477 /* TODO This which_protocol() sickness should be URL::new()->protocol() */
478 char const *cp, *orig_name;
479 enum protocol rv = PROTO_UNKNOWN;
480 NYD_ENTER;
482 if(name[0] == '%' && name[1] == ':')
483 name += 2;
484 orig_name = name;
486 for (cp = name; *cp && *cp != ':'; cp++)
487 if (!alnumchar(*cp))
488 goto jfile;
490 if(cp[0] == ':' && cp[1] == '/' && cp[2] == '/'){
491 if(!strncmp(name, "file", sizeof("file") -1) ||
492 !strncmp(name, "mbox", sizeof("mbox") -1))
493 rv = PROTO_FILE;
494 else if(!strncmp(name, "maildir", sizeof("maildir") -1)){
495 #ifdef HAVE_MAILDIR
496 rv = PROTO_MAILDIR;
497 #else
498 n_err(_("No Maildir directory support compiled in\n"));
499 #endif
500 }else if(!strncmp(name, "pop3", sizeof("pop3") -1)){
501 #ifdef HAVE_POP3
502 rv = PROTO_POP3;
503 #else
504 n_err(_("No POP3 support compiled in\n"));
505 #endif
506 }else if(!strncmp(name, "pop3s", sizeof("pop3s") -1)){
507 #if defined HAVE_POP3 && defined HAVE_TLS
508 rv = PROTO_POP3;
509 #else
510 n_err(_("No POP3S support compiled in\n"));
511 #endif
512 }else if(!strncmp(name, "imap", sizeof("imap") -1)){
513 #ifdef HAVE_IMAP
514 rv = PROTO_IMAP;
515 #else
516 n_err(_("No IMAP support compiled in\n"));
517 #endif
518 }else if(!strncmp(name, "imaps", sizeof("imaps") -1)){
519 #if defined HAVE_IMAP && defined HAVE_TLS
520 rv = PROTO_IMAP;
521 #else
522 n_err(_("No IMAPS support compiled in\n"));
523 #endif
525 orig_name = &cp[3];
526 goto jleave;
529 jfile:
530 rv = PROTO_FILE;
532 if(check_stat || try_hooks){
533 struct n_file_type ft;
534 struct stat stb;
535 char *np;
536 size_t sz;
538 np = n_lofi_alloc((sz = strlen(name)) + 4 +1);
539 memcpy(np, name, sz + 1);
541 if(!stat(name, &stb)){
542 if(S_ISDIR(stb.st_mode)
543 #ifdef HAVE_MAILDIR
544 && (memcpy(&np[sz], "/tmp", 5),
545 !stat(np, &stb) && S_ISDIR(stb.st_mode)) &&
546 (memcpy(&np[sz], "/new", 5),
547 !stat(np, &stb) && S_ISDIR(stb.st_mode)) &&
548 (memcpy(&np[sz], "/cur", 5),
549 !stat(np, &stb) && S_ISDIR(stb.st_mode))
550 #endif
552 #ifdef HAVE_MAILDIR
553 rv = PROTO_MAILDIR;
554 #else
555 rv = PROTO_UNKNOWN;
556 #endif
558 }else if(try_hooks && n_filetype_trial(&ft, name))
559 orig_name = savecatsep(name, '.', ft.ft_ext_dat);
560 else if((cp = ok_vlook(newfolders)) != NULL &&
561 !asccasecmp(cp, "maildir")){
562 #ifdef HAVE_MAILDIR
563 rv = PROTO_MAILDIR;
564 #else
565 n_err(_("*newfolders*: no Maildir directory support compiled in\n"));
566 #endif
569 n_lofi_free(np);
571 jleave:
572 if(adjusted_or_null != NULL)
573 *adjusted_or_null = orig_name;
574 NYD_LEAVE;
575 return rv;
578 FL char *
579 n_c_to_hex_base16(char store[3], char c){
580 static char const itoa16[] = "0123456789ABCDEF";
581 NYD2_ENTER;
583 store[2] = '\0';
584 store[1] = itoa16[(ui8_t)c & 0x0F];
585 c = ((ui8_t)c >> 4) & 0x0F;
586 store[0] = itoa16[(ui8_t)c];
587 NYD2_LEAVE;
588 return store;
591 FL si32_t
592 n_c_from_hex_base16(char const hex[2]){
593 static ui8_t const atoi16[] = {
594 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x30-0x37 */
595 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x38-0x3F */
596 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, /* 0x40-0x47 */
597 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x48-0x4f */
598 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x50-0x57 */
599 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x58-0x5f */
600 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF /* 0x60-0x67 */
602 ui8_t i1, i2;
603 si32_t rv;
604 NYD2_ENTER;
606 if ((i1 = (ui8_t)hex[0] - '0') >= n_NELEM(atoi16) ||
607 (i2 = (ui8_t)hex[1] - '0') >= n_NELEM(atoi16))
608 goto jerr;
609 i1 = atoi16[i1];
610 i2 = atoi16[i2];
611 if ((i1 | i2) & 0xF0u)
612 goto jerr;
613 rv = i1;
614 rv <<= 4;
615 rv += i2;
616 jleave:
617 NYD2_LEAVE;
618 return rv;
619 jerr:
620 rv = -1;
621 goto jleave;
624 FL enum n_idec_state
625 n_idec_buf(void *resp, char const *cbuf, uiz_t clen, ui8_t base,
626 enum n_idec_mode idm, char const **endptr_or_null){
627 ui8_t currc;
628 ui64_t res, cut;
629 enum n_idec_state rv;
630 NYD_ENTER;
632 idm &= n__IDEC_MODE_MASK;
633 rv = n_IDEC_STATE_NONE | idm;
634 res = 0;
636 if(clen == UIZ_MAX){
637 if(*cbuf == '\0')
638 goto jeinval;
639 }else if(clen == 0)
640 goto jeinval;
642 assert(base != 1 && base <= 36);
643 /*if(base == 1 || base > 36)
644 * goto jeinval;*/
646 /* Leading WS */
647 while(spacechar(*cbuf))
648 if(*++cbuf == '\0' || --clen == 0)
649 goto jeinval;
651 /* Check sign */
652 switch(*cbuf){
653 case '-':
654 rv |= n_IDEC_STATE_SEEN_MINUS;
655 /* FALLTHROUGH */
656 case '+':
657 if(*++cbuf == '\0' || --clen == 0)
658 goto jeinval;
659 break;
662 /* Base detection/skip */
663 if(*cbuf != '0'){
664 if(base == 0){
665 base = 10;
667 /* Support BASE#number prefix, where BASE is decimal 2-36 */
668 if(clen > 2){
669 char c1, c2, c3;
671 if(((c1 = cbuf[0]) >= '0' && c1 <= '9') &&
672 (((c2 = cbuf[1]) == '#') ||
673 (c2 >= '0' && c2 <= '9' && clen > 3 && cbuf[2] == '#'))){
674 base = a_aux_idec_atoi[(ui8_t)c1];
675 if(c2 == '#')
676 c3 = cbuf[2];
677 else{
678 c3 = cbuf[3];
679 base *= 10; /* xxx Inline atoi decimal base */
680 base += a_aux_idec_atoi[(ui8_t)c2];
683 /* We do not interpret this as BASE#number at all if either we
684 * did not get a valid base or if the first char is not valid
685 * according to base, to comply to the latest interpretion of
686 * "prefix", see comment for standard prefixes below */
687 if(base < 2 || base > 36 || a_aux_idec_atoi[(ui8_t)c3] >= base)
688 base = 10;
689 else if(c2 == '#')
690 clen -= 2, cbuf += 2;
691 else
692 clen -= 3, cbuf += 3;
697 /* Character must be valid for base */
698 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
699 if(currc >= base)
700 goto jeinval;
701 }else{
702 /* 0 always valid as is, fallback base 10 */
703 if(*++cbuf == '\0' || --clen == 0)
704 goto jleave;
706 /* Base "detection" */
707 if(base == 0 || base == 2 || base == 16){
708 switch(*cbuf){
709 case 'x':
710 case 'X':
711 if((base & 2) == 0){
712 base = 0x10;
713 goto jprefix_skip;
715 break;
716 case 'b':
717 case 'B':
718 if((base & 16) == 0){
719 base = 2; /* 0b10 */
720 /* Char after prefix must be valid. However, after some error
721 * in the tor software all libraries (which had to) turned to
722 * an interpretation of the C standard which says that the
723 * prefix may optionally precede an otherwise valid sequence,
724 * which means that "0x" is not a STATE_INVAL error but gives
725 * a "0" result with a "STATE_BASE" error and a rest of "x" */
726 jprefix_skip:
727 #if 1
728 if(clen > 1 && a_aux_idec_atoi[(ui8_t)cbuf[1]] < base)
729 --clen, ++cbuf;
730 #else
731 if(*++cbuf == '\0' || --clen == 0)
732 goto jeinval;
734 /* Character must be valid for base, invalid otherwise */
735 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
736 if(currc >= base)
737 goto jeinval;
738 #endif
740 break;
741 default:
742 if(base == 0)
743 base = 010;
744 break;
748 /* Character must be valid for base, _EBASE otherwise */
749 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
750 if(currc >= base)
751 goto jebase;
754 for(cut = a_aux_idec_cutlimit[base - 2];;){
755 if(res >= cut){
756 if(res == cut){
757 res *= base;
758 if(res > UI64_MAX - currc)
759 goto jeover;
760 res += currc;
761 }else
762 goto jeover;
763 }else{
764 res *= base;
765 res += currc;
768 if(*++cbuf == '\0' || --clen == 0)
769 break;
771 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
772 if(currc >= base)
773 goto jebase;
776 jleave:
778 ui64_t uimask;
780 switch(rv & n__IDEC_MODE_LIMIT_MASK){
781 case n_IDEC_MODE_LIMIT_8BIT: uimask = UI8_MAX; break;
782 case n_IDEC_MODE_LIMIT_16BIT: uimask = UI16_MAX; break;
783 case n_IDEC_MODE_LIMIT_32BIT: uimask = UI32_MAX; break;
784 default: uimask = UI64_MAX; break;
786 if((rv & n_IDEC_MODE_SIGNED_TYPE) &&
787 (!(rv & n_IDEC_MODE_POW2BASE_UNSIGNED) || !n_ISPOW2(base)))
788 uimask >>= 1;
790 if(res & ~uimask){
791 /* XXX never entered unless _SIGNED_TYPE! */
792 if((rv & (n_IDEC_MODE_SIGNED_TYPE | n_IDEC_STATE_SEEN_MINUS)
793 ) == (n_IDEC_MODE_SIGNED_TYPE | n_IDEC_STATE_SEEN_MINUS)){
794 if(res > uimask + 1){
795 res = uimask << 1;
796 res &= ~uimask;
797 }else{
798 res = -res;
799 break;
801 }else
802 res = uimask;
803 if(!(rv & n_IDEC_MODE_LIMIT_NOERROR))
804 rv |= n_IDEC_STATE_EOVERFLOW;
805 }else if(rv & n_IDEC_STATE_SEEN_MINUS)
806 res = -res;
807 }while(0);
809 switch(rv & n__IDEC_MODE_LIMIT_MASK){
810 case n_IDEC_MODE_LIMIT_8BIT:
811 if(rv & n_IDEC_MODE_SIGNED_TYPE)
812 *(si8_t*)resp = (si8_t)res;
813 else
814 *(ui8_t*)resp = (ui8_t)res;
815 break;
816 case n_IDEC_MODE_LIMIT_16BIT:
817 if(rv & n_IDEC_MODE_SIGNED_TYPE)
818 *(si16_t*)resp = (si16_t)res;
819 else
820 *(ui16_t*)resp = (ui16_t)res;
821 break;
822 case n_IDEC_MODE_LIMIT_32BIT:
823 if(rv & n_IDEC_MODE_SIGNED_TYPE)
824 *(si32_t*)resp = (si32_t)res;
825 else
826 *(ui32_t*)resp = (ui32_t)res;
827 break;
828 default:
829 if(rv & n_IDEC_MODE_SIGNED_TYPE)
830 *(si64_t*)resp = (si64_t)res;
831 else
832 *(ui64_t*)resp = (ui64_t)res;
833 break;
836 if(endptr_or_null != NULL)
837 *endptr_or_null = cbuf;
838 if(*cbuf == '\0' || clen == 0)
839 rv |= n_IDEC_STATE_CONSUMED;
840 NYD_LEAVE;
841 return rv;
843 jeinval:
844 rv |= n_IDEC_STATE_EINVAL;
845 goto j_maxval;
846 jebase:
847 /* Not a base error for terminator and whitespace! */
848 if(*cbuf != '\0' && !spacechar(*cbuf))
849 rv |= n_IDEC_STATE_EBASE;
850 goto jleave;
852 jeover:
853 /* Overflow error: consume input until bad character or length out */
854 for(;;){
855 if(*++cbuf == '\0' || --clen == 0)
856 break;
857 currc = a_aux_idec_atoi[(ui8_t)*cbuf];
858 if(currc >= base)
859 break;
862 rv |= n_IDEC_STATE_EOVERFLOW;
863 j_maxval:
864 if(rv & n_IDEC_MODE_SIGNED_TYPE)
865 res = (rv & n_IDEC_STATE_SEEN_MINUS) ? (ui64_t)SI64_MIN
866 : (ui64_t)SI64_MAX;
867 else
868 res = UI64_MAX;
869 rv &= ~n_IDEC_STATE_SEEN_MINUS;
870 goto jleave;
873 FL char *
874 n_ienc_buf(char cbuf[n_IENC_BUFFER_SIZE], ui64_t value, ui8_t base,
875 enum n_ienc_mode iem){
876 enum{a_ISNEG = 1u<<n__IENC_MODE_SHIFT};
878 ui8_t shiftmodu;
879 char const *itoa;
880 char *rv;
881 NYD_ENTER;
883 iem &= n__IENC_MODE_MASK;
885 assert(base != 1 && base <= 36);
886 /*if(base == 1 || base > 36){
887 * rv = NULL;
888 * goto jleave;
889 *}*/
891 rv = &cbuf[n_IENC_BUFFER_SIZE];
892 *--rv = '\0';
893 itoa = (iem & n_IENC_MODE_LOWERCASE) ? a_aux_ienc_itoa_lower
894 : a_aux_ienc_itoa_upper;
896 if((si64_t)value < 0){
897 iem |= a_ISNEG;
898 if(iem & n_IENC_MODE_SIGNED_TYPE){
899 /* self->is_negative = TRU1; */
900 value = -value;
904 if((shiftmodu = a_aux_ienc_shifts[base - 2]) != 0){
905 --base; /* convert to mask */
907 *--rv = itoa[value & base];
908 value >>= shiftmodu;
909 }while(value != 0);
911 if(!(iem & n_IENC_MODE_NO_PREFIX)){
912 /* self->before_prefix = cp; */
913 if(shiftmodu == 4)
914 *--rv = 'x';
915 else if(shiftmodu == 1)
916 *--rv = 'b';
917 else if(shiftmodu != 3){
918 ++base; /* Reconvert from mask */
919 goto jnumber_sign_prefix;
921 *--rv = '0';
923 }else{
925 shiftmodu = value % base;
926 value /= base;
927 *--rv = itoa[shiftmodu];
928 }while(value != 0);
930 if(!(iem & n_IENC_MODE_NO_PREFIX) && base != 10){
931 jnumber_sign_prefix:
932 value = base;
933 base = 10;
934 *--rv = '#';
936 shiftmodu = value % base;
937 value /= base;
938 *--rv = itoa[shiftmodu];
939 }while(value != 0);
942 if(iem & n_IENC_MODE_SIGNED_TYPE){
943 char c;
945 if(iem & a_ISNEG)
946 c = '-';
947 else if(iem & n_IENC_MODE_SIGNED_PLUS)
948 c = '+';
949 else if(iem & n_IENC_MODE_SIGNED_SPACE)
950 c = ' ';
951 else
952 c = '\0';
954 if(c != '\0')
955 *--rv = c;
958 NYD_LEAVE;
959 return rv;
962 FL ui32_t
963 n_torek_hash(char const *name){
964 /* Chris Torek's hash */
965 char c;
966 ui32_t h;
967 NYD2_ENTER;
969 for(h = 0; (c = *name++) != '\0';)
970 h = (h * 33) + c;
971 NYD2_LEAVE;
972 return h;
975 FL ui32_t
976 n_torek_ihashn(char const *dat, size_t len){
977 /* See n_torek_hash() */
978 char c;
979 ui32_t h;
980 NYD2_ENTER;
982 if(len == UIZ_MAX)
983 for(h = 0; (c = *dat++) != '\0';)
984 h = (h * 33) + lowerconv(c);
985 else
986 for(h = 0; len > 0; --len){
987 c = *dat++;
988 h = (h * 33) + lowerconv(c);
990 NYD2_LEAVE;
991 return h;
994 FL ui32_t
995 n_prime_next(ui32_t n){
996 static ui32_t const primes[] = {
997 5, 11, 23, 47, 97, 157, 283,
998 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
999 131071, 262139, 524287, 1048573, 2097143, 4194301,
1000 8388593, 16777213, 33554393, 67108859, 134217689,
1001 268435399, 536870909, 1073741789, 2147483647
1003 ui32_t i, mprime;
1004 NYD2_ENTER;
1006 i = (n < primes[n_NELEM(primes) / 4] ? 0
1007 : (n < primes[n_NELEM(primes) / 2] ? n_NELEM(primes) / 4
1008 : n_NELEM(primes) / 2));
1010 do if((mprime = primes[i]) > n)
1011 break;
1012 while(++i < n_NELEM(primes));
1014 if(i == n_NELEM(primes) && mprime < n)
1015 mprime = n;
1016 NYD2_LEAVE;
1017 return mprime;
1020 FL char const *
1021 n_getdeadletter(void){
1022 char const *cp;
1023 bool_t bla;
1024 NYD_ENTER;
1026 bla = FAL0;
1027 jredo:
1028 cp = fexpand(ok_vlook(DEAD), FEXP_LOCAL | FEXP_NSHELL);
1029 if(cp == NULL || strlen(cp) >= PATH_MAX){
1030 if(!bla){
1031 n_err(_("Failed to expand *DEAD*, setting default (%s): %s\n"),
1032 VAL_DEAD, n_shexp_quote_cp((cp == NULL ? n_empty : cp), FAL0));
1033 ok_vclear(DEAD);
1034 bla = TRU1;
1035 goto jredo;
1036 }else{
1037 cp = savecatsep(ok_vlook(TMPDIR), '/', VAL_DEAD_BASENAME);
1038 n_err(_("Cannot expand *DEAD*, using: %s\n"), cp);
1041 NYD_LEAVE;
1042 return cp;
1045 FL char *
1046 n_nodename(bool_t mayoverride){
1047 static char *sys_hostname, *hostname; /* XXX free-at-exit */
1049 struct utsname ut;
1050 char *hn;
1051 #ifdef HAVE_SOCKETS
1052 # ifdef HAVE_GETADDRINFO
1053 struct addrinfo hints, *res;
1054 # else
1055 struct hostent *hent;
1056 # endif
1057 #endif
1058 NYD2_ENTER;
1060 if(n_psonce & n_PSO_REPRODUCIBLE)
1061 hn = n_UNCONST(n_reproducible_name);
1062 else if(mayoverride && (hn = ok_vlook(hostname)) != NULL && *hn != '\0'){
1064 }else if((hn = sys_hostname) == NULL){
1065 bool_t lofi;
1067 lofi = FAL0;
1068 uname(&ut);
1069 hn = ut.nodename;
1071 #ifdef HAVE_SOCKETS
1072 # ifdef HAVE_GETADDRINFO
1073 memset(&hints, 0, sizeof hints);
1074 hints.ai_family = AF_UNSPEC;
1075 hints.ai_flags = AI_CANONNAME;
1076 if(getaddrinfo(hn, NULL, &hints, &res) == 0){
1077 if(res->ai_canonname != NULL){
1078 size_t l;
1080 l = strlen(res->ai_canonname) +1;
1081 hn = n_lofi_alloc(l);
1082 lofi = TRU1;
1083 memcpy(hn, res->ai_canonname, l);
1085 freeaddrinfo(res);
1087 # else
1088 hent = gethostbyname(hn);
1089 if(hent != NULL)
1090 hn = hent->h_name;
1091 # endif
1092 #endif /* HAVE_SOCKETS */
1094 #ifdef HAVE_IDNA
1095 /* C99 */{
1096 struct n_string cnv;
1098 n_string_creat(&cnv);
1099 if(!n_idna_to_ascii(&cnv, hn, UIZ_MAX))
1100 n_panic(_("The system hostname is invalid, "
1101 "IDNA conversion failed: %s\n"),
1102 n_shexp_quote_cp(hn, FAL0));
1103 sys_hostname = n_string_cp(&cnv);
1104 n_string_drop_ownership(&cnv);
1105 /*n_string_gut(&cnv);*/
1107 #else
1108 sys_hostname = sstrdup(hn);
1109 #endif
1111 if(lofi)
1112 n_lofi_free(hn);
1113 hn = sys_hostname;
1116 if(hostname != NULL && hostname != sys_hostname)
1117 n_free(hostname);
1118 hostname = sstrdup(hn);
1119 NYD2_LEAVE;
1120 return hostname;
1123 #ifdef HAVE_IDNA
1124 FL bool_t
1125 n_idna_to_ascii(struct n_string *out, char const *ibuf, size_t ilen){
1126 char *idna_utf8;
1127 bool_t lofi, rv;
1128 NYD_ENTER;
1130 if(ilen == UIZ_MAX)
1131 ilen = strlen(ibuf);
1133 lofi = FAL0;
1135 if((rv = (ilen == 0)))
1136 goto jleave;
1137 if(ibuf[ilen] != '\0'){
1138 lofi = TRU1;
1139 idna_utf8 = n_lofi_alloc(ilen +1);
1140 memcpy(idna_utf8, ibuf, ilen);
1141 idna_utf8[ilen] = '\0';
1142 ibuf = idna_utf8;
1144 ilen = 0;
1146 # ifndef HAVE_ALWAYS_UNICODE_LOCALE
1147 if(n_psonce & n_PSO_UNICODE)
1148 # endif
1149 idna_utf8 = n_UNCONST(ibuf);
1150 # ifndef HAVE_ALWAYS_UNICODE_LOCALE
1151 else if((idna_utf8 = n_iconv_onetime_cp(n_ICONV_NONE, "utf-8",
1152 ok_vlook(ttycharset), ibuf)) == NULL)
1153 goto jleave;
1154 # endif
1156 # if HAVE_IDNA == n_IDNA_IMPL_LIBIDN2
1157 /* C99 */{
1158 char *idna_ascii;
1159 int f, rc;
1161 f = IDN2_NONTRANSITIONAL;
1162 jidn2_redo:
1163 if((rc = idn2_to_ascii_8z(idna_utf8, &idna_ascii, f)) == IDN2_OK){
1164 out = n_string_assign_cp(out, idna_ascii);
1165 idn2_free(idna_ascii);
1166 rv = TRU1;
1167 ilen = out->s_len;
1168 }else if(rc == IDN2_DISALLOWED && f != IDN2_TRANSITIONAL){
1169 f = IDN2_TRANSITIONAL;
1170 goto jidn2_redo;
1174 # elif HAVE_IDNA == n_IDNA_IMPL_LIBIDN
1175 /* C99 */{
1176 char *idna_ascii;
1178 if(idna_to_ascii_8z(idna_utf8, &idna_ascii, 0) == IDNA_SUCCESS){
1179 out = n_string_assign_cp(out, idna_ascii);
1180 idn_free(idna_ascii);
1181 rv = TRU1;
1182 ilen = out->s_len;
1186 # elif HAVE_IDNA == n_IDNA_IMPL_IDNKIT
1187 ilen = strlen(idna_utf8);
1188 jredo:
1189 switch(idn_encodename(
1190 /* LOCALCONV changed meaning in v2 and is no longer available for
1191 * encoding. This makes sense, bu */
1193 # ifdef IDN_UNICODECONV /* v2 */
1194 IDN_ENCODE_APP & ~IDN_UNICODECONV
1195 # else
1196 IDN_DELIMMAP | IDN_LOCALMAP | IDN_NAMEPREP | IDN_IDNCONV |
1197 IDN_LENCHECK | IDN_ASCCHECK
1198 # endif
1199 ), idna_utf8,
1200 n_string_resize(n_string_trunc(out, 0), ilen)->s_dat, ilen)){
1201 case idn_buffer_overflow:
1202 ilen += HOST_NAME_MAX +1;
1203 goto jredo;
1204 case idn_success:
1205 rv = TRU1;
1206 ilen = strlen(out->s_dat);
1207 break;
1208 default:
1209 ilen = 0;
1210 break;
1213 # else
1214 # error Unknown HAVE_IDNA
1215 # endif
1216 jleave:
1217 if(lofi)
1218 n_lofi_free(n_UNCONST(ibuf));
1219 out = n_string_trunc(out, ilen);
1220 NYD_LEAVE;
1221 return rv;
1223 #endif /* HAVE_IDNA */
1225 FL char *
1226 n_random_create_buf(char *dat, size_t len, ui32_t *reprocnt_or_null){
1227 struct str b64;
1228 char *indat, *cp, *oudat;
1229 size_t i, inlen, oulen;
1230 NYD_ENTER;
1232 if(!(n_psonce & n_PSO_RANDOM_INIT)){
1233 n_psonce |= n_PSO_RANDOM_INIT;
1235 if(n_poption & n_PO_D_V){
1236 char const *prngn;
1238 #if HAVE_RANDOM == n_RANDOM_IMPL_ARC4
1239 prngn = "arc4random";
1240 #elif HAVE_RANDOM == n_RANDOM_IMPL_TLS
1241 prngn = "*TLS RAND_*";
1242 #elif HAVE_RANDOM == n_RANDOM_IMPL_GETRANDOM
1243 prngn = "getrandom(2/3) + builtin ARC4";
1244 #elif HAVE_RANDOM == n_RANDOM_IMPL_URANDOM
1245 prngn = "/dev/urandom + builtin ARC4";
1246 #elif HAVE_RANDOM == n_RANDOM_IMPL_BUILTIN
1247 prngn = "builtin ARC4";
1248 #else
1249 # error n_random_create_buf(): the value of HAVE_RANDOM is not supported
1250 #endif
1251 n_err(_("P(seudo)R(andomNumber)G(enerator): %s\n"), prngn);
1254 #if HAVE_RANDOM != n_RANDOM_IMPL_ARC4 && HAVE_RANDOM != n_RANDOM_IMPL_TLS
1255 a_aux_rand_init();
1256 #endif
1259 /* We use our base64 encoder with _NOPAD set, so ensure the encoded result
1260 * with PAD stripped is still longer than what the user requests, easy way.
1261 * The relation of base64 is fixed 3 in = 4 out, and we do not want to
1262 * include the base64 PAD characters in our random string: give some pad */
1263 i = len;
1264 if((inlen = i % 3) != 0)
1265 i += 3 - inlen;
1266 jinc1:
1267 inlen = i >> 2;
1268 oulen = inlen << 2;
1269 if(oulen < len){
1270 i += 3;
1271 goto jinc1;
1273 inlen = inlen + (inlen << 1);
1275 indat = n_lofi_alloc(inlen +1);
1277 if(!(n_psonce & n_PSO_REPRODUCIBLE) || reprocnt_or_null == NULL){
1278 #if HAVE_RANDOM == n_RANDOM_IMPL_TLS
1279 n_tls_rand_bytes(indat, inlen);
1280 #elif HAVE_RANDOM != n_RANDOM_IMPL_ARC4
1281 for(i = inlen; i-- > 0;)
1282 indat[i] = (char)a_aux_rand_get8();
1283 #else
1284 for(cp = indat, i = inlen; i > 0;){
1285 union {ui32_t i4; char c[4];} r;
1286 size_t j;
1288 r.i4 = (ui32_t)arc4random();
1289 switch((j = i & 3)){
1290 case 0: cp[3] = r.c[3]; j = 4; /* FALLTHRU */
1291 case 3: cp[2] = r.c[2]; /* FALLTHRU */
1292 case 2: cp[1] = r.c[1]; /* FALLTHRU */
1293 default: cp[0] = r.c[0]; break;
1295 cp += j;
1296 i -= j;
1298 #endif
1299 }else{
1300 for(cp = indat, i = inlen; i > 0;){
1301 union {ui32_t i4; char c[4];} r;
1302 size_t j;
1304 r.i4 = ++*reprocnt_or_null;
1305 if(n_psonce & n_PSO_BIG_ENDIAN){ /* TODO BSWAP */
1306 char x;
1308 x = r.c[0];
1309 r.c[0] = r.c[3];
1310 r.c[3] = x;
1311 x = r.c[1];
1312 r.c[1] = r.c[2];
1313 r.c[2] = x;
1315 switch((j = i & 3)){
1316 case 0: cp[3] = r.c[3]; j = 4; /* FALLTHRU */
1317 case 3: cp[2] = r.c[2]; /* FALLTHRU */
1318 case 2: cp[1] = r.c[1]; /* FALLTHRU */
1319 default: cp[0] = r.c[0]; break;
1321 cp += j;
1322 i -= j;
1326 oudat = (len >= oulen) ? dat : n_lofi_alloc(oulen +1);
1327 b64.s = oudat;
1328 b64_encode_buf(&b64, indat, inlen, B64_BUF | B64_RFC4648URL | B64_NOPAD);
1329 assert(b64.l >= len);
1330 memcpy(dat, b64.s, len);
1331 dat[len] = '\0';
1332 if(oudat != dat)
1333 n_lofi_free(oudat);
1335 n_lofi_free(indat);
1337 NYD_LEAVE;
1338 return dat;
1341 FL char *
1342 n_random_create_cp(size_t len, ui32_t *reprocnt_or_null){
1343 char *dat;
1344 NYD_ENTER;
1346 dat = n_autorec_alloc(len +1);
1347 dat = n_random_create_buf(dat, len, reprocnt_or_null);
1348 NYD_LEAVE;
1349 return dat;
1352 FL bool_t
1353 n_boolify(char const *inbuf, uiz_t inlen, bool_t emptyrv){
1354 bool_t rv;
1355 NYD2_ENTER;
1356 assert(inlen == 0 || inbuf != NULL);
1358 if(inlen == UIZ_MAX)
1359 inlen = strlen(inbuf);
1361 if(inlen == 0)
1362 rv = (emptyrv >= FAL0) ? (emptyrv == FAL0 ? FAL0 : TRU1) : TRU2;
1363 else{
1364 if((inlen == 1 && (*inbuf == '1' || *inbuf == 'y' || *inbuf == 'Y')) ||
1365 !ascncasecmp(inbuf, "true", inlen) ||
1366 !ascncasecmp(inbuf, "yes", inlen) ||
1367 !ascncasecmp(inbuf, "on", inlen))
1368 rv = TRU1;
1369 else if((inlen == 1 &&
1370 (*inbuf == '0' || *inbuf == 'n' || *inbuf == 'N')) ||
1371 !ascncasecmp(inbuf, "false", inlen) ||
1372 !ascncasecmp(inbuf, "no", inlen) ||
1373 !ascncasecmp(inbuf, "off", inlen))
1374 rv = FAL0;
1375 else{
1376 ui64_t ib;
1378 if((n_idec_buf(&ib, inbuf, inlen, 0, 0, NULL
1379 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
1380 ) != n_IDEC_STATE_CONSUMED)
1381 rv = TRUM1;
1382 else
1383 rv = (ib != 0);
1386 NYD2_LEAVE;
1387 return rv;
1390 FL bool_t
1391 n_quadify(char const *inbuf, uiz_t inlen, char const *prompt, bool_t emptyrv){
1392 bool_t rv;
1393 NYD2_ENTER;
1394 assert(inlen == 0 || inbuf != NULL);
1396 if(inlen == UIZ_MAX)
1397 inlen = strlen(inbuf);
1399 if(inlen == 0)
1400 rv = (emptyrv >= FAL0) ? (emptyrv == FAL0 ? FAL0 : TRU1) : TRU2;
1401 else if((rv = n_boolify(inbuf, inlen, emptyrv)) < FAL0 &&
1402 !ascncasecmp(inbuf, "ask-", 4) &&
1403 (rv = n_boolify(&inbuf[4], inlen - 4, emptyrv)) >= FAL0 &&
1404 (n_psonce & n_PSO_INTERACTIVE) && !(n_pstate & n_PS_ROBOT))
1405 rv = getapproval(prompt, rv);
1406 NYD2_LEAVE;
1407 return rv;
1410 FL bool_t
1411 n_is_all_or_aster(char const *name){
1412 bool_t rv;
1413 NYD2_ENTER;
1415 rv = ((name[0] == '*' && name[1] == '\0') || !asccasecmp(name, "all"));
1416 NYD2_LEAVE;
1417 return rv;
1420 FL struct n_timespec const *
1421 n_time_now(bool_t force_update){ /* TODO event loop update IF cmd requests! */
1422 static struct n_timespec ts_now;
1423 NYD2_ENTER;
1425 if(n_UNLIKELY((n_psonce & n_PSO_REPRODUCIBLE) != 0)){
1426 /* Guaranteed 32-bit posnum TODO SOURCE_DATE_EPOCH should be 64-bit! */
1427 (void)n_idec_si64_cp(&ts_now.ts_sec, ok_vlook(SOURCE_DATE_EPOCH), 0,NULL);
1428 ts_now.ts_nsec = 0;
1429 }else if(force_update || ts_now.ts_sec == 0){
1430 #ifdef HAVE_CLOCK_GETTIME
1431 struct timespec ts;
1433 clock_gettime(CLOCK_REALTIME, &ts);
1434 ts_now.ts_sec = (si64_t)ts.tv_sec;
1435 ts_now.ts_nsec = (siz_t)ts.tv_nsec;
1436 #elif defined HAVE_GETTIMEOFDAY
1437 struct timeval tv;
1439 gettimeofday(&tv, NULL);
1440 ts_now.ts_sec = (si64_t)tv.tv_sec;
1441 ts_now.ts_nsec = (siz_t)tv.tv_usec * 1000;
1442 #else
1443 ts_now.ts_sec = (si64_t)time(NULL);
1444 ts_now.ts_nsec = 0;
1445 #endif
1448 /* Just in case.. */
1449 if(n_UNLIKELY(ts_now.ts_sec < 0))
1450 ts_now.ts_sec = 0;
1451 NYD2_LEAVE;
1452 return &ts_now;
1455 FL void
1456 time_current_update(struct time_current *tc, bool_t full_update){
1457 NYD_ENTER;
1458 tc->tc_time = (time_t)n_time_now(TRU1)->ts_sec;
1460 if(full_update){
1461 char *cp;
1462 struct tm *tmp;
1463 time_t t;
1465 t = tc->tc_time;
1466 jredo:
1467 if((tmp = gmtime(&t)) == NULL){
1468 t = 0;
1469 goto jredo;
1471 memcpy(&tc->tc_gm, tmp, sizeof tc->tc_gm);
1472 if((tmp = localtime(&t)) == NULL){
1473 t = 0;
1474 goto jredo;
1476 memcpy(&tc->tc_local, tmp, sizeof tc->tc_local);
1477 cp = sstpcpy(tc->tc_ctime, n_time_ctime((si64_t)tc->tc_time, tmp));
1478 *cp++ = '\n';
1479 *cp = '\0';
1480 assert(PTR2SIZE(++cp - tc->tc_ctime) < sizeof(tc->tc_ctime));
1482 NYD_LEAVE;
1485 FL char *
1486 n_time_ctime(si64_t secsepoch, struct tm const *localtime_or_nil){/* TODO err*/
1487 /* Problem is that secsepoch may be invalid for representation of ctime(3),
1488 * which indeed is asctime(localtime(t)); musl libc says for asctime(3):
1489 * ISO C requires us to use the above format string,
1490 * even if it will not fit in the buffer. Thus asctime_r
1491 * is _supposed_ to crash if the fields in tm are too large.
1492 * We follow this behavior and crash "gracefully" to warn
1493 * application developers that they may not be so lucky
1494 * on other implementations (e.g. stack smashing..).
1495 * So we need to do it on our own or the libc may kill us */
1496 static char buf[32]; /* TODO static buffer (-> datetime_to_format()) */
1498 si32_t y, md, th, tm, ts;
1499 char const *wdn, *mn;
1500 struct tm const *tmp;
1501 NYD_ENTER;
1503 if((tmp = localtime_or_nil) == NULL){
1504 time_t t;
1506 t = (time_t)secsepoch;
1507 jredo:
1508 if((tmp = localtime(&t)) == NULL){
1509 /* TODO error log */
1510 t = 0;
1511 goto jredo;
1515 if(n_UNLIKELY((y = tmp->tm_year) < 0 || y >= 9999/*SI32_MAX*/ - 1900)){
1516 y = 1970;
1517 wdn = n_weekday_names[4];
1518 mn = n_month_names[0];
1519 md = 1;
1520 th = tm = ts = 0;
1521 }else{
1522 y += 1900;
1523 wdn = (tmp->tm_wday >= 0 && tmp->tm_wday <= 6)
1524 ? n_weekday_names[tmp->tm_wday] : n_qm;
1525 mn = (tmp->tm_mon >= 0 && tmp->tm_mon <= 11)
1526 ? n_month_names[tmp->tm_mon] : n_qm;
1528 if((md = tmp->tm_mday) < 1 || md > 31)
1529 md = 1;
1531 if((th = tmp->tm_hour) < 0 || th > 23)
1532 th = 0;
1533 if((tm = tmp->tm_min) < 0 || tm > 59)
1534 tm = 0;
1535 if((ts = tmp->tm_sec) < 0 || ts > 60)
1536 ts = 0;
1539 (void)snprintf(buf, sizeof buf, "%3s %3s%3d %.2d:%.2d:%.2d %d",
1540 wdn, mn, md, th, tm, ts, y);
1541 NYD_LEAVE;
1542 return buf;
1545 FL uiz_t
1546 n_msleep(uiz_t millis, bool_t ignint){
1547 uiz_t rv;
1548 NYD2_ENTER;
1550 #ifdef HAVE_NANOSLEEP
1551 /* C99 */{
1552 struct timespec ts, trem;
1553 int i;
1555 ts.tv_sec = millis / 1000;
1556 ts.tv_nsec = (millis %= 1000) * 1000 * 1000;
1558 while((i = nanosleep(&ts, &trem)) != 0 && ignint)
1559 ts = trem;
1560 rv = (i == 0) ? 0 : (trem.tv_sec * 1000) + (trem.tv_nsec / (1000 * 1000));
1563 #elif defined HAVE_SLEEP
1564 if((millis /= 1000) == 0)
1565 millis = 1;
1566 while((rv = sleep((unsigned int)millis)) != 0 && ignint)
1567 millis = rv;
1568 #else
1569 # error Configuration should have detected a function for sleeping.
1570 #endif
1572 NYD2_LEAVE;
1573 return rv;
1576 FL void
1577 n_err(char const *format, ...){
1578 va_list ap;
1579 NYD2_ENTER;
1581 va_start(ap, format);
1582 #ifdef HAVE_ERRORS
1583 if(n_psonce & n_PSO_INTERACTIVE)
1584 n_verr(format, ap);
1585 else
1586 #endif
1588 size_t len;
1589 bool_t doname;
1591 doname = FAL0;
1593 while(*format == '\n'){
1594 doname = TRU1;
1595 putc('\n', n_stderr);
1596 ++format;
1599 if(doname)
1600 a_aux_err_linelen = 0;
1602 if((len = strlen(format)) > 0){
1603 if(doname || a_aux_err_linelen == 0){
1604 char const *cp;
1606 if(*(cp = ok_vlook(log_prefix)) != '\0')
1607 fputs(cp, n_stderr);
1609 vfprintf(n_stderr, format, ap);
1611 /* C99 */{
1612 size_t i = len;
1614 if(format[--len] == '\n'){
1615 a_aux_err_linelen = (i -= ++len);
1616 break;
1618 ++a_aux_err_linelen;
1619 }while(len > 0);
1623 fflush(n_stderr);
1625 va_end(ap);
1626 NYD2_LEAVE;
1629 FL void
1630 n_verr(char const *format, va_list ap){
1631 #ifdef HAVE_ERRORS
1632 struct a_aux_err_node *enp;
1633 #endif
1634 bool_t doname;
1635 size_t len;
1636 NYD2_ENTER;
1638 doname = FAL0;
1640 while(*format == '\n'){
1641 putc('\n', n_stderr);
1642 doname = TRU1;
1643 ++format;
1646 if(doname){
1647 a_aux_err_linelen = 0;
1648 #ifdef HAVE_ERRORS
1649 if(n_psonce & n_PSO_INTERACTIVE){
1650 if((enp = a_aux_err_tail) != NULL &&
1651 (enp->ae_str.s_len > 0 &&
1652 enp->ae_str.s_dat[enp->ae_str.s_len - 1] != '\n'))
1653 n_string_push_c(&enp->ae_str, '\n');
1655 #endif
1658 if((len = strlen(format)) == 0)
1659 goto jleave;
1660 #ifdef HAVE_ERRORS
1661 n_pstate |= n_PS_ERRORS_PROMPT;
1662 #endif
1664 if(doname || a_aux_err_linelen == 0){
1665 char const *cp;
1667 if(*(cp = ok_vlook(log_prefix)) != '\0')
1668 fputs(cp, n_stderr);
1671 /* C99 */{
1672 size_t i = len;
1674 if(format[--len] == '\n'){
1675 a_aux_err_linelen = (i -= ++len);
1676 break;
1678 ++a_aux_err_linelen;
1679 }while(len > 0);
1682 #ifdef HAVE_ERRORS
1683 if(!(n_psonce & n_PSO_INTERACTIVE))
1684 #endif
1685 vfprintf(n_stderr, format, ap);
1686 #ifdef HAVE_ERRORS
1687 else{
1688 int imax, i;
1689 n_LCTAV(ERRORS_MAX > 3);
1691 /* Link it into the `errors' message ring */
1692 if((enp = a_aux_err_tail) == NULL){
1693 jcreat:
1694 enp = n_alloc(sizeof *enp);
1695 enp->ae_next = NULL;
1696 n_string_creat(&enp->ae_str);
1697 if(a_aux_err_tail != NULL)
1698 a_aux_err_tail->ae_next = enp;
1699 else
1700 a_aux_err_head = enp;
1701 a_aux_err_tail = enp;
1702 ++a_aux_err_cnt;
1703 }else if(doname ||
1704 (enp->ae_str.s_len > 0 &&
1705 enp->ae_str.s_dat[enp->ae_str.s_len - 1] == '\n')){
1706 if(a_aux_err_cnt < ERRORS_MAX)
1707 goto jcreat;
1709 a_aux_err_head = (enp = a_aux_err_head)->ae_next;
1710 a_aux_err_tail->ae_next = enp;
1711 a_aux_err_tail = enp;
1712 enp->ae_next = NULL;
1713 n_string_trunc(&enp->ae_str, 0);
1716 # ifdef HAVE_N_VA_COPY
1717 imax = 64;
1718 # else
1719 imax = n_MIN(LINESIZE, 1024);
1720 # endif
1721 for(i = imax;; imax = ++i /* xxx could wrap, maybe */){
1722 # ifdef HAVE_N_VA_COPY
1723 va_list vac;
1725 n_va_copy(vac, ap);
1726 # else
1727 # define vac ap
1728 # endif
1730 n_string_resize(&enp->ae_str, (len = enp->ae_str.s_len) + (size_t)i);
1731 i = vsnprintf(&enp->ae_str.s_dat[len], (size_t)i, format, vac);
1732 # ifdef HAVE_N_VA_COPY
1733 va_end(vac);
1734 # else
1735 # undef vac
1736 # endif
1737 if(i <= 0)
1738 goto jleave;
1739 if(UICMP(z, i, >=, imax)){
1740 # ifdef HAVE_N_VA_COPY
1741 /* XXX Check overflow for upcoming LEN+++i! */
1742 n_string_trunc(&enp->ae_str, len);
1743 continue;
1744 # else
1745 i = (int)strlen(&enp->ae_str.s_dat[len]);
1746 # endif
1748 break;
1750 n_string_trunc(&enp->ae_str, len + (size_t)i);
1752 fwrite(&enp->ae_str.s_dat[len], 1, (size_t)i, n_stderr);
1754 #endif /* HAVE_ERRORS */
1756 jleave:
1757 fflush(n_stderr);
1758 NYD2_LEAVE;
1761 FL void
1762 n_err_sighdl(char const *format, ...){ /* TODO sigsafe; obsolete! */
1763 va_list ap;
1764 NYD_X;
1766 va_start(ap, format);
1767 vfprintf(n_stderr, format, ap);
1768 va_end(ap);
1769 fflush(n_stderr);
1772 FL void
1773 n_perr(char const *msg, int errval){
1774 int e;
1775 char const *fmt;
1776 NYD2_ENTER;
1778 if(msg == NULL){
1779 fmt = "%s%s\n";
1780 msg = n_empty;
1781 }else
1782 fmt = "%s: %s\n";
1784 e = (errval == 0) ? n_err_no : errval;
1785 n_err(fmt, msg, n_err_to_doc(e));
1786 if(errval == 0)
1787 n_err_no = e;
1788 NYD2_LEAVE;
1791 FL void
1792 n_alert(char const *format, ...){
1793 va_list ap;
1794 NYD2_ENTER;
1796 n_err(a_aux_err_linelen > 0 ? _("\nAlert: ") : _("Alert: "));
1798 va_start(ap, format);
1799 n_verr(format, ap);
1800 va_end(ap);
1802 n_err("\n");
1803 NYD2_LEAVE;
1806 FL void
1807 n_panic(char const *format, ...){
1808 va_list ap;
1809 NYD2_ENTER;
1811 if(a_aux_err_linelen > 0){
1812 putc('\n', n_stderr);
1813 a_aux_err_linelen = 0;
1815 fprintf(n_stderr, "%sPanic: ", ok_vlook(log_prefix));
1817 va_start(ap, format);
1818 vfprintf(n_stderr, format, ap);
1819 va_end(ap);
1821 putc('\n', n_stderr);
1822 fflush(n_stderr);
1823 NYD2_LEAVE;
1824 abort(); /* Was exit(n_EXIT_ERR); for a while, but no */
1827 #ifdef HAVE_ERRORS
1828 FL int
1829 c_errors(void *v){
1830 char **argv = v;
1831 struct a_aux_err_node *enp;
1832 NYD_ENTER;
1834 if(*argv == NULL)
1835 goto jlist;
1836 if(argv[1] != NULL)
1837 goto jerr;
1838 if(!asccasecmp(*argv, "show"))
1839 goto jlist;
1840 if(!asccasecmp(*argv, "clear"))
1841 goto jclear;
1842 jerr:
1843 fprintf(n_stderr,
1844 _("Synopsis: errors: (<show> or) <clear> the error ring\n"));
1845 v = NULL;
1846 jleave:
1847 NYD_LEAVE;
1848 return (v == NULL) ? !STOP : !OKAY; /* xxx 1:bad 0:good -- do some */
1850 jlist:{
1851 FILE *fp;
1852 size_t i;
1854 if(a_aux_err_head == NULL){
1855 fprintf(n_stderr, _("The error ring is empty\n"));
1856 goto jleave;
1859 if((fp = Ftmp(NULL, "errors", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
1860 NULL){
1861 fprintf(n_stderr, _("tmpfile"));
1862 v = NULL;
1863 goto jleave;
1866 for(i = 0, enp = a_aux_err_head; enp != NULL; enp = enp->ae_next)
1867 fprintf(fp, "%4" PRIuZ ". %s", ++i, n_string_cp(&enp->ae_str));
1868 /* We don't know whether last string ended with NL; be simple XXX */
1869 putc('\n', fp);
1871 page_or_print(fp, 0);
1872 Fclose(fp);
1874 /* FALLTHRU */
1876 jclear:
1877 a_aux_err_tail = NULL;
1878 a_aux_err_cnt = a_aux_err_cnt_noted = 0;
1879 a_aux_err_linelen = 0;
1880 while((enp = a_aux_err_head) != NULL){
1881 a_aux_err_head = enp->ae_next;
1882 n_string_gut(&enp->ae_str);
1883 n_free(enp);
1885 goto jleave;
1887 #endif /* HAVE_ERRORS */
1889 FL char const *
1890 n_err_to_doc(si32_t eno){
1891 char const *rv;
1892 struct a_aux_err_map const *aemp;
1893 NYD2_ENTER;
1895 aemp = a_aux_err_map_from_no(eno);
1896 #ifdef HAVE_DOCSTRINGS
1897 rv = &a_aux_err_docs[aemp->aem_docoff];
1898 #else
1899 rv = &a_aux_err_names[aemp->aem_nameoff];
1900 #endif
1901 NYD2_LEAVE;
1902 return rv;
1905 FL char const *
1906 n_err_to_name(si32_t eno){
1907 char const *rv;
1908 struct a_aux_err_map const *aemp;
1909 NYD2_ENTER;
1911 aemp = a_aux_err_map_from_no(eno);
1912 rv = &a_aux_err_names[aemp->aem_nameoff];
1913 NYD2_LEAVE;
1914 return rv;
1917 FL si32_t
1918 n_err_from_name(char const *name){
1919 struct a_aux_err_map const *aemp;
1920 ui32_t hash, i, j, x;
1921 si32_t rv;
1922 NYD2_ENTER;
1924 hash = n_torek_hash(name);
1926 for(i = hash % a_AUX_ERR_REV_PRIME, j = 0; j <= a_AUX_ERR_REV_LONGEST; ++j){
1927 if((x = a_aux_err_revmap[i]) == a_AUX_ERR_REV_ILL)
1928 break;
1930 aemp = &a_aux_err_map[x];
1931 if(aemp->aem_hash == hash &&
1932 !strcmp(&a_aux_err_names[aemp->aem_nameoff], name)){
1933 rv = aemp->aem_err_no;
1934 goto jleave;
1937 if(++i == a_AUX_ERR_REV_PRIME){
1938 #ifdef a_AUX_ERR_REV_WRAPAROUND
1939 i = 0;
1940 #else
1941 break;
1942 #endif
1946 /* Have not found it. But wait, it could be that the user did, e.g.,
1947 * eval echo \$^ERR-$: \$^ERRDOC-$!: \$^ERRNAME-$! */
1948 if((n_idec_si32_cp(&rv, name, 0, NULL) &
1949 (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
1950 ) == n_IDEC_STATE_CONSUMED){
1951 aemp = a_aux_err_map_from_no(rv);
1952 rv = aemp->aem_err_no;
1953 goto jleave;
1956 rv = a_aux_err_map[n__ERR_NUMBER_VOIDOFF].aem_err_no;
1957 jleave:
1958 NYD2_LEAVE;
1959 return rv;
1962 #ifdef HAVE_REGEX
1963 FL char const *
1964 n_regex_err_to_doc(const regex_t *rep, int e){
1965 char *cp;
1966 size_t i;
1967 NYD2_ENTER;
1969 i = regerror(e, rep, NULL, 0) +1;
1970 cp = n_autorec_alloc(i);
1971 regerror(e, rep, cp, i);
1972 NYD2_LEAVE;
1973 return cp;
1975 #endif
1977 /* s-it-mode */