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