2 #@ Either create src/su/gen-errors.h, or, at compile time, the OS<>SU map.
6 IN
="${SRCDIR}"su
/gen-errors.h
7 XOUT
=src
/su
/gen-errors.h
9 # We use `csop' for hashing
10 MAILX
='LC_ALL=C s-nail -#:/'
12 # Acceptable "longest distance" from hash-modulo-index to key
15 # Generate a more verbose output. Not for shipout versions.
21 export LC_ALL MAXDISTANCE_PENALTY VERB MAILX IN XOUT
28 # The set of errors we support {{{
31 2BIG='Argument list too long' \
32 ACCES='Permission denied' \
33 ADDRINUSE='Address already in use' \
34 ADDRNOTAVAIL='Cannot assign requested address' \
35 AFNOSUPPORT='Address family not supported by protocol family' \
36 AGAIN='Resource temporarily unavailable' \
37 ALREADY='Operation already in progress' \
38 BADF='Bad file descriptor' \
39 BADMSG='Bad message' \
41 CANCELED='Operation canceled' \
42 CHILD='No child processes' \
43 CONNABORTED='Software caused connection abort' \
44 CONNREFUSED='Connection refused' \
45 CONNRESET='Connection reset by peer' \
46 DEADLK='Resource deadlock avoided' \
47 DESTADDRREQ='Destination address required' \
48 DOM='Numerical argument out of domain' \
49 DQUOT='Disc quota exceeded' \
52 FBIG='File too large' \
53 HOSTUNREACH='No route to host' \
54 IDRM='Identifier removed' \
55 ILSEQ='Illegal byte sequence' \
56 INPROGRESS='Operation now in progress' \
57 INTR='Interrupted system call' \
58 INVAL='Invalid argument' \
59 IO='Input/output error' \
60 ISCONN='Socket is already connected' \
61 ISDIR='Is a directory' \
62 LOOP='Too many levels of symbolic links' \
63 MFILE='Too many open files' \
64 MLINK='Too many links' \
65 MSGSIZE='Message too long' \
66 MULTIHOP='Multihop attempted' \
67 NAMETOOLONG='File name too long' \
68 NETDOWN='Network is down' \
69 NETRESET='Network dropped connection on reset' \
70 NETUNREACH='Network is unreachable' \
71 NFILE='Too many open files in system' \
72 NOBUFS='No buffer space available' \
73 NODATA='No data available' \
74 NODEV='Operation not supported by device' \
75 NOENT='No such entry, file or directory' \
76 NOEXEC='Exec format error' \
77 NOLCK='No locks available' \
78 NOLINK='Link has been severed' \
79 NOMEM='Cannot allocate memory' \
80 NOMSG='No message of desired type' \
81 NOPROTOOPT='Protocol not available' \
82 NOSPC='No space left on device' \
83 NOSR='Out of streams resource' \
84 NOSTR='Device not a stream' \
85 NOSYS='Function not implemented' \
86 NOTCONN='Socket is not connected' \
87 NOTDIR='Not a directory' \
88 NOTEMPTY='Directory not empty' \
89 NOTOBACCO='No tobacco, snorkeling on empty pipe' \
90 NOTSOCK='Socket operation on non-socket' \
91 NOTSUP='Operation not supported' \
92 NOTTY='Inappropriate ioctl for device' \
93 NXIO='Device not configured' \
94 OPNOTSUPP='Operation not supported' \
95 OVERFLOW='Value too large to be stored in data type' \
96 PERM='Operation not permitted' \
98 PROTO='Protocol error' \
99 PROTONOSUPPORT='Protocol not supported' \
100 PROTOTYPE='Protocol wrong type for socket' \
101 RANGE='Result too large' \
102 ROFS='Read-only filesystem' \
103 SPIPE='Invalid seek' \
104 SRCH='No such process' \
105 STALE='Stale NFS file handle' \
106 TIME='Timer expired' \
107 TIMEDOUT='Operation timed out' \
108 TXTBSY='Text file busy' \
109 WOULDBLOCK='Operation would block' \
110 XDEV='Cross-device link' \
117 ${awk} -v dodoc="${1}" -v incnone="${2}" -v input="${ERRORS}" '
120 voff = match(input, /[0-9a-zA-Z_]+(='${j}'[^'${j}']+)?/)
123 v = substr(input, voff, RLENGTH)
124 input = substr(input, voff + RLENGTH)
127 d = substr(v, doff + 2, length(v) - doff - 1)
128 v = substr(v, 1, doff - 1)
130 if(!incnone && (v == "NONE
" || v == "NOTOBACCO
"))
138 compile_time() { # {{{
139 [ -n "${TARGET}" ] || {
140 echo >&2 'Invalid usage'
146 printf '#include <errno.h>\nsu_ERROR_START\n'
147 for n in `error_parse 0 0`; do
148 printf '#ifdef E%s\nE%s %s\n#else\n-1 %s\n#endif\n' $n $n $n $n
152 # The problem is that at least (some versions of) gcc mangle output.
153 # Ensure we get both arguments on one line.
154 # While here sort numerically.
155 "${CC}" -E "${TARGET}".c |
157 BEGIN{hot=0; conti=0}
160 /^su_ERROR_START$/{hot=1; next}
174 ${sort} -n > "${TARGET}".txt
176 # EBCDIC/ASCII: we use \134 for reverse solidus \
178 ${awk} -v verb="${VERB}" -v input="${ERRORS}" -v dat="${TARGET}.txt" '
180 verb = (verb != 0) ? " " : ""
182 # Read in our OS data
187 while((getline dl < dat) > 0){
203 # Maximum error number defines the datatype to use.
204 # We need a value for NOTOBACCO, we warp all non-available errors to
205 # numbers too high to be regular errors, counting backwards
207 i = max + unavail + 1
218 print "#define su__ERR_NUMBER_TYPE " t
219 print "#define su__ERR_NUMBER_MAX " max
224 print "#define su__ERR_NUMBER_ENUM_C \134"
226 print verb "su_ERR_NONE = 0,\134"
229 # Since our final table is searched with binary sort,
230 # place the non-available backward counting once last
232 for(i = 1; i <= oscnt; ++i){
234 map[osnaa[i]] = osnoa[i]
235 print verb "su_ERR_" osnaa[i] " = " osnoa[i] ",\134"
239 the_unavail[unavail] = "su_ERR_" osnaa[i] " = " \
240 "(su__ERR_NUMBER_MAX - " unavail ")"
243 for(i = unavail; i >= 1; --i){
244 print verb the_unavail[i] ",\134"
248 print verb "su_ERR_NOTOBACCO = su__ERR_NUMBER_MAX,\134"
251 print verb "su__ERR_NUMBER = " cnt
253 # The C++ mapping table
255 print "#ifdef __cplusplus"
256 print "# define su__CXX_ERR_NUMBER_ENUM \134"
257 print verb "enone = su_ERR_NONE,\134"
260 for(i = 1; i <= oscnt; ++i){
262 print verb "e" tolower(osnaa[i]) " = su_ERR_" osnaa[i] ",\134"
265 the_unavail[unavail] = "e" tolower(osnaa[i]) " = su_ERR_" \
269 for(i = unavail; i >= 1; --i){
270 print verb the_unavail[i] ",\134"
273 print verb "enotobacco = su_ERR_NOTOBACCO,\134"
274 print verb "e__number = su__ERR_NUMBER"
276 print "#endif /* __cplusplus */"
278 # And our OS errno -> name map offset table
279 # (This "OS" however includes the unavail ones)
282 for(mapoff = 0;; ++mapoff){
283 voff = match(input, /[0-9a-zA-Z_]+(='${j}'[^'${j}']+)?/)
287 v = substr(input, voff, RLENGTH)
288 input = substr(input, voff + RLENGTH)
291 d = substr(v, doff + 2, length(v) - doff - 1)
292 v = substr(v, 1, doff - 1)
300 print "#define su__ERR_NUMBER_VOIDOFF " voidoff
301 print "#define su__ERR_NUMBER_TO_MAPOFF \134"
303 print verb "a_X(0, 0) \134"
308 for(i = 1; i <= oscnt; ++i){
310 the_unavail[++unavail] = i
313 if(mapdups[osnoa[i]])
315 mapdups[osnoa[i]] = 1
316 print verb "a_X(" osnoa[i] ", " mapo[osnaa[i]] ") \134"
320 for(i = unavail; i >= 1; --i){
321 print verb "a_X(" "su__ERR_NUMBER_MAX - " i ", " \
322 mapo[osnaa[the_unavail[i]]] ")\134"
326 print verb "a_X(su__ERR_NUMBER_MAX, su__ERR_NUMBER_VOIDOFF) \134"
328 print verb "/* " uniq " unique members */"
336 if [ ${#} -ne 0 ]; then
337 if [ "${1}" = noverbose
]; then
344 if [ ${#} -eq 1 ]; then
345 [ "${1}" = compile_time
] && compile_time
346 elif [ ${#} -eq 0 ]; then
347 # Now start perl(1) without PERL5OPT set to avoid multibyte sequence errors
348 PERL5OPT
= PERL5LIB
= exec perl
-x "${0}"
350 echo >&2 'Invalid usage'
354 # Thanks to perl(5) and it's -x / #! perl / __END__ mechanism!
355 # Why can env(1) not be used for such easy things in #!?
358 #use diagnostics -verbose;
365 use sigtrap qw
(handler cleanup normal-signals
);
367 my
($S, @ENTS
, $CTOOL, $CTOOL_EXE) = ($ENV{VERB
} ?
' ' : '');
376 dump_map
(); # Starts output file
378 reverser
(); # Ends output file
385 die
"$CTOOL_EXE: couldn't unlink: $^E"
386 if $CTOOL_EXE && -f $CTOOL_EXE && 1 != unlink
$CTOOL_EXE;
387 die
"$CTOOL: couldn't unlink: $^E"
388 if $CTOOL && -f $CTOOL && 1 != unlink
$CTOOL;
389 die
"Terminating due to signal $_[0]" if $_[0]
394 $n =~ s
/^
(.
*\
/)?
([^\
/]+)$
/$2/;
399 my
$input = $ENV{ERRORS
};
400 while($input =~
/([[:alnum
:]_
]+)='([^']+)'(.*)/){
410 $CTOOL = '.
/tmp-errors-tool-
' . $$ . '.c
';
411 $CTOOL_EXE = $CTOOL . '.exe
';
413 die "$CTOOL: open: $^E" unless open F, '>', $CTOOL;
414 print F '#define MAX_DISTANCE_PENALTY ', $ENV{MAXDISTANCE_PENALTY}, "\n";
415 # >>>>>>>>>>>>>>>>>>>
417 #define __CREATE_ERRORS_SH
424 #define su_NELEM(A) (sizeof(A) / sizeof((A)[0]))
432 u32 cem_hash; /* Hash of name */
433 u32 cem_nameoff; /* Into a_corerr_names[] */
434 u32 cem_docoff; /* Into a_corerr_docs[] */
435 s32 cem_errno; /* OS errno value for this one */
440 print F
'#include "', $ENV{XOUT
}, "\"\n\n";
443 static u8 seen_wraparound;
444 static size_t longest_distance;
447 next_prime(size_t no){ /* blush (brute force) */
450 for(size_t i = 3; i < no; i += 2)
457 reversy(size_t size){
458 struct a_corerr_map const *cemp = a_corerr_map,
459 *cemaxp = cemp + su_NELEM(a_corerr_map);
460 size_t ldist = 0, *arr;
462 arr = (size_t*)malloc(sizeof *arr * size);
463 for(size_t i = 0; i < size; ++i)
464 arr[i] = su_NELEM(a_corerr_map);
467 longest_distance = 0;
469 while(cemp < cemaxp){
470 u32 hash = cemp->cem_hash, i = hash % size, l;
472 for(l = 0; arr[i] != su_NELEM(a_corerr_map); ++l)
477 if(l > longest_distance)
478 longest_distance = l;
479 arr[i] = (size_t)(cemp++ - a_corerr_map);
485 main(int argc, char **argv){
486 size_t *arr, size = su_NELEM(a_corerr_map);
488 fprintf(stderr, "Starting reversy, okeys=%zu\n", size);
490 arr = reversy(size = next_prime(size));
491 fprintf(stderr, " - size=%zu longest_distance=%zu seen_wraparound=%d\n",
492 size, longest_distance, seen_wraparound);
493 if(longest_distance <= MAX_DISTANCE_PENALTY)
499 "#ifdef su_SOURCE /* Lock-out compile-time-tools */\n"
500 "# define a_CORERR_REV_ILL %zuu\n"
501 "# define a_CORERR_REV_PRIME %zuu\n"
502 "# define a_CORERR_REV_LONGEST %zuu\n"
503 "# define a_CORERR_REV_WRAPAROUND %d\n"
504 "static %s const a_corerr_revmap[a_CORERR_REV_PRIME] = {\n%s",
505 su_NELEM(a_corerr_map), size, longest_distance, seen_wraparound,
506 argv[1], (argc > 2 ? " " : ""));
507 for(size_t i = 0; i < size; ++i)
508 printf("%s%zuu", (i == 0 ? ""
509 : (i % 10 == 0 ? (argc > 2 ? ",\n " : ",\n")
510 : (argc > 2 ? ", " : ","))),
512 printf("\n};\n#endif /* su_SOURCE */\n");
516 # <<<<<<<<<<<<<<<<<<<
521 die
"hash_em: open: $^E"
522 unless my
$pid = open2
*RFD
, *WFD
, $ENV{MAILX
};
523 foreach my
$e (@ENTS
){
524 print WFD
"csop hash32 $e->{name}\n";
536 die
"$ENV{XOUT}: open: $^E" unless open F
, '>', $ENV{XOUT
};
537 print F
'/*@ ', scalar basen
($ENV{XOUT
}), ', generated by ',
538 scalar basen
($0), ".\n *@ See core-errors.c for more */\n\n";
540 ($i, $alen) = (0, 0);
541 print F
'#ifdef su_SOURCE', "\n",
542 'static char const a_corerr_names[] = {', "\n";
543 ($i, $alen) = (0, 0);
544 foreach my
$e (@ENTS
){
545 $e->{nameoff
} = $alen;
548 my
$a = join '\',\'', split(//, $k);
550 print F
"${S}/* $i. [$alen]+$l $k */\n" if $ENV{VERB
};
551 print F
"${S}'$a','\\0',\n";
555 print F
'};', "\n\n";
557 print F
'# ifdef su_HAVE_DOCSTRINGS', "\n";
558 print F
'# undef a_X', "\n", '# define a_X(X)', "\n";
559 print F
'static char const a_corerr_docs[] = {', "\n";
560 ($i, $alen) = (0, 0);
561 foreach my
$e (@ENTS
){
562 $e->{docoff
} = $alen;
565 my
$a = join '\',\'', split(//, $k);
567 print F
"${S}/* $i. [$alen]+$l $e->{name} */ ",
568 "a_X(N_(\"$e->{doc}\"))\n" if $ENV{VERB
};
569 print F
"${S}'$a','\\0',\n";
573 print F
'};', "\n", '# undef a_X',
574 "\n# endif /* su_HAVE_DOCSTRINGS */\n\n";
578 # ifndef __CREATE_ERRORS_SH
583 static struct a_corerr_map const a_corerr_map[] = {
585 foreach my
$e (@ENTS
){
586 print F
"${S}{$e->{hash}u, $e->{nameoff}u, $e->{docoff}u, ",
587 "a_X(su_ERR_$e->{name})},\n"
589 print F
'};', "\n", '# undef a_X', "\n",
590 '#endif /* su_SOURCE */', "\n\n";
592 die
"$ENV{XOUT}: close: $^E" unless close F
596 my
$argv2 = $ENV{VERB
} ?
' verb' : '';
597 system
("\$CC -I. -o $CTOOL_EXE $CTOOL");
598 my
$t = (@ENTS
< 0xFF ?
'u8' : (@ENTS
< 0xFFFF ?
'u16' : 'u32'));
599 `$CTOOL_EXE $t$argv2 >> $ENV{XOUT}`
602 {package main
; main_fun
()}