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-30 11:27:57.000000000 -0500
@@ -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,32 @@
 	/* 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:
+	*
+	* If more than two values are supplies, the map will keep trying to compare
+	* subsuquent strings with the first one.
+	* 
+	* 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
+	*
+	* The map also understands the -b for basic regex and -e for extended regex.
+	*
+	* This is of limited value since regex strings tend to get mangled, but it
+	* will cause the map to compare the strings to eachother as regex strings
+	* It will also swap the strings to compare the strings.
+	*
+	*/
+	"_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-30 11:22:50.000000000 -0500
@@ -7692,3 +7692,448 @@
 	return rval;
 }
 #endif /* SOCKETMAP */
+#if _FFR_COMPARE_MAP
+
+/* 
+ * copied from Debian GNU/Linux echo -- GPL2 may apply, and might
+ * need to be replaced. 
+ */
+static void
+convert_escape(s)
+	char *s;
+{
+	register char *s1 = s;
+	char c;
+
+	if (tTd(38, 10))
+		sm_dprintf("convert_escape(%s)\n", s);
+	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';
+	if (tTd(38, 10))
+		sm_dprintf("convert_escape converts to %s\n", s);
+}
+/* end copy */
+
+static void * 
+printline_memmem(needle, nlen, haystack, hlen, pmtch)
+	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 (tTd(38, 10))
+		sm_dprintf("printline_memmem(%s, %d, %s, %d, %p)\n",
+				needle, nlen, haystack, hlen, pmtch);
+		
+	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;
+			if (tTd(38, 10))
+				sm_dprintf("printline_memmem finds %s\n",
+						haystack + j);
+		}
+		j++;
+	}
+	if(pmtch)
+	{
+		*pmtch = i;
+		if (tTd(38, 10))
+			sm_dprintf("printline_memmem almost finds %s, possible match in %d chars\n",
+					haystack+j, 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.
+	*/
+	if (tTd(38, 10))
+		sm_dprintf("compare_map_parseargs %s: before map_parseargs\n", 
+				map->map_mname);
+
+	for (;;)
+	{
+		while (*p && *p != '-')
+			p++;
+		if (*p == '\0')
+			break;
+
+		switch (*++p)
+		{
+		  case 'n':
+			map->map_mflags |= MF_REGEX_NOT;
+			if (tTd(38, 5))
+				sm_dprintf("compare_map_parseargs %s: inverting matches\n", 
+						map->map_mname);
+			*p = ' ';/* hide */
+			break;
+
+		  case 'b':	/* basic regular expressions */
+			map->map_mflags |= MF_MATCHONLY;
+			if (tTd(38, 5))
+				sm_dprintf("compare_map_parseargs %s: basic regex\n", 
+						map->map_mname);
+			*p = ' ';/* hide */
+			break;
+			
+		  case 'e':	/* extended regular expressions */
+			map->map_mflags |= MF_SINGLEMATCH;
+			if (tTd(38, 5))
+				sm_dprintf("compare_map_parseargs %s: extended regex\n", 
+						map->map_mname);
+			*p = ' ';/* hide */
+			break;
+
+		  default:
+			/* do nothing */
+			break;
+		}
+	}
+
+	if (map->map_keycolnm)
+	{
+		convert_escape(map->map_keycolnm);
+		if (tTd(38, 10))
+			sm_dprintf("compare_map_parseargs %s: keycolnm %s\n", 
+					map->map_mname, map->map_keycolnm);
+	}
+	if (map->map_valcolnm)
+	{
+		convert_escape(map->map_valcolnm);
+		if (tTd(38, 10))
+			sm_dprintf("compare_map_parseargs %s: valcolnm %s\n", 
+					map->map_mname, map->map_valcolnm);
+	}
+
+	rval = map_parseargs(map, ap);
+
+	if (tTd(38, 10))
+		sm_dprintf("compare_map_parseargs %s: after map_parseargs\n", 
+				map->map_mname);
+
+}
+
+# if MAP_REGEX
+static int
+regex_cmp(str1, str2, flags)
+	const char * str1;
+	const char * str2;
+	const int flags;
+{
+	const char *s1 = str1;
+	const char *s2 = str2;
+	int ival = 0;
+	regex_t preg;
+
+	if (tTd(38, 10))
+		sm_dprintf("regex_cmp(%s, %s, flags)\n",
+				str1, str2);
+retry:
+	ival = regcomp(&preg, s2, flags);
+	if (ival)
+		goto done;
+	ival = regexec(&preg, s1, 0, NULL, 0);
+	if (!ival || str1 == s2)
+		goto done;
+	else
+	{
+		if (tTd(38, 10))
+			sm_dprintf("regex_cmp swapping arguments\n");
+	
+		s1 = str2;
+		s2 = str1;
+		regfree(&preg);
+		goto retry;
+	}
+
+done:
+	regfree(&preg);
+
+	if (tTd(38, 10))
+		sm_dprintf("regex_cmp returns %s\n", (ival) ? "NO MATCH" : "MATCH");
+	return ival;
+}
+
+
+static int
+extendcaseregex_cmp(s1, s2)
+	const char * s1;
+	const char * s2;
+{
+	int flags = REG_EXTENDED | REG_ICASE | REG_NOSUB;
+	return regex_cmp(s1, s2, flags);
+}
+
+static int
+extendregex_cmp(s1, s2)
+	const char * s1;
+	const char * s2;
+{
+	int flags = REG_EXTENDED | REG_NOSUB;
+	return regex_cmp(s1, s2, flags);
+}
+
+static int
+basicregex_cmp(s1, s2)
+	const char * s1;
+	const char * s2;
+{
+	int flags = REG_NOSUB;
+	return regex_cmp(s1, s2, flags);
+}
+
+static int
+basiccaseregex_cmp(s1, s2)
+	const char * s1;
+	const char * s2;
+{
+	int flags = REG_ICASE | REG_NOSUB;
+	return regex_cmp(s1, s2, flags);
+}
+
+# endif /* MAP_REGEX */
+
+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;
+	char * delimstr = NULL;
+
+# if MAP_REGEX
+	if (bitset(MF_MATCHONLY, map->map_mflags))
+	{
+		if (bitset(MF_NOFOLDCASE, map->map_mflags))
+		{
+			cmpfunc = basicregex_cmp; 
+			if (tTd(38, 10))
+				sm_dprintf("compare_map_lookup %s: selects basic case sensitive regex\n",
+						map->map_mname);
+		}
+		else
+		{
+			cmpfunc = basiccaseregex_cmp;
+			if (tTd(38, 10))
+				sm_dprintf("compare_map_lookup %s: selects basic case insensitive regex\n",
+						map->map_mname);
+		}
+	}
+	else if (bitset(MF_SINGLEMATCH, map->map_mflags))
+	{
+		if (bitset(MF_NOFOLDCASE, map->map_mflags))
+		{
+			cmpfunc = extendregex_cmp; 
+			if (tTd(38, 10))
+				sm_dprintf("compare_map_lookup %s: selects extended case sensitive regex\n",
+						map->map_mname);
+		}
+		else
+		{
+			cmpfunc = extendcaseregex_cmp;
+			if (tTd(38, 10))
+				sm_dprintf("compare_map_lookup %s: selects extended case insensitive regex\n",
+						map->map_mname);
+		}
+	}
+	else 
+# endif /*MAP_REGEX */
+	if (bitset(MF_NOFOLDCASE, map->map_mflags))
+	{
+		cmpfunc = strcmp;
+		if (tTd(38, 10))
+			sm_dprintf("compare_map_lookup %s: selects case sensitive\n",
+					map->map_mname);
+	}
+
+	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 (tTd(38, 10))
+			sm_dprintf("compare_map_lookup %s: inverted return results\n",
+					map->map_mname);
+	}
+
+	if (av && av[0] && av[1])
+	{
+		int i = 1;
+		if (tTd(38, 10))
+			sm_dprintf("compare_map_lookup %s: using supplied arguments\n",
+					map->map_mname);
+		while(av[i] && result)
+		{
+			if (tTd(38, 5))
+				sm_dprintf("compare_map_lookup %s: comparing \"%s\" to \"%s\"\n",
+						map->map_mname, av[0], av[i]);
+			result = cmpfunc(av[0], av[i++]);
+		}
+		if (tTd(38, 5))
+			sm_dprintf("compare_map_lookup %s: match %sfound between %s and %s\n",
+					map->map_mname, (result) ? "not ": "",
+					av[0], av[i-1]);
+	}
+	else
+	{
+		if(name && !str2 && map->map_coldelim &&
+			(str2 = strchr(name,  map->map_coldelim))
+		  )
+		{
+			if (!str1)
+				str1 = newstr(name);
+			delimlen = 1;
+			if (tTd(38, 10))
+				sm_dprintf("compare_map_lookup %s: using delimiter %c\n",
+						map->map_mname, map->map_coldelim);
+		}
+		if(name && !str2 && (delimstr = map->map_keycolnm))
+		{
+			if (!str1)
+				str1 = newstr(name);
+			delimlen = strlen(delimstr);
+			if (tTd(38, 10))
+				sm_dprintf("compare_map_lookup %s: using delimiter %s\n",
+						map->map_mname, delimstr);
+			str2 = printline_memmem(delimstr, delimlen,
+					name, strlen(name), NULL);
+		}
+		if(name && !str2 && (delimstr = map->map_valcolnm))
+		{
+			if (!str1)
+				str1 = newstr(name);
+			delimlen = strlen(delimstr);
+			if (tTd(38, 10))
+				sm_dprintf("compare_map_lookup %s: using delimiter %s\n",
+						map->map_mname, delimstr);
+			str2 = printline_memmem(delimstr, delimlen,
+					name, strlen(name), NULL);
+		}
+		if (name && str1 && str2)
+		{
+			char *str3 = str2;
+			int fudge = (delimlen > 1) ? 1 : 0; /*whitespace between strings */
+
+			if (isspace(str1[str2-name-fudge]))
+				str1[str2-name - fudge] = '\0';
+			else
+				str1[str2-name] = '\0';
+
+			while(str3)
+			{
+				char csav;
+
+				str2 += delimlen;
+				if (tTd(38, 20))
+					sm_dprintf("compare_map_lookup %s: comparing %s\n",
+							map->map_mname, str2);
+				if(isspace(*(str2)))
+					str2 += fudge;
+				
+				if (tTd(38, 20))
+					sm_dprintf("compare_map_lookup %s: comparing %s\n",
+							map->map_mname, str2);
+
+				str3 = printline_memmem(delimstr, delimlen, 
+						str2, strlen(str2), NULL);
+				if (tTd(38, 20))
+					sm_dprintf("compare_map_lookup %s: found %s\n",
+							map->map_mname, str3);
+				if (str3)
+				{
+					csav = *str3;
+					*str3 = '\0';
+					if (isspace(*(str3-1)))
+						*(str3-1) = '\0';
+				}
+
+				if (tTd(38, 5))
+					sm_dprintf("compare_map_lookup %s: comparing \"%s\" to \"%s\"\n",
+							map->map_mname, str1, str2);
+				result = cmpfunc(str1, str2);
+				if(str3)
+				{
+					*str3 = csav;
+					str2 = str3;
+				}
+			}
+			if (tTd(38, 5))
+				sm_dprintf("compare_map_lookup %s: match %sfound between %s and %s\n",
+						map->map_mname, (result) ? "not ": "",
+						str1, (str3) ? str3 : str2);
+		}
+	}
+
+	if (str1)
+		sm_free(str1);
+	if (statp)
+		statp = EX_OK;
+	return (result) ? frval : srval;
+}
+
+#endif /* _FFR_COMPARE_MAP */
