#!/bin/sh
# This is a shar archive.
# The rest of this file is a shell script which will extract:
#
# 8_13a.c 8_13b.c 8_13c.c all.c makefile pat.input patb.input tst.cmp tst13.c tst13b.c tstb.cmp
#
# To extract the files from this shell archive file simply
# create a directory for this file, move the archive file
# to it and enter the command
#
#	sh filename
# 
# The files will be extracted automatically.
# Note: Do not use csh.
#
# Archive created: Mon Jul 30 23:12:25 EDT 1990
#
echo x - 8_13a.c
sed 's/^X//' > 8_13a.c << '!EOF!'
/* Copyright (c) 1990 by AT&T Bell Telephone Laboratories, Incorporated. */
/* The C++ Answer Book */
/* Tony Hansen */
/* All rights reserved. */
// match a pattern on the input stream
// %d looks for an integer & stops at the first
//	non-integer character
// %o looks for an octal integer & stops ...
// %x looks for a hexadecimal integer & stops ...
// %i looks for a C++ integer & stops ...
// %f looks for a floating point number & stops ...
// %c matches a single character
// %[xyz] matches a character within the set
// %[^xyz] matches a character not within the set
// %s matches a white-space delineated string
// % may be followed by a field width
// %% matches %
// ' ' matches any white space
// 'X' matches the character X

#include <pat.h>	/* DELETE */
#include <ctype.h>	/* DELETE */
#include <limits.h>	/* DELETE */
#include <stdlib.h>	/* DELETE */
#include "8_13b.c"	/* DELETE */
int pat::match(char *pattern)
{
    // reset base ptr to pt to beginning of buffer
    ebp->setbuffering();

    // loop through the pattern looking
    // for things to match.
    int goodpattern = 1;
    while (*pattern && goodpattern)
	{
	int c = ebp->sgetc();
	char p = *pattern++;

// define a couple of small macros for
// some common sequences
#define Next()				\
    ebp->stossc(), c = ebp->sgetc()

#define WNext()				\
    width--, ebp->stossc(), c = ebp->sgetc()

#define doloop(testfunc)		\
    do { Next(); } while (testfunc(c))

#define dowloop(testfunc)		\
    do { width--; ebp->stossc();	\
    } while ((width > 0) &&		\
	     testfunc(c = ebp->sgetc()))

	switch (p)
	    {
	    // space matches arbitrary white space
	    case ' ':
		if (isspace(c))
		    doloop(isspace);

		else
		    goodpattern = 0;
		break;

	    // a pattern: %d, %f, %c, %s, %%
	    case '%':
		p = *pattern++;

		// check pattern for a field
		// width specification
		long width = LONG_MAX;
		if (isdigit(p))
		    {
		    width = strtol(&pattern[-1],
			&pattern, 10);
		    p = *pattern++;
		    }

		// the default for these two is 1
		else if ((p == '[') || (p == 'c'))
		    width = 1;

		// check for patterns which
		// toss white space first
		switch (p)
		    {
		    case 'd': case 'f':
		    case 'o': case 'x':
		    case 'i': case 's':
			if (isspace(c))
			    doloop(isspace);
		    }

		// now skip past the particular type
		switch (p)
		    {
		    // decimal number
		    case 'd':
			if ((width > 0) &&
			    ((c == '-') || (c == '+')))
			    WNext();

			if ((width > 0) && isdigit(c))
			    dowloop(isdigit);

			else
			    goodpattern = 0;
			break;

		    // octal number
		    case 'o':
			if ((width > 0) &&
			    ((c == '-') || (c == '+')))
			    WNext();

			if ((width > 0) && isodigit(c))
			    dowloop(isodigit);

			else
			    goodpattern = 0;
			break;

		    // hexdecimal number
		    case 'x':
			if ((width > 0) &&
			    ((c == '-') || (c == '+')))
			    WNext();

			if ((width > 0) && isxdigit(c))
			    dowloop(isxdigit);

			else
			    goodpattern = 0;
			break;

		    // decimal, octal or hex number
		    case 'i':
			if ((width > 0) &&
			    ((c == '-') || (c == '+')))
			    WNext();

			// check for leading 0 or 0x
			if ((width > 0) && (c == '0'))
			    {
			    WNext();

			    if ((width > 0) &&
				((c == 'x') || (c == 'X')))
				WNext();
			    }

			if ((width > 0) && isdigit(c))
			    dowloop(isdigit);

			else
			    goodpattern = 0;
			break;

		    // floating point
		    case 'f':
			if ((width > 0) &&
			    ((c == '-') || (c == '+')))
			    WNext();

			int digitsbefore = 0;
			int hasdecimal = 0;
			int digitsafter = 0;
			int hasexponent = 0;

			if ((width > 0) && isdigit(c))
			    {
			    digitsbefore = 1;
			    dowloop(isdigit);
			    }

			if ((width > 0) && (c == '.'))
			    {
			    hasdecimal = 1;
			    WNext();

			    if (isdigit(c))
				{
				digitsafter = 1;
				dowloop(isdigit);
				}
			    }

			if ((width > 0) &&
			    ((c == 'e') || (c == 'E')))
			    {
			    hasexponent = 1;
			    WNext();

			    if ((width > 0) &&
				((c == '-') || (c == '+')))
				WNext();

			    if ((width > 0) && isdigit(c))
				dowloop(isdigit);

			    else
				goodpattern = 0;
			    }

			if (!((digitsbefore &&
			       (hasdecimal ||
				hasexponent)) ||
			      digitsafter))
			    goodpattern = 0;

			break;

		    // any string. leading white
		    // space was tossed above
		    case 's':
			if (width > 0)
			    dowloop(!isspace);
			break;

		    // a scanset
		    case '[':
			int x = 0;
			char *patbegin = pattern;
			if (pattern[1] == '^')
			    {
			    x = 1;
			    patbegin++;
			    }

			while ((width > 0) &&
			    checkcharset(patbegin, x, c))
			    WNext();

			// found the right # of chars?
			if (width > 0)
			    goodpattern = 0;
			pattern += x + 2 +
			    checkcharset(patbegin, 0, -1);
			break;

		    // any single character
		    case 'c':
			while ((width > 0) &&
			       (c != EOF))
			    WNext();

			if (width > 0)
			    goodpattern = 0;
			break;

		    // match single %
		    case '%':
			while ((width > 0) &&
			       (c == '%'))
			    WNext();

			if (width > 0)
			    goodpattern = 0;
			break;

		    // unknown pattern
		    default:
			goodpattern = 0;
			break;
		    }
		break;

	    // non-pattern, match the character
	    default:
		if (c == p)
		    ebp->stossc();

		else
		    goodpattern = 0;
		break;
	    }
	}

    ebp->resetbuffering();
    return goodpattern;
}
!EOF!
ls -l 8_13a.c
echo x - 8_13b.c
sed 's/^X//' > 8_13b.c << '!EOF!'
/* Copyright (c) 1990 by AT&T Bell Telephone Laboratories, Incorporated. */
/* The C++ Answer Book */
/* Tony Hansen */
/* All rights reserved. */
// Check the scanset pattern for the character c.
// Return complement for false and !complement for true.
int checkcharset(char *pattern, int complement, int c)
{
    char *svpat = pattern;
    
    // special case for pattern[0] == ']'
    if ((*pattern == ']') || (*pattern == '-'))
	if (c == *pattern)
	    return !complement;

        else
	    pattern++;
	    
    // run down the pattern
    for ( ; *pattern && (*pattern != ']'); pattern++)
        // either a range or trailing `-'
	if (*pattern == '-')
	    {
	    // special case for trailing `-'
	    if (pattern[1] == ']')
		{
		if (c == '-')
		    return !complement;
		}
		
	    // char range, a-z
	    else if ((c > pattern[-1]) &&
	             (c <= pattern[1]))
		return !complement;

	    // skip past the end value of the range
	    else
		pattern++;
	    }

	else if (*pattern == c)
	    return !complement;
    
    // The character is not in the set. pattern now
    // points to the ']'. If c==-1, then return index
    // of ']', else false.
    if (c == -1)
        return pattern - svpat;
    return complement;
}
!EOF!
ls -l 8_13b.c
echo x - 8_13c.c
sed 's/^X//' > 8_13c.c << '!EOF!'
/* Copyright (c) 1990 by AT&T Bell Telephone Laboratories, Incorporated. */
/* The C++ Answer Book */
/* Tony Hansen */
/* All rights reserved. */
// Match a pattern on the input stream.
// Use the UNIX System V regcmp(3) routines
// to do full regular expression matching.

#include <pat.h>	/* DELETE */
#include <ctype.h>	/* DELETE */
#include <limits.h>	/* DELETE */
#include <stdlib.h>	/* DELETE */
extern char *regcmp(char* ...);
extern int regex(char *re, char *subj, char *ret0 ...);

int pat::match(char *pattern)
{
    // reset base ptr to pt to beginning of buffer
    ebp->setbuffering();

    // make sure that a full line's worth is available
    for (int c = ebp->sgetc();
	 (c != '\n') && (c != EOF);
	 c = ebp->sgetc())
	ebp->stossc();
    int foundnewline = (c == '\n');

    // null out the newline
    if (foundnewline)
	{
	ebp->stossc();
	ebp->sputbackc(0);
	}

    // compile the pattern
    char *re = regcmp(pattern, (char*)0);

    // check the line against the compiled pattern
    int goodpattern = regex(re, ebp->base, 0);

    // put the newline back
    if (foundnewline)
	{
	ebp->stossc();
	ebp->sputbackc('\n');
	}

    ebp->resetbuffering();
    return goodpattern;
}
!EOF!
ls -l 8_13c.c
echo x - all.c
sed 's/^X//' > all.c << '!EOF!'
/* Copyright (c) 1990 by AT&T Bell Telephone Laboratories, Incorporated. */
/* The C++ Answer Book */
/* Tony Hansen */
/* All rights reserved. */
#include <pat.h>
#include <limits.h>
#include <string.h>
#include <ctype.h>
#include "8_12a1.c"	/* int extrabuf::overflow(int) */
#include "8_12a2.c"	/* int extrabuf::underflow() */
#include "8_12a3.c"	/* int extrabuf::reallocate() */
#include "8_12a4.c"	/* void extrabuf::setbuffering() */
#include "8_12a5.c"	/* void extrabuf::resetbuffering() */
!EOF!
ls -l all.c
echo x - makefile
sed 's/^X//' > makefile << '!EOF!'
CC= CC -I. -I../8.12dir -I../../CC
CFLAGS=

all: tst13 tst13b

tst13: tst13.o all.o 8_13a.o ../8.12dir/memmove.o
	$(CC) tst13.o all.o 8_13a.o ../8.12dir/memmove.o -o tst13 -lPW

tst13b: tst13b.o all.o 8_13c.o ../8.12dir/memmove.o
	$(CC) tst13b.o all.o 8_13c.o ../8.12dir/memmove.o -o tst13b -lPW

all.o: all.c
	$(CC) $(CFLAGS) -c all.c

8_13a.o: 8_13a.c 8_13b.c
	$(CC) $(CFLAGS) -c 8_13a.c

8_13c.o: 8_13c.c
	$(CC) $(CFLAGS) -c 8_13c.c

CMP= tst.cmp tstb.cmp
OUT= tst.out tstb.out

tst.out: tst13 pat.input ; tst13 < pat.input > tst.out
tstb.out: tst13b patb.input ; tst13b < patb.input > tstb.out

test: all $(OUT) $(CMP)
	cmp tst.out tst.cmp
	cmp tstb.out tstb.cmp
	@echo test done
!EOF!
ls -l makefile
echo x - pat.input
sed 's/^X//' > pat.input << '!EOF!'
1.75 75 5 123 4213 12341 2332
!EOF!
ls -l pat.input
echo x - patb.input
sed 's/^X//' > patb.input << '!EOF!'
Hello: 1.75 75 5
123 4213
Are: 12341 2332
!EOF!
ls -l patb.input
echo x - tst.cmp
sed 's/^X//' > tst.cmp << '!EOF!'
match 2 %f %d %d succeeded
x = 75
y = 5
f = 1.75
a = 123
b = 4213
!EOF!
ls -l tst.cmp
echo x - tst13.c
sed 's/^X//' > tst13.c << '!EOF!'
/* Copyright (c) 1990 by AT&T Bell Telephone Laboratories, Incorporated. */
/* The C++ Answer Book */
/* Tony Hansen */
/* All rights reserved. */
#include <pat.h>

main()
{
    pat p(cin);
    int x, y;
    float f;

    if (p.match("%d %f %d")) {
	cout << "match 1 %d %f %d succeeded\n";
	p >> x >> f >> y;
    } else if (p.match("%f %d %d")) {
	cout << "match 2 %f %d %d succeeded\n";
	p >> f >> x >> y;
    } else {
	cout << "default succeeded\n";
	p >> x >> y >> f;
    }

    cout << "x = " << x << "\n"
	 << "y = " << y << "\n"
	 << "f = " << f << "\n";

    int a = -1, b = -2;
    p >> a >> b;
    cout << "a = " << a << "\n"
	 << "b = " << b << "\n";
    return 0;
}
!EOF!
ls -l tst13.c
echo x - tst13b.c
sed 's/^X//' > tst13b.c << '!EOF!'
/* Copyright (c) 1990 by AT&T Bell Telephone Laboratories, Incorporated. */
/* The C++ Answer Book */
/* Tony Hansen */
/* All rights reserved. */
#include <pat.h>

main()
{
    pat p(cin);
    int x, y;
    float f;
    char s[100];

    if (p.match("Name:")) {
	cout << "match 1 %s %f %d succeeded\n";
	p >> s >> f >> y >> x;
    } else if (p.match("Hello:")) {
	cout << "match 2 %s %f %d succeeded\n";
	p >> s >> f >> x >> y;
    } else {
	cout << "default succeeded\n";
	p >> s >> x >> y >> f;
    }

    cout << "s = '" << s << "'\n"
         << "x = '" << x << "'\n"
	 << "y = '" << y << "'\n"
	 << "f = '" << f << "'\n";

    int a = -1, b = -2;
    p >> a >> b;
    cout << "a = " << a << "\n"
	 << "b = " << b << "\n";
    return 0;
}
!EOF!
ls -l tst13b.c
echo x - tstb.cmp
sed 's/^X//' > tstb.cmp << '!EOF!'
match 2 %s %f %d succeeded
s = 'Hello:'
x = '75'
y = '5'
f = '1.75'
a = 123
b = 4213
!EOF!
ls -l tstb.cmp
# The following exit is to ensure that extra garbage 
# after the end of the shar file will be ignored.
exit 0
