#!/bin/awk -f # # Copyright © 2023 Nick Bowler # # Hackjob to replace gperf's generated string table with a custom function. # # Sometimes it is not desired to use a gperf-generated string table, as this # can be quite large and is mostly wasted space if we have some other means # of creating strings. This script replaces the "wordlist" initializer with # one that does not include any strings, and replaces the reference to the # ".name" member of the wordlist with a function call which can be supplied # by the user. # # This transformation only occurs if the %define word-array-name is used to # define an identifier that ends in _wrapped. If this option is not used, # no modifications are made to the gperf output, so that this script can be # used in generic build recipes and selectively enabled/disabled. # # To work, the following gperf options are assumed: # # %struct-type # %null-strings # # Do not use %pic. Since this script removes all keyword strings from the # table initializer it is not required to avoid relocations in PIC code. # # The user must supply a function with the following signature in the gperf file: # # const char *wordlist_func(const struct tag *); # # where "wordlist" is the same identifier provided to word-array-name, without # the _wrapped suffix, and "tag" is the identifier used for the gperf struct-type # declaration. This function returns the string corresponding to a given table # entry. # # The structure declaration should not include the first "name" member which is # ordinarily required. All struct members for nonempty table entries are set # exactly as specified in the gperf input, without the keyword name. All empty # table entries are initialized with {0}. BEGIN { wl_tag = wl_name = ""; in_wordlist = 0; linecount = 0; } NF == 1 && $1 == "};" { in_wordlist = 0; } !in_wordlist && wl_name { # Convert wordlist "name" member references to function call. re = wl_name "_wrapped\\[key\\]\\.name"; gsub(re, wl_name "_func(" wl_name "_wrapped+key)"); } in_wordlist && $1 ~ /^[^#]/ { # Convert empty wordlist entries to {0}. gsub(/[{][(]char\*[)]0[}]/, "{0}"); # Remove string portion of populated entry initializers. gsub(/\\"/, "\1"); sub(/"[^"]*",? */, ""); gsub("\1", "\\\""); } # Locate the wordlist array definition, which is identified by a magic # name ending in "_wrapped" $NF == "=" && $(NF-1) ~ /._wrapped\[\]$/ { wl_tag = $(NF-2); wl_name = $(NF-1); sub(/_[^_]*$/, "", wl_name); in_wordlist = 1; dump_lines(); } !wl_name { # Buffer lines until we know the structure and wordlist names lines[linecount++] = $0; next; } { print; } END { dump_lines(); } function dump_lines(i, flag) { flag = 0; for (i = 0; i < linecount; i++) { if (wl_name && !(flag > 0 || lines[i] ~ /^\//)) { print "/* Postprocessed by gperf-wordwrap.awk */"; flag = 1; } if (flag == 1 && (lines[i] ~ "struct *" wl_tag " *{")) { print "static const char *" wl_name "_func(const struct " wl_tag " *);" flag = 2; } print lines[i]; delete lines[i]; } linecount = 0; }