#!/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!'; my (%defines_0, %defines_1, %defines_2, %defines_4); { my @argv; for my $arg (@ARGV) { if ($arg !~ /^-/) { push(@argv, $arg); next; } if ($arg =~ /^-D(.*?)=(.*)$/) { $defines_0{$1} = $2; next; } if ($arg =~ /^-D(.*?)$/) { $defines_0{$1} = '1'; next; } } @ARGV = @argv; ## no critic (RequireLocalizedPunctuationVars) } print("/******** GENERATED FILE ********/\n"); my $rewritten_input = ''; my @sections; my @slots; my $ifdefs = 0; # collect keywords and rewrite input file with in lookup keys sub def_sub { my ($subs, @vals) = @_; my $i = 0; my $repl = $subs->[$i++]; for my $r (@vals) { my $key = $subs->[$i++]; $repl =~ s/(\W|^)\Q$key\E(\W|$)/$1$r$2/g; } return $repl; } while (my $line = ) { my $num = scalar(@sections); my $new_section; if ($line =~ /CSH_SECTION|CSH_LOOKUP|CSH_NUM_LOOKUPS/ && $line =~ /^\s*#define\s+(\w+)(?:\s+(.*?)|\(\s*(\w+)\s*\)\s+(.*?)|\(\s*(\w+)\s*,\s*(\w+)\s*\)\s+(.*?)|\(\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*\)\s+(.*?))$/) { if ($2) { $defines_0{$1} = $2; } elsif ($3) { $defines_1{$1} = [$4, $3]; } elsif ($5) { $defines_2{$1} = [$7, $5, $6]; } elsif ($8) { $defines_4{$1} = [$12, $8, $9, $10, $11]; } $rewritten_input .= "\n"; next; } if ($line =~ /^\s*#endif/) { if ($ifdefs) { $ifdefs--; } $rewritten_input .= $line; next; } if ($line =~ /^\s*#if(n?)def\s+(\S+)/) { if ((!$1 && !$defines_0{$2}) || ($1 && $defines_0{$2}) || $ifdefs) { $ifdefs++; } $rewritten_input .= $line; next; } if ($line =~ /^\s*#if\s+/) { if ($ifdefs) { $ifdefs++; } $rewritten_input .= $line; next; } if ($ifdefs) { $rewritten_input .= $line; next; } for my $def (keys(%defines_0)) { my $sub = $defines_0{$def}; $line =~ s/(\W|^)\Q$def\E(\W|$)/$1$sub$2/g; } for my $def (keys(%defines_1)) { my $subs = $defines_1{$def}; $line =~ s/(\W|^)\Q$def\E\(\s*(\S+|"[^"]*")\s*\)(\W|$)/$1 . def_sub($subs, $2) . $3/eg; } for my $def (keys(%defines_2)) { my $subs = $defines_2{$def}; $line =~ s/(\W|^)\Q$def\E\(\s*(\S+|"[^"]*")\s*,\s*(\S+|"[^"]*")\s*\)(\W|$)/$1 . def_sub($subs, $2, $3) . $4/eg; } for my $def (keys(%defines_4)) { my $subs = $defines_4{$def}; $line =~ s/(\W|^)\Q$def\E\(\s*(\S+|"[^"]*")\s*,\s*(\S+|"[^"]*")\s*,\s*(\S+|"[^"]*")\s*,\s*(\S+|"[^"]*")\s*\)(\W|$)/$1 . def_sub($subs, $2, $3, $4, $5) . $6/eg; } 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; } elsif (($line =~ s/CSH_SECTION/$num/)) { $new_section = 0; } if (defined($new_section)) { $rewritten_input .= $line; my $section = { keys => '', vals => {}, num => $num }; push(@sections, $section); $slots[$new_section] = $num; next; } if ($line =~ s/CSH_NUM_LOOKUPS/{}/) { my $section = $sections[$slots[0]]; die unless $section; my $n = values(%{$section->{vals}}); $line =~ s/{}/$n/; $rewritten_input .= $line; 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 <s) return -1; const struct __csh_hash_lookup *h = __csh_lookup_raw_$num(s->s, s->len); if (!h) return -1; return h->num; } #pragma GCC diagnostic pop END } print("static const struct __csh_hash_lookup *(*__csh_lookup_funcs[])(const char *, size_t) = {\n"); for my $section (@sections) { print("\t__csh_lookup_raw_$section->{num},\n"); } print <= G_N_ELEMENTS(__csh_lookup_funcs)) return -1; if (!s->s) return -1; const struct __csh_hash_lookup *h = __csh_lookup_funcs[n](s->s, s->len); if (!h) return -1; return h->num; } #pragma GCC diagnostic pop END # adjust diagnostic line numbers and originating file print $ARGV[0] ? "#line 1 \"$ARGV[0]\"\n" : "#line 1\n"; print $rewritten_input;