MLE:a_tty_readline(): FIX possible buffer-overflow with HAVE_KEY_BINDINGS..
[s-mailx.git] / mk / su-make-errors.sh
bloba62af31bb4549f1d87ec2f905bfe7b9ea38fcf9c
1 #!/bin/sh -
2 #@ Either create src/su/gen-errors.h, or, at compile time, the OS<>SU map.
4 # Public Domain
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
13 MAXDISTANCE_PENALTY=5
15 # Generate a more verbose output. Not for shipout versions.
16 VERB=1
20 LC_ALL=C
21 export LC_ALL MAXDISTANCE_PENALTY VERB MAILX IN XOUT
23 : ${awk:=awk}
24 # Compile-time only
25 : ${rm:=rm}
26 : ${sort:=sort}
28 # The set of errors we support {{{
29 ERRORS="\
30 NONE='No error' \
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' \
40 BUSY='Device busy' \
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' \
50 EXIST='File exists' \
51 FAULT='Bad address' \
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' \
97 PIPE='Broken pipe' \
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' \
112 export ERRORS
113 # }}}
115 error_parse() {
116 j=\'
117 ${awk} -v dodoc="${1}" -v incnone="${2}" -v input="${ERRORS}" '
118 BEGIN{
119 for(i = 0;;){
120 voff = match(input, /[0-9a-zA-Z_]+(='${j}'[^'${j}']+)?/)
121 if(voff == 0)
122 break
123 v = substr(input, voff, RLENGTH)
124 input = substr(input, voff + RLENGTH)
125 doff = index(v, "=")
126 if(doff > 0){
127 d = substr(v, doff + 2, length(v) - doff - 1)
128 v = substr(v, 1, doff - 1)
130 if(!incnone && (v == "NONE" || v == "NOTOBACCO"))
131 continue
132 print dodoc ? d : v
138 compile_time() { # {{{
139 [ -n "${TARGET}" ] || {
140 echo >&2 'Invalid usage'
141 exit 1
143 set -e
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
149 done
150 } > "${TARGET}".c
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 |
156 ${awk} '
157 function stripsym(sym){
158 sym = substr(sym, 2)
159 sym = substr(sym, 1, length(sym) - 1)
160 return sym
162 BEGIN{hot=0; conti=0}
163 /^[ ]*$/{next}
164 /^[ ]*#/{next}
165 /^su_ERROR_START$/{hot=1; next}
167 if(!hot)
168 next
169 i = conti ? stripsym($1) "\n" : $1 " "
170 printf i
171 if(conti)
172 conti = 0
173 else if($2 != "")
174 printf "%s\n", stripsym($2)
175 else
176 conti = 1
179 ${sort} -n > "${TARGET}".txt
181 # EBCDIC/ASCII: we use \134 for reverse solidus \
182 j=\'
183 ${awk} -v verb="${VERB}" -v input="${ERRORS}" -v dat="${TARGET}.txt" '
184 BEGIN{
185 verb = (verb != 0) ? " " : ""
187 # Read in our OS data
189 unavail = 0
190 max = 0
191 oscnt = 0
192 while((getline dl < dat) > 0){
193 split(dl, ia)
195 ++oscnt
196 osnoa[oscnt] = ia[1]
197 osnaa[oscnt] = ia[2]
199 if(ia[1] < 0)
200 ++unavail
201 else{
202 if(ia[1] > max)
203 max = ia[1]
206 close(dat)
208 # Maximum error number defines the datatype to use.
209 # We need a value for NOTOBACCO, we warp all non-available errors to
210 # numbers too high to be regular errors, counting backwards
212 i = max + unavail + 1
213 if(i >= 65535){
214 t = "u32"
215 max = "0xFFFFFFFFu"
216 }else if(i >= 255){
217 t = "u16"
218 max = "0xFFFFu"
219 }else{
220 t = "u8"
221 max = "0xFFu"
223 print "#define su__ERR_NUMBER_TYPE " t
224 print "#define su__ERR_NUMBER_MAX " max
226 # Dump C table
228 cnt = 0
229 print "#define su__ERR_NUMBER_ENUM_C \134"
231 print verb "su_ERR_NONE = 0,\134"
232 ++cnt
234 # Since our final table is searched with binary sort,
235 # place the non-available backward counting once last
236 unavail = j = k = 0
237 for(i = 1; i <= oscnt; ++i){
238 if(osnoa[i] >= 0){
239 map[osnaa[i]] = osnoa[i]
240 print verb "su_ERR_" osnaa[i] " = " osnoa[i] ",\134"
241 ++cnt
242 }else{
243 ++unavail
244 the_unavail[unavail] = "su_ERR_" osnaa[i] " = " \
245 "(su__ERR_NUMBER_MAX - " unavail ")"
248 for(i = unavail; i >= 1; --i){
249 print verb the_unavail[i] ",\134"
250 ++cnt
253 print verb "su_ERR_NOTOBACCO = su__ERR_NUMBER_MAX,\134"
254 ++cnt
256 print verb "su__ERR_NUMBER = " cnt
258 # The C++ mapping table
260 print "#ifdef __cplusplus"
261 print "# define su__CXX_ERR_NUMBER_ENUM \134"
262 print verb "enone = su_ERR_NONE,\134"
264 unavail = 0
265 for(i = 1; i <= oscnt; ++i){
266 if(osnoa[i] >= 0)
267 print verb "e" tolower(osnaa[i]) " = su_ERR_" osnaa[i] ",\134"
268 else{
269 ++unavail
270 the_unavail[unavail] = "e" tolower(osnaa[i]) " = su_ERR_" \
271 osnaa[i]
274 for(i = unavail; i >= 1; --i){
275 print verb the_unavail[i] ",\134"
276 ++cnt
278 print verb "enotobacco = su_ERR_NOTOBACCO,\134"
279 print verb "e__number = su__ERR_NUMBER"
281 print "#endif /* __cplusplus */"
283 # And our OS errno -> name map offset table
284 # (This "OS" however includes the unavail ones)
286 voidoff = 0
287 for(mapoff = 0;; ++mapoff){
288 voff = match(input, /[0-9a-zA-Z_]+(='${j}'[^'${j}']+)?/)
289 if(voff == 0)
290 break
292 v = substr(input, voff, RLENGTH)
293 input = substr(input, voff + RLENGTH)
294 doff = index(v, "=")
295 if(doff > 0){
296 d = substr(v, doff + 2, length(v) - doff - 1)
297 v = substr(v, 1, doff - 1)
299 mapo[v] = mapoff
300 if(v == "NOTOBACCO")
301 voidoff = mapoff
304 uniq = 0
305 print "#define su__ERR_NUMBER_VOIDOFF " voidoff
306 print "#define su__ERR_NUMBER_TO_MAPOFF \134"
308 print verb "a_X(0, 0) \134"
309 ++uniq
311 unavail = 0
312 mapdups[0] = 1
313 for(i = 1; i <= oscnt; ++i){
314 if(osnoa[i] < 0){
315 the_unavail[++unavail] = i
316 continue
318 if(mapdups[osnoa[i]])
319 continue
320 mapdups[osnoa[i]] = 1
321 print verb "a_X(" osnoa[i] ", " mapo[osnaa[i]] ") \134"
322 ++uniq
325 for(i = unavail; i >= 1; --i){
326 print verb "a_X(" "su__ERR_NUMBER_MAX - " i ", " \
327 mapo[osnaa[the_unavail[i]]] ")\134"
328 ++uniq
331 print verb "a_X(su__ERR_NUMBER_MAX, su__ERR_NUMBER_VOIDOFF) \134"
332 ++uniq
333 print verb "/* " uniq " unique members */"
335 ' >> "${TARGET}"
337 ${rm} "${TARGET}".*
338 exit 0
339 } # }}}
341 if [ ${#} -ne 0 ]; then
342 if [ "${1}" = noverbose ]; then
343 shift
344 VERB=0
345 export VERB
349 if [ ${#} -eq 1 ]; then
350 [ "${1}" = compile_time ] && compile_time
351 elif [ ${#} -eq 0 ]; then
352 # Now start perl(1) without PERL5OPT set to avoid multibyte sequence errors
353 PERL5OPT= PERL5LIB= exec perl -x "${0}"
355 echo >&2 'Invalid usage'
356 exit 1
358 # PERL {{{
359 # Thanks to perl(5) and it's -x / #! perl / __END__ mechanism!
360 # Why can env(1) not be used for such easy things in #!?
361 #!perl
363 #use diagnostics -verbose;
364 use strict;
365 use warnings;
367 use FileHandle;
368 use IPC::Open2;
370 use sigtrap qw(handler cleanup normal-signals);
372 my ($S, @ENTS, $CTOOL, $CTOOL_EXE) = ($ENV{VERB} ? ' ' : '');
374 sub main_fun{
375 create_ents();
377 create_c_tool();
379 hash_em();
381 dump_map(); # Starts output file
383 reverser(); # Ends output file
385 cleanup(undef);
386 exit 0
389 sub cleanup{
390 die "$CTOOL_EXE: couldn't unlink: $^E"
391 if $CTOOL_EXE && -f $CTOOL_EXE && 1 != unlink $CTOOL_EXE;
392 die "$CTOOL: couldn't unlink: $^E"
393 if $CTOOL && -f $CTOOL && 1 != unlink $CTOOL;
394 die "Terminating due to signal $_[0]" if $_[0]
397 sub basen{
398 my $n = $_[0];
399 $n =~ s/^(.*\/)?([^\/]+)$/$2/;
403 sub create_ents{
404 my $input = $ENV{ERRORS};
405 while($input =~ /([[:alnum:]_]+)='([^']+)'(.*)/){
406 $input = $3;
407 my %vals;
408 $vals{name} = $1;
409 $vals{doc} = $2;
410 push @ENTS, \%vals
414 sub create_c_tool{
415 $CTOOL = './tmp-errors-tool-' . $$ . '.c';
416 $CTOOL_EXE = $CTOOL . '.exe';
418 die "$CTOOL: open: $^E" unless open F, '>', $CTOOL;
419 print F '#define MAX_DISTANCE_PENALTY ', $ENV{MAXDISTANCE_PENALTY}, "\n";
420 # >>>>>>>>>>>>>>>>>>>
421 print F <<'_EOT';
422 #define __CREATE_ERRORS_SH
423 #define su_SOURCE
424 #include <stdint.h>
425 #include <stdlib.h>
426 #include <stdio.h>
427 #include <string.h>
429 #define su_NELEM(A) (sizeof(A) / sizeof((A)[0]))
431 #define u32 uint32_t
432 #define s32 int32_t
433 #define u16 uint16_t
434 #define u8 uint8_t
436 struct a_corerr_map{
437 u32 cem_hash; /* Hash of name */
438 u32 cem_nameoff; /* Into a_corerr_names[] */
439 u32 cem_docoff; /* Into a_corerr_docs[] */
440 s32 cem_errno; /* OS errno value for this one */
443 _EOT
445 print F '#include "', $ENV{XOUT}, "\"\n\n";
447 print F <<'_EOT';
448 static u8 seen_wraparound;
449 static size_t longest_distance;
451 static size_t
452 next_prime(size_t no){ /* blush (brute force) */
453 jredo:
454 ++no;
455 for(size_t i = 3; i < no; i += 2)
456 if(no % i == 0)
457 goto jredo;
458 return no;
461 static size_t *
462 reversy(size_t size){
463 struct a_corerr_map const *cemp = a_corerr_map,
464 *cemaxp = cemp + su_NELEM(a_corerr_map);
465 size_t ldist = 0, *arr;
467 arr = (size_t*)malloc(sizeof *arr * size);
468 for(size_t i = 0; i < size; ++i)
469 arr[i] = su_NELEM(a_corerr_map);
471 seen_wraparound = 0;
472 longest_distance = 0;
474 while(cemp < cemaxp){
475 u32 hash = cemp->cem_hash, i = hash % size, l;
477 for(l = 0; arr[i] != su_NELEM(a_corerr_map); ++l)
478 if(++i == size){
479 seen_wraparound = 1;
480 i = 0;
482 if(l > longest_distance)
483 longest_distance = l;
484 arr[i] = (size_t)(cemp++ - a_corerr_map);
486 return arr;
490 main(int argc, char **argv){
491 size_t *arr, size = su_NELEM(a_corerr_map);
493 fprintf(stderr, "Starting reversy, okeys=%zu\n", size);
494 for(;;){
495 arr = reversy(size = next_prime(size));
496 fprintf(stderr, " - size=%zu longest_distance=%zu seen_wraparound=%d\n",
497 size, longest_distance, seen_wraparound);
498 if(longest_distance <= MAX_DISTANCE_PENALTY)
499 break;
500 free(arr);
503 printf(
504 "#ifdef su_SOURCE /* Lock-out compile-time-tools */\n"
505 "# define a_CORERR_REV_ILL %zuu\n"
506 "# define a_CORERR_REV_PRIME %zuu\n"
507 "# define a_CORERR_REV_LONGEST %zuu\n"
508 "# define a_CORERR_REV_WRAPAROUND %d\n"
509 "static %s const a_corerr_revmap[a_CORERR_REV_PRIME] = {\n%s",
510 su_NELEM(a_corerr_map), size, longest_distance, seen_wraparound,
511 argv[1], (argc > 2 ? " " : ""));
512 for(size_t i = 0; i < size; ++i)
513 printf("%s%zuu", (i == 0 ? ""
514 : (i % 10 == 0 ? (argc > 2 ? ",\n " : ",\n")
515 : (argc > 2 ? ", " : ","))),
516 arr[i]);
517 printf("\n};\n#endif /* su_SOURCE */\n");
518 return 0;
520 _EOT
521 # <<<<<<<<<<<<<<<<<<<
522 close F
525 sub hash_em{
526 die "hash_em: open: $^E"
527 unless my $pid = open2 *RFD, *WFD, $ENV{MAILX};
528 foreach my $e (@ENTS){
529 print WFD "csop hash32 $e->{name}\n";
530 my $h = <RFD>;
531 chomp $h;
532 $e->{hash} = $h
534 print WFD "x\n";
535 waitpid $pid, 0;
538 sub dump_map{
539 my ($i, $alen);
541 die "$ENV{XOUT}: open: $^E" unless open F, '>', $ENV{XOUT};
542 print F '/*@ ', scalar basen($ENV{XOUT}), ', generated by ',
543 scalar basen($0), ".\n *@ See core-errors.c for more */\n\n";
545 ($i, $alen) = (0, 0);
546 print F '#ifdef su_SOURCE', "\n",
547 'static char const a_corerr_names[] = {', "\n";
548 ($i, $alen) = (0, 0);
549 foreach my $e (@ENTS){
550 $e->{nameoff} = $alen;
551 my $k = $e->{name};
552 my $l = length $k;
553 my $a = join '\',\'', split(//, $k);
554 my (@fa);
555 print F "${S}/* $i. [$alen]+$l $k */\n" if $ENV{VERB};
556 print F "${S}'$a','\\0',\n";
557 ++$i;
558 $alen += $l + 1
560 print F '};', "\n\n";
562 print F '# ifdef su_HAVE_DOCSTRINGS', "\n";
563 print F '# undef a_X', "\n", '# define a_X(X)', "\n";
564 print F 'static char const a_corerr_docs[] = {', "\n";
565 ($i, $alen) = (0, 0);
566 foreach my $e (@ENTS){
567 $e->{docoff} = $alen;
568 my $k = $e->{doc};
569 my $l = length $k;
570 my $a = join '\',\'', split(//, $k);
571 my (@fa);
572 print F "${S}/* $i. [$alen]+$l $e->{name} */ ",
573 "a_X(N_(\"$e->{doc}\"))\n" if $ENV{VERB};
574 print F "${S}'$a','\\0',\n";
575 ++$i;
576 $alen += $l + 1
578 print F '};', "\n", '# undef a_X',
579 "\n# endif /* su_HAVE_DOCSTRINGS */\n\n";
581 print F <<_EOT;
582 # undef a_X
583 # ifndef __CREATE_ERRORS_SH
584 # define a_X(X) X
585 # else
586 # define a_X(X) 0
587 # endif
588 static struct a_corerr_map const a_corerr_map[] = {
589 _EOT
590 foreach my $e (@ENTS){
591 print F "${S}{$e->{hash}u, $e->{nameoff}u, $e->{docoff}u, ",
592 "a_X(su_ERR_$e->{name})},\n"
594 print F '};', "\n", '# undef a_X', "\n",
595 '#endif /* su_SOURCE */', "\n\n";
597 die "$ENV{XOUT}: close: $^E" unless close F
600 sub reverser{
601 my $argv2 = $ENV{VERB} ? ' verb' : '';
602 system("\$CC -I. -o $CTOOL_EXE $CTOOL");
603 my $t = (@ENTS < 0xFF ? 'u8' : (@ENTS < 0xFFFF ? 'u16' : 'u32'));
604 `$CTOOL_EXE $t$argv2 >> $ENV{XOUT}`
607 {package main; main_fun()}
608 # }}}
610 # s-it-mode