Only in sendmail-8.13.4-compare/devtools/Site: site.config.m4
Only in sendmail-8.13.4-compare/devtools/Site: site.config.m4.Debian
Only in sendmail-8.13.4-orig/doc/op: op.txt
diff -ur sendmail-8.13.4-orig/sendmail/conf.c sendmail-8.13.4-compare/sendmail/conf.c
--- sendmail-8.13.4-orig/sendmail/conf.c	2005-03-07 12:18:44.000000000 -0500
+++ sendmail-8.13.4-compare/sendmail/conf.c	2005-10-23 14:02:14.000000000 -0400
@@ -655,6 +655,14 @@
 		socket_map_lookup, null_map_store);
 #endif /* SOCKETMAP */
 
+#if _FFR_COMPARE_MAP
+	/* Compare map -- textcomparisons */
+	MAPDEF("compare", NULL, 0,
+		compare_map_parseargs, null_map_open, null_map_close,
+		compare_map_lookup, null_map_store);
+
+#endif /* _FFR_COMPARE_MAP */
+
 	if (tTd(38, 2))
 	{
 		/* bogus map -- always return tempfail */
@@ -6055,6 +6063,21 @@
 	/* DeliveryMode per DaemonPortOptions: 'D' */
 	"_FFR_DM_PER_DAEMON",
 #endif /* _FFR_DM_PER_DAEMON */
+#if _FFR_COMPARE_MAP
+	/* Map type usefull for comparing strings
+	* default use is to compare 2 values seperarated by $@ 
+	* if the second argument is not supplied, the first will be parsed to find
+	* the second string according to:
+	* 
+	* the first of  -z'singlechardelim' or with -k'stringdelim' or with -v'stringdelim'
+	* 
+	* The map also understands -a"MATCH" and -T"" for the success and fail return values
+	* respecutfully (defaults shown). Nothing else is returned.
+	*
+	* The map also understands the -n map flag, to invert the sense of the test
+	*/
+	"_FFR_COMPARE_MAP",
+#endif /* _FFR_COMPARE_MAP */
 #if _FFR_DNSMAP_ALIASABLE
 	/* Allow dns map type to be used for aliases. */
 /* Don Lewis of TDK */
diff -ur sendmail-8.13.4-orig/sendmail/map.c sendmail-8.13.4-compare/sendmail/map.c
--- sendmail-8.13.4-orig/sendmail/map.c	2005-02-08 20:46:35.000000000 -0500
+++ sendmail-8.13.4-compare/sendmail/map.c	2005-10-23 14:04:55.000000000 -0400
@@ -7692,3 +7692,192 @@
 	return rval;
 }
 #endif /* SOCKETMAP */
+#if _FFR_COMPARE_MAP
+
+/* copied from Debian GNU/Linux echo */
+static void
+convert_escape(char *s)
+{
+	register char *s1 = s;
+	char c;
+
+	while ((c = *s++))
+	{
+			if (c == '\\' && *s)
+			{
+			switch (c = *s++)
+			{
+				case 'a': c = '\007'; break;
+				case 'b': c = '\b'; break;
+				case 'f': c = '\f'; break;
+				case 'n': c = '\n'; break;
+				case 'r': c = '\r'; break;
+				case 't': c = '\t'; break;
+				case 'v': c = (int) 0x0B; break;
+				case '0': case '1': case '2': case '3':
+				case '4': case '5': case '6': case '7':
+				c -= '0';
+				if (*s >= '0' && *s <= '7')
+				        c = c * 8 + (*s++ - '0');
+				if (*s >= '0' && *s <= '7')
+				        c = c * 8 + (*s++ - '0');
+				break;
+				case '\\': break;
+				default: ; break;
+			}
+	}
+	*s1++ = c;
+	}
+	*s1 = '\0';
+}
+
+/* copied from JM printline */
+static void * 
+printline_memmem(void * needle, size_t nlen, void * haystack, size_t hlen, size_t *pmtch)
+{
+	register size_t i = 0,j = 0;
+	register char * s = haystack;
+	register char * n = needle;
+
+	if(!needle || !nlen)
+		return haystack;
+	if(!haystack || !hlen)
+		return NULL;
+
+	while(j+i < hlen)
+	{
+		i=0;
+		while( i < nlen && j+i <hlen && (*(s+j+i)) == (*(n+i)) )
+			i++;
+
+		if(i == nlen)
+			return haystack + j;
+		j++;
+	}
+	if(pmtch)
+		*pmtch = i;
+	return NULL;
+}
+
+
+bool
+compare_map_parseargs(map, ap)
+	MAP *map;
+	char *ap;
+{
+	bool rval;
+	register char *p = ap;
+
+	/*
+	**  There is no check whether there is really an argument,
+	**  but that's not important enough to warrant extra code.
+	*/
+
+	for (;;)
+	{
+		while (isascii(*p) && isspace(*p))
+			p++;
+		if (*p != '-')
+			break;
+		switch (*++p)
+		{
+		  case 'n':
+			map->map_mflags |= MF_REGEX_NOT;
+			break;
+		  default:
+			/* do nothing */
+			break;
+		}
+	}
+
+	rval = map_parseargs(map, ap);
+	if (map->map_keycolnm)
+		convert_escape(map->map_keycolnm);
+	if (map->map_valcolnm)
+		convert_escape(map->map_valcolnm);
+}
+
+char *
+compare_map_lookup(map, name, av, statp)
+        MAP *map;
+        char *name;
+        char **av;
+        int *statp;
+{
+	char * frval = "";
+	char * srval = "MATCH";
+	int result = 1;
+	int (*cmpfunc)(const char*, const char*) = strcasecmp;
+	char * str2 = NULL;
+	char * str1 = NULL;
+	int delimlen = 0;
+
+
+	if (bitset(MF_NOFOLDCASE, map->map_mflags))
+		cmpfunc = strcmp;
+
+	if (map->map_app)
+		srval = map->map_app;
+	if (map->map_tapp)
+		frval = map->map_tapp;
+
+	if (bitset(MF_REGEX_NOT, map->map_mflags))
+	{
+		char *p = frval;
+		frval = srval;
+		srval = p;
+	}
+
+	if (av && av[0] && av[1])
+		result = cmpfunc(av[0], av[1]);
+	else
+	{
+		
+		if(name && !str2 && map->map_coldelim &&
+			(str2 = strchr(name,  map->map_coldelim))
+				)
+		{
+			str1 = newstr(name);
+			delimlen = 1;
+		}
+
+		if(name && !str2 && map->map_keycolnm)
+		{
+			str1 = newstr(name);
+			delimlen = strlen(map->map_keycolnm);
+			if (str1)
+				str2 = printline_memmem(map->map_keycolnm, 
+						delimlen,
+						name, strlen(name), NULL);
+				
+		}
+
+
+		if(name && !str2 && map->map_valcolnm)
+		{
+			str1 = newstr(name);
+			delimlen = strlen(map->map_valcolnm);
+			if (str1)
+				str2 = printline_memmem(map->map_valcolnm, 
+						strlen(map->map_valcolnm),
+						name, strlen(name), NULL);
+			
+		}
+
+		if (name && str1 && str2)
+		{
+			int fudge = (delimlen > 1) ? 1 : 0; /*whitespace between strings */
+			str1[str2-name - fudge] = '\0';
+			str2+=delimlen+fudge;
+			result = cmpfunc(str1, str2);
+		}
+	}
+
+	if (str1)
+		sm_free(str1);
+	if (statp)
+		statp = EX_OK;
+	return (result) ? frval : srval;
+}
+
+#endif /* _FFR_COMPARE_MAP */
