From db3bf25bdf898a60ec5906036847a0c4c06aea08 Mon Sep 17 00:00:00 2001
From: Michael Tuexen <tuexen@fh-muenster.de>
Date: Sun, 3 Jun 2018 14:48:13 +0200
Subject: [PATCH] Add support for defines as in Google version 2.0.

You can use -Dproto=IPPROTO_SCTP or --define proto=IPPROTO_SCTP
and run the script
```
 0.0 socket(..., SOCK_STREAM, proto) = 3
+0.0 close (3)
```
to control the protocol being used from the command line.
---
 gtests/net/packetdrill/config.c | 42 ++++++++++++++++++++--------
 gtests/net/packetdrill/config.h | 49 +++++++++++++++++++++++++++++++++
 gtests/net/packetdrill/lexer.l  | 31 ++++++++++++++++++++-
 gtests/net/packetdrill/parse.h  |  5 +++-
 gtests/net/packetdrill/parser.y |  8 +++---
 5 files changed, 117 insertions(+), 18 deletions(-)

diff --git a/gtests/net/packetdrill/config.c b/gtests/net/packetdrill/config.c
index bff0d71e..1d344781 100644
--- a/gtests/net/packetdrill/config.c
+++ b/gtests/net/packetdrill/config.c
@@ -58,13 +58,14 @@ enum option_codes {
 	OPT_TCP_TS_TICK_USECS,
 	OPT_NON_FATAL,
 	OPT_DRY_RUN,
-	OPT_VERBOSE = 'v',	/* our only single-letter option */
 	OPT_DEBUG,
 	OPT_UDP_ENCAPS,
 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
 	OPT_TUN_DEV,
 	OPT_PERSISTENT_TUN_DEV,
 #endif
+	OPT_DEFINE = 'D',	/* a '-D' single-letter option */
+	OPT_VERBOSE = 'v',	/* a '-v' single-letter option */
 };
 
 /* Specification of command line options for getopt_long(). */
@@ -92,6 +93,7 @@ struct option options[] = {
 	{ "tcp_ts_tick_usecs",	.has_arg = true,  NULL, OPT_TCP_TS_TICK_USECS },
 	{ "non_fatal",		.has_arg = true,  NULL, OPT_NON_FATAL },
 	{ "dry_run",		.has_arg = false, NULL, OPT_DRY_RUN },
+	{ "define",		.has_arg = true,  NULL, OPT_DEFINE },
 	{ "verbose",		.has_arg = false, NULL, OPT_VERBOSE },
 	{ "debug",		.has_arg = false, NULL, OPT_DEBUG },
 	{ "udp_encapsulation",	.has_arg = true,  NULL, OPT_UDP_ENCAPS },
@@ -128,6 +130,7 @@ void show_usage(void)
 		"\t[--wire_client_dev=<eth_dev_name>]\n"
 		"\t[--wire_server_dev=<eth_dev_name>]\n"
 		"\t[--dry_run]\n"
+		"\t[--define symbol1=val1 --define symbol2=val2 ...]\n"
 		"\t[--verbose|-v]\n"
 		"\t[--debug] * requires compilation with DEBUG *\n"
 		"\t[--udp_encapsulation=[sctp,tcp]]\n"
@@ -391,7 +394,7 @@ static void process_option(int opt, char *optarg, struct config *config,
 			   char *where)
 {
 	int port = 0;
-	char *end = NULL;
+	char *end = NULL, *equals = NULL, *symbol = NULL, *value = NULL;
 	unsigned long speed = 0;
 
 	DEBUGP("process_option %d = %s\n", opt, optarg);
@@ -494,6 +497,14 @@ static void process_option(int opt, char *optarg, struct config *config,
 	case OPT_DRY_RUN:
 		config->dry_run = true;
 		break;
+	case OPT_DEFINE:
+		equals = strstr(optarg, "=");
+		if (equals == optarg || equals == NULL)
+			die("%s: bad definition: %s\n", where, optarg);
+		symbol = strndup(optarg, equals - optarg);
+		value = strdup(equals + 1);
+		definition_set(&config->defines, symbol, value);
+		break;
 	case OPT_VERBOSE:
 		config->verbose = true;
 		break;
@@ -550,7 +561,7 @@ char **parse_command_line_options(int argc, char *argv[],
 
 	/* Parse the arguments. */
 	optind = 0;
-	while ((c = getopt_long(argc, argv, "v", options, NULL)) > 0)
+	while ((c = getopt_long(argc, argv, "vD:", options, NULL)) > 0)
 		process_option(c, optarg, config, "Command Line");
 	return argv + optind;
 }
@@ -560,6 +571,7 @@ static void parse_script_options(struct config *config,
 {
 	struct option_list *opt = option_list;
 	while (opt != NULL) {
+printf("opt->name = %s\n", opt->name);
 		int i;
 		int c = 0;
 		for (i = 0; options[i].name != NULL; i++) {
@@ -568,15 +580,21 @@ static void parse_script_options(struct config *config,
 				break;
 			}
 		}
-		if (c != 0) {
-			process_option(options[i].val,
-				       opt->value, config,
-				       config->script_path);
-		} else {
-			die("%s: option '%s' unknown in file: %s\n",
-			    config->script_path, opt->name,
-			    config->script_path);
-		}
+
+		if (!c)
+			die("%s: option '%s' unknown\n",
+			    config->script_path, opt->name);
+		if (opt->value && !options[i].has_arg)
+			die("%s: option '%s' forbids an argument\n",
+			    config->script_path, opt->name);
+		if (!opt->value && options[i].has_arg)
+			die("%s: option '%s' requires an argument\n",
+			    config->script_path, opt->name);
+
+		process_option(options[i].val,
+			       opt->value, config,
+			       config->script_path);
+
 		opt = opt->next;
 	}
 }
diff --git a/gtests/net/packetdrill/config.h b/gtests/net/packetdrill/config.h
index 3ee061d4..561785d3 100644
--- a/gtests/net/packetdrill/config.h
+++ b/gtests/net/packetdrill/config.h
@@ -40,6 +40,52 @@
 
 extern struct option options[];
 
+/* A linked list of symbol->value (FOO=bar) definitions from command line. */
+struct definition {
+	char *symbol;	/* name of the symbol; owns the string */
+	char *value;	/* value of the symbol; owns the string */
+	struct definition *next;	/* link for linked list */
+};
+
+/* Return the definition in the linked list with a matching symbol, or NULL */
+static inline struct definition *definition_find(struct definition *defs,
+						 char *symbol)
+{
+	struct definition *def = NULL;
+
+	for (def = defs; def != NULL; def = def->next) {
+		if (strcmp(def->symbol, symbol) == 0)
+			return def;
+	}
+	return NULL;
+}
+
+/* Set the value of the given symbol to the given value. */
+static inline void definition_set(struct definition **defs,
+				  char *symbol, char *value)
+{
+	struct definition *def = definition_find(*defs, symbol);
+
+	if (def) {
+		free(def->value);
+		def->value = value;
+	} else {
+		def = calloc(1, sizeof(struct definition));
+		def->symbol = symbol;
+		def->value = value;
+		def->next = *defs;	/* link to existing entries */
+		*defs = def;	/* insert at head of linked list */
+	}
+}
+
+/* Return the value of the given symbol, or NULL if not found. */
+static inline char *definition_get(struct definition *defs, char *symbol)
+{
+	struct definition *def = definition_find(defs, symbol);
+
+	return def ? def->value : NULL;
+}
+
 struct config {
 	const char **argv;			/* a copy of process argv */
 
@@ -112,6 +158,9 @@ struct config {
 	char *tun_device;
 	bool persistent_tun_device;
 #endif
+
+	/* List of FOO=bar definitions from command line. */
+	struct definition *defines;
 };
 
 /* Top-level info about the invocation of a test script */
diff --git a/gtests/net/packetdrill/lexer.l b/gtests/net/packetdrill/lexer.l
index a91ab140..b46d1c32 100644
--- a/gtests/net/packetdrill/lexer.l
+++ b/gtests/net/packetdrill/lexer.l
@@ -49,6 +49,8 @@
 #include <stdio.h>
 #include "script.h"
 #include "tcp_options.h"
+#include "parse.h"
+#include "config.h"
 
 /* This include of the bison-generated .h file must go last so that we
  * can first include all of the declarations on which it depends.
@@ -75,6 +77,33 @@ static char *quoted(const char *s)
 	return strndup(s + delim_len, strlen(s) - 2*delim_len);
 }
 
+/* Check to see if the word in yytext is a user-defined symbol, and if so then
+ * return its value. Otherwise return the word itself.
+ */
+int word(void)
+{
+	char *word = yytext;
+	char *value = NULL;
+
+	/* Look in symbol table for matching user-defined symbol->value map. */
+	value = definition_get(in_config->defines, word);
+	if (value) {
+		if (value[0] == '"') {
+			yylval.string = quoted(value);		/* SYM="val" */
+			return STRING;
+		} else if (value[0] == '`') {
+			yylval.string = quoted(value);		/* SYM=`val` */
+			return BACK_QUOTED;
+		} else {
+			yylval.string = strdup(value);		/* SYM=val */
+			return WORD;
+		}
+	}
+	/* A literal word (e.g. system call name or socket option name). */
+	yylval.string = strdup(word);
+	return WORD;
+}
+
 /* Copy the code inside a code snippet that is enclosed in %{ }% after
  * first stripping the space and tab characters from either end of the
  * snippet. We strip leading and trailing whitespace for Python users
@@ -655,7 +684,7 @@ NULL				return NULL_;
 [-]?[0-9]*[.][0-9]+		yylval.floating	= atof(yytext);   return FLOAT;
 [-]?[0-9]+			yylval.integer	= atoll(yytext);  return INTEGER;
 0x[0-9a-fA-F]+			yylval.integer	= hextol(yytext); return HEX_INTEGER;
-[a-zA-Z0-9_]+			yylval.string	= strdup(yytext); return WORD;
+[a-zA-Z0-9_]+			return word();
 \"(\\.|[^"])*\"			yylval.string	= quoted(yytext); return STRING;
 \`(\\.|[^`])*\`			yylval.string	= quoted(yytext); return BACK_QUOTED;
 [^ \t\n]			return (int) yytext[0];
diff --git a/gtests/net/packetdrill/parse.h b/gtests/net/packetdrill/parse.h
index f7ba2b78..989ebced 100644
--- a/gtests/net/packetdrill/parse.h
+++ b/gtests/net/packetdrill/parse.h
@@ -52,8 +52,11 @@ extern void read_script(const char *script_path,
  * implementation for this function is in the bison parser file
  * parser.y.
  */
-extern int parse_script(const struct config *config,
+extern int parse_script(struct config *config,
 			struct script *script,
 			struct invocation *callback_invocation);
 
+/* Config for lexing and parsing. */
+extern struct config *in_config;
+
 #endif /* __PARSER_H__ */
diff --git a/gtests/net/packetdrill/parser.y b/gtests/net/packetdrill/parser.y
index 739184eb..d00f6775 100644
--- a/gtests/net/packetdrill/parser.y
+++ b/gtests/net/packetdrill/parser.y
@@ -141,7 +141,7 @@ static int current_script_line = -1;
  * We uses this object to look up configuration info needed during
  * parsing (such as whether packets are IPv4 or IPv6).
  */
-static const struct config *in_config = NULL;
+struct config *in_config = NULL;
 
 /* The output of the parser: an output script containing
  * 1) a linked list of options
@@ -228,9 +228,9 @@ void read_script(const char *script_path, struct script *script)
  * text script file with the given path name and fills in the script
  * object with the parsed representation.
  */
-int parse_script(const struct config *config,
-			 struct script *script,
-			 struct invocation *callback_invocation)
+int parse_script(struct config *config,
+		 struct script *script,
+		 struct invocation *callback_invocation)
 {
 	/* This bison-generated parser is not multi-thread safe, so we
 	 * have a lock to prevent more than one thread using the
-- 
GitLab