Just in case of interest.. Herbert Xu wrote in <Zizl53et9J5Hg2D5@xxxxxxxxxxxxxxxxxxx>: |Henrik Lindström <henrik@xxxxxx> wrote: |> They were previously generated at buildtime by mksignames.c, but that |> approach had two flaws: |> 1. The signal names were generated for the host system rather than the |> target system, resulting in broken cross-compiled builds. |> 2. The SIGRTMIN and SIGRTMAX macros are usually implemented as |> function calls and can only be surely known at runtime. |> |> The new implementation has been tested to generate identical signal names |> as before on these systems: |> * Debian 12 (glibc, odd number of realtime signals) |> * Alpine 3.18 (musl, even number of realtime signals) |> * FreeBSD 14 |> |> Signed-off-by: Henrik Lindström <henrik@xxxxxx> | |Now that glibc has sigabbrev_np we should switch to using that |on Linux, perhaps with the existing code as a fallback. BSD |has always had ways of getting the signal name, though it may |not be very portable so we'd need different flavours if people |cared enough to add them. I can only offer a script of mine again which does this for me. It does more as my MUA can do things like "echo $^ERRDOC-42" -> "No message of desired type", "echo $^ERRNAME-42" -> "NOMSG", "echo $^ERR-NOMSG" -> "42", etc, but is a solid foundation base for whatever dash would need. It needs perl (tarball time) and $CC and $awk at compile time. It is portable (Linux, BSD, SunOS/Solaris [with right sh(1) and awk(1)]). What you want is likely a variation of TARGET=/tmp/errlist sh mk/su-make-signals.sh compile_time --steffen | |Der Kragenbaer, The moon bear, |der holt sich munter he cheerfully and one by one |einen nach dem anderen runter wa.ks himself off |(By Robert Gernhardt)
#!/bin/sh - #@ Either create src/su/gen-signals.h, or, at compile time, the OS<>SU map. # # Public Domain IN="${SRCDIR}"su/gen-signals.h XOUT=src/su/gen-signals.h # We use `csop' for hashing MAILX='LC_ALL=C s-nail -#:/' # Acceptable "longest distance" from hash-modulo-index to key MAXDISTANCE_PENALTY=6 # Generate a more verbose output. Not for shipout versions. VERB=1 ## LC_ALL=C export LC_ALL MAXDISTANCE_PENALTY VERB MAILX IN XOUT : ${awk:=awk} # Compile-time only : ${rm:=rm} : ${sort:=sort} # The set of signals we support {{{ SIGNALS=" NONE='No signal' \ ABRT='Abort signal' \ ALRM='Timer signal' \ BUS='Bus error (bad memory access)' \ CHLD='Child stopped or terminated' \ CLD='Child stopped or terminated' \ CONT='Continue process if stopped' \ EMT='Emulator trap' \ FPE='Floating-point exception' \ HUP='Hangup of controlling terminal, or death of controlling process' \ ILL='Illegal instruction' \ INFO='Power failure' \ INT='Interrupt from keyboard' \ IO='I/O now possible' \ IOT='IOT trap / abort signal' \ KILL='Kill signal' \ LOST='File lock lost' \ PIPE='Broken pipe: write to pipe with no readers' \ POLL='Pollable event' \ PROF='Profiling timer expired' \ PWR='Power failure' \ QUIT='Quit from keyboard' \ SEGV='Segementation violation: invalid memory reference' \ STKFLT='Stack fault on coprocessor' \ STOP='Stop process' \ TSTP='Stop from terminal' \ SYS='Bad system call' \ TERM='Termination signal' \ TRAP='Trace or breakpoint trap' \ TTIN='Terminal input for background process' \ TTOU='Terminal output from background process' \ UNUSED='Bad system call' \ URG='Urgent condition on socket' \ USR1='User-defined signal 1' \ USR2='User-defined signal 2' \ VTALRM='Virtual alarm clock' \ XCPU='CPU time limit exceeded' \ XFSZ='File size limit exceeded' \ WINCH='Window resize signal' \ " export SIGNALS # }}} signal_parse() { j=\' ${awk} -v dodoc="${1}" -v incnone="${2}" -v input="${SIGNALS}" ' BEGIN{ for(i = 0;;){ voff = match(input, /[0-9a-zA-Z_]+(='${j}'[^'${j}']+)?/) if(voff == 0) break v = substr(input, voff, RLENGTH) input = substr(input, voff + RLENGTH) doff = index(v, "=") if(doff > 0){ d = substr(v, doff + 2, length(v) - doff - 1) v = substr(v, 1, doff - 1) } if(!incnone && v == "NONE") continue print dodoc ? d : v } } ' } compile_time() { # {{{ [ -n "${TARGET}" ] || { echo >&2 'Invalid usage' exit 1 } set -e pipefail= ( set -o pipefail ) >/dev/null 2>&1 && pipefail=y [ -n "${pipefail}" ] && set -o pipefail { printf '#include <signal.h>\nsu_SIGNAL_START\n' for n in $(signal_parse 0 0); do printf '#ifdef SIG%s\nSIG%s "%s"\n#else\n-1 "%s"\n#endif\n' $n $n $n $n done } > "${TARGET}".c # The problem is that at least (some versions of) gcc mangle output. # Ensure we get both arguments on one line. # While here sort numerically. ${CC} -E "${TARGET}".c | ${awk} ' function stripsym(sym){ sym = substr(sym, 2) sym = substr(sym, 1, length(sym) - 1) return sym } BEGIN{hot=0; conti=0} /^[ ]*$/{next} /^[ ]*#/{next} /^su_SIGNAL_START$/{hot=1; next} { if(!hot) next i = conti ? stripsym($1) "\n" : $1 " " printf i if(conti) conti = 0 else if($2 != "") printf "%s\n", stripsym($2) else conti = 1 } ' | ${sort} -n > "${TARGET}".txt # EBCDIC/ASCII: we use \134 for reverse solidus \ j=\' ${awk} -v verb="${VERB}" -v input="${SIGNALS}" -v dat="${TARGET}.txt" ' BEGIN{ verb = (verb != 0) ? " " : "" # Read in our OS data unavail = 0 max = 0 oscnt = 0 while((getline dl < dat) > 0){ split(dl, ia) ++oscnt osnoa[oscnt] = ia[1] osnaa[oscnt] = ia[2] if(ia[1] < 0) ++unavail else{ if(ia[1] > max) max = ia[1] } } close(dat) # Maximum signal number defines the datatype to use. # we warp all non-available errors to numbers too high # to be regular errors, counting backwards i = max + unavail + 1 if(i >= 65535){ t = "u32" max = "0xFFFFFFFFu" }else if(i >= 255){ t = "u16" max = "0xFFFFu" }else{ t = "u8" max = "0xFFu" } print "#define su__SIG_NUMBER_TYPE " t print "#define su__SIG_NUMBER_MAX " max # Dump C table cnt = 0 print "#define su__SIG_NUMBER_ENUM_C \134" print verb "su_SIG_NONE = 0,\134" ++cnt # Since our final table is searched with binary sort, # place the non-available backward counting once last unavail = j = k = 0 for(i = 1; i <= oscnt; ++i){ if(osnoa[i] >= 0){ map[osnaa[i]] = osnoa[i] print verb "su_SIG_" osnaa[i] " = " osnoa[i] ",\134" ++cnt }else{ ++unavail the_unavail[unavail] = "su_SIG_" osnaa[i] " = " "(su__SIG_NUMBER_MAX - " unavail ")" } } for(i = unavail; i >= 1; --i){ print verb the_unavail[i] ",\134" ++cnt } print verb "su__SIG_NUMBER = " cnt # The C++ mapping table print "#ifdef __cplusplus" print "# define su__CXX_SIG_NUMBER_ENUM \134" print verb "none = su_SIG_NONE,\134" unavail = 0 for(i = 1; i <= oscnt; ++i){ cxxn = tolower(osnaa[i]) if(cxxn !~ "^[_[:alpha:]]") cxxn = "s" cxxn if(osnoa[i] >= 0) print verb cxxn " = su_SIG_" osnaa[i] ",\134" else{ ++unavail the_unavail[unavail] = cxxn " = su_SIG_" osnaa[i] } } for(i = unavail; i >= 1; --i){ print verb the_unavail[i] ",\134" ++cnt } print verb "s__number = su__SIG_NUMBER" print "#endif /* __cplusplus */" # And our OS signal -> name map offset table # (This "OS" however includes the unavail ones) voidoff = 0 for(mapoff = 0;; ++mapoff){ voff = match(input, /[0-9a-zA-Z_]+(='${j}'[^'${j}']+)?/) if(voff == 0) break v = substr(input, voff, RLENGTH) input = substr(input, voff + RLENGTH) doff = index(v, "=") if(doff > 0){ d = substr(v, doff + 2, length(v) - doff - 1) v = substr(v, 1, doff - 1) } mapo[v] = mapoff if(v == "UNUSED") voidoff = mapoff } uniq = 0 print "#define su__SIG_NUMBER_VOIDOFF " voidoff print "#define su__SIG_NUMBER_TO_MAPOFF \134" print verb "a_X(0, 0) \134" ++uniq unavail = 0 mapdups[0] = 1 for(i = 1; i <= oscnt; ++i){ if(osnoa[i] < 0){ the_unavail[++unavail] = i continue } if(mapdups[osnoa[i]]) continue mapdups[osnoa[i]] = 1 print verb "a_X(" osnoa[i] ", " mapo[osnaa[i]] ") \134" ++uniq } for(i = unavail; i >= 1; --i){ print verb "a_X(" "su__SIG_NUMBER_MAX - " i ", " mapo[osnaa[the_unavail[i]]] ")\134" ++uniq } print verb "a_X(su__SIG_NUMBER_MAX, su__SIG_NUMBER_VOIDOFF) \134" ++uniq print verb "/* " uniq " unique members */" } ' >> "${TARGET}" ${rm} "${TARGET}".* exit 0 } # }}} if [ ${#} -ne 0 ]; then if [ "${1}" = noverbose ]; then shift VERB=0 export VERB fi fi if [ ${#} -eq 1 ]; then [ "${1}" = compile_time ] && compile_time elif [ ${#} -eq 0 ]; then # Now start perl(1) without PERL5OPT set to avoid multibyte sequence errors PERL5OPT= PERL5LIB= exec perl -x "${0}" fi echo >&2 'Invalid usage' exit 1 #' PERL {{{ # Thanks to perl(5) and its -x / #! perl / __END__ mechanism! # Why can env(1) not be used for such easy things in #!? #!perl use strict; use warnings; use FileHandle; use IPC::Open2; use sigtrap qw(handler cleanup normal-signals); my ($S, @ENTS, $CTOOL, $CTOOL_EXE) = ($ENV{VERB} ? ' ' : ''); sub main_fun{ create_ents(); create_c_tool(); hash_em(); dump_map(); # Starts output file reverser(); # Ends output file cleanup(undef); exit 0 } sub cleanup{ die "$CTOOL_EXE: couldn't unlink: $^E" if $CTOOL_EXE && -f $CTOOL_EXE && 1 != unlink $CTOOL_EXE; die "$CTOOL: couldn't unlink: $^E" if $CTOOL && -f $CTOOL && 1 != unlink $CTOOL; die "Terminating due to signal $_[0]" if $_[0] } sub basen{ my $n = $_[0]; $n =~ s/^(.*\/)?([^\/]+)$/$2/; $n } sub create_ents{ my $input = $ENV{SIGNALS}; while($input =~ /([[:alnum:]_]+)='([^']+)'(.*)/){ $input = $3; my %vals; $vals{name} = $1; $vals{doc} = $2; push @ENTS, \%vals } } sub create_c_tool{ $CTOOL = './tmp-signals-tool-' . $$ . '.c'; $CTOOL_EXE = $CTOOL . '.exe'; die "$CTOOL: open: $^E" unless open F, '>', $CTOOL; print F '#define MAX_DISTANCE_PENALTY ', $ENV{MAXDISTANCE_PENALTY}, "\n"; # >>>>>>>>>>>>>>>>>>> print F <<'_EOT'; #define __CREATE_SIGNALS_SH #define su_SOURCE #include <stdint.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #define su_NELEM(A) (sizeof(A) / sizeof((A)[0])) #define u32 uint32_t #define s32 int32_t #define u16 uint16_t #define u8 uint8_t struct a_corsig_map{ u32 csm_hash; /* Hash of name */ u32 csm_nameoff; /* Into a_corsig_names[] */ u32 csm_docoff; /* Into a_corsig_docs[] */ s32 csm_errno; /* OS signal value for this one */ }; _EOT print F '#include "', $ENV{XOUT}, "\"\n\n"; print F <<'_EOT'; static u8 seen_wraparound; static size_t longest_distance; static size_t next_prime(size_t no){ /* blush (brute force) */ jredo: ++no; for(size_t i = 3; i < no; i += 2) if(no % i == 0) goto jredo; return no; } static size_t * reversy(size_t size){ struct a_corsig_map const *csmp = a_corsig_map, *csmaxp = csmp + su_NELEM(a_corsig_map); size_t ldist = 0, *arr; arr = (size_t*)malloc(sizeof *arr * size); for(size_t i = 0; i < size; ++i) arr[i] = su_NELEM(a_corsig_map); seen_wraparound = 0; longest_distance = 0; while(csmp < csmaxp){ u32 hash = csmp->csm_hash, i = hash % size, l; for(l = 0; arr[i] != su_NELEM(a_corsig_map); ++l) if(++i == size){ seen_wraparound = 1; i = 0; } if(l > longest_distance) longest_distance = l; arr[i] = (size_t)(csmp++ - a_corsig_map); } return arr; } int main(int argc, char **argv){ size_t *arr, size = su_NELEM(a_corsig_map); fprintf(stderr, "Starting reversy, okeys=%zu\n", size); for(;;){ arr = reversy(size = next_prime(size)); fprintf(stderr, " - size=%zu longest_distance=%zu seen_wraparound=%d\n", size, longest_distance, seen_wraparound); if(longest_distance <= MAX_DISTANCE_PENALTY) break; free(arr); } printf( "#ifdef su_SOURCE /* Lock-out compile-time-tools */\n" "# define a_CORSIG_REV_ILL %zuu\n" "# define a_CORSIG_REV_PRIME %zuu\n" "# define a_CORSIG_REV_LONGEST %zuu\n" "# define a_CORSIG_REV_WRAPAROUND %d\n" "static %s const a_corsig_revmap[a_CORSIG_REV_PRIME] = {\n%s", su_NELEM(a_corsig_map), size, longest_distance, seen_wraparound, argv[1], (argc > 2 ? " " : "")); for(size_t i = 0; i < size; ++i) printf("%s%zuu", (i == 0 ? "" : (i % 10 == 0 ? (argc > 2 ? ",\n " : ",\n") : (argc > 2 ? ", " : ","))), arr[i]); printf("\n};\n#endif /* su_SOURCE */\n"); return 0; } _EOT # <<<<<<<<<<<<<<<<<<< close F } sub hash_em{ die "hash_em: open: $^E" unless my $pid = open2 *RFD, *WFD, $ENV{MAILX}; foreach my $e (@ENTS){ print WFD "csop hash32?case $e->{name}\n"; my $h = <RFD>; chomp $h; $e->{hash} = $h } print WFD "x\n"; waitpid $pid, 0; } sub dump_map{ my ($i, $alen); die "$ENV{XOUT}: open: $^E" unless open F, '>', $ENV{XOUT}; print F '/*@ ', scalar basen($ENV{XOUT}), ', generated by ', scalar basen($0), ".\n *@ See core-signals.c for more */\n\n"; ($i, $alen) = (0, 0); print F '#ifdef su_SOURCE', "\n", 'static char const a_corsig_names[] = {', "\n"; ($i, $alen) = (0, 0); foreach my $e (@ENTS){ $e->{nameoff} = $alen; my $k = $e->{name}; my $l = length $k; my $a = join '\',\'', split(//, $k); my (@fa); print F "${S}/* $i. [$alen]+$l $k */\n" if $ENV{VERB}; print F "${S}'$a','\\0',\n"; ++$i; $alen += $l + 1 } print F '};', "\n\n"; print F '# undef a_X', "\n", '# define a_X(X)', "\n"; print F 'static char const a_corsig_docs[] = {', "\n"; ($i, $alen) = (0, 0); foreach my $e (@ENTS){ $e->{docoff} = $alen; my $k = $e->{doc}; my $l = length $k; my $a = join '\',\'', split(//, $k); my (@fa); print F "${S}/* $i. [$alen]+$l $e->{name} */ ", "a_X(N_(\"$e->{doc}\"))\n" if $ENV{VERB}; print F "${S}'$a','\\0',\n"; ++$i; $alen += $l + 1 } print F '};', "\n# undef a_X\n\n"; print F <<_EOT; # undef a_X # ifndef __CREATE_SIGNALS_SH # define a_X(X) X # else # define a_X(X) 0 # endif static struct a_corsig_map const a_corsig_map[] = { _EOT foreach my $e (@ENTS){ print F "${S}{$e->{hash}u, $e->{nameoff}u, $e->{docoff}u, a_X(su_SIG_$e->{name})},\n" } print F '};', "\n", '# undef a_X', "\n", '#endif /* su_SOURCE */', "\n\n"; die "$ENV{XOUT}: close: $^E" unless close F } sub reverser{ my $argv2 = $ENV{VERB} ? ' verb' : ''; system("\$CC -I. -o $CTOOL_EXE $CTOOL"); my $t = (@ENTS < 0xFF ? 'u8' : (@ENTS < 0xFFFF ? 'u16' : 'u32')); `$CTOOL_EXE $t$argv2 >> $ENV{XOUT}` } {package main; main_fun()} # }}} # s-itt-mode