[comp.sources.unix] v12i037: C News alpha release, Part12/14

rsalz@uunet.UU.NET (Rich Salz) (10/23/87)

Submitted-by: utzoo!henry (Henry Spencer)
Posting-number: Volume 12, Issue 37
Archive-name: cnews/part12

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 12 (of 14)."
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'libc/strings/tester.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'libc/strings/tester.c'\"
else
echo shar: Extracting \"'libc/strings/tester.c'\" \(19922 characters\)
sed "s/^X//" >'libc/strings/tester.c' <<'END_OF_FILE'
X/*
X * Test program for string(3) routines.
X * 
X * Note that at least one Bell Labs implementation of the string
X * routines flunks a couple of these tests -- the ones which test
X * behavior on "negative" characters.
X */
X
X#include <stdio.h>
X#include <string.h>
X
X#define	STREQ(a, b)	(strcmp((a), (b)) == 0)
X
Xchar *it = "<UNSET>";		/* Routine name for message routines. */
Xint waserror = 0;		/* For exit status. */
X
Xchar uctest[] = "\004\203";	/* For testing signedness of chars. */
Xint charsigned;			/* Result. */
X
X/*
X - check - complain if condition is not true
X */
Xvoid
Xcheck(thing, number)
Xint thing;
Xint number;			/* Test number for error message. */
X{
X	if (!thing) {
X		printf("%s flunked test %d\n", it, number);
X		waserror = 1;
X	}
X}
X
X/*
X - equal - complain if first two args don't strcmp as equal
X */
Xvoid
Xequal(a, b, number)
Xchar *a;
Xchar *b;
Xint number;			/* Test number for error message. */
X{
X	check(a != NULL && b != NULL && STREQ(a, b), number);
X}
X
Xchar one[50];
Xchar two[50];
X
X#ifdef UNIXERR
X#define ERR 1
X#endif
X#ifdef BERKERR
X#define ERR 1
X#endif
X#ifdef ERR
Xint f;
Xextern char *sys_errlist[];
Xextern int sys_nerr;
Xextern int errno;
X#endif
X
X/* ARGSUSED */
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X	/*
X	 * First, establish whether chars are signed.
X	 */
X	if (uctest[0] < uctest[1])
X		charsigned = 0;
X	else
X		charsigned = 1;
X
X	/*
X	 * Then, do the rest of the work.  Split into two functions because
X	 * some compilers get unhappy about a single immense function.
X	 */
X	first();
X	second();
X
X	exit((waserror) ? 1 : 0);
X}
X
Xfirst()
X{
X	/*
X	 * Test strcmp first because we use it to test other things.
X	 */
X	it = "strcmp";
X	check(strcmp("", "") == 0, 1);		/* Trivial case. */
X	check(strcmp("a", "a") == 0, 2);	/* Identity. */
X	check(strcmp("abc", "abc") == 0, 3);	/* Multicharacter. */
X	check(strcmp("abc", "abcd") < 0, 4);	/* Length mismatches. */
X	check(strcmp("abcd", "abc") > 0, 5);
X	check(strcmp("abcd", "abce") < 0, 6);	/* Honest miscompares. */
X	check(strcmp("abce", "abcd") > 0, 7);
X	check(strcmp("a\203", "a") > 0, 8);	/* Tricky if char signed. */
X	if (charsigned)				/* Sign-bit comparison. */
X		check(strcmp("a\203", "a\003") < 0, 9);
X	else
X		check(strcmp("a\203", "a\003") > 0, 9);
X
X	/*
X	 * Test strcpy next because we need it to set up other tests.
X	 */
X	it = "strcpy";
X	check(strcpy(one, "abcd") == one, 1);	/* Returned value. */
X	equal(one, "abcd", 2);			/* Basic test. */
X
X	(void) strcpy(one, "x");
X	equal(one, "x", 3);			/* Writeover. */
X	equal(one+2, "cd", 4);			/* Wrote too much? */
X
X	(void) strcpy(two, "hi there");
X	(void) strcpy(one, two);
X	equal(one, "hi there", 5);		/* Basic test encore. */
X	equal(two, "hi there", 6);		/* Stomped on source? */
X
X	(void) strcpy(one, "");
X	equal(one, "", 7);			/* Boundary condition. */
X
X	/*
X	 * strcat
X	 */
X	it = "strcat";
X	(void) strcpy(one, "ijk");
X	check(strcat(one, "lmn") == one, 1);	/* Returned value. */
X	equal(one, "ijklmn", 2);		/* Basic test. */
X
X	(void) strcpy(one, "x");
X	(void) strcat(one, "yz");
X	equal(one, "xyz", 3);			/* Writeover. */
X	equal(one+4, "mn", 4);			/* Wrote too much? */
X
X	(void) strcpy(one, "gh");
X	(void) strcpy(two, "ef");
X	(void) strcat(one, two);
X	equal(one, "ghef", 5);			/* Basic test encore. */
X	equal(two, "ef", 6);			/* Stomped on source? */
X
X	(void) strcpy(one, "");
X	(void) strcat(one, "");
X	equal(one, "", 7);			/* Boundary conditions. */
X	(void) strcpy(one, "ab");
X	(void) strcat(one, "");
X	equal(one, "ab", 8);
X	(void) strcpy(one, "");
X	(void) strcat(one, "cd");
X	equal(one, "cd", 9);
X
X	/*
X	 * strncat - first test it as strcat, with big counts, then
X	 * test the count mechanism.
X	 */
X	it = "strncat";
X	(void) strcpy(one, "ijk");
X	check(strncat(one, "lmn", 99) == one, 1);	/* Returned value. */
X	equal(one, "ijklmn", 2);		/* Basic test. */
X
X	(void) strcpy(one, "x");
X	(void) strncat(one, "yz", 99);
X	equal(one, "xyz", 3);			/* Writeover. */
X	equal(one+4, "mn", 4);			/* Wrote too much? */
X
X	(void) strcpy(one, "gh");
X	(void) strcpy(two, "ef");
X	(void) strncat(one, two, 99);
X	equal(one, "ghef", 5);			/* Basic test encore. */
X	equal(two, "ef", 6);			/* Stomped on source? */
X
X	(void) strcpy(one, "");
X	(void) strncat(one, "", 99);
X	equal(one, "", 7);			/* Boundary conditions. */
X	(void) strcpy(one, "ab");
X	(void) strncat(one, "", 99);
X	equal(one, "ab", 8);
X	(void) strcpy(one, "");
X	(void) strncat(one, "cd", 99);
X	equal(one, "cd", 9);
X
X	(void) strcpy(one, "ab");
X	(void) strncat(one, "cdef", 2);
X	equal(one, "abcd", 10);			/* Count-limited. */
X
X	(void) strncat(one, "gh", 0);
X	equal(one, "abcd", 11);			/* Zero count. */
X
X	(void) strncat(one, "gh", 2);
X	equal(one, "abcdgh", 12);		/* Count and length equal. */
X
X	/*
X	 * strncmp - first test as strcmp with big counts, then test
X	 * count code.
X	 */
X	it = "strncmp";
X	check(strncmp("", "", 99) == 0, 1);	/* Trivial case. */
X	check(strncmp("a", "a", 99) == 0, 2);	/* Identity. */
X	check(strncmp("abc", "abc", 99) == 0, 3);	/* Multicharacter. */
X	check(strncmp("abc", "abcd", 99) < 0, 4);	/* Length unequal. */
X	check(strncmp("abcd", "abc", 99) > 0, 5);
X	check(strncmp("abcd", "abce", 99) < 0, 6);	/* Honestly unequal. */
X	check(strncmp("abce", "abcd", 99) > 0, 7);
X	check(strncmp("a\203", "a", 2) > 0, 8);	/* Tricky if '\203' < 0 */
X	if (charsigned)				/* Sign-bit comparison. */
X		check(strncmp("a\203", "a\003", 2) < 0, 9);
X	else
X		check(strncmp("a\203", "a\003", 2) > 0, 9);
X	check(strncmp("abce", "abcd", 3) == 0, 10);	/* Count limited. */
X	check(strncmp("abce", "abc", 3) == 0, 11);	/* Count == length. */
X	check(strncmp("abcd", "abce", 4) < 0, 12);	/* Nudging limit. */
X	check(strncmp("abc", "def", 0) == 0, 13);	/* Zero count. */
X
X	/*
X	 * strncpy - testing is a bit different because of odd semantics
X	 */
X	it = "strncpy";
X	check(strncpy(one, "abc", 4) == one, 1);	/* Returned value. */
X	equal(one, "abc", 2);			/* Did the copy go right? */
X
X	(void) strcpy(one, "abcdefgh");
X	(void) strncpy(one, "xyz", 2);
X	equal(one, "xycdefgh", 3);		/* Copy cut by count. */
X
X	(void) strcpy(one, "abcdefgh");
X	(void) strncpy(one, "xyz", 3);		/* Copy cut just before NUL. */
X	equal(one, "xyzdefgh", 4);
X
X	(void) strcpy(one, "abcdefgh");
X	(void) strncpy(one, "xyz", 4);		/* Copy just includes NUL. */
X	equal(one, "xyz", 5);
X	equal(one+4, "efgh", 6);		/* Wrote too much? */
X
X	(void) strcpy(one, "abcdefgh");
X	(void) strncpy(one, "xyz", 5);		/* Copy includes padding. */
X	equal(one, "xyz", 7);
X	equal(one+4, "", 8);
X	equal(one+5, "fgh", 9);
X
X	(void) strcpy(one, "abc");
X	(void) strncpy(one, "xyz", 0);		/* Zero-length copy. */
X	equal(one, "abc", 10);	
X
X	(void) strncpy(one, "", 2);		/* Zero-length source. */
X	equal(one, "", 11);
X	equal(one+1, "", 12);	
X	equal(one+2, "c", 13);
X
X	(void) strcpy(one, "hi there");
X	(void) strncpy(two, one, 9);
X	equal(two, "hi there", 14);		/* Just paranoia. */
X	equal(one, "hi there", 15);		/* Stomped on source? */
X
X	/*
X	 * strlen
X	 */
X	it = "strlen";
X	check(strlen("") == 0, 1);		/* Empty. */
X	check(strlen("a") == 1, 2);		/* Single char. */
X	check(strlen("abcd") == 4, 3);		/* Multiple chars. */
X
X	/*
X	 * strchr
X	 */
X	it = "strchr";
X	check(strchr("abcd", 'z') == NULL, 1);	/* Not found. */
X	(void) strcpy(one, "abcd");
X	check(strchr(one, 'c') == one+2, 2);	/* Basic test. */
X	check(strchr(one, 'd') == one+3, 3);	/* End of string. */
X	check(strchr(one, 'a') == one, 4);	/* Beginning. */
X	check(strchr(one, '\0') == one+4, 5);	/* Finding NUL. */
X	(void) strcpy(one, "ababa");
X	check(strchr(one, 'b') == one+1, 6);	/* Finding first. */
X	(void) strcpy(one, "");
X	check(strchr(one, 'b') == NULL, 7);	/* Empty string. */
X	check(strchr(one, '\0') == one, 8);	/* NUL in empty string. */
X
X	/*
X	 * index - just like strchr
X	 */
X	it = "index";
X	check(index("abcd", 'z') == NULL, 1);	/* Not found. */
X	(void) strcpy(one, "abcd");
X	check(index(one, 'c') == one+2, 2);	/* Basic test. */
X	check(index(one, 'd') == one+3, 3);	/* End of string. */
X	check(index(one, 'a') == one, 4);	/* Beginning. */
X	check(index(one, '\0') == one+4, 5);	/* Finding NUL. */
X	(void) strcpy(one, "ababa");
X	check(index(one, 'b') == one+1, 6);	/* Finding first. */
X	(void) strcpy(one, "");
X	check(index(one, 'b') == NULL, 7);	/* Empty string. */
X	check(index(one, '\0') == one, 8);	/* NUL in empty string. */
X
X	/*
X	 * strrchr
X	 */
X	it = "strrchr";
X	check(strrchr("abcd", 'z') == NULL, 1);	/* Not found. */
X	(void) strcpy(one, "abcd");
X	check(strrchr(one, 'c') == one+2, 2);	/* Basic test. */
X	check(strrchr(one, 'd') == one+3, 3);	/* End of string. */
X	check(strrchr(one, 'a') == one, 4);	/* Beginning. */
X	check(strrchr(one, '\0') == one+4, 5);	/* Finding NUL. */
X	(void) strcpy(one, "ababa");
X	check(strrchr(one, 'b') == one+3, 6);	/* Finding last. */
X	(void) strcpy(one, "");
X	check(strrchr(one, 'b') == NULL, 7);	/* Empty string. */
X	check(strrchr(one, '\0') == one, 8);	/* NUL in empty string. */
X
X	/*
X	 * rindex - just like strrchr
X	 */
X	it = "rindex";
X	check(rindex("abcd", 'z') == NULL, 1);	/* Not found. */
X	(void) strcpy(one, "abcd");
X	check(rindex(one, 'c') == one+2, 2);	/* Basic test. */
X	check(rindex(one, 'd') == one+3, 3);	/* End of string. */
X	check(rindex(one, 'a') == one, 4);	/* Beginning. */
X	check(rindex(one, '\0') == one+4, 5);	/* Finding NUL. */
X	(void) strcpy(one, "ababa");
X	check(rindex(one, 'b') == one+3, 6);	/* Finding last. */
X	(void) strcpy(one, "");
X	check(rindex(one, 'b') == NULL, 7);	/* Empty string. */
X	check(rindex(one, '\0') == one, 8);	/* NUL in empty string. */
X}
X
Xsecond()
X{
X	/*
X	 * strpbrk - somewhat like strchr
X	 */
X	it = "strpbrk";
X	check(strpbrk("abcd", "z") == NULL, 1);	/* Not found. */
X	(void) strcpy(one, "abcd");
X	check(strpbrk(one, "c") == one+2, 2);	/* Basic test. */
X	check(strpbrk(one, "d") == one+3, 3);	/* End of string. */
X	check(strpbrk(one, "a") == one, 4);	/* Beginning. */
X	check(strpbrk(one, "") == NULL, 5);	/* Empty search list. */
X	check(strpbrk(one, "cb") == one+1, 6);	/* Multiple search. */
X	(void) strcpy(one, "abcabdea");
X	check(strpbrk(one, "b") == one+1, 7);	/* Finding first. */
X	check(strpbrk(one, "cb") == one+1, 8);	/* With multiple search. */
X	check(strpbrk(one, "db") == one+1, 9);	/* Another variant. */
X	(void) strcpy(one, "");
X	check(strpbrk(one, "bc") == NULL, 10);	/* Empty string. */
X	check(strpbrk(one, "") == NULL, 11);	/* Both strings empty. */
X
X	/*
X	 * strstr - somewhat like strchr
X	 */
X	it = "strstr";
X	check(strstr("abcd", "z") == NULL, 1);	/* Not found. */
X	check(strstr("abcd", "abx") == NULL, 2);	/* Dead end. */
X	(void) strcpy(one, "abcd");
X	check(strstr(one, "c") == one+2, 3);	/* Basic test. */
X	check(strstr(one, "bc") == one+1, 4);	/* Multichar. */
X	check(strstr(one, "d") == one+3, 5);	/* End of string. */
X	check(strstr(one, "cd") == one+2, 6);	/* Tail of string. */
X	check(strstr(one, "abc") == one, 7);	/* Beginning. */
X	check(strstr(one, "abcd") == one, 8);	/* Exact match. */
X	check(strstr(one, "abcde") == NULL, 9);	/* Too long. */
X	check(strstr(one, "de") == NULL, 10);	/* Past end. */
X	check(strstr(one, "") == one+4, 11);	/* Finding empty. */
X	(void) strcpy(one, "ababa");
X	check(strstr(one, "ba") == one+1, 12);	/* Finding first. */
X	(void) strcpy(one, "");
X	check(strstr(one, "b") == NULL, 13);	/* Empty string. */
X	check(strstr(one, "") == one, 14);	/* Empty in empty string. */
X	(void) strcpy(one, "bcbca");
X	check(strstr(one, "bca") == one+2, 15);	/* False start. */
X	(void) strcpy(one, "bbbcabbca");
X	check(strstr(one, "bbca") == one+1, 16);	/* With overlap. */
X
X	/*
X	 * strspn
X	 */
X	it = "strspn";
X	check(strspn("abcba", "abc") == 5, 1);	/* Whole string. */
X	check(strspn("abcba", "ab") == 2, 2);	/* Partial. */
X	check(strspn("abc", "qx") == 0, 3);	/* None. */
X	check(strspn("", "ab") == 0, 4);	/* Null string. */
X	check(strspn("abc", "") == 0, 5);	/* Null search list. */
X
X	/*
X	 * strcspn
X	 */
X	it = "strcspn";
X	check(strcspn("abcba", "qx") == 5, 1);	/* Whole string. */
X	check(strcspn("abcba", "cx") == 2, 2);	/* Partial. */
X	check(strcspn("abc", "abc") == 0, 3);	/* None. */
X	check(strcspn("", "ab") == 0, 4);	/* Null string. */
X	check(strcspn("abc", "") == 3, 5);	/* Null search list. */
X
X	/*
X	 * strtok - the hard one
X	 */
X	it = "strtok";
X	(void) strcpy(one, "first, second, third");
X	equal(strtok(one, ", "), "first", 1);	/* Basic test. */
X	equal(one, "first", 2);
X	equal(strtok((char *)NULL, ", "), "second", 3);
X	equal(strtok((char *)NULL, ", "), "third", 4);
X	check(strtok((char *)NULL, ", ") == NULL, 5);
X	(void) strcpy(one, ", first, ");
X	equal(strtok(one, ", "), "first", 6);	/* Extra delims, 1 tok. */
X	check(strtok((char *)NULL, ", ") == NULL, 7);
X	(void) strcpy(one, "1a, 1b; 2a, 2b");
X	equal(strtok(one, ", "), "1a", 8);	/* Changing delim lists. */
X	equal(strtok((char *)NULL, "; "), "1b", 9);
X	equal(strtok((char *)NULL, ", "), "2a", 10);
X	(void) strcpy(two, "x-y");
X	equal(strtok(two, "-"), "x", 11);	/* New string before done. */
X	equal(strtok((char *)NULL, "-"), "y", 12);
X	check(strtok((char *)NULL, "-") == NULL, 13);
X	(void) strcpy(one, "a,b, c,, ,d");
X	equal(strtok(one, ", "), "a", 14);	/* Different separators. */
X	equal(strtok((char *)NULL, ", "), "b", 15);
X	equal(strtok((char *)NULL, " ,"), "c", 16);	/* Permute list too. */
X	equal(strtok((char *)NULL, " ,"), "d", 17);
X	check(strtok((char *)NULL, ", ") == NULL, 18);
X	check(strtok((char *)NULL, ", ") == NULL, 19);	/* Persistence. */
X	(void) strcpy(one, ", ");
X	check(strtok(one, ", ") == NULL, 20);	/* No tokens. */
X	(void) strcpy(one, "");
X	check(strtok(one, ", ") == NULL, 21);	/* Empty string. */
X	(void) strcpy(one, "abc");
X	equal(strtok(one, ", "), "abc", 22);	/* No delimiters. */
X	check(strtok((char *)NULL, ", ") == NULL, 23);
X	(void) strcpy(one, "abc");
X	equal(strtok(one, ""), "abc", 24);	/* Empty delimiter list. */
X	check(strtok((char *)NULL, "") == NULL, 25);
X	(void) strcpy(one, "abcdefgh");
X	(void) strcpy(one, "a,b,c");
X	equal(strtok(one, ","), "a", 26);	/* Basics again... */
X	equal(strtok((char *)NULL, ","), "b", 27);
X	equal(strtok((char *)NULL, ","), "c", 28);
X	check(strtok((char *)NULL, ",") == NULL, 29);
X	equal(one+6, "gh", 30);			/* Stomped past end? */
X	equal(one, "a", 31);			/* Stomped old tokens? */
X	equal(one+2, "b", 32);
X	equal(one+4, "c", 33);
X
X	/*
X	 * memcmp
X	 */
X	it = "memcmp";
X	check(memcmp("a", "a", 1) == 0, 1);	/* Identity. */
X	check(memcmp("abc", "abc", 3) == 0, 2);	/* Multicharacter. */
X	check(memcmp("abcd", "abce", 4) < 0, 3);	/* Honestly unequal. */
X	check(memcmp("abce", "abcd", 4) > 0, 4);
X	check(memcmp("alph", "beta", 4) < 0, 5);
X	if (charsigned)				/* Sign-bit comparison. */
X		check(memcmp("a\203", "a\003", 2) < 0, 6);
X	else
X		check(memcmp("a\203", "a\003", 2) > 0, 6);
X	check(memcmp("abce", "abcd", 3) == 0, 7);	/* Count limited. */
X	check(memcmp("abc", "def", 0) == 0, 8);	/* Zero count. */
X
X	/*
X	 * memchr
X	 */
X	it = "memchr";
X	check(memchr("abcd", 'z', 4) == NULL, 1);	/* Not found. */
X	(void) strcpy(one, "abcd");
X	check(memchr(one, 'c', 4) == one+2, 2);	/* Basic test. */
X	check(memchr(one, 'd', 4) == one+3, 3);	/* End of string. */
X	check(memchr(one, 'a', 4) == one, 4);	/* Beginning. */
X	check(memchr(one, '\0', 5) == one+4, 5);	/* Finding NUL. */
X	(void) strcpy(one, "ababa");
X	check(memchr(one, 'b', 5) == one+1, 6);	/* Finding first. */
X	check(memchr(one, 'b', 0) == NULL, 7);	/* Zero count. */
X	check(memchr(one, 'a', 1) == one, 8);	/* Singleton case. */
X	(void) strcpy(one, "a\203b");
X	check(memchr(one, 0203, 3) == one+1, 9);	/* Unsignedness. */
X
X	/*
X	 * memcpy
X	 *
X	 * Note that X3J11 says memcpy must work regardless of overlap.
X	 * The SVID says it might fail.
X	 */
X	it = "memcpy";
X	check(memcpy(one, "abc", 4) == one, 1);	/* Returned value. */
X	equal(one, "abc", 2);			/* Did the copy go right? */
X
X	(void) strcpy(one, "abcdefgh");
X	(void) memcpy(one+1, "xyz", 2);
X	equal(one, "axydefgh", 3);		/* Basic test. */
X
X	(void) strcpy(one, "abc");
X	(void) memcpy(one, "xyz", 0);
X	equal(one, "abc", 4);			/* Zero-length copy. */
X
X	(void) strcpy(one, "hi there");
X	(void) strcpy(two, "foo");
X	(void) memcpy(two, one, 9);
X	equal(two, "hi there", 5);		/* Just paranoia. */
X	equal(one, "hi there", 6);		/* Stomped on source? */
X
X	(void) strcpy(one, "abcdefgh");
X	(void) memcpy(one+1, one, 9);
X	equal(one, "aabcdefgh", 7);		/* Overlap, right-to-left. */
X
X	(void) strcpy(one, "abcdefgh");
X	(void) memcpy(one+1, one+2, 7);
X	equal(one, "acdefgh", 8);		/* Overlap, left-to-right. */
X
X	(void) strcpy(one, "abcdefgh");
X	(void) memcpy(one, one, 9);
X	equal(one, "abcdefgh", 9);		/* 100% overlap. */
X
X	/*
X	 * memccpy - first test like memcpy, then the search part
X	 *
X	 * The SVID, the only place where memccpy is mentioned, says
X	 * overlap might fail, so we don't try it.  Besides, it's hard
X	 * to see the rationale for a non-left-to-right memccpy.
X	 */
X	it = "memccpy";
X	check(memccpy(one, "abc", 'q', 4) == NULL, 1);	/* Returned value. */
X	equal(one, "abc", 2);			/* Did the copy go right? */
X
X	(void) strcpy(one, "abcdefgh");
X	(void) memccpy(one+1, "xyz", 'q', 2);
X	equal(one, "axydefgh", 3);		/* Basic test. */
X
X	(void) strcpy(one, "abc");
X	(void) memccpy(one, "xyz", 'q', 0);
X	equal(one, "abc", 4);			/* Zero-length copy. */
X
X	(void) strcpy(one, "hi there");
X	(void) strcpy(two, "foo");
X	(void) memccpy(two, one, 'q', 9);
X	equal(two, "hi there", 5);		/* Just paranoia. */
X	equal(one, "hi there", 6);		/* Stomped on source? */
X
X	(void) strcpy(one, "abcdefgh");
X	(void) strcpy(two, "horsefeathers");
X	check(memccpy(two, one, 'f', 9) == two+6, 7);	/* Returned value. */
X	equal(one, "abcdefgh", 8);		/* Source intact? */
X	equal(two, "abcdefeathers", 9);		/* Copy correct? */
X
X	(void) strcpy(one, "abcd");
X	(void) strcpy(two, "bumblebee");
X	check(memccpy(two, one, 'a', 4) == two+1, 10);	/* First char. */
X	equal(two, "aumblebee", 11);
X	check(memccpy(two, one, 'd', 4) == two+4, 12);	/* Last char. */
X	equal(two, "abcdlebee", 13);
X	(void) strcpy(one, "xyz");
X	check(memccpy(two, one, 'x', 1) == two+1, 14);	/* Singleton. */
X	equal(two, "xbcdlebee", 15);
X
X	/*
X	 * memset
X	 */
X	it = "memset";
X	(void) strcpy(one, "abcdefgh");
X	check(memset(one+1, 'x', 3) == one+1, 1);	/* Return value. */
X	equal(one, "axxxefgh", 2);		/* Basic test. */
X
X	(void) memset(one+2, 'y', 0);
X	equal(one, "axxxefgh", 3);		/* Zero-length set. */
X
X	(void) memset(one+5, 0, 1);
X	equal(one, "axxxe", 4);			/* Zero fill. */
X	equal(one+6, "gh", 5);			/* And the leftover. */
X
X	(void) memset(one+2, 010045, 1);
X	equal(one, "ax\045xe", 6);		/* Unsigned char convert. */
X
X	/*
X	 * bcopy - much like memcpy
X	 *
X	 * Berklix manual is silent about overlap, so don't test it.
X	 */
X	it = "bcopy";
X	(void) bcopy("abc", one, 4);
X	equal(one, "abc", 1);			/* Simple copy. */
X
X	(void) strcpy(one, "abcdefgh");
X	(void) bcopy("xyz", one+1, 2);
X	equal(one, "axydefgh", 2);		/* Basic test. */
X
X	(void) strcpy(one, "abc");
X	(void) bcopy("xyz", one, 0);
X	equal(one, "abc", 3);			/* Zero-length copy. */
X
X	(void) strcpy(one, "hi there");
X	(void) strcpy(two, "foo");
X	(void) bcopy(one, two, 9);
X	equal(two, "hi there", 4);		/* Just paranoia. */
X	equal(one, "hi there", 5);		/* Stomped on source? */
X
X	/*
X	 * bzero
X	 */
X	it = "bzero";
X	(void) strcpy(one, "abcdef");
X	bzero(one+2, 2);
X	equal(one, "ab", 1);			/* Basic test. */
X	equal(one+3, "", 2);
X	equal(one+4, "ef", 3);
X
X	(void) strcpy(one, "abcdef");
X	bzero(one+2, 0);
X	equal(one, "abcdef", 4);		/* Zero-length copy. */
X
X	/*
X	 * bcmp - somewhat like memcmp
X	 */
X	it = "bcmp";
X	check(bcmp("a", "a", 1) == 0, 1);	/* Identity. */
X	check(bcmp("abc", "abc", 3) == 0, 2);	/* Multicharacter. */
X	check(bcmp("abcd", "abce", 4) != 0, 3);	/* Honestly unequal. */
X	check(bcmp("abce", "abcd", 4) != 0, 4);
X	check(bcmp("alph", "beta", 4) != 0, 5);
X	check(bcmp("abce", "abcd", 3) == 0, 6);	/* Count limited. */
X	check(bcmp("abc", "def", 0) == 0, 8);	/* Zero count. */
X
X#ifdef ERR
X	/*
X	 * strerror - VERY system-dependent
X	 */
X	it = "strerror";
X	f = open("/", 1);	/* Should always fail. */
X	check(f < 0 && errno > 0 && errno < sys_nerr, 1);
X	equal(strerror(errno), sys_errlist[errno], 2);
X#ifdef UNIXERR
X	equal(strerror(errno), "Is a directory", 3);
X#endif
X#ifdef BERKERR
X	equal(strerror(errno), "Permission denied", 3);
X#endif
X#endif
X}
END_OF_FILE
if test 19922 -ne `wc -c <'libc/strings/tester.c'`; then
    echo shar: \"'libc/strings/tester.c'\" unpacked with wrong size!
fi
# end of 'libc/strings/tester.c'
fi
if test -f 'rnews/headers.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'rnews/headers.c'\"
else
echo shar: Extracting \"'rnews/headers.c'\" \(18247 characters\)
sed "s/^X//" >'rnews/headers.c' <<'END_OF_FILE'
X/*
X * Usenet header parsing and generation (see RFCs 850 & 822;
X *	for a second opinion, see The Hideous Name by Pike & Weinberger).
X *
X * Headers are parsed and modified and copied in one pass.
X * Nevertheless, this file is split into two pieces: header parsing
X * and remembering (aka eating) and header copying (aka munging).
X * The split is marked and the two pieces are fairly independent.
X *
X * --- a quick tour of RFC 850 headers.  Hold on tight ---
X *
X * mandatory headers follow:
X * Path: ucbvax!ukc!decvax!mcvax!ih*!kaist!mh*!ho*!brahms!nsu	# must prepend to
X * From: whosit@brahms.b.uc.berkeley.edu.BERKELEY.EDU.uucp (Arthur ``Two-Sheds'' Jackson)
X * Newsgroups: talk.philosophy.meaning.meaning
X * Subject: Re: RE: re: rE: Orphaned Response - (nf)	# previously Title:
X * Date: Sat, 25-Dec-86 04:05:06 GMT	# previously Posted:
X * Message-ID: <unique@brahms.b.uc.berkeley.edu.BERKELEY.EDU.uucp> # previously Article-I.D.:
X *
X * optional headers follow:
X * Relay-Version: C;utcs			# must replace; must be 1st (recently demoted)
X * Posting-Version: version C alpha;site utcs.uucp	# (recently demoted)
X * Date-Received: Sat, 25-Jan-87 12:34:56 GMT	# must replace or snuff * # previously Received:
X * Organization: UCB society for arguing the meaning of meaning
X * Distribution: ucb
X * Sender: twosheds@arpa.b.uc.berkeley.edu.BERKELEY.EDU.uucp
X * Followup-To: talk.tv.i.love.lucy
X * Control: newgroup talk.philosophy.meaning.meaning.meaning	# magic; not for the uninitiated
X * References: <345.wanker@isi-wankers.gov> <123.toadsexers@kcl-cs.uk>
X * Reply-To: info-wankers@wankvax.b.uc.berkeley.edu.BERKELEY.EDU.uucp
X * Expires: Sun, 31-dec-99 23:59:59 GMT
X * Approved: kinsey@Uchicago.uucp		# the mark of the moderator
X * Lines: 6766					# redundant & pointless `wc -l`
X *
X * new headers not (yet?) in RFC850 follow:
X * Summary: It was a dark and stormy night,	# retain
X * Keywords: toadsexing, ether, tetanus		# retain
X * Nf-*: transmitted via Notesviles		# might snuff
X *
X * abusive & silly non-RFC-850 headers follow:
X * Sccs-Id: @(#) 5.2.vax.2.women.only  rob  3/5/83
X * Hideous-Name: psuvax1!rhea::@brl.arpa:ucbvax!mit-mc%udel-relay.arpa@chris:umcp-cs::udel-relay%csnet-relay.vision.ubc.cdn
X * Shoe-Size: 8
X * Header-Questions-To: mark@pavo.cb.d.osg.cb.cbosgd.att.com.uucp
X * Upas-To: eric@vax.b.uc.berkeley.edu.BERKELEY.EDU.uucp
X * V9-Capable-Machines-To: utstat!geoff
X * Uglix-Version: 5.3.vax.1.4/7/85.21:37:45.binary.only
X *
X * --- Ah well, it will only get worse. ---
X */
X
X#include <stdio.h>
X#include <ctype.h>
X#include <sys/types.h>
X#include "news.h"
X#include "headers.h"
X
X#ifndef DEFDIST
X#define DEFDIST "world"		/* default Distribution: */
X#endif
X#ifndef DEFMSGID
X#define DEFMSGID "<wanker@isi-wankers.isi.usc.edu>"
X#endif
X
X#define JUNK "junk"
X#define ALL "all"
X
X#define OLDCNTRL "all.all.ctl"
X
X/*
X * Derivation: 752 is a large header, 120 is approx. size of *-Version because
X *	this rnews throws them away.  HDRMEMSIZ can be too small if
X *	memory is tight and will only hurt performance.
X */
X#ifndef HDRMEMSIZ
X#ifndef notdef
X#define HDRMEMSIZ 8192			/* room for headers */
X#else
X#define HDRMEMSIZ (752-120)
X#endif	/* notdef */
X#endif	/* HDRMEMSIZ */
X
X	/* from here down are recognised in eatheaders() */
Xstatic char ctlnm[] =	"Control:";
Xstatic char distrnm[] =	"Distribution:";
Xstatic char appnm[] =	"Approved:";
Xstatic char subjnm[] =	"Subject:";
Xstatic char msgnm[] =	"Message-ID:";
Xstatic char artidnm[] =	"Article-I.D.:";	/* obsolete Message-ID: */
X	/* from here down recognised by mungehdrs() */
Xstatic char pathnm[] =	"Path:";		/* so we can extend it (damn) */
Xstatic char ngsnm[] = "Newsgroups:";		/* to clone for Xref */
X	/* down to here recognised by eatheaders() */
Xstatic char xrefnm[] = "Xref:";			/* to *replace* Xref (damn!)*/
X	/*
X	 * the following noxious headers are deleted because
X	 * neighbours still send them and they are big.
X	 * in an ideal world, they wouldn't be sent and thus
X	 * we wouldn't need to delete them.
X	 */
X/* TODO: what about Posted: and Article-I.D.:? */
Xstatic char datercvnm[] = "Date-Received:";	/* so we can snuff it */
Xstatic char rcvnm[] = "Received:";		/* obsolete Date-Received: */
Xstatic char rlyversnm[] = "Relay-Version:";	/* so we can snuff it */
Xstatic char postversnm[] = "Posting-Version:";	/* so we can snuff it */
X	/* down to here recognised by mungehdrs() */
X
X
X/* TODO: permit multiples of hdrprs and hpsp */
Xstruct hdrparsestate {
X	char **prevvalp;	/* points at previous header value string */
X	short prevhist;		/* previous line was a header line */
X	short nextcont;		/* next line must be a header continuation */
X	short newnextcont;	/* nextcont for next line, based on this line */
X	char hdraccum[HDRMEMSIZ+MAXLINE];
X	char *endlnp;		/* pointer to end of input buffer - 1 */
X};
Xstatic struct hdrparsestate hdrprs = { 0, NO, NO, 0 };	/* parser state */
Xstatic struct hdrparsestate *hpsp = &hdrprs;
X/* end of parser state */
X
X
Xstatic int debug = NO;
X
X/* forward decls */
Xchar *skipsp();
X
X/*
X * --- common header code start ---
X */
X
Xhdrdebug(state)
Xint state;
X{
X	debug = state;
X}
X
Xhdrinit(hdrs)			/* zero all pointers in hdrs */
Xregister struct headers *hdrs;
X{
X	hdrs->h_subj = NULL;
X	hdrs->h_ngs = NULL;
X	hdrs->h_files[0] = '\0';
X	hdrs->h_distr = NULL;
X	hdrs->h_ctlcmd = NULL;
X	hdrs->h_approved = NULL;
X	hdrs->h_msgid = NULL;
X	hdrs->h_artid = NULL;
X	hdrs->h_expiry = NULL;
X	hdrs->h_path = NULL;
X	hdrs->h_tmpf[0] = '\0';
X	hdrs->h_unlink = NO;
X	hdrs->h_filed = NO;
X	hdrs->h_xref = NO;
X	hdrs->h_octlchked = NO;
X	hdrs->h_oldctl = NO;
X	hdrs->h_accum = NULL;
X	hdrs->h_charswritten = 0;
X}
X
Xstatic int
Xoldctl(hdrs)			/* true iff ngs are OLDCNTRL (cache in hdrs) */
Xregister struct headers *hdrs;
X{
X	if (!hdrs->h_octlchked) {		/* don't know yet */
X		/* TODO: special case this, avoid ngmatch */
X		hdrs->h_oldctl = ngmatch(OLDCNTRL, hdrs->h_ngs);
X		hdrs->h_octlchked = YES;	/* now we know */
X	}
X	return hdrs->h_oldctl;
X}
X
Xint
Xhdrmutate(hdrs, buffer, tfp)		/* eat & munge headers */
Xstruct headers *hdrs;
Xchar *buffer;
XFILE **tfp;
X{
X    	eatheaders(hdrs, buffer);	/* mungehdrs needs n_ngs set */
X	return mungehdrs(buffer, tfp, hdrs);	/* save or write hdr */
X}
X
X/*
X * --- header parsing and remembering starts here ---
X */
X
X/*
X * Reset internal state of header parser.
X * (Empty the stomach of partially-digested headers.  Waarrrgggh!)
X */
Xhdrwretch()
X{
X	hpsp->prevvalp = NULL;
X	hpsp->prevhist = NO;
X	hpsp->nextcont = NO;
X}
X
X/*
X * Parse RFC822/850 header into "hdrs".  Retain significant values.
X * Assumes ishdr has been called first.
X */
Xeatheaders(hdrs, line)
Xregister struct headers *hdrs;
Xregister char *line;
X{
X	/*
X	 * One would really like to use a loop through a structure here,
X	 * but it's hard because one can't initialise a static struct
X	 * with e.g. &hdrs->h_path.
X	 */
X	if (!contin(line) &&
X	    !hdrmatch(line, pathnm, STRLEN(pathnm), &hdrs->h_path) &&
X	    !hdrmatch(line, msgnm,  STRLEN(msgnm),  &hdrs->h_msgid) &&
X	    !hdrmatch(line, artidnm,STRLEN(artidnm),&hdrs->h_artid) &&	/* obs. */
X	    !hdrmatch(line, subjnm, STRLEN(subjnm), &hdrs->h_subj) &&
X	    !hdrmatch(line, ngsnm,  STRLEN(ngsnm),  &hdrs->h_ngs) &&
X	    !hdrmatch(line, distrnm,STRLEN(distrnm),&hdrs->h_distr) &&
X	    !hdrmatch(line, appnm,  STRLEN(appnm),  &hdrs->h_approved) &&
X	    !hdrmatch(line, ctlnm,  STRLEN(ctlnm),  &hdrs->h_ctlcmd)) {
X		static char *dummy = NULL;
X
X		/*
X		 * silly header - just set hpsp->prevvalp for contin()
X		 * so that unrecognised headers may be continued.
X		 */
X		if (dummy != NULL)
X			free(dummy);
X		dummy = strsave("");	/* may be realloced in contin() */
X		hpsp->prevvalp = &dummy;	/* ditto */
X	}
X	hpsp->nextcont = hpsp->newnextcont;	/* set nextcont for next line */
X}
X
Xhdrdeflt(hdrs)				/* default missing header values */
Xregister struct headers *hdrs;
X{
X	/*
X	 * if strsave ever returns NULL on failure, instead of exiting,
X	 * then the following calls need to check for failure.
X	 */
X	if (hdrs->h_ngs == NULL)
X		hdrs->h_ngs = strsave(JUNK);
X	if (hdrs->h_msgid == NULL && hdrs->h_artid != NULL)
X		hdrs->h_msgid = strsave(hdrs->h_artid);
X	if (hdrs->h_msgid == NULL)
X		hdrs->h_msgid = strsave(DEFMSGID);
X	if (hdrs->h_expiry == NULL)
X		hdrs->h_expiry = strsave("-");	/* - means "default" */
X	if (hdrs->h_subj == NULL)
X		hdrs->h_subj = strsave("");
X
X	/*
X	 * Control message backwards compatibility, and I mean *backwards*.
X	 * We're talking stone age here; we're probably talking A news: if
X	 * no Control: header exists and the newsgroup matches all.all.ctl,
X	 * use the Subject: as the control message.
X	 *
X	 * Since RFC 850 is vague on the subject, we will henceforth
X	 * internally (but not on disk), treat the Newsgroup: value as
X	 * foo.bar, not foo.bar.ctl.  This simplifies the rest of
X	 * processing somewhat.
X	 */
X	if (hdrs->h_ctlcmd == NULL && oldctl(hdrs)) {
X		hdrs->h_ctlcmd = strsave(hdrs->h_subj);
X		hdrs->h_ngs[strlen(hdrs->h_ngs) - STRLEN(".ctl")] = '\0';
X	}
X
X	if (hdrs->h_ctlcmd != NULL)		/* control message */
X		hdrs->h_octlchked = NO;		/* invalidate old comparison */
X	if (hdrs->h_distr == NULL)
X		hdrs->h_distr = strsave(DEFDIST);
X}
X
Xstatic int
Xiscontin(s)				/* is s an RFC 822 header continuation? */
Xregister char *s;
X{
X	return hpsp->nextcont || hpsp->prevhist && iswhite(*s);
X}
X
Xint
Xishdr(s)				/* is s an RFC 822 header line? */
Xchar *s;
X{
X	register char *cp = s;
X
X	if (iscontin(s))
X		hpsp->prevhist = YES;
X	else {
X		register int c;
X
X		/* look for first of NUL, whitespace, colon */
X		while ((c = *cp) != '\0' && !(isascii(c) && isspace(c)) &&
X		    c != ':')
X			++cp;
X		hpsp->prevhist = (c == ':' && cp > s);	/* colon not 1st char */
X	}
X	/*
X	 * If this is a header line and there is no trailing newline,
X	 * assume fgets couldn't fit a very long header into the buffer
X	 * for header lines, so the next line fgets sees must be a
X	 * continuation of this line.
X	 */
X	if (hpsp->prevhist) {
X#ifndef DIRTYHDRCONT
X		INDEX(cp, '\n', cp);
X		hpsp->newnextcont = (cp == NULL);	/* no \n -> continue */
X#else	/* DIRTYHDRCONT */
X		hpsp->newnextcont =
X			*hpsp->endlnp != '\0' && *hpsp->endlnp != '\n';
X#endif	/* DIRTYHDRCONT */
X	} else
X		hpsp->newnextcont = NO;
X	return hpsp->prevhist;
X}
X
Xint
Xcontin(line)		/* append continuation value to old value */
Xchar *line;
X{
X	/*
X	 * If there is a previous header value and this line starts
X	 * with whitespace other than a newline, realloc *hpsp->prevvalp
X	 * with enough space for the old value, the new value and a NUL.
X	 * Then append the new value.
X	 */
X	if (hpsp->prevvalp != NULL && iscontin(line)) {
X		char *valp = /* skipsp */ (line);	/* continuation value */
X
X		/* hpsp->prevvalp was previously set in hdrmatch() or eatheaders() */
X		*hpsp->prevvalp = realloc(*hpsp->prevvalp,
X			(unsigned)strlen(*hpsp->prevvalp) + strlen(valp) + 1);
X		if (*hpsp->prevvalp == NULL)
X			warning("realloc failed in contin", "");
X		else {
X			(void) strcat(*hpsp->prevvalp, valp);
X			trim(*hpsp->prevvalp);	/* remove trailing newline */
X		}
X		return YES;
X	} else
X		return NO;
X}
X
X/*
X * Match line with keyword (return truth value).
X * If it matches, store the value in *malloc'ed memory* (N.B.)
X * and set *ptrp to point there.  freeheader() will free this memory.
X */
Xint
Xhdrmatch(line, keyword, keylen, ptrp)
Xregister char *line, *keyword;
Xregister int keylen;			/* an optimisation */
Xregister char **ptrp;			/* make it point at valuep */
X{
X#ifdef notdef
X	register int keylen = strlen(keyword);	/* the slower way */
X#endif
X	register int match = STREQN(line, keyword, keylen);
X
X	if (match && *ptrp != NULL)	/* value already set */
X		free(*ptrp);		/* return storage */
X	if (match && (*ptrp = strsave(skipsp(&line[keylen]))) != NULL) {
X		trim(*ptrp);		/* remove trailing new line */
X		hpsp->prevvalp = ptrp;	/* for contin() */
X	}
X	return match;
X}
X
Xfreeheaders(hdrs)		/* free (assumed) malloced storage */
Xregister struct headers *hdrs;
X{
X	nnfree(&hdrs->h_subj);
X	nnfree(&hdrs->h_ngs);
X	nnfree(&hdrs->h_distr);
X	nnfree(&hdrs->h_ctlcmd);
X	nnfree(&hdrs->h_approved);
X	nnfree(&hdrs->h_msgid);
X	nnfree(&hdrs->h_artid);
X	nnfree(&hdrs->h_expiry);
X	nnfree(&hdrs->h_path);
X}
X
Xnnfree(mempp)				/* free non-null pointer's memory */
Xregister char **mempp;			/* pointer to malloc'ed ptr. */
X{
X	if (*mempp != NULL) {
X		free(*mempp);
X		*mempp = NULL;
X	}
X}
X
Xint
Xemitxref(tf, hdrs)		/* splat out an Xref: line from Newsgroups: */
Xregister FILE *tf;
Xstruct headers *hdrs;
X{
X	register char *slashp;
X	int status = ST_OKAY;
X	char xrefs[MAXLINE];
X
X	if (!hdrs->h_xref) {		/* this article has no Xref: yet */
X		hdrs->h_xref = YES;
X		(void) strcpy(xrefs, hdrs->h_files);
X		/* turn slashes into colons for the benefit of rn */
X		for (slashp = xrefs; (slashp = index(slashp, FNDELIM)) != NULL; )
X			*slashp++ = ':';
X		if (fprintf(tf, "%s %s %s\n", xrefnm, hostname(), xrefs) == EOF)
X			status = fulldisk(status|ST_DROPPED,
X				(hdrs->h_unlink? hdrs->h_tmpf: hdrs->h_files));
X	}
X	return status;
X}
X
X/*
X * --- header munging (copying) starts here ---
X */
X
X/*
X * Copy headers and munge a few.  Assumes eatheaders has been called.
X *
X * Don't copy Date-Received nor *-Version nor Xref.
X * Prepend hostname! to Path: value.
X * Recognise Newsgroups: and if more than one, generate Xref: &
X * leave holes for the article numbers - fileart will fill them in.
X * (Make rn look in history instead?)
X *
X * (Header munging should be a felony.  When they make me dictator,
X * it will be: punishable by having to use 4.2BSD networking.)
X *
X * New strategy: pile up headers into a static buffer until
X * end of buffer (next line might not fit) [checked in hdrsave()]; end of
X * headers, file or byte count [checked in cparttofp].
X * During reading, discard swill headers.  Path: is munged on output.
X * Copy (save) or discard header lines.
X */
Xint
Xmungehdrs(buffer, tfp, hdrs)
Xregister char *buffer;
Xregister FILE **tfp;
Xstruct headers *hdrs;
X{
X	struct vilesthdrs {
X		char *vh_name;
X		unsigned vh_len;
X	};
X	register struct vilesthdrs *vhp;
X	static struct vilesthdrs vilest[] = {
X		datercvnm,	STRLEN(datercvnm),
X		rcvnm,		STRLEN(rcvnm),
X		rlyversnm,	STRLEN(rlyversnm),
X		postversnm,	STRLEN(postversnm),
X		xrefnm,		STRLEN(xrefnm),
X		NULL,		0
X	};
X
X	if (debug)
X		(void) fputs(buffer, stderr);
X	/*
X	 * Toss the most vile of the trash headers.
X	 * In an ideal world, this code wouldn't exist.
X	 */
X	for (vhp = vilest; vhp->vh_name != NULL; vhp++)
X		if (STREQN(buffer, vhp->vh_name, (int)vhp->vh_len))
X			return ST_OKAY;
X	/* wasn't vile; save it.  *tfp may be NULL.  Be Prepared. */
X	return hdrsave(buffer, hdrs, tfp);	/* may set *tfp */
X}
X
X/*
X * If headers already dumped (hdrs->h_filed), just write to *tfp.
X * If there is room, stash "hdr" away until Newsgroups: is seen,
X * then open the first article link (on *tfp)
X * and dump the saved headers to it.
X * Copy into hdrs->h_accum (TODO: read in directly, iff high on profile).
X * TODO: may want an end-of-accum pointer for speed, iff high on profile.
X */
Xint
Xhdrsave(hdr, hdrs, tfp)
Xchar *hdr;
Xregister struct headers *hdrs;
XFILE **tfp;
X{
X	int status = ST_OKAY;
X	unsigned hdrlen = strlen(hdr);
X
X	if (hdrs->h_filed)			/* *tfp != NULL */
X		return emithdr(hdrs, hdr, *tfp);
X	if (hdrs->h_accum == NULL) {
X		hdrs->h_accum = hpsp->hdraccum;	/* primitive storage allocation */
X		hdrs->h_accum[0] = '\0';
X		hdrs->h_bytesleft = sizeof hpsp->hdraccum;
X	}
X	if (hdrs->h_bytesleft > hdrlen) {	/* it fits! */
X		(void) strcat(hdrs->h_accum, hdr);	/* whomp it on the end */
X		hdrs->h_bytesleft -= hdrlen;
X	} else {				/* no room; barf out headers */
X		status |= hdrdump(tfp, hdrs, NO);	/* don't trigger fileart */
X		if (*tfp != NULL)
X			status |= emithdr(hdrs, hdr, *tfp);
X	}
X	return status;
X}
X
Xstatic int
Xemithdr(hdrs, hdr, tf)	/* munge Path: else just dump the header (hdr) */
Xregister struct headers *hdrs;
Xchar *hdr;
XFILE *tf;
X{
X	register int status = ST_OKAY;
X
X	if (STREQN(hdr, pathnm, STRLEN(pathnm))) {	/* Path: */
X		register int hdrbytes;
X		register char *oldpath;
X
X		oldpath = skipsp(&hdr[STRLEN(pathnm)]);
X		hdrbytes = fprintf(tf, "%s %s!", pathnm, hostname());
X		if (hdrbytes == EOF || fputs(oldpath, tf) == EOF)
X			status = fulldisk(status|ST_DROPPED,
X				(hdrs->h_unlink? hdrs->h_tmpf: hdrs->h_files));
X		else
X			hdrs->h_charswritten += hdrbytes + strlen(oldpath);
X	} else {					/* ordinary header */
X		if (fputs(hdr, tf) == EOF)
X			status = fulldisk(status|ST_DROPPED,
X				(hdrs->h_unlink? hdrs->h_tmpf: hdrs->h_files));
X		else
X			hdrs->h_charswritten += strlen(hdr);
X	}
X	return status;
X}
X
X/*
X * Barf out headers after opening on *tfp either a temporary file (using
X * mktemp(3)) or the first article link, based on the h_ngs & nxtartnum();
X * set h_tmpf to which ever name is opened.  Modify Path: value on the way.
X */
Xint
Xhdrdump(tfp, hdrs, allhdrsseen)
XFILE **tfp;
Xregister struct headers *hdrs;
Xint allhdrsseen;
X{
X	int status = ST_OKAY;
X
X	if (hdrs->h_filed)
X		return status;
X	/*
X	 * If all headers were seen & the group was not an old backward-
X	 * compatible control group (which won't exist), then open the
X	 * first link, link to the rest, generate Xref:, else open a
X	 * temporary name and write the article there
X	 * (it will get filed later in insart()).
X	 */
X	if (allhdrsseen && hdrs->h_ngs != NULL && !oldctl(hdrs))
X		status |= fileart(hdrs, tfp, 1);
X	else {
X		(void) strcpy(hdrs->h_tmpf, SPOOLTMP);
X		(void) mktemp(hdrs->h_tmpf);	/* make a temporary name */
X		hdrs->h_unlink = 1;		/* unlink it when done */
X		if ((*tfp = fopen(hdrs->h_tmpf, "w")) == NULL) {
X			warning("can't open temporary name `%s'", hdrs->h_tmpf);
X			status |= ST_DROPPED;
X		}
X	}
X	if (*tfp != NULL) {
X		register char *line, *nlp;
X		register int saved;
X
X		/* this is a deadly tedious job and I really should automate it */
X		for (line = hdrs->h_accum; line != NULL && line[0] != '\0';
X		     line = nlp) {
X		     	/*
X		     	 * Could this call on INDEX be eliminated without
X		     	 * restricting the number of header lines?
X		     	 */
X		     	INDEX(line, '\n', nlp);
X			if (nlp != NULL) {
X				++nlp;		/* byte after \n is NUL or text */
X				saved = *nlp;
X				*nlp = '\0';	/* will be restored below */
X			}
X			if (emithdr(hdrs, line, *tfp) == EOF)	/* dump saved headers */
X				status = fulldisk(status|ST_DROPPED,
X					(hdrs->h_unlink? hdrs->h_tmpf: hdrs->h_files));
X		     	if (nlp != NULL)
X		     		*nlp = saved;	/* restore  */
X		}
X	}
X#ifdef notdef
X	hdrs->h_accum = NULL;		/* primitive memory deallocation */
X#endif
X	return status;
X}
END_OF_FILE
if test 18247 -ne `wc -c <'rnews/headers.c'`; then
    echo shar: \"'rnews/headers.c'\" unpacked with wrong size!
fi
# end of 'rnews/headers.c'
fi
echo shar: End of archive 12 \(of 14\).
##  End of shell archivets ag.g.g