#!/usr/bin/perl
|
|
|
|
use strict;
|
|
use warnings;
|
|
use IPC::Open2;
|
|
use IPC::Cmd qw[can_run];
|
|
|
|
can_run('gperf') or die 'No gperf binary found, make sure to have gperf installed!';
|
|
|
|
print("/******** GENERATED FILE ********/\n");
|
|
|
|
my $rewritten_input = '';
|
|
my @sections;
|
|
my @slots;
|
|
|
|
# collect keywords and rewrite input file with in lookup keys
|
|
|
|
while (my $line = <STDIN>) {
|
|
my $num = scalar(@sections);
|
|
my $new_section;
|
|
if (($line =~ s/(__csh_lookup)(\s*\()/$1_$num$2/)) {
|
|
$new_section = 0;
|
|
}
|
|
elsif (($line =~ s/(__csh_lookup)_n(\s*\()\s*(\d+)\s*,\s*/$1_$num$2/)) {
|
|
$new_section = $3;
|
|
}
|
|
if (defined($new_section)) {
|
|
$rewritten_input .= $line;
|
|
my $section = { keys => '', vals => {}, num => $num };
|
|
push(@sections, $section);
|
|
$slots[$new_section] = $num;
|
|
next;
|
|
}
|
|
my ($rewrite, $key);
|
|
if ($line =~ s/CSH_LOOKUP\(\s*"(.*?)"\s*\)/{}/) {
|
|
$rewrite = 0;
|
|
$key = $1;
|
|
}
|
|
elsif ($line =~ s/CSH_LOOKUP_N\(\s*(\d+)\s*,\s*"(.*?)"\s*\)/{}/) {
|
|
$rewrite = $1;
|
|
$key = $2;
|
|
}
|
|
if (!defined($rewrite)) {
|
|
$rewritten_input .= $line;
|
|
next;
|
|
}
|
|
my $section = $sections[$slots[$rewrite]];
|
|
die unless $section;
|
|
if (exists($section->{vals}{$key})) {
|
|
$line =~ s/{}/$section->{vals}{$key}/;
|
|
}
|
|
else {
|
|
my $iter = values(%{$section->{vals}});
|
|
$line =~ s/{}/$iter/;
|
|
$section->{keys} .= "$key,$iter\n";
|
|
$section->{vals}{$key} = $iter;
|
|
}
|
|
$rewritten_input .= $line;
|
|
}
|
|
|
|
# pass collected outputs to gperf
|
|
|
|
print "struct __csh_hash_lookup { char *name; int num; };\n";
|
|
|
|
for my $section (@sections) {
|
|
my $num = $section->{num};
|
|
|
|
my ($rd, $wr);
|
|
my $pid = open2($rd, $wr, qw(gperf -t -E -l -c -I -C -H), "__csh_hash_$num", '-N', "__csh_lookup_raw_$num");
|
|
|
|
# gperf header and keys
|
|
|
|
print { $wr } "struct __csh_hash_lookup;\n%%\n";
|
|
print { $wr } $section->{keys};
|
|
|
|
# read gperf output
|
|
|
|
close($wr);
|
|
my $hash_func_code;
|
|
{
|
|
local $/ = undef;
|
|
$hash_func_code = <$rd>;
|
|
}
|
|
close($rd);
|
|
waitpid($pid, 0);
|
|
exit(1) if $?;
|
|
|
|
# convert lookup function to static
|
|
|
|
$hash_func_code =~ s/(^|\s)(const\s+)?struct\s+__csh_hash_lookup\s*\*/\nstatic$&/s;
|
|
|
|
# print combined output
|
|
|
|
print "#pragma GCC diagnostic push\n";
|
|
print "#pragma GCC diagnostic ignored \"-Wmissing-field-initializers\"\n";
|
|
print "#pragma GCC diagnostic ignored \"-Wshadow=global\"\n";
|
|
print $hash_func_code;
|
|
print "#pragma GCC diagnostic pop\n";
|
|
# add convenience function
|
|
print <<END;
|
|
#include "str.h"
|
|
static int __csh_lookup_$num(const str *s) {
|
|
if (!s->s)
|
|
return -1;
|
|
const struct __csh_hash_lookup *h = __csh_lookup_raw_$num(s->s, s->len);
|
|
if (!h)
|
|
return -1;
|
|
return h->num;
|
|
}
|
|
END
|
|
}
|
|
|
|
# adjust diagnostic line numbers and originating file
|
|
print $ARGV[0] ? "#line 1 \"$ARGV[0]\"\n" : "#line 1\n";
|
|
print $rewritten_input;
|