Subject: Non strict REQUIRE/BEFORE in rcorder(8)
To: None <tech-userlevel@netbsd.org, current-users@netbsd.org>
From: Mike M. Volokhov <mishka@apk.od.ua>
List: current-users
Date: 05/20/2004 17:28:31
Greetings!

In http://mail-index.NetBSD.org/tech-userlevel/2004/02/04/0001.html I've
wrote about useful extension for rcorder(8), which will made possible
non-strict BEFORE/REQUIRE dependencies.

Accordingly to Jason Thorpe's proposition about using special keywords
instead of using special marks for values (see
http://mail-index.NetBSD.org/tech-userlevel/2004/02/04/0002.html), I
decide to implement this strategy in rcorder(8). Now, for providing non
strict dependencies just use "OPT-REQUIRE" and "OPT-BEFORE" keywords.
The "REQUIRE" and "BEFORE" keywords may be used in the same file as
well. For example:

	# PROVIDE: avdaemon

	# PROVIDE: mail sendmail

	# PROVIDE: avmilter
	# OPT-REQUIRE: avdaemon
	# OPT-BEFORE: sendmail

And if all sendmail, AV daemon, and AV filter will be installed on three
separate hosts, there are no errors will be produced for "avmilter"
script.

Please see a patch below. Shortly said, I've expand structures for
require/before lists within "optional" flag. Thus, we can choose how to
handle non available dependencies.

Any comments are welcome.

--
Mishka.


Index: rcorder.c
===================================================================
RCS file: /cvsroot/src/sbin/rcorder/rcorder.c,v
retrieving revision 1.12
diff -u -r1.12 rcorder.c
--- rcorder.c	13 Oct 2003 14:22:20 -0000	1.12
+++ rcorder.c	20 May 2004 14:06:03 -0000
@@ -81,12 +81,18 @@
 #define REQUIRE_LEN	(sizeof(REQUIRE_STR) - 1)
 #define REQUIRES_STR	"# REQUIRES:"
 #define REQUIRES_LEN	(sizeof(REQUIRES_STR) - 1)
+#define OPT_REQUIRE_STR	"# OPT-REQUIRE:"
+#define OPT_REQUIRE_LEN	(sizeof(OPT_REQUIRE_STR) - 1)
+#define OPT_REQUIRES_STR	"# OPT-REQUIRES:"
+#define OPT_REQUIRES_LEN	(sizeof(OPT_REQUIRES_STR) - 1)
 #define PROVIDE_STR	"# PROVIDE:"
 #define PROVIDE_LEN	(sizeof(PROVIDE_STR) - 1)
 #define PROVIDES_STR	"# PROVIDES:"
 #define PROVIDES_LEN	(sizeof(PROVIDES_STR) - 1)
 #define BEFORE_STR	"# BEFORE:"
 #define BEFORE_LEN	(sizeof(BEFORE_STR) - 1)
+#define OPT_BEFORE_STR	"# OPT-BEFORE:"
+#define OPT_BEFORE_LEN	(sizeof(OPT_BEFORE_STR) - 1)
 #define KEYWORD_STR	"# KEYWORD:"
 #define KEYWORD_LEN	(sizeof(KEYWORD_STR) - 1)
 #define KEYWORDS_STR	"# KEYWORDS:"
@@ -122,11 +128,13 @@
 };
 
 struct f_reqnode {
+	int		optional;
 	Hash_Entry	*entry;
 	f_reqnode	*next;
 };
 
 struct strnodelist {
+	int		optional;
 	filenode	*node;
 	strnodelist	*next;
 	char		s[1];
@@ -156,8 +164,10 @@
 void parse_line(filenode *, char *, void (*)(filenode *, char *));
 filenode *filenode_new(char *);
 void add_require(filenode *, char *);
+void add_opt_require(filenode *, char *);
 void add_provide(filenode *, char *);
 void add_before(filenode *, char *);
+void add_opt_before(filenode *, char *);
 void add_keyword(filenode *, char *);
 void insert_before(void);
 Hash_Entry *make_fake_provision(filenode *);
@@ -227,6 +237,7 @@
 	strnodelist *ent;
 
 	ent = emalloc(sizeof *ent + strlen(s));
+	ent->optional = RESET;
 	ent->node = fnode;
 	strcpy(ent->s, s);
 	ent->next = *listp;
@@ -285,6 +296,18 @@
 	rnode->entry = entry;
 	rnode->next = fnode->req_list;
 	fnode->req_list = rnode;
+	fnode->req_list->optional = RESET;
+}
+
+/*
+ * add an optional requirement to a filenode.
+ */
+void
+add_opt_require(filenode *fnode, char *s)
+{
+
+	add_require(fnode, s);
+	fnode->req_list->optional = SET;
 }
 
 /*
@@ -376,6 +399,16 @@
 
 	strnode_add(&bl_list, s, fnode);
 }
+/*
+ * put the OPT-BEFORE: lines to a list and handle them later.
+ */
+void
+add_opt_before(filenode *fnode, char *s)
+{
+
+	add_before(fnode, s);
+	bl_list->optional = SET;
+}
 
 /*
  * add a key to a filenode.
@@ -411,6 +444,7 @@
 	FILE *fp;
 	char *buf;
 	int require_flag, provide_flag, before_flag, keyword_flag;
+	int opt_require_flag, opt_before_flag;
 	enum { BEFORE_PARSING, PARSING, PARSING_DONE } state;
 	filenode *node;
 	char delims[3] = { '\\', '\\', '\0' };
@@ -444,16 +478,23 @@
 	for (state = BEFORE_PARSING; state != PARSING_DONE &&
 	    (buf = fparseln(fp, NULL, NULL, delims, 0)) != NULL; free(buf)) {
 		require_flag = provide_flag = before_flag = keyword_flag = 0;
+		opt_require_flag = opt_before_flag = 0;
 		if (strncmp(REQUIRE_STR, buf, REQUIRE_LEN) == 0)
 			require_flag = REQUIRE_LEN;
 		else if (strncmp(REQUIRES_STR, buf, REQUIRES_LEN) == 0)
 			require_flag = REQUIRES_LEN;
+		else if (strncmp(OPT_REQUIRE_STR, buf, OPT_REQUIRE_LEN) == 0)
+			opt_require_flag = OPT_REQUIRE_LEN;
+		else if (strncmp(OPT_REQUIRES_STR, buf, OPT_REQUIRES_LEN) == 0)
+			opt_require_flag = OPT_REQUIRES_LEN;
 		else if (strncmp(PROVIDE_STR, buf, PROVIDE_LEN) == 0)
 			provide_flag = PROVIDE_LEN;
 		else if (strncmp(PROVIDES_STR, buf, PROVIDES_LEN) == 0)
 			provide_flag = PROVIDES_LEN;
 		else if (strncmp(BEFORE_STR, buf, BEFORE_LEN) == 0)
 			before_flag = BEFORE_LEN;
+		else if (strncmp(OPT_BEFORE_STR, buf, OPT_BEFORE_LEN) == 0)
+			opt_before_flag = OPT_BEFORE_LEN;
 		else if (strncmp(KEYWORD_STR, buf, KEYWORD_LEN) == 0)
 			keyword_flag = KEYWORD_LEN;
 		else if (strncmp(KEYWORDS_STR, buf, KEYWORDS_LEN) == 0)
@@ -467,10 +508,14 @@
 		state = PARSING;
 		if (require_flag)
 			parse_line(node, buf + require_flag, add_require);
+		else if (opt_require_flag)
+			parse_line(node, buf+opt_require_flag, add_opt_require);
 		else if (provide_flag)
 			parse_line(node, buf + provide_flag, add_provide);
 		else if (before_flag)
-			parse_line(node, buf + before_flag,  add_before);
+			parse_line(node, buf + before_flag, add_before);
+		else if (opt_before_flag)
+			parse_line(node, buf + opt_before_flag, add_opt_before);
 		else if (keyword_flag)
 			parse_line(node, buf + keyword_flag, add_keyword);
 	}
@@ -538,8 +583,16 @@
 		fake_prov_entry = make_fake_provision(bl_list->node);
 
 		entry = Hash_CreateEntry(provide_hash, bl_list->s, &new);
-		if (new == 1)
-			warnx("file `%s' is before unknown provision `%s'", bl_list->node->filename, bl_list->s);
+		if (new == 1) {
+			if (bl_list->optional == SET) {
+			    DPRINTF((stderr,
+				"unresolved OPT-BEFORE `%s' in file `%s'\n",
+				bl_list->s, bl_list->node->filename));
+			} else {
+			    warnx("file `%s' is before unknown provision `%s'",
+				bl_list->node->filename, bl_list->s);
+			}
+		}
 
 		for (pnode = Hash_GetValue(entry); pnode; pnode = pnode->next) {
 			if (pnode->head)
@@ -596,10 +649,17 @@
 	head = Hash_GetValue(entry);
 
 	if (head == NULL) {
-		warnx("requirement `%s' in file `%s' has no providers.",
-		    Hash_GetKey(entry), filename);
-		exit_code = 1;
-		return;
+		if (rnode->optional == SET) {
+			DPRINTF((stderr,
+			    "unresolved OPT-REQUIRE `%s' in file `%s'\n",
+			    Hash_GetKey(entry), filename));
+			return;
+		} else {
+			warnx("requirement `%s' in file `%s' has no providers.",
+			    Hash_GetKey(entry), filename);
+			exit_code = 1;
+			return;
+		}
 	}
 
 	/* return if the requirement is already satisfied. */