4 #@ Parse 'enum okeys' from nail.h and create okeys.h. And see accmacvar.c.
7 # Acceptable "longest distance" from hash-modulo-index to key
8 my $MAXDISTANCE_PENALTY = 5;
10 # Generate a more verbose output. Not for shipout versions.
17 use diagnostics
-verbose
;
21 use sigtrap
qw(handler cleanup normal-signals);
23 my ($S, @ENTS, $CTOOL, $CTOOL_EXE) = ($VERB ?
' ' : '');
26 if(@ARGV) {$VERB = 0; $S = ''}
42 die "$CTOOL_EXE: couldn't unlink: $^E"
43 if $CTOOL_EXE && -f
$CTOOL_EXE && 1 != unlink $CTOOL_EXE;
44 die "$CTOOL: couldn't unlink: $^E"
45 if $CTOOL && -f
$CTOOL && 1 != unlink $CTOOL;
46 die "Terminating due to signal $_[0]" if $_[0]
50 die "nail.h: open: $^E" unless open F
, '<', 'nail.h';
53 # Only want the enum okeys content
54 if(/^enum okeys/) {$init = 1; next}
55 if(/^};/) {if($init) {$init = 2; last}; next}
58 # Ignore empty and comment lines
62 # An entry may have a comment with special directives
63 /^\s*(\w+),?\s*(?:\/\
*\s
*(?
:{(.*)})\s
*\
*\
/\s*)?$/;
65 my ($k, $x) = ($1, $2);
68 $vals{bool
} = ($k =~ /^ok_b/ ?
1 : 0);
69 $k = $1 if $k =~ /^ok_[bv]_(.+)$/;
73 while($x && $x =~ /^([^,]+?)(?:,(.*))?$/){
76 die "Unsupported special directive: $1"
78 $1 ne 'virt' && $1 ne 'nolopts' &&
79 $1 ne 'rdonly' && $1 ne 'nodel' && $1 ne 'notempty' &&
81 $1 ne 'num' && $1 ne 'posnum' && $1 ne 'lower' &&
82 $1 ne 'vip' && $1 ne 'import' && $1 ne 'env' &&
83 $1 ne 'i3val' && $1 ne 'defval');
89 if($init != 2) {die 'nail.h does not have the expected content'}
94 $CTOOL = './tmp-okey-tool-' . $$ . '.c';
95 $CTOOL_EXE = $CTOOL . '.exe';
97 die "$CTOOL: open: $^E" unless open F
, '>', $CTOOL;
98 # xxx optimize: could read lines and write lines in HASH_MODE..
99 print F
'#define MAX_DISTANCE_PENALTY ', $MAXDISTANCE_PENALTY, "\n";
100 # >>>>>>>>>>>>>>>>>>>
102 #define __CREATE_OKEY_MAP_PL
108 #define n_NELEM(A) (sizeof(A) / sizeof(A[0]))
110 #define ui32_t uint32_t
111 #define ui16_t uint16_t
112 #define ui8_t uint8_t
114 enum a_amv_var_flags
{
116 a_AMV_VF_BOOL
= 1<<0, /* ok_b_* */
117 a_AMV_VF_VIRT
= 1<<1, /* "Stateless" automatic variable */
118 a_AMV_VF_NOLOPTS
= 1<<2, /* May not be tracked by `localopts' */
119 a_AMV_VF_RDONLY
= 1<<3, /* May not be set by user */
120 a_AMV_VF_NODEL
= 1<<4, /* May not be deleted */
121 a_AMV_VF_NOTEMPTY
= 1<<5, /* May not be assigned an empty value */
122 a_AMV_VF_NOCNTRLS
= 1<<6, /* Value may not contain control characters */
123 a_AMV_VF_NUM
= 1<<7, /* Value must be a 32-bit number */
124 a_AMV_VF_POSNUM
= 1<<8, /* Value must be positive 32-bit number */
125 a_AMV_VF_LOWER
= 1<<9, /* Values will be stored in a lowercase version */
126 a_AMV_VF_VIP
= 1<<10, /* Wants _var_check_vips() evaluation */
127 a_AMV_VF_IMPORT
= 1<<11, /* Import ONLY from environ (pre n_PSO_STARTED) */
128 a_AMV_VF_ENV
= 1<<12, /* Update environment on change */
129 a_AMV_VF_I3VAL
= 1<<13, /* Has an initial value */
130 a_AMV_VF_DEFVAL
= 1<<14, /* Has a default value */
131 a_AMV_VF_LINKED
= 1<<15, /* `environ' linked */
132 a_AMV_VF__MASK
= (1<<(15+1)) - 1
135 struct a_amv_var_map
{
138 ui16_t avm_flags
; /* enum a_amv_var_flags */
142 /* NOTE: copied over verbatim from auxlily.c */
144 torek_hash
(char const
*name
){
145 /* Chris Torek
's hash.
146 * NOTE: need to change *at least* mk-okey-map.pl when changing the
150 while(*name != '\
0'){
158 /* Include what has been written in HASH_MODE */
162 static ui8_t seen_wraparound;
163 static size_t longest_distance;
166 next_prime(size_t no){ /* blush (brute force) */
169 for(size_t i = 3; i < no; i += 2)
176 reversy(size_t size){
177 struct a_amv_var_map const *vmp = a_amv_var_map,
178 *vmaxp = vmp + n_NELEM(a_amv_var_map);
179 size_t ldist = 0, *arr;
181 arr = malloc(sizeof *arr * size);
182 for(size_t i = 0; i < size; ++i)
183 arr[i] = n_NELEM(a_amv_var_map);
186 longest_distance = 0;
189 ui32_t hash = vmp->avm_hash, i = hash % size, l;
191 for(l = 0; arr[i] != n_NELEM(a_amv_var_map); ++l)
196 if(l > longest_distance)
197 longest_distance = l;
198 arr[i] = (size_t)(vmp++ - a_amv_var_map);
202 #endif /* !HASH_MODE */
205 main(int argc, char **argv){
207 size_t h = torek_hash(argv[1]);
209 printf("%lu\n", (unsigned long)h);
212 size_t *arr, size = n_NELEM(a_amv_var_map);
214 fprintf(stderr, "Starting reversy, okeys=%zu\n", size);
216 arr = reversy(size = next_prime(size));
217 fprintf(stderr, " - size=%zu longest_distance=%zu seen_wraparound=%d\n",
218 size, longest_distance, seen_wraparound);
219 if(longest_distance <= MAX_DISTANCE_PENALTY)
225 "#define a_AMV_VAR_REV_ILL %zuu\n"
226 "#define a_AMV_VAR_REV_PRIME %zuu\n"
227 "#define a_AMV_VAR_REV_LONGEST %zuu\n"
228 "#define a_AMV_VAR_REV_WRAPAROUND %d\n"
229 "static %s const a_amv_var_revmap[a_AMV_VAR_REV_PRIME] = {\n%s",
230 n_NELEM(a_amv_var_map), size, longest_distance, seen_wraparound,
231 argv[1], (argc > 2 ? " " : ""));
232 for(size_t i = 0; i < size; ++i)
233 printf("%s%zuu", (i == 0 ? ""
234 : (i % 10 == 0 ? (argc > 2 ? ",\n " : ",\n")
235 : (argc > 2 ? ", " : ","))),
242 # <<<<<<<<<<<<<<<<<<<
247 system("c99 -DHASH_MODE -I. -o $CTOOL_EXE $CTOOL");
249 foreach my $e (@ENTS){
250 my $h = `$CTOOL_EXE $e->{name}`;
257 die "$OUT: open: $^E" unless open F, '>', $OUT;
258 print F "/*@ $OUT, generated by $0 on ", scalar gmtime(), ".\n",
259 " *@ See accmacvar.c for more */\n\n";
261 print F 'static char const a_amv_var_names
[] = {', "\n";
262 my ($i, $alen) = (0, 0);
263 my (%virts, %defvals, %i3vals);
264 foreach my $e (@ENTS){
265 $e->{keyoff} = $alen;
268 my $a = join '\',\'', split(//, $k);
270 if($e->{bool}) {push @fa, 'a_AMV_VF_BOOL
'}
272 # Virtuals are implicitly rdonly and nodel
273 $e->{rdonly} = $e->{nodel} = 1;
275 push @fa, 'a_AMV_VF_VIRT
'
277 if(defined $e->{i3val}){
279 push @fa, 'a_AMV_VF_I3VAL
'
284 push @fa, 'a_AMV_VF_DEFVAL
'
288 push @fa, 'a_AMV_VF_IMPORT
'
290 if($e->{nolopts}) {push @fa, 'a_AMV_VF_NOLOPTS
'}
291 if($e->{rdonly}) {push @fa, 'a_AMV_VF_RDONLY
'}
292 if($e->{nodel}) {push @fa, 'a_AMV_VF_NODEL
'}
293 if($e->{notempty}) {push @fa, 'a_AMV_VF_NOTEMPTY
'}
294 if($e->{nocntrls}) {push @fa, 'a_AMV_VF_NOCNTRLS
'}
295 if($e->{num}) {push @fa, 'a_AMV_VF_NUM
'}
296 if($e->{posnum}) {push @fa, 'a_AMV_VF_POSNUM
'}
297 if($e->{lower}) {push @fa, 'a_AMV_VF_LOWER
'}
298 if($e->{vip}) {push @fa, 'a_AMV_VF_VIP
'}
299 if($e->{env}) {push @fa, 'a_AMV_VF_ENV
'}
301 my $f = join('|', @fa);
302 $f = ', ' . $f if length $f;
303 print F "${S}/* $i. [$alen]+$l $k$f */\n" if $VERB;
304 print F "${S}'$a','\\0',\n";
308 print F '};', "\n\n";
310 print F 'n_CTA
(a_AMV_VF_NONE
== 0, "Value not 0 as expected");', "\n";
311 print F 'static struct a_amv_var_map const a_amv_var_map
[] = {', "\n";
312 foreach my $e (@ENTS){
313 my $f = $VERB ? 'a_AMV_VF_NONE
' : '0';
314 my $fa = join '|', @{$e->{flags}};
315 $f .= '|' . $fa if length $fa;
316 print F "${S}{$e->{hash}u, $e->{keyoff}u, $f},";
317 if($VERB) {print F "${S}/* $e->{name} */\n"}
320 print F '};', "\n\n";
322 # We have at least version stuff in here
323 # The problem is that struct var uses a variable sized character buffer
324 # which cannot be initialized in a conforming way :(
326 #ifndef __CREATE_OKEY_MAP_PL
333 /* Unfortunately init of varsized buffer won't work
: define
"subclass"es
*/
335 my @skeys = sort keys %virts;
339 $e->{vname
} = $1 if $e->{enum
} =~ /ok_._(.*)/;
340 $e->{vstruct
} = "var_virt_$e->{vname}";
341 print F
"static char const a_amv_$e->{vstruct}_val[] = {$e->{virt}};\n";
342 print F
"static struct{\n";
343 print F
"${S}struct a_amv_var *av_link;\n";
344 print F
"${S}char const *av_value;\n";
345 print F
"${S}a_X(char *av_env;)\n";
346 print F
"${S}ui16_t av_flags;\n";
347 print F
"${S}char const av_name[", length($e->{name
}), " +1];\n";
348 my $f = $VERB ?
'a_AMV_VF_NONE' : '0';
349 my $fa = join '|', @
{$e->{flags
}};
350 $f .= '|' . $fa if length $fa;
351 print F
"} const a_amv_$e->{vstruct} = ",
352 "{NULL, a_amv_$e->{vstruct}_val, a_X(0 COMMA) $f, ",
353 "\"$e->{name}\"};\n\n"
355 print F
"# undef a_X\n";
358 print F
'#define a_AMV_VAR_VIRTS_CNT ', scalar @skeys, "\n";
359 print F
'static struct a_amv_var_virt const a_amv_var_virts[] = {', "\n";
362 my $n = $1 if $e->{enum
} =~ /ok_._(.*)/;
363 print F
"${S}{$e->{enum}, {0,}, (void const*)&a_amv_$e->{vstruct}},\n";
368 @skeys = sort keys %i3vals;
371 print F
'#define a_AMV_VAR_I3VALS_CNT ', scalar @skeys, "\n";
372 print F
'static struct a_amv_var_defval const a_amv_var_i3vals[] = {', "\n";
375 print F
"${S}{", $e->{enum
}, ', {0,}, ',
376 (!$e->{bool
} ?
$e->{i3val
} : "NULL"), "},\n"
381 @skeys = sort keys %defvals;
384 print F
'#define a_AMV_VAR_DEFVALS_CNT ', scalar @skeys, "\n";
385 print F
'static struct a_amv_var_defval const a_amv_var_defvals[] = {', "\n";
387 my $e = $defvals{$_};
388 print F
"${S}{", $e->{enum
}, ', {0,}, ',
389 (!$e->{bool
} ?
$e->{defval
} : "NULL"), "},\n"
393 print F
"#endif /* __CREATE_OKEY_MAP_PL */\n";
395 # Special var backing [#@*?]|[1-9][0-9]*|0
398 foreach my $e (@ENTS){
399 if($e->{name
} eq '--special-param'){
400 print F
"#define a_AMV_VAR_SPECIAL_PARAM_MAP_IDX ${i}u\n"
402 # The rest are only speedups
403 elsif($e->{name
} eq '?'){
404 print F
"#define a_AMV_VAR_QM_MAP_IDX ${i}u\n"
405 }elsif($e->{name
} eq '!'){
406 print F
"#define a_AMV_VAR_EM_MAP_IDX ${i}u\n"
412 die "$OUT: close: $^E" unless close F
416 my $argv2 = $VERB ?
' verb' : '';
417 system("c99 -I. -o $CTOOL_EXE $CTOOL");
418 my $t = (@ENTS < 0xFF ?
'ui8_t' : (@ENTS < 0xFFFF ?
'ui16_t' : 'ui32_t'));
419 `$CTOOL_EXE $t$argv2 >> $OUT`
422 {package main
; main_fun
()}