2 #@ Create gen-errors.h. And see auxlily.c.
5 # Acceptable "longest distance" from hash-modulo-index to key
8 # Generate a more verbose output. Not for shipout versions.
11 MAILX
='LC_ALL=C s-nail -#:/'
12 OUT
="${SRCDIR}"gen-errors.h
17 export LC_ALL MAXDISTANCE_PENALTY VERB MAILX OUT
19 [ -n "${awk}" ] ||
awk=awk
21 # The set of errors we support
24 2BIG='Argument list too long' \
25 ACCES='Permission denied' \
26 ADDRINUSE='Address already in use' \
27 ADDRNOTAVAIL='Cannot assign requested address' \
28 AFNOSUPPORT='Address family not supported by protocol family' \
29 AGAIN='Resource temporarily unavailable' \
30 ALREADY='Operation already in progress' \
31 BADF='Bad file descriptor' \
32 BADMSG='Bad message' \
34 CANCELED='Operation canceled' \
35 CHILD='No child processes' \
36 CONNABORTED='Software caused connection abort' \
37 CONNREFUSED='Connection refused' \
38 CONNRESET='Connection reset by peer' \
39 DEADLK='Resource deadlock avoided' \
40 DESTADDRREQ='Destination address required' \
41 DOM='Numerical argument out of domain' \
42 DQUOT='Disc quota exceeded' \
45 FBIG='File too large' \
46 HOSTUNREACH='No route to host' \
47 IDRM='Identifier removed' \
48 ILSEQ='Illegal byte sequence' \
49 INPROGRESS='Operation now in progress' \
50 INTR='Interrupted system call' \
51 INVAL='Invalid argument' \
52 IO='Input/output error' \
53 ISCONN='Socket is already connected' \
54 ISDIR='Is a directory' \
55 LOOP='Too many levels of symbolic links' \
56 MFILE='Too many open files' \
57 MLINK='Too many links' \
58 MSGSIZE='Message too long' \
59 MULTIHOP='Multihop attempted' \
60 NAMETOOLONG='File name too long' \
61 NETDOWN='Network is down' \
62 NETRESET='Network dropped connection on reset' \
63 NETUNREACH='Network is unreachable' \
64 NFILE='Too many open files in system' \
65 NOBUFS='No buffer space available' \
66 NODATA='No data available' \
67 NODEV='Operation not supported by device' \
68 NOENT='No such entry, file or directory' \
69 NOEXEC='Exec format error' \
70 NOLCK='No locks available' \
71 NOLINK='Link has been severed' \
72 NOMEM='Cannot allocate memory' \
73 NOMSG='No message of desired type' \
74 NOPROTOOPT='Protocol not available' \
75 NOSPC='No space left on device' \
76 NOSR='Out of streams resource' \
77 NOSTR='Device not a stream' \
78 NOSYS='Function not implemented' \
79 NOTCONN='Socket is not connected' \
80 NOTDIR='Not a directory' \
81 NOTEMPTY='Directory not empty' \
82 NOTOBACCO='No tobacco, snorkeling on empty pipe' \
83 NOTSOCK='Socket operation on non-socket' \
84 NOTSUP='Operation not supported' \
85 NOTTY='Inappropriate ioctl for device' \
86 NXIO='Device not configured' \
87 OPNOTSUPP='Operation not supported' \
88 OVERFLOW='Value too large to be stored in data type' \
89 PERM='Operation not permitted' \
91 PROTO='Protocol error' \
92 PROTONOSUPPORT='Protocol not supported' \
93 PROTOTYPE='Protocol wrong type for socket' \
94 RANGE='Result too large' \
95 ROFS='Read-only filesystem' \
96 SPIPE='Invalid seek' \
97 SRCH='No such process' \
98 STALE='Stale NFS file handle' \
99 TIME='Timer expired' \
100 TIMEDOUT='Operation timed out' \
101 TXTBSY='Text file busy' \
102 WOULDBLOCK='Operation would block' \
103 XDEV='Cross-device link' \
109 ${awk} -v dodoc="${1}" -v incnone="${2}" -v input="${ERRORS}" '
112 voff = match(input, /[[:alnum:]_]+(='${j}'[^'${j}']+)?/)
115 v = substr(input, voff, RLENGTH)
116 input = substr(input, voff + RLENGTH)
119 d = substr(v, doff + 2, length(v) - doff - 1)
120 v = substr(v, 1, doff - 1)
122 if(!incnone && v == "NONE
")
131 [ -n "${TARGET}" ] || {
132 echo >&2 'Invalid usage'
135 # Note this may be ISO C89, so we cannot
142 #if defined __STDC_VERSION__ && __STDC_VERSION__ + 0 >= 199901L
145 # include <inttypes.h>
148 typedef uint32_t ui32_t;
149 typedef int32_t si32_t;
150 #elif ULONG_MAX == 0xFFFFFFFFu
151 typedef unsigned long int ui32_t;
152 typedef signed long int si32_t;
154 typedef unsigned int ui32_t;
155 typedef signed int si32_t;
157 struct a_in {struct a_in *next; char const *name; si32_t no; ui32_t uno;};
158 static int a_sortin(void const *a, void const *b){
159 return (*(struct a_in const* const *)a)->uno -
160 (*(struct a_in const* const *)b)->uno;
163 char line[1024], *lp;
164 struct a_in *head, *tail, *np, **nap;
166 si32_t xavail = 0, total = 1, imin = 0, imax = 0, voidoff = 0,
168 FILE *ifp = fopen("${OUT}", "r
"), *ofp = fopen("${TARGET}", "a
");
169 if(ifp == NULL || ofp == NULL){
170 fprintf(stderr, "ERROR
: cannot open in- or output
\n");
174 /* Create a list of all errors */
175 head = tail = malloc(sizeof *head);
176 head->next = NULL; head->name = "n_ERR_NONE
"; head->no = 0;
178 for n in `error_parse 0 0`; do
186 if(imin > i) imin = i; if(imax < i) imax = i;
187 np = malloc(sizeof *np);
188 np->next = NULL; np->name = "n_ERR_
${n}"; np->no = i;
189 tail->next = np; tail = np;
193 /* The unsigned type used for storage */
195 fputs("#define n__ERR_NUMBER_TYPE ", ofp);
196 if((ui32_t
)imax
<= 0xFFu
&& (ui32_t
)-imin <= 0xFFu
){
197 fputs
("ui8_t\n", ofp
);
199 }else if(((ui32_t
)imax
<= 0xFFFFu
&& (ui32_t
)-imin <= 0xFFFFu
)){
200 fputs
("ui16_t\n", ofp
);
203 fputs
("ui32_t\n", ofp
);
204 maxsub
= 0xFFFFFFFFu
;
207 /* Now that we know the storage
type, create the unsigned numbers
*/
208 for(umax
= 0, np
= head; np
!= NULL
; np
= np-
>next
){
210 np-
>uno
= maxsub
+ np-
>no
+ 1;
216 if(umax
<= (ui32_t
)imax
){
217 fprintf
(stderr
, "ERROR: errno ranges overlap\n");
223 nap
= malloc
(sizeof
(*nap
) * (unsigned
)total
);
224 for(i
= 0, np
= head; np
!= NULL
; ++i
, np
= np-
>next
)
227 fprintf
(stderr
, "ERROR: implementation error i != total\n");
230 qsort
(nap
, (ui32_t
)i
, sizeof
*nap
, &a_sortin
);
232 /* The enumeration of numbers
*/
234 fputs
("enum n_err_number{\\n", ofp
);
235 for(i
= 0; i
< total
; ++i
)
236 fprintf
(ofp
, " %s = %lu,\\n",
237 nap
[i
]->name
, (unsigned long
)nap
[i
]->uno
);
238 fprintf
(ofp
, " n__ERR_NUMBER = %ld\\n", (long
)total
);
241 /* The binary search mapping table from OS error value to our internal
242 * a_aux_err_map
[] error description table
*/
244 /* A real hack to have a fast NUMBER
-> MAP mapping without compiling
245 * and running a second C program during config
*/
246 while((lp = fgets
(line
, sizeof line
, ifp
)) != NULL
&&
247 strstr
(lp, "a_aux_err_map[]") == NULL
)
249 if(feof
(ifp
) || ferror
(ifp
)){
250 fprintf
(stderr
, "ERROR: I/O error when reading \"ifp\"\n");
253 telloff
= (int
)ftell
(ifp
);
255 fprintf
(ofp
, "#define n__ERR_NUMBER_TO_MAPOFF \\\\\\n");
256 for(xavail
= 0, i
= 0; i
< total
; ++i
){
257 if(i
== 0 || nap
[i
]->no
!= nap
[i
- 1]->no
){
258 if(fseek
(ifp
, telloff
, SEEK_SET
) == -1){
259 fprintf
(stderr
, "ERROR: I/O error when searching \"ifp\", I.\n");
263 while((lp = fgets
(line
, sizeof line
, ifp
)) != NULL
&&
264 strstr
(lp, nap
[i
]->name
) == NULL
)
266 if(feof
(ifp
) || ferror
(ifp
)){
267 fprintf
(stderr
, "ERROR: I/O error when reading \"ifp\", II.\n");
270 fprintf
(ofp
, "\ta_X(%lu, %lu) %s%s%s\\\\\\n",
271 (unsigned long
)nap
[i
]->uno
, (long
)(ui32_t
)j
,
272 ((${VERB}) ?
"/* " : ""), ((${VERB}) ? nap
[i
]->name
: ""),
273 ((${VERB}) ?
" */ " : ""));
274 if(!strcmp
("n_ERR_NOTOBACCO", nap
[i
]->name
))
279 fprintf
(ofp
, "\\t/* %ld unique members */\\n", (long
)xavail
);
280 fprintf
(ofp
, "#define n__ERR_NUMBER_VOIDOFF %ld\\n", (long
)voidoff
);
283 while((np
= head) != NULL
){
294 if [ ${#} -ne 0 ]; then
295 if [ "${1}" = noverbose
]; then
302 if [ ${#} -eq 1 ]; then
303 [ "${1}" = config
] && config
304 elif [ ${#} -eq 0 ]; then
305 # Now start perl(1) without PERL5OPT set to avoid multibyte sequence errors
306 PERL5OPT
= PERL5LIB
= exec perl
-x "${0}"
308 echo >&2 'Invalid usage'
311 # Thanks to perl(5) and it's -x / #! perl / __END__ mechanism!
312 # Why can env(1) not be used for such easy things in #!?
315 use diagnostics
-verbose;
322 use sigtrap qw
(handler cleanup normal-signals
);
324 my
($S, @ENTS
, $CTOOL, $CTOOL_EXE) = ($ENV{VERB
} ?
' ' : '');
342 die
"$CTOOL_EXE: couldn't unlink: $^E"
343 if $CTOOL_EXE && -f $CTOOL_EXE && 1 != unlink
$CTOOL_EXE;
344 die
"$CTOOL: couldn't unlink: $^E"
345 if $CTOOL && -f $CTOOL && 1 != unlink
$CTOOL;
346 die
"Terminating due to signal $_[0]" if $_[0]
350 my
$input = $ENV{ERRORS
};
351 while($input =~
/([[:alnum
:]_
]+)='([^']+)'(.*)/){
361 $CTOOL = '.
/tmp-errors-tool-
' . $$ . '.c
';
362 $CTOOL_EXE = $CTOOL . '.exe
';
364 die "$CTOOL: open: $^E" unless open F, '>', $CTOOL;
365 print F '#define MAX_DISTANCE_PENALTY ', $ENV{MAXDISTANCE_PENALTY}, "\n";
366 # >>>>>>>>>>>>>>>>>>>
368 #define __CREATE_ERRORS_SH
374 #define n_NELEM(A) (sizeof(A) / sizeof(A[0]))
376 #define ui32_t uint32_t
377 #define si32_t int32_t
378 #define ui16_t uint16_t
379 #define ui8_t uint8_t
381 struct a_aux_err_map{
382 ui32_t aem_hash; /* Hash of name */
383 ui32_t aem_nameoff; /* Into a_aux_err_names[] */
384 ui32_t aem_docoff; /* Into a_aux_err docs[] */
385 si32_t aem_errno; /* The OS errno value for this one */
388 #include "gen-errors.h"
390 static ui8_t seen_wraparound;
391 static size_t longest_distance;
394 next_prime(size_t no){ /* blush (brute force) */
397 for(size_t i = 3; i < no; i += 2)
404 reversy(size_t size){
405 struct a_aux_err_map const *aemp = a_aux_err_map,
406 *aemaxp = aemp + n_NELEM(a_aux_err_map);
407 size_t ldist = 0, *arr;
409 arr = malloc(sizeof *arr * size);
410 for(size_t i = 0; i < size; ++i)
411 arr[i] = n_NELEM(a_aux_err_map);
414 longest_distance = 0;
416 while(aemp < aemaxp){
417 ui32_t hash = aemp->aem_hash, i = hash % size, l;
419 for(l = 0; arr[i] != n_NELEM(a_aux_err_map); ++l)
424 if(l > longest_distance)
425 longest_distance = l;
426 arr[i] = (size_t)(aemp++ - a_aux_err_map);
432 main(int argc, char **argv){
433 size_t *arr, size = n_NELEM(a_aux_err_map);
435 fprintf(stderr, "Starting reversy, okeys=%zu\n", size);
437 arr = reversy(size = next_prime(size));
438 fprintf(stderr, " - size=%zu longest_distance=%zu seen_wraparound=%d\n",
439 size, longest_distance, seen_wraparound);
440 if(longest_distance <= MAX_DISTANCE_PENALTY)
446 "#define a_AUX_ERR_REV_ILL %zuu\n"
447 "#define a_AUX_ERR_REV_PRIME %zuu\n"
448 "#define a_AUX_ERR_REV_LONGEST %zuu\n"
449 "#define a_AUX_ERR_REV_WRAPAROUND %d\n"
450 "static %s const a_aux_err_revmap[a_AUX_ERR_REV_PRIME] = {\n%s",
451 n_NELEM(a_aux_err_map), size, longest_distance, seen_wraparound,
452 argv[1], (argc > 2 ? " " : ""));
453 for(size_t i = 0; i < size; ++i)
454 printf("%s%zuu", (i == 0 ? ""
455 : (i % 10 == 0 ? (argc > 2 ? ",\n " : ",\n")
456 : (argc > 2 ? ", " : ","))),
462 # <<<<<<<<<<<<<<<<<<<
467 die
"hash_em: open: $^E"
468 unless my
$pid = open2
*RFD
, *WFD
, $ENV{MAILX
};
469 foreach my
$e (@ENTS
){
470 print WFD
"vexpr hash $e->{name}\n";
480 die
"$ENV{OUT}: open: $^E" unless open F
, '>', $ENV{OUT
};
481 print F
"/*@ $ENV{OUT}, generated by $0.\n",
482 " *@ See auxlily.c for more */\n\n";
484 print F
'static char const a_aux_err_names[] = {', "\n";
485 my
($i, $alen) = (0, 0);
486 foreach my
$e (@ENTS
){
487 $e->{nameoff
} = $alen;
490 my
$a = join '\',\'', split(//, $k);
492 print F
"${S}/* $i. [$alen]+$l $k */\n" if $ENV{VERB
};
493 print F
"${S}'$a','\\0',\n";
497 print F
'};', "\n\n";
499 print F
'#ifdef HAVE_DOCSTRINGS', "\n";
500 print F
'#undef a_X', "\n", '#define a_X(X)', "\n";
501 print F
'static char const a_aux_err_docs[] = {', "\n";
502 ($i, $alen) = (0, 0);
503 foreach my
$e (@ENTS
){
504 $e->{docoff
} = $alen;
507 my
$a = join '\',\'', split(//, $k);
509 print F
"${S}/* $i. [$alen]+$l $e->{name} */ ",
510 "a_X(N_(\"$e->{doc}\"))\n" if $ENV{VERB
};
511 print F
"${S}'$a','\\0',\n";
515 print F
'};', "\n", '#undef a_X', "\n#endif /* HAVE_DOCSTRINGS */\n\n";
519 #ifndef __CREATE_ERRORS_SH
524 static struct a_aux_err_map const a_aux_err_map[] = {
526 foreach my
$e (@ENTS
){
527 print F
"${S}{$e->{hash}u, $e->{nameoff}u, $e->{docoff}u, ",
528 "a_X(n_ERR_$e->{name})},\n"
530 print F
'};', "\n", '#undef a_X', "\n\n";
532 die
"$ENV{OUT}: close: $^E" unless close F
536 my
$argv2 = $ENV{VERB
} ?
' verb' : '';
537 system
("\$CC -I. -o $CTOOL_EXE $CTOOL");
538 my
$t = (@ENTS
< 0xFF ?
'ui8_t' : (@ENTS
< 0xFFFF ?
'ui16_t' : 'ui32_t'));
539 `$CTOOL_EXE $t$argv2 >> $ENV{OUT}`
542 {package main
; main_fun
()}