1*eabc0478Schristos /* $NetBSD: keyword-gen.c,v 1.14 2024/08/18 20:47:17 christos Exp $ */ 2abb0f93cSkardel 3abb0f93cSkardel /* 4abb0f93cSkardel * keyword-gen.c -- generate keyword scanner finite state machine and 5abb0f93cSkardel * keyword_text array. 62950cc38Schristos * 7abb0f93cSkardel * This program is run to generate ntp_keyword.h 82950cc38Schristos * After making a change here, two output files should be committed at 92950cc38Schristos * the same time as keyword-gen.c: 102950cc38Schristos * ntp_keyword.h 112950cc38Schristos * keyword-gen-utd 122950cc38Schristos * 132950cc38Schristos * keyword-gen-utd is a sentinel used by Makefile.am to avoid compiling 142950cc38Schristos * keyword_gen.c and generating ntp_keyword.h if the input keyword-gen.c 152950cc38Schristos * has not changed. This is not solely an optimization, it also breaks 162950cc38Schristos * a dependency chain that otherwise would cause programs to be compiled 172950cc38Schristos * when running "make dist" or "make distdir". We want these to package 182950cc38Schristos * the existing source without building anything but a tarball. See 192950cc38Schristos * [Bug 1470]. 20abb0f93cSkardel */ 21abb0f93cSkardel #include <config.h> 22abb0f93cSkardel #include <stdio.h> 23abb0f93cSkardel #include <stdlib.h> 24abb0f93cSkardel #include <time.h> 25abb0f93cSkardel 26abb0f93cSkardel #include <ntp_stdlib.h> 27abb0f93cSkardel #include <ntp_config.h> 28abb0f93cSkardel #include "ntp_scanner.h" 29abb0f93cSkardel #include "ntp_parser.h" 30abb0f93cSkardel 31abb0f93cSkardel 32abb0f93cSkardel /* Define a structure to hold a (keyword, token) pair */ 33abb0f93cSkardel struct key_tok { 34abb0f93cSkardel char * key; /* Keyword */ 352950cc38Schristos u_short token; /* Associated Token */ 36abb0f93cSkardel follby followedby; /* nonzero indicates the next token(s) 37abb0f93cSkardel forced to be string(s) */ 38abb0f93cSkardel }; 39abb0f93cSkardel 40abb0f93cSkardel struct key_tok ntp_keywords[] = { 413123f114Skardel { "...", T_Ellipsis, FOLLBY_TOKEN }, 422950cc38Schristos { "allpeers", T_Allpeers, FOLLBY_TOKEN }, 43abb0f93cSkardel { "automax", T_Automax, FOLLBY_TOKEN }, 44abb0f93cSkardel { "broadcast", T_Broadcast, FOLLBY_STRING }, 45abb0f93cSkardel { "broadcastclient", T_Broadcastclient, FOLLBY_TOKEN }, 46abb0f93cSkardel { "broadcastdelay", T_Broadcastdelay, FOLLBY_TOKEN }, 47cdfa2a7eSchristos { "checkhash", T_Checkhash, FOLLBY_TOKEN }, 482950cc38Schristos { "ctl", T_Ctl, FOLLBY_TOKEN }, 49*eabc0478Schristos { "delrestrict", T_Delrestrict, FOLLBY_TOKEN }, 50*eabc0478Schristos { "device", T_Device, FOLLBY_STRING }, 51abb0f93cSkardel { "disable", T_Disable, FOLLBY_TOKEN }, 52abb0f93cSkardel { "driftfile", T_Driftfile, FOLLBY_STRING }, 535d681e99Schristos { "dscp", T_Dscp, FOLLBY_TOKEN }, 54abb0f93cSkardel { "enable", T_Enable, FOLLBY_TOKEN }, 55abb0f93cSkardel { "end", T_End, FOLLBY_TOKEN }, 56abb0f93cSkardel { "filegen", T_Filegen, FOLLBY_TOKEN }, 57abb0f93cSkardel { "fudge", T_Fudge, FOLLBY_STRING }, 58cdfa2a7eSchristos { "ignorehash", T_Ignorehash, FOLLBY_TOKEN }, 592950cc38Schristos { "io", T_Io, FOLLBY_TOKEN }, 60abb0f93cSkardel { "includefile", T_Includefile, FOLLBY_STRING }, 61abb0f93cSkardel { "leapfile", T_Leapfile, FOLLBY_STRING }, 625d681e99Schristos { "leapsmearinterval", T_Leapsmearinterval, FOLLBY_TOKEN }, 63abb0f93cSkardel { "logconfig", T_Logconfig, FOLLBY_STRINGS_TO_EOC }, 64abb0f93cSkardel { "logfile", T_Logfile, FOLLBY_STRING }, 65abb0f93cSkardel { "manycastclient", T_Manycastclient, FOLLBY_STRING }, 66abb0f93cSkardel { "manycastserver", T_Manycastserver, FOLLBY_STRINGS_TO_EOC }, 672950cc38Schristos { "mem", T_Mem, FOLLBY_TOKEN }, 68abb0f93cSkardel { "multicastclient", T_Multicastclient, FOLLBY_STRINGS_TO_EOC }, 69abb0f93cSkardel { "peer", T_Peer, FOLLBY_STRING }, 70abb0f93cSkardel { "phone", T_Phone, FOLLBY_STRINGS_TO_EOC }, 71abb0f93cSkardel { "pidfile", T_Pidfile, FOLLBY_STRING }, 72cdfa2a7eSchristos { "pollskewlist", T_PollSkewList, FOLLBY_TOKEN }, 73abb0f93cSkardel { "pool", T_Pool, FOLLBY_STRING }, 74abb0f93cSkardel { "discard", T_Discard, FOLLBY_TOKEN }, 752950cc38Schristos { "reset", T_Reset, FOLLBY_TOKEN }, 76abb0f93cSkardel { "restrict", T_Restrict, FOLLBY_TOKEN }, 772950cc38Schristos { "rlimit", T_Rlimit, FOLLBY_TOKEN }, 78abb0f93cSkardel { "server", T_Server, FOLLBY_STRING }, 79cdfa2a7eSchristos { "serverresponse", T_Serverresponse, FOLLBY_TOKEN }, 80cdfa2a7eSchristos { "fuzz", T_Fuzz, FOLLBY_TOKEN }, 81cdfa2a7eSchristos { "poll", T_Poll, FOLLBY_TOKEN }, 82abb0f93cSkardel { "setvar", T_Setvar, FOLLBY_STRING }, 83abb0f93cSkardel { "statistics", T_Statistics, FOLLBY_TOKEN }, 84abb0f93cSkardel { "statsdir", T_Statsdir, FOLLBY_STRING }, 852950cc38Schristos { "sys", T_Sys, FOLLBY_TOKEN }, 86abb0f93cSkardel { "tick", T_Tick, FOLLBY_TOKEN }, 872950cc38Schristos { "timer", T_Timer, FOLLBY_TOKEN }, 88abb0f93cSkardel { "tinker", T_Tinker, FOLLBY_TOKEN }, 89abb0f93cSkardel { "tos", T_Tos, FOLLBY_TOKEN }, 90abb0f93cSkardel { "trap", T_Trap, FOLLBY_STRING }, 91abb0f93cSkardel { "unconfig", T_Unconfig, FOLLBY_STRING }, 92abb0f93cSkardel { "unpeer", T_Unpeer, FOLLBY_STRING }, 93cdfa2a7eSchristos { "xmtnonce", T_Xmtnonce, FOLLBY_TOKEN }, 94abb0f93cSkardel /* authentication_command */ 95abb0f93cSkardel { "controlkey", T_ControlKey, FOLLBY_TOKEN }, 96abb0f93cSkardel { "crypto", T_Crypto, FOLLBY_TOKEN }, 97abb0f93cSkardel { "keys", T_Keys, FOLLBY_STRING }, 98abb0f93cSkardel { "keysdir", T_Keysdir, FOLLBY_STRING }, 99abb0f93cSkardel { "ntpsigndsocket", T_NtpSignDsocket, FOLLBY_STRING }, 100abb0f93cSkardel { "requestkey", T_Requestkey, FOLLBY_TOKEN }, 101abb0f93cSkardel { "revoke", T_Revoke, FOLLBY_TOKEN }, 102abb0f93cSkardel { "trustedkey", T_Trustedkey, FOLLBY_TOKEN }, 103abb0f93cSkardel /* IPv4/IPv6 protocol override flag */ 104abb0f93cSkardel { "-4", T_Ipv4_flag, FOLLBY_TOKEN }, 105abb0f93cSkardel { "-6", T_Ipv6_flag, FOLLBY_TOKEN }, 106abb0f93cSkardel /* option */ 107abb0f93cSkardel { "autokey", T_Autokey, FOLLBY_TOKEN }, 108abb0f93cSkardel { "burst", T_Burst, FOLLBY_TOKEN }, 109abb0f93cSkardel { "iburst", T_Iburst, FOLLBY_TOKEN }, 110abb0f93cSkardel { "key", T_Key, FOLLBY_TOKEN }, 111abb0f93cSkardel { "maxpoll", T_Maxpoll, FOLLBY_TOKEN }, 11296061387Schristos { "mdnstries", T_Mdnstries, FOLLBY_TOKEN }, 113abb0f93cSkardel { "minpoll", T_Minpoll, FOLLBY_TOKEN }, 114abb0f93cSkardel { "mode", T_Mode, FOLLBY_TOKEN }, 115abb0f93cSkardel { "noselect", T_Noselect, FOLLBY_TOKEN }, 116abb0f93cSkardel { "preempt", T_Preempt, FOLLBY_TOKEN }, 117abb0f93cSkardel { "true", T_True, FOLLBY_TOKEN }, 118abb0f93cSkardel { "prefer", T_Prefer, FOLLBY_TOKEN }, 119abb0f93cSkardel { "ttl", T_Ttl, FOLLBY_TOKEN }, 120abb0f93cSkardel { "version", T_Version, FOLLBY_TOKEN }, 121abb0f93cSkardel { "xleave", T_Xleave, FOLLBY_TOKEN }, 122abb0f93cSkardel /* crypto_command */ 123abb0f93cSkardel { "host", T_Host, FOLLBY_STRING }, 124abb0f93cSkardel { "ident", T_Ident, FOLLBY_STRING }, 125abb0f93cSkardel { "pw", T_Pw, FOLLBY_STRING }, 126abb0f93cSkardel { "randfile", T_Randfile, FOLLBY_STRING }, 127abb0f93cSkardel { "digest", T_Digest, FOLLBY_STRING }, 128abb0f93cSkardel /*** MONITORING COMMANDS ***/ 129abb0f93cSkardel /* stat */ 130abb0f93cSkardel { "clockstats", T_Clockstats, FOLLBY_TOKEN }, 131abb0f93cSkardel { "cryptostats", T_Cryptostats, FOLLBY_TOKEN }, 132abb0f93cSkardel { "loopstats", T_Loopstats, FOLLBY_TOKEN }, 133abb0f93cSkardel { "peerstats", T_Peerstats, FOLLBY_TOKEN }, 134abb0f93cSkardel { "rawstats", T_Rawstats, FOLLBY_TOKEN }, 135abb0f93cSkardel { "sysstats", T_Sysstats, FOLLBY_TOKEN }, 136abb0f93cSkardel { "protostats", T_Protostats, FOLLBY_TOKEN }, 137abb0f93cSkardel { "timingstats", T_Timingstats, FOLLBY_TOKEN }, 138abb0f93cSkardel /* filegen_option */ 139abb0f93cSkardel { "file", T_File, FOLLBY_STRING }, 140abb0f93cSkardel { "link", T_Link, FOLLBY_TOKEN }, 141abb0f93cSkardel { "nolink", T_Nolink, FOLLBY_TOKEN }, 142abb0f93cSkardel { "type", T_Type, FOLLBY_TOKEN }, 143abb0f93cSkardel /* filegen_type */ 144abb0f93cSkardel { "age", T_Age, FOLLBY_TOKEN }, 145abb0f93cSkardel { "day", T_Day, FOLLBY_TOKEN }, 146abb0f93cSkardel { "month", T_Month, FOLLBY_TOKEN }, 147abb0f93cSkardel { "none", T_None, FOLLBY_TOKEN }, 148abb0f93cSkardel { "pid", T_Pid, FOLLBY_TOKEN }, 149abb0f93cSkardel { "week", T_Week, FOLLBY_TOKEN }, 150abb0f93cSkardel { "year", T_Year, FOLLBY_TOKEN }, 151abb0f93cSkardel /*** ORPHAN MODE COMMANDS ***/ 152abb0f93cSkardel /* tos_option */ 153abb0f93cSkardel { "minclock", T_Minclock, FOLLBY_TOKEN }, 154abb0f93cSkardel { "maxclock", T_Maxclock, FOLLBY_TOKEN }, 155abb0f93cSkardel { "minsane", T_Minsane, FOLLBY_TOKEN }, 156abb0f93cSkardel { "floor", T_Floor, FOLLBY_TOKEN }, 157abb0f93cSkardel { "ceiling", T_Ceiling, FOLLBY_TOKEN }, 158abb0f93cSkardel { "cohort", T_Cohort, FOLLBY_TOKEN }, 159abb0f93cSkardel { "mindist", T_Mindist, FOLLBY_TOKEN }, 160abb0f93cSkardel { "maxdist", T_Maxdist, FOLLBY_TOKEN }, 16103cfe0ffSchristos { "bcpollbstep", T_Bcpollbstep, FOLLBY_TOKEN }, 162abb0f93cSkardel { "beacon", T_Beacon, FOLLBY_TOKEN }, 163abb0f93cSkardel { "orphan", T_Orphan, FOLLBY_TOKEN }, 1642950cc38Schristos { "orphanwait", T_Orphanwait, FOLLBY_TOKEN }, 1652950cc38Schristos { "nonvolatile", T_Nonvolatile, FOLLBY_TOKEN }, 1664eea345dSchristos { "basedate", T_Basedate, FOLLBY_STRING }, 167abb0f93cSkardel /* access_control_flag */ 168abb0f93cSkardel { "default", T_Default, FOLLBY_TOKEN }, 1692950cc38Schristos { "source", T_Source, FOLLBY_TOKEN }, 1704eea345dSchristos { "epeer", T_Epeer, FOLLBY_TOKEN }, 1714eea345dSchristos { "noepeer", T_Noepeer, FOLLBY_TOKEN }, 172abb0f93cSkardel { "flake", T_Flake, FOLLBY_TOKEN }, 173abb0f93cSkardel { "ignore", T_Ignore, FOLLBY_TOKEN }, 1744eea345dSchristos { "ippeerlimit", T_Ippeerlimit, FOLLBY_TOKEN }, 175abb0f93cSkardel { "limited", T_Limited, FOLLBY_TOKEN }, 176abb0f93cSkardel { "mssntp", T_Mssntp, FOLLBY_TOKEN }, 177abb0f93cSkardel { "kod", T_Kod, FOLLBY_TOKEN }, 178abb0f93cSkardel { "lowpriotrap", T_Lowpriotrap, FOLLBY_TOKEN }, 179abb0f93cSkardel { "mask", T_Mask, FOLLBY_TOKEN }, 180abb0f93cSkardel { "nomodify", T_Nomodify, FOLLBY_TOKEN }, 1812950cc38Schristos { "nomrulist", T_Nomrulist, FOLLBY_TOKEN }, 182abb0f93cSkardel { "nopeer", T_Nopeer, FOLLBY_TOKEN }, 183abb0f93cSkardel { "noquery", T_Noquery, FOLLBY_TOKEN }, 184abb0f93cSkardel { "noserve", T_Noserve, FOLLBY_TOKEN }, 185abb0f93cSkardel { "notrap", T_Notrap, FOLLBY_TOKEN }, 186abb0f93cSkardel { "notrust", T_Notrust, FOLLBY_TOKEN }, 187abb0f93cSkardel { "ntpport", T_Ntpport, FOLLBY_TOKEN }, 188abb0f93cSkardel /* discard_option */ 189abb0f93cSkardel { "average", T_Average, FOLLBY_TOKEN }, 190abb0f93cSkardel { "minimum", T_Minimum, FOLLBY_TOKEN }, 191abb0f93cSkardel { "monitor", T_Monitor, FOLLBY_TOKEN }, 1922950cc38Schristos /* mru_option */ 1932950cc38Schristos { "incalloc", T_Incalloc, FOLLBY_TOKEN }, 1942950cc38Schristos { "incmem", T_Incmem, FOLLBY_TOKEN }, 1952950cc38Schristos { "initalloc", T_Initalloc, FOLLBY_TOKEN }, 1962950cc38Schristos { "initmem", T_Initmem, FOLLBY_TOKEN }, 1972950cc38Schristos { "mindepth", T_Mindepth, FOLLBY_TOKEN }, 1982950cc38Schristos { "maxage", T_Maxage, FOLLBY_TOKEN }, 1992950cc38Schristos { "maxdepth", T_Maxdepth, FOLLBY_TOKEN }, 2002950cc38Schristos { "maxmem", T_Maxmem, FOLLBY_TOKEN }, 2012950cc38Schristos { "mru", T_Mru, FOLLBY_TOKEN }, 202abb0f93cSkardel /* fudge_factor */ 2032950cc38Schristos { "abbrev", T_Abbrev, FOLLBY_STRING }, 204abb0f93cSkardel { "flag1", T_Flag1, FOLLBY_TOKEN }, 205abb0f93cSkardel { "flag2", T_Flag2, FOLLBY_TOKEN }, 206abb0f93cSkardel { "flag3", T_Flag3, FOLLBY_TOKEN }, 207abb0f93cSkardel { "flag4", T_Flag4, FOLLBY_TOKEN }, 208abb0f93cSkardel { "refid", T_Refid, FOLLBY_STRING }, 209abb0f93cSkardel { "stratum", T_Stratum, FOLLBY_TOKEN }, 210abb0f93cSkardel { "time1", T_Time1, FOLLBY_TOKEN }, 211abb0f93cSkardel { "time2", T_Time2, FOLLBY_TOKEN }, 212cdfa2a7eSchristos { "minjitter", T_Minjitter, FOLLBY_TOKEN }, 213*eabc0478Schristos /* device spec */ 214*eabc0478Schristos { "ppsdata", T_PpsData, FOLLBY_STRING }, 215*eabc0478Schristos { "timedata", T_TimeData, FOLLBY_STRING }, 216abb0f93cSkardel /* system_option */ 217abb0f93cSkardel { "auth", T_Auth, FOLLBY_TOKEN }, 218abb0f93cSkardel { "bclient", T_Bclient, FOLLBY_TOKEN }, 219abb0f93cSkardel { "calibrate", T_Calibrate, FOLLBY_TOKEN }, 220abb0f93cSkardel { "kernel", T_Kernel, FOLLBY_TOKEN }, 2212950cc38Schristos { "mode7", T_Mode7, FOLLBY_TOKEN }, 222717847f5Schristos { "ntp", T_Ntp, FOLLBY_TOKEN }, 223717847f5Schristos { "peer_clear_digest_early", T_PCEdigest, FOLLBY_TOKEN }, 224abb0f93cSkardel { "stats", T_Stats, FOLLBY_TOKEN }, 22568dbbb44Schristos { "unpeer_crypto_early", T_UEcrypto, FOLLBY_TOKEN }, 22668dbbb44Schristos { "unpeer_crypto_nak_early", T_UEcryptonak, FOLLBY_TOKEN }, 22768dbbb44Schristos { "unpeer_digest_early", T_UEdigest, FOLLBY_TOKEN }, 2282950cc38Schristos /* rlimit_option */ 2292950cc38Schristos { "memlock", T_Memlock, FOLLBY_TOKEN }, 2302950cc38Schristos { "stacksize", T_Stacksize, FOLLBY_TOKEN }, 2312950cc38Schristos { "filenum", T_Filenum, FOLLBY_TOKEN }, 232abb0f93cSkardel /* tinker_option */ 233abb0f93cSkardel { "step", T_Step, FOLLBY_TOKEN }, 2347476e6e4Schristos { "stepback", T_Stepback, FOLLBY_TOKEN }, 2357476e6e4Schristos { "stepfwd", T_Stepfwd, FOLLBY_TOKEN }, 236abb0f93cSkardel { "panic", T_Panic, FOLLBY_TOKEN }, 237abb0f93cSkardel { "dispersion", T_Dispersion, FOLLBY_TOKEN }, 238abb0f93cSkardel { "stepout", T_Stepout, FOLLBY_TOKEN }, 239abb0f93cSkardel { "allan", T_Allan, FOLLBY_TOKEN }, 240abb0f93cSkardel { "huffpuff", T_Huffpuff, FOLLBY_TOKEN }, 241abb0f93cSkardel { "freq", T_Freq, FOLLBY_TOKEN }, 242abb0f93cSkardel /* miscellaneous_command */ 243abb0f93cSkardel { "port", T_Port, FOLLBY_TOKEN }, 244abb0f93cSkardel { "interface", T_Interface, FOLLBY_TOKEN }, 245abb0f93cSkardel { "saveconfigdir", T_Saveconfigdir, FOLLBY_STRING }, 246abb0f93cSkardel /* interface_command (ignore and interface already defined) */ 247abb0f93cSkardel { "nic", T_Nic, FOLLBY_TOKEN }, 248abb0f93cSkardel { "all", T_All, FOLLBY_TOKEN }, 249abb0f93cSkardel { "ipv4", T_Ipv4, FOLLBY_TOKEN }, 250abb0f93cSkardel { "ipv6", T_Ipv6, FOLLBY_TOKEN }, 251abb0f93cSkardel { "wildcard", T_Wildcard, FOLLBY_TOKEN }, 252abb0f93cSkardel { "listen", T_Listen, FOLLBY_TOKEN }, 253abb0f93cSkardel { "drop", T_Drop, FOLLBY_TOKEN }, 254abb0f93cSkardel /* simulator commands */ 255abb0f93cSkardel { "simulate", T_Simulate, FOLLBY_TOKEN }, 256abb0f93cSkardel { "simulation_duration",T_Sim_Duration, FOLLBY_TOKEN }, 257abb0f93cSkardel { "beep_delay", T_Beep_Delay, FOLLBY_TOKEN }, 258abb0f93cSkardel { "duration", T_Duration, FOLLBY_TOKEN }, 259abb0f93cSkardel { "server_offset", T_Server_Offset, FOLLBY_TOKEN }, 260abb0f93cSkardel { "freq_offset", T_Freq_Offset, FOLLBY_TOKEN }, 261abb0f93cSkardel { "wander", T_Wander, FOLLBY_TOKEN }, 262abb0f93cSkardel { "jitter", T_Jitter, FOLLBY_TOKEN }, 263abb0f93cSkardel { "prop_delay", T_Prop_Delay, FOLLBY_TOKEN }, 264abb0f93cSkardel { "proc_delay", T_Proc_Delay, FOLLBY_TOKEN }, 265abb0f93cSkardel }; 266abb0f93cSkardel 267abb0f93cSkardel typedef struct big_scan_state_tag { 268abb0f93cSkardel char ch; /* Character this state matches on */ 269abb0f93cSkardel char followedby; /* Forces next token(s) to T_String */ 270abb0f93cSkardel u_short finishes_token; /* nonzero ID if last keyword char */ 271abb0f93cSkardel u_short match_next_s; /* next state to check matching ch */ 272abb0f93cSkardel u_short other_next_s; /* next state to check if not ch */ 273abb0f93cSkardel } big_scan_state; 274abb0f93cSkardel 275abb0f93cSkardel /* 276abb0f93cSkardel * Note: to increase MAXSTATES beyond 2048, be aware it is currently 277abb0f93cSkardel * crammed into 11 bits in scan_state form. Raising to 4096 would be 278abb0f93cSkardel * relatively easy by storing the followedby value in a separate 279abb0f93cSkardel * array with one entry per token, and shrinking the char value to 280abb0f93cSkardel * 7 bits to free a bit for accepting/non-accepting. More than 4096 281abb0f93cSkardel * states will require expanding scan_state beyond 32 bits each. 282abb0f93cSkardel */ 283abb0f93cSkardel #define MAXSTATES 2048 2842950cc38Schristos #define MAX_TOK_LEN 63 285abb0f93cSkardel 286abb0f93cSkardel const char * current_keyword;/* for error reporting */ 287abb0f93cSkardel big_scan_state sst[MAXSTATES]; /* scanner FSM state entries */ 2882950cc38Schristos u_short sst_highwater; /* next entry index to consider */ 289abb0f93cSkardel char * symb[1024]; /* map token ID to symbolic name */ 290abb0f93cSkardel 291abb0f93cSkardel /* for libntp */ 292abb0f93cSkardel const char * progname = "keyword-gen"; 293abb0f93cSkardel 294abb0f93cSkardel int main (int, char **); 295abb0f93cSkardel static void generate_preamble (void); 296abb0f93cSkardel static void generate_fsm (void); 297abb0f93cSkardel static void generate_token_text (void); 2982950cc38Schristos static u_short create_keyword_scanner (void); 2992950cc38Schristos static u_short create_scan_states (char *, u_short, follby, u_short); 3002950cc38Schristos int compare_key_tok_id (const void *, const void *); 3012950cc38Schristos int compare_key_tok_text (const void *, const void *); 302abb0f93cSkardel void populate_symb (char *); 3032950cc38Schristos const char * symbname (u_short); 304abb0f93cSkardel 305abb0f93cSkardel 306abb0f93cSkardel int main(int argc, char **argv) 307abb0f93cSkardel { 308abb0f93cSkardel if (argc < 2) { 309abb0f93cSkardel fprintf(stderr, "Usage:\n%s t_header.h\n", argv[0]); 310abb0f93cSkardel exit(1); 311abb0f93cSkardel } 3122950cc38Schristos debug = 1; 3132950cc38Schristos 314abb0f93cSkardel populate_symb(argv[1]); 315abb0f93cSkardel 316abb0f93cSkardel generate_preamble(); 317abb0f93cSkardel generate_token_text(); 318abb0f93cSkardel generate_fsm(); 319abb0f93cSkardel 320abb0f93cSkardel return 0; 321abb0f93cSkardel } 322abb0f93cSkardel 323abb0f93cSkardel 324abb0f93cSkardel static void 325abb0f93cSkardel generate_preamble(void) 326abb0f93cSkardel { 327abb0f93cSkardel time_t now; 328abb0f93cSkardel char timestamp[128]; 329abb0f93cSkardel char preamble[] = 330abb0f93cSkardel "/*\n" 331abb0f93cSkardel " * ntp_keyword.h\n" 332abb0f93cSkardel " * \n" 333abb0f93cSkardel " * NOTE: edit this file with caution, it is generated by keyword-gen.c\n" 334abb0f93cSkardel " *\t Generated %s UTC diff_ignore_line\n" 335abb0f93cSkardel " *\n" 336abb0f93cSkardel " */\n" 337abb0f93cSkardel "#include \"ntp_scanner.h\"\n" 338abb0f93cSkardel "#include \"ntp_parser.h\"\n" 339abb0f93cSkardel "\n"; 340abb0f93cSkardel 341abb0f93cSkardel time(&now); 342abb0f93cSkardel if (!strftime(timestamp, sizeof(timestamp), 343abb0f93cSkardel "%Y-%m-%d %H:%M:%S", gmtime(&now))) 344abb0f93cSkardel timestamp[0] = '\0'; 345abb0f93cSkardel 346abb0f93cSkardel printf(preamble, timestamp); 347abb0f93cSkardel } 348abb0f93cSkardel 349abb0f93cSkardel 350abb0f93cSkardel static void 351abb0f93cSkardel generate_fsm(void) 352abb0f93cSkardel { 3532950cc38Schristos char rprefix[MAX_TOK_LEN + 1]; 3542950cc38Schristos char prefix[MAX_TOK_LEN + 1]; 3552950cc38Schristos char token_id_comment[16 + MAX_TOK_LEN + 1]; 3562950cc38Schristos size_t prefix_len; 3572950cc38Schristos char *p; 3582950cc38Schristos char *r; 3592950cc38Schristos u_short initial_state; 3602950cc38Schristos u_short this_state; 3612950cc38Schristos u_short state; 3622950cc38Schristos u_short i; 3632950cc38Schristos u_short token; 364abb0f93cSkardel 365abb0f93cSkardel /* 366abb0f93cSkardel * Sort ntp_keywords in alphabetical keyword order. This is 367abb0f93cSkardel * not necessary, but minimizes nonfunctional changes in the 368abb0f93cSkardel * generated finite state machine when keywords are modified. 369abb0f93cSkardel */ 370abb0f93cSkardel qsort(ntp_keywords, COUNTOF(ntp_keywords), 371abb0f93cSkardel sizeof(ntp_keywords[0]), compare_key_tok_text); 372abb0f93cSkardel 373abb0f93cSkardel /* 374abb0f93cSkardel * To save space, reserve the state array entry matching each 375abb0f93cSkardel * token number for its terminal state, so the token identifier 376abb0f93cSkardel * does not need to be stored in each state, but can be 377abb0f93cSkardel * recovered trivially. To mark the entry reserved, 378abb0f93cSkardel * finishes_token is nonzero. 379abb0f93cSkardel */ 380abb0f93cSkardel 381abb0f93cSkardel for (i = 0; i < COUNTOF(ntp_keywords); i++) { 382abb0f93cSkardel token = ntp_keywords[i].token; 383abb0f93cSkardel if (1 > token || token >= COUNTOF(sst)) { 384abb0f93cSkardel fprintf(stderr, 385abb0f93cSkardel "keyword-gen sst[%u] too small " 386abb0f93cSkardel "for keyword '%s' id %d\n", 38796061387Schristos (int)COUNTOF(sst), 388abb0f93cSkardel ntp_keywords[i].key, 389abb0f93cSkardel token); 390abb0f93cSkardel exit(4); 391abb0f93cSkardel } 392abb0f93cSkardel sst[token].finishes_token = token; 393abb0f93cSkardel } 394abb0f93cSkardel 395abb0f93cSkardel initial_state = create_keyword_scanner(); 396abb0f93cSkardel 397abb0f93cSkardel fprintf(stderr, 398abb0f93cSkardel "%d keywords consumed %d states of %d max.\n", 399abb0f93cSkardel (int)COUNTOF(ntp_keywords), 400abb0f93cSkardel sst_highwater - 1, 401abb0f93cSkardel (int)COUNTOF(sst) - 1); 402abb0f93cSkardel 403abb0f93cSkardel printf("#define SCANNER_INIT_S %d\n\n", initial_state); 404abb0f93cSkardel 405abb0f93cSkardel printf("const scan_state sst[%d] = {\n" 406abb0f93cSkardel "/*SS_T( ch,\tf-by, match, other ),\t\t\t\t */\n" 407abb0f93cSkardel " 0,\t\t\t\t /* %5d %-17s */\n", 408abb0f93cSkardel sst_highwater, 409abb0f93cSkardel 0, ""); 410abb0f93cSkardel 411abb0f93cSkardel for (i = 1; i < sst_highwater; i++) { 412abb0f93cSkardel 413abb0f93cSkardel /* verify fields will fit */ 414abb0f93cSkardel if (sst[i].followedby & ~0x3) { 415abb0f93cSkardel fprintf(stderr, 416abb0f93cSkardel "keyword-gen internal error " 417abb0f93cSkardel "sst[%d].followedby %d too big\n", 418abb0f93cSkardel i, sst[i].followedby); 419abb0f93cSkardel exit(7); 420abb0f93cSkardel } 421abb0f93cSkardel 422abb0f93cSkardel if (sst_highwater <= sst[i].match_next_s 423abb0f93cSkardel || sst[i].match_next_s & ~0x7ff) { 424abb0f93cSkardel fprintf(stderr, 425abb0f93cSkardel "keyword-gen internal error " 426abb0f93cSkardel "sst[%d].match_next_s %d too big\n", 427abb0f93cSkardel i, sst[i].match_next_s); 428abb0f93cSkardel exit(8); 429abb0f93cSkardel } 430abb0f93cSkardel 431abb0f93cSkardel if (sst_highwater <= sst[i].other_next_s 432abb0f93cSkardel || sst[i].other_next_s & ~0x7ff) { 433abb0f93cSkardel fprintf(stderr, 434abb0f93cSkardel "keyword-gen internal error " 435abb0f93cSkardel "sst[%d].other_next_s %d too big\n", 436abb0f93cSkardel i, sst[i].other_next_s); 437abb0f93cSkardel exit(9); 438abb0f93cSkardel } 439abb0f93cSkardel 4402950cc38Schristos if (sst[i].finishes_token) { 441abb0f93cSkardel snprintf(token_id_comment, 442abb0f93cSkardel sizeof(token_id_comment), "%5d %-17s", 443abb0f93cSkardel i, symbname(sst[i].finishes_token)); 444abb0f93cSkardel if (i != sst[i].finishes_token) { 445abb0f93cSkardel fprintf(stderr, 446abb0f93cSkardel "keyword-gen internal error " 447abb0f93cSkardel "entry %d finishes token %d\n", 448abb0f93cSkardel i, sst[i].finishes_token); 449abb0f93cSkardel exit(5); 450abb0f93cSkardel } 4512950cc38Schristos } else { 4522950cc38Schristos /* 4532950cc38Schristos * Determine the keyword prefix that leads to this 4542950cc38Schristos * state. This is expensive but keyword-gen is run 4552950cc38Schristos * only when it changes. Distributing keyword-gen-utd 4562950cc38Schristos * achieves that, which is why it must be committed 4572950cc38Schristos * at the same time as keyword-gen.c and ntp_keyword.h. 4582950cc38Schristos * 4592950cc38Schristos * Scan the state array iteratively looking for a state 4602950cc38Schristos * which leads to the current one, collecting matching 4612950cc38Schristos * characters along the way. There is only one such 4622950cc38Schristos * path back to the starting state given the way our 4632950cc38Schristos * scanner state machine is built and the practice of 4642950cc38Schristos * using the spelling of the keyword as its T_* token 4652950cc38Schristos * identifier, which results in never having two 4662950cc38Schristos * spellings result in the same T_* value. 4672950cc38Schristos */ 4682950cc38Schristos prefix_len = 0; 4692950cc38Schristos this_state = i; 4702950cc38Schristos do { 4712950cc38Schristos for (state = 1; state < sst_highwater; state++) 4722950cc38Schristos if (sst[state].other_next_s == this_state) { 4732950cc38Schristos this_state = state; 4742950cc38Schristos break; 4752950cc38Schristos } else if (sst[state].match_next_s == this_state) { 4762950cc38Schristos this_state = state; 4772950cc38Schristos rprefix[prefix_len] = sst[state].ch; 4782950cc38Schristos prefix_len++; 4792950cc38Schristos break; 4802950cc38Schristos } 4812950cc38Schristos } while (this_state != initial_state); 4822950cc38Schristos 4832950cc38Schristos if (prefix_len) { 4842950cc38Schristos /* reverse rprefix into prefix */ 4852950cc38Schristos p = prefix + prefix_len; 4862950cc38Schristos r = rprefix; 4872950cc38Schristos while (r < rprefix + prefix_len) 4882950cc38Schristos *--p = *r++; 4892950cc38Schristos } 4902950cc38Schristos prefix[prefix_len] = '\0'; 4912950cc38Schristos 4922950cc38Schristos snprintf(token_id_comment, 4932950cc38Schristos sizeof(token_id_comment), "%5d %-17s", 4942950cc38Schristos i, (initial_state == i) 4952950cc38Schristos ? "[initial state]" 4962950cc38Schristos : prefix); 497abb0f93cSkardel } 498abb0f93cSkardel 499abb0f93cSkardel printf(" S_ST( '%c',\t%d, %5u, %5u )%s /* %s */\n", 500abb0f93cSkardel sst[i].ch, 501abb0f93cSkardel sst[i].followedby, 502abb0f93cSkardel sst[i].match_next_s, 503abb0f93cSkardel sst[i].other_next_s, 504abb0f93cSkardel (i + 1 < sst_highwater) 505abb0f93cSkardel ? "," 506abb0f93cSkardel : " ", 507abb0f93cSkardel token_id_comment); 508abb0f93cSkardel } 509abb0f93cSkardel 510abb0f93cSkardel printf("};\n\n"); 511abb0f93cSkardel } 512abb0f93cSkardel 513abb0f93cSkardel 514abb0f93cSkardel /* Define a function to create the states of the scanner. This function 515abb0f93cSkardel * is used by the create_keyword_scanner function below. 516abb0f93cSkardel * 517abb0f93cSkardel * This function takes a suffix of a keyword, the token to be returned on 518abb0f93cSkardel * recognizing the complete keyword, and any pre-existing state that exists 519abb0f93cSkardel * for some other keyword that has the same prefix as the current one. 520abb0f93cSkardel */ 5212950cc38Schristos static u_short 522abb0f93cSkardel create_scan_states( 523abb0f93cSkardel char * text, 5242950cc38Schristos u_short token, 525abb0f93cSkardel follby followedby, 5262950cc38Schristos u_short prev_state 527abb0f93cSkardel ) 528abb0f93cSkardel { 5292950cc38Schristos u_short my_state; 5302950cc38Schristos u_short return_state; 5312950cc38Schristos u_short prev_char_s; 5322950cc38Schristos u_short curr_char_s; 533abb0f93cSkardel 534abb0f93cSkardel return_state = prev_state; 535abb0f93cSkardel curr_char_s = prev_state; 536abb0f93cSkardel prev_char_s = 0; 537abb0f93cSkardel 538abb0f93cSkardel /* Find the correct position to insert the state. 539abb0f93cSkardel * All states should be in alphabetical order 540abb0f93cSkardel */ 541abb0f93cSkardel while (curr_char_s && (text[0] < sst[curr_char_s].ch)) { 542abb0f93cSkardel prev_char_s = curr_char_s; 543abb0f93cSkardel curr_char_s = sst[curr_char_s].other_next_s; 544abb0f93cSkardel } 545abb0f93cSkardel 546abb0f93cSkardel /* 547abb0f93cSkardel * Check if a previously seen keyword has the same prefix as 548abb0f93cSkardel * the current keyword. If so, simply use the state for that 549abb0f93cSkardel * keyword as my_state, otherwise, allocate a new state. 550abb0f93cSkardel */ 551abb0f93cSkardel if (curr_char_s && (text[0] == sst[curr_char_s].ch)) { 552abb0f93cSkardel my_state = curr_char_s; 553abb0f93cSkardel if ('\0' == text[1]) { 554abb0f93cSkardel fprintf(stderr, 555abb0f93cSkardel "Duplicate entries for keyword '%s' in" 556abb0f93cSkardel " keyword_gen.c ntp_keywords[].\n", 557abb0f93cSkardel current_keyword); 558abb0f93cSkardel exit(2); 559abb0f93cSkardel } 560abb0f93cSkardel } else { 561abb0f93cSkardel do 562abb0f93cSkardel my_state = sst_highwater++; 563abb0f93cSkardel while (my_state < COUNTOF(sst) 564abb0f93cSkardel && sst[my_state].finishes_token); 565abb0f93cSkardel if (my_state >= COUNTOF(sst)) { 566abb0f93cSkardel fprintf(stderr, 567abb0f93cSkardel "fatal, keyword scanner state array " 568abb0f93cSkardel "sst[%d] is too small, modify\n" 569abb0f93cSkardel "keyword-gen.c to increase.\n", 570abb0f93cSkardel (int)COUNTOF(sst)); 571abb0f93cSkardel exit(3); 572abb0f93cSkardel } 573abb0f93cSkardel /* Store the next character of the keyword */ 574abb0f93cSkardel sst[my_state].ch = text[0]; 575abb0f93cSkardel sst[my_state].other_next_s = curr_char_s; 576abb0f93cSkardel sst[my_state].followedby = FOLLBY_NON_ACCEPTING; 577abb0f93cSkardel 578abb0f93cSkardel if (prev_char_s) 579abb0f93cSkardel sst[prev_char_s].other_next_s = my_state; 580abb0f93cSkardel else 581abb0f93cSkardel return_state = my_state; 582abb0f93cSkardel } 583abb0f93cSkardel 584abb0f93cSkardel /* Check if the next character is '\0'. 585abb0f93cSkardel * If yes, we are done with the recognition and this is an accepting 586abb0f93cSkardel * state. 587abb0f93cSkardel * If not, we need to continue scanning 588abb0f93cSkardel */ 589abb0f93cSkardel if ('\0' == text[1]) { 590abb0f93cSkardel sst[my_state].finishes_token = (u_short)token; 591abb0f93cSkardel sst[my_state].followedby = (char)followedby; 592abb0f93cSkardel 593abb0f93cSkardel if (sst[token].finishes_token != (u_short)token) { 594abb0f93cSkardel fprintf(stderr, 595abb0f93cSkardel "fatal, sst[%d] not reserved for %s.\n", 596abb0f93cSkardel token, symbname(token)); 597abb0f93cSkardel exit(6); 598abb0f93cSkardel } 599abb0f93cSkardel /* relocate so token id is sst[] index */ 600abb0f93cSkardel if (my_state != token) { 601abb0f93cSkardel sst[token] = sst[my_state]; 6022950cc38Schristos ZERO(sst[my_state]); 603abb0f93cSkardel do 604abb0f93cSkardel sst_highwater--; 605abb0f93cSkardel while (sst[sst_highwater].finishes_token); 606abb0f93cSkardel my_state = token; 607abb0f93cSkardel if (prev_char_s) 608abb0f93cSkardel sst[prev_char_s].other_next_s = my_state; 609abb0f93cSkardel else 610abb0f93cSkardel return_state = my_state; 611abb0f93cSkardel } 612abb0f93cSkardel } else 613abb0f93cSkardel sst[my_state].match_next_s = 614abb0f93cSkardel create_scan_states( 615abb0f93cSkardel &text[1], 616abb0f93cSkardel token, 617abb0f93cSkardel followedby, 618abb0f93cSkardel sst[my_state].match_next_s); 619abb0f93cSkardel 620abb0f93cSkardel return return_state; 621abb0f93cSkardel } 622abb0f93cSkardel 623abb0f93cSkardel 624abb0f93cSkardel /* Define a function that takes a list of (keyword, token) values and 625abb0f93cSkardel * creates a keywords scanner out of it. 626abb0f93cSkardel */ 627abb0f93cSkardel 6282950cc38Schristos static u_short 629abb0f93cSkardel create_keyword_scanner(void) 630abb0f93cSkardel { 6312950cc38Schristos u_short scanner; 6322950cc38Schristos u_short i; 633abb0f93cSkardel 634abb0f93cSkardel sst_highwater = 1; /* index 0 invalid, unused */ 635abb0f93cSkardel scanner = 0; 636abb0f93cSkardel 637abb0f93cSkardel for (i = 0; i < COUNTOF(ntp_keywords); i++) { 638abb0f93cSkardel current_keyword = ntp_keywords[i].key; 639abb0f93cSkardel scanner = 640abb0f93cSkardel create_scan_states( 641abb0f93cSkardel ntp_keywords[i].key, 642abb0f93cSkardel ntp_keywords[i].token, 643abb0f93cSkardel ntp_keywords[i].followedby, 644abb0f93cSkardel scanner); 645abb0f93cSkardel } 646abb0f93cSkardel 647abb0f93cSkardel return scanner; 648abb0f93cSkardel } 649abb0f93cSkardel 650abb0f93cSkardel 651abb0f93cSkardel static void 652abb0f93cSkardel generate_token_text(void) 653abb0f93cSkardel { 6542950cc38Schristos u_short lowest_id; 6552950cc38Schristos u_short highest_id; 6562950cc38Schristos u_short id_count; 6572950cc38Schristos u_short id; 6582950cc38Schristos u_short i; 659abb0f93cSkardel 660abb0f93cSkardel /* sort ntp_keywords in token ID order */ 661abb0f93cSkardel qsort(ntp_keywords, COUNTOF(ntp_keywords), 662abb0f93cSkardel sizeof(ntp_keywords[0]), compare_key_tok_id); 663abb0f93cSkardel 664abb0f93cSkardel lowest_id = ntp_keywords[0].token; 665abb0f93cSkardel highest_id = ntp_keywords[COUNTOF(ntp_keywords) - 1].token; 666abb0f93cSkardel id_count = highest_id - lowest_id + 1; 667abb0f93cSkardel 668abb0f93cSkardel printf("#define LOWEST_KEYWORD_ID %d\n\n", lowest_id); 669abb0f93cSkardel 670abb0f93cSkardel printf("const char * const keyword_text[%d] = {", id_count); 671abb0f93cSkardel 672abb0f93cSkardel id = lowest_id; 673abb0f93cSkardel i = 0; 674abb0f93cSkardel while (i < COUNTOF(ntp_keywords)) { 675abb0f93cSkardel while (id < ntp_keywords[i].token) { 676abb0f93cSkardel printf(",\n\t/* %-5d %5d %20s */\tNULL", 677abb0f93cSkardel id - lowest_id, id, symbname(id)); 678abb0f93cSkardel id++; 679abb0f93cSkardel } 680abb0f93cSkardel if (i > 0) 681abb0f93cSkardel printf(","); 682abb0f93cSkardel printf("\n\t/* %-5d %5d %20s */\t\"%s\"", 683abb0f93cSkardel id - lowest_id, id, symbname(id), 684abb0f93cSkardel ntp_keywords[i].key); 685abb0f93cSkardel i++; 686abb0f93cSkardel id++; 687abb0f93cSkardel } 688abb0f93cSkardel 689abb0f93cSkardel printf("\n};\n\n"); 690abb0f93cSkardel } 691abb0f93cSkardel 692abb0f93cSkardel 693abb0f93cSkardel int 694abb0f93cSkardel compare_key_tok_id( 6952950cc38Schristos const void *a1, 6962950cc38Schristos const void *a2 697abb0f93cSkardel ) 698abb0f93cSkardel { 6992950cc38Schristos const struct key_tok *p1 = a1; 7002950cc38Schristos const struct key_tok *p2 = a2; 701abb0f93cSkardel 702abb0f93cSkardel if (p1->token == p2->token) 703abb0f93cSkardel return 0; 704abb0f93cSkardel 705abb0f93cSkardel if (p1->token < p2->token) 706abb0f93cSkardel return -1; 707abb0f93cSkardel else 708abb0f93cSkardel return 1; 709abb0f93cSkardel } 710abb0f93cSkardel 711abb0f93cSkardel 712abb0f93cSkardel int 713abb0f93cSkardel compare_key_tok_text( 7142950cc38Schristos const void *a1, 7152950cc38Schristos const void *a2 716abb0f93cSkardel ) 717abb0f93cSkardel { 7182950cc38Schristos const struct key_tok *p1 = a1; 7192950cc38Schristos const struct key_tok *p2 = a2; 720abb0f93cSkardel 721abb0f93cSkardel return strcmp(p1->key, p2->key); 722abb0f93cSkardel } 723abb0f93cSkardel 724abb0f93cSkardel 725abb0f93cSkardel /* 726abb0f93cSkardel * populate_symb() - populate symb[] lookup array with symbolic token 727abb0f93cSkardel * names such that symb[T_Age] == "T_Age", etc. 728abb0f93cSkardel */ 729abb0f93cSkardel void 730abb0f93cSkardel populate_symb( 731abb0f93cSkardel char *header_file 732abb0f93cSkardel ) 733abb0f93cSkardel { 734abb0f93cSkardel FILE * yh; 7352950cc38Schristos char line[2 * MAX_TOK_LEN]; 7362950cc38Schristos char name[2 * MAX_TOK_LEN]; 737abb0f93cSkardel int token; 738abb0f93cSkardel 739abb0f93cSkardel yh = fopen(header_file, "r"); 740abb0f93cSkardel if (NULL == yh) { 741abb0f93cSkardel perror("unable to open yacc/bison header file"); 742abb0f93cSkardel exit(4); 743abb0f93cSkardel } 744abb0f93cSkardel 745abb0f93cSkardel while (NULL != fgets(line, sizeof(line), yh)) 746abb0f93cSkardel if (2 == sscanf(line, "#define %s %d", name, &token) 747abb0f93cSkardel && 'T' == name[0] && '_' == name[1] && token >= 0 7482950cc38Schristos && token < COUNTOF(symb)) { 749abb0f93cSkardel 750abb0f93cSkardel symb[token] = estrdup(name); 7512950cc38Schristos if (strlen(name) > MAX_TOK_LEN) { 7522950cc38Schristos fprintf(stderr, 7532950cc38Schristos "MAX_TOK_LEN %d too small for '%s'\n" 7542950cc38Schristos "Edit keyword-gen.c to raise.\n", 7552950cc38Schristos MAX_TOK_LEN, name); 7562950cc38Schristos exit(10); 7572950cc38Schristos } 7582950cc38Schristos } 759abb0f93cSkardel fclose(yh); 760abb0f93cSkardel } 761abb0f93cSkardel 762abb0f93cSkardel 763abb0f93cSkardel const char * 764abb0f93cSkardel symbname( 7652950cc38Schristos u_short token 766abb0f93cSkardel ) 767abb0f93cSkardel { 768abb0f93cSkardel char *name; 769abb0f93cSkardel 7702950cc38Schristos if (token < COUNTOF(symb) && symb[token] != NULL) { 7712950cc38Schristos name = symb[token]; 7722950cc38Schristos } else { 773abb0f93cSkardel LIB_GETBUF(name); 774abb0f93cSkardel snprintf(name, LIB_BUFLENGTH, "%d", token); 7752950cc38Schristos } 7762950cc38Schristos 777abb0f93cSkardel return name; 778abb0f93cSkardel } 779