[comp.os.minix] PC-AT-386 debugger source 6 of 7

brucee@runx.ips.oz (Bruce Evans) (11/26/88)

#! /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 6 (of 7)."
# Contents:  kernel/db/db.c
# Wrapped by sys@besplex on Sat Nov 26 06:00:29 1988
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'kernel/db/db.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'kernel/db/db.c'\"
else
echo shar: Extracting \"'kernel/db/db.c'\" \(17761 characters\)
sed "s/^X//" >'kernel/db/db.c' <<'END_OF_FILE'
X/* db.c */
X
X#include "const.h"
X#include "type.h"
X#include "chars.h"
X#include "/usr/include/a.out.h"		/* full pathname to get right one! */
X#undef EXTERN
X#define EXTERN
X#include "var.h"
X
X#define NBREAKPOINTS 16
X#define VECTOR_SEGMENT 0
X#define makevectoff( intnum ) (4 * (intnum))
X
X/* command keys for db */
X
X#define BREAK1 'b'
X#define BREAK2 ETOA( F9 )
X#define CASEFLIP1 'c'
X#define DUMP1 'd'
X#define ENTER1 'e'
X#define FORK1 'f'
X#define GO1 'g'
X#define GO2 ETOA( F5 )
X#define FLIP1 'x'
X#define FLIP2 ETOA( F4 )
X#define HELP 'h'
X#define INPORT1 'i'
X#define ITRACEFLIP1 'j'
X#define MAPDUMP1 'm'
X#define MAPDUMP2 ETOA( F2 )
X#define OUTPORT1 'o'
X#define PROCDUMP1 'p'
X#define PROCDUMP2 ETOA( F1 )
X#define QUIT1 'q'
X#define QUIT2 ESCAPE
X#define REG1 'r'
X#define REG2 ETOA( F3 )
X#define SLEVEL1 's'
X#define SYMBOL1 'n'
X#define TRACE1 't'
X#define TRACE2 ETOA( F8 )
X#define UNASM1 'u'
X#define VERBOSE_ENTRY_FLIP 'v'
XPRIVATE struct
X{
X	bool_t armed;
X	struct breakentry *end;
X	struct breakentry
X	{
X		struct adr eadr;
X		opcode_pt oldval;
X	}
X		entry[NBREAKPOINTS];
X}
X	breaktable = { 0, breaktable.entry };
X
XFORWARD struct breakentry *findbreakentry();
X#define getbigcount_t( x ) getoffset_t( (x) )
XFORWARD void getch();
X#define getcount_t( x ) getopcode_pt( (x) )
X#define getport_t( x ) getopcode_pt( (x) )
XFORWARD void getsym();
XFORWARD physoff_t linearadr();
XFORWARD offset_t pop();
X
XPRIVATE unsigned base = 16;
XPRIVATE char_pt ch;
XPRIVATE count_t dlength = 8;
XPRIVATE struct adr dptr;
XPRIVATE count_t dwidth = 16;
XPRIVATE bool_t gotrace = FALSE;
XPRIVATE port_t inportnum = 0;
XPRIVATE bool_t ints_while_tracing = FALSE;
XPRIVATE char *lineptr;
XPRIVATE char_pt lowch;
XPRIVATE struct adr mptr = { 0, 0 };
XPRIVATE flags_t pretrace_f;
XPRIVATE bigcount_t tracecount = 0;
XPRIVATE bool_t traceprint = FALSE;
XPRIVATE struct adr tracestack = { 0, 0 };
XPRIVATE bigcount_t traceremainder;
XPRIVATE count_t ulength = 16;
XPRIVATE bool_t verbose_entry = TRUE;
X
XPUBLIC void db_main( is_sstep, regs )
Xbool_pt is_sstep;
Xstruct regs regs;
X{
X	char_pt key;
X
X	if ( is_sstep )
X	{
X		if ( traceremainder == 0 )
X		{
X			/* false alarm */
X			regs.f &= ~TF;
X			return;
X		}
X		regs.f = regs.f & ~(IF | TF) | pretrace_f & (IF | TF);
Xtrace_entry:
X		++tracecount;
X		/*
X			count-down only if stack is above current level
X			print the trace only if finished or printing enabled as well
X		*/
X		if ( regs.sp >= tracestack.off &&
X			 (tracestack.off == 0 || regs.ss == tracestack.seg) &&
X			 ((--traceremainder == 0 || traceprint)) && !gotrace )
X		{
X			if ( !showentry( &regs ) )
X				traceremainder = 0;
X			if ( traceremainder != 0 )
X				closeio();
X		}
X		if ( traceremainder != 0 )
X		{
X			if ( trace1( &regs ) )
X				/* trace was faked */
X				goto trace_entry;
X			/* continue tracing */
X			return;
X		}
X		if ( gotrace )
X		{
X			gotrace = FALSE;
X			armbreakpoints();
X			return;
X		}
X		if ( tracecount > 1 )
X		{
X			outh32( tracecount );
X			outnstr( " instructions traced" );
X		}
X	}
X	else
X	{
X		/* breakpoint interrupt */
X		--regs.ip;
X		if ( !breaktable.armed ||
X		     findbreakentry( (struct adr *) &regs.ip ) == NULL )
X			++regs.ip;
X		disarmbreakpoints();
X		showentry( &regs );
X	}
X	traceprint = FALSE;
X	dptr.seg = regs.ds;
X	while ( TRUE )
X	{
X		nextline();
X		key = lowch;
X		getsym();
X		if ( ch == ':' || ch == ';' )
X		{
X			setproc( key, &dptr, &mptr );
X			continue;
X		}
X		switch( key )
X		{
X		case BREAK1:
X			if ( ch == '-' )
X			{
X				getsym();
X				clearbreakpoint();
X			}
X			else
X				setbreakpoint();
X		case BREAK2:
X			showbreakpoints();
X			break;
X		case CASEFLIP1:		/* case CLOSE: overloaded */
X			switch( lowch )
X			{
X			case 'k': can_keyboard(); break;
X			case 's': can_screen(); break;
X			case 't':
X				getch();
X				if ( lowch == 'i' )
X					can_itty();
X				else if ( lowch == 'i' )
X					can_otty();
X				else
X				{
X					can_itty();
X					can_otty();
X				}
X				break;
X			default: flipcase(); break;
X			}
X			break;
X		case DUMP1:
X			if ( lowch == 'l' )
X			{
X				getsym();
X				getcount_t( &dlength );
X				break;
X			}
X			if ( lowch == 'w' )
X			{
X				getsym();
X				getcount_t( &dwidth );
X				break;
X			}
X			getadr( &dptr );
X/* case DUMP2: */
X			showmem();
X			break;
X		case ENTER1:
X			getadr( &mptr );
X			setmem();
X			break;
X		case FLIP1:
X		case FLIP2:
X			show_user_screen();
X			while ( testchar() == EOF )	/* inchar() messes cursor */
X				;
X			show_db_screen();
X			break;
X		case FORK1:
X			--regs.ip;
X			if ( peek8 ( (struct adr *) &regs.ip ) != INT_BREAKPOINT )
X				++regs.ip;
X			else
X			{
X				mptr.off = regs.ip;
X				mptr.seg = regs.cs;
X				setmem();
X			}
X			break;
X		case GO1:
X			setbreakpoint();
X		case GO2:
X			if ( findbreakentry( (struct adr *) &regs.ip ) != NULL )
X			{
X				tracestack.off = 0;
X				ints_while_tracing = FALSE;
X				gotrace = TRUE;
X				goto trace;
X			}
X			armbreakpoints();
X			/* fall into QUIT1 */
X		case QUIT1:
X		case QUIT2:
X			tracestack.off = 0;
X			ints_while_tracing = FALSE;
X			closeio();
X			return;
X		case HELP:
X			info();
X			break;
X		case INPORT1:
X			showport();
X			break;
X		case ITRACEFLIP1:
X			if ( (ints_while_tracing = !ints_while_tracing) == FALSE )
X				outnstr( "Interrupts disabled while tracing" );
X			else
X				outnstr( "Interrupts enabled while tracing" );
X			break;
X#ifndef DOS
X		case MAPDUMP1:
X		case MAPDUMP2:
X			map_dmp();
X			break;
X#endif
X		case OUTPORT1:		/* case OPEN: overloaded */
X			switch( lowch )
X			{
X			case 'k': enab_keyboard(); break;
X			case 's': enab_screen(); break;
X			case 't':
X				getch();
X				if ( lowch == 'i' )
X					enab_itty();
X				else if ( lowch == 'i' )
X					enab_otty();
X				else
X				{
X					enab_itty();
X					enab_otty();
X				}
X				break;
X			}
X			setport();
X			break;
X#ifndef DOS
X		case PROCDUMP1:
X		case PROCDUMP2:
X			p_dmp();
X			break;
X#endif
X		case REG1:
X		case REG2:
X			showregs( &regs );
X			break;
X		case SLEVEL1:		/* case STTY: is overloaded */
X			if ( lowch == 't' )
X			{
X				set_tty();
X				break;
X			}
X			if ( !getoffset_t( &tracestack.off ) )
X			{
X				if ( tracestack.off == 0 )
X					tracestack.off = regs.sp;
X				else
X					tracestack.off = 0;
X			}
X			outh32( tracestack.off );
X			outnl();
X			break;
X		case SYMBOL1:
X			if ( ch != 0 )
X				--lineptr;
X			findsname( lineptr, CSEG, TRUE );
X			break;
X		case TRACE1:
X			if ( lowch == 'p' )
X			{
X				traceprint = TRUE;
X				getsym();
X			}
X			if ( getbigcount_t( &traceremainder ) && traceremainder == 0 )
X			{
X				traceprint = FALSE;
X				break;
X			}
X		case TRACE2:
Xtrace:
X			if ( traceremainder == 0 )
X				++traceremainder;
X			tracestack.seg = regs.ss;
X			closeio();
X			tracecount = 0;
X			if ( trace1( &regs ) )
X				goto trace_entry;
X			return;
X		case UNASM1:
X			if ( lowch == 'l' )
X			{
X				getsym();
X				getcount_t( &ulength );
X				break;
X			}
X			getadr( &uptr );
X/* case UNASM2: */
X			showinstructions();
X			break;
X		case VERBOSE_ENTRY_FLIP:
X			verbose_entry = !verbose_entry;
X			break;
X		}
X	}
X}
X
XPRIVATE void armbreakpoints()
X{
X	struct breakentry *entry;
X
X	for ( entry = breaktable.entry; entry < breaktable.end; ++entry )
X	{
X		entry->oldval = peek8( &entry->eadr );
X		poke8( &entry->eadr, INT_BREAKPOINT );
X	}
X	breaktable.armed = TRUE;
X}
X
XPRIVATE void clearbreakpoint()
X{
X	struct breakentry *entry;
X	struct adr adr;
X
X	if ( lowch == '-' )
X		breaktable.end = breaktable.entry;
X	else if ( adr.seg = uptr.seg, getadr( &adr ) )
X	{
X		if ( (entry = findbreakentry( &adr )) == NULL )
X			error( "No Breakpoint Set" );
X		else
X		{
X			memcpy( (char *) entry, (char *) (entry + 1),
X			        (unsigned) ((char *) breaktable.end - (char *) entry) );
X			--breaktable.end;
X		}
X	}
X}
X
XPRIVATE void disarmbreakpoints()
X{
X	struct breakentry *entry;
X
X	if ( breaktable.armed )
X	{
X		for ( entry = breaktable.entry; entry < breaktable.end; ++entry )
X			poke8( &entry->eadr, entry->oldval );
X		breaktable.armed = FALSE;
X	}
X}
X
XPRIVATE void error( s )
Xchar *s;
X{
X	outnstr( s );
X}
X
XPRIVATE void fakeint( intnum, regs )
Xunsigned intnum;
Xstruct regs *regs;
X{
X	push( regs->f, regs );
X	regs->f &= ~(IF | TF);
X	push( (offset_t) regs->cs, regs );
X	regs->cs = peekw( VECTOR_SEGMENT, makevectoff( intnum ) + 2 );
X	push( regs->ip, regs );
X	regs->ip = peekw( VECTOR_SEGMENT, makevectoff( intnum ) );
X}
X
XPRIVATE struct breakentry *findbreakentry( adr )
Xstruct adr *adr;
X{
X	struct breakentry *entry;
X
X	for ( entry = breaktable.entry; entry < breaktable.end; ++entry )
X		if ( linearadr( &entry->eadr ) == linearadr( adr ) )
X			return entry;
X	return NULL;
X}
X
XPRIVATE void get1char()
X{
X	static char line[2] = { 0, 0 };
X
X	line[0] = ch = inchar();
X	lineptr = line;
X	getch();
X}
X
X
XPUBLIC void info()
X{
X	outstr( "MINIX db v1.0" );
X	outnl();
X	outstr( "Processor type is " );
X	switch( db_processor )
X	{
X	case 86: outstr( "8088 or 8086" ); break;
X	case 186: outstr( "80188 or 80186" ); break;
X	case 286: outstr( "80286" ); break;
X	case 386: outstr( "80386" ); break;
X	}
X	outnl();
X}
X
XPRIVATE physoff_t linearadr( adr )
Xstruct adr *adr;
X{
X	return CLICK_SIZE * (physoff_t) adr->seg + adr->off;
X}
X 
XPRIVATE offset_t pop( regs )
Xstruct regs *regs;
X{
X	offset_t value;
X
X	/* need mods here for 32 bits and protected mode */
X	value = peek16( (struct adr *) &regs->sp );
X	regs->sp += 2;
X	return value;
X}
X
XPRIVATE void push( value, regs )
Xoffset_t value;
Xstruct regs *regs;
X{
X	regs->sp -= 2;
X	/* need mods here for 32 bits and protected mode */
X	poke16( (struct adr *) &regs->sp, (u16_t) value );
X}
X
XPRIVATE void setbreakpoint()
X{
X	struct breakentry *entry;
X
X	if ( (entry = breaktable.end) >= (breaktable.entry + NBREAKPOINTS) )
X	{
X		error( "Breakpoint Table Full" );
X	}
X	else if ( entry->eadr.seg = uptr.seg, getadr( &entry->eadr ) )
X	{
X		if ( findbreakentry( entry ) != NULL )
X			error( "Breakpoint Already Set" );
X		else
X		{
X			entry->oldval = peek8( &entry->eadr );
X			if ( (poke8( &entry->eadr, 0x55 ), peek8( &entry->eadr ) != 0x55) ||
X				 (poke8( &entry->eadr, 0xAA ), peek8( &entry->eadr ) != 0xAA) )
X				error( "Breakpoint in ROM?" );
X			else
X				++breaktable.end;
X			poke8( &entry->eadr, entry->oldval );
X		}
X	}
X}
X
XPRIVATE void setmem()
X{
X	opcode_pt byte;
X	char_pt digit;
X
X	outssegadr( &mptr );
X	while ( TRUE )
X	{
X		outh8( peek8( &mptr ) );
X		outbyte( '-' );
X		get1char();
X		if ( getdigit( &digit ) )
X		{
X			outbyte( lineptr[-1] );
X			byte = digit;
X			while ( get1char(), getdigit( &digit ) )
X			{
X				outbyte( lineptr[-1] );
X				byte = byte * base + digit;
X			}
X			poke8( &mptr, byte );
X		}
X		if ( ch == ' ' )
X			++mptr.off;
X		else if ( ch == '-' )
X		{
X			--mptr.off;
X			outnl();
X			outsegadr( &mptr );
X		}
X		else
X			break;
X		out2space();
X	}
X	outnl();
X}
X
XPRIVATE void setport()
X{
X	opcode_pt byte;
X	port_t outportnum;
X
X	if ( getport_t( &outportnum ) && getopcode_pt( &byte ) )
X		/* don't display port, may be side affect */
X		oportb( outportnum, byte );
X}
X
XPRIVATE bool_pt show1instruction()
X{
X	bool_pt idone;
X	static char line[81];
X	struct adr newuptr;
X	struct adr olduptr;
X	bool_pt outnlstatus;
X	struct nlist *sp;
X
X	outbyte( '\r' );
X	do
X	{
X		if ( (sp = findsval( uptr.off, CSEG )) != NULL && sp->n_value == uptr.off )
X		{
X			outsym( sp, uptr.off );
X			outcolon();
X			outnl();
X		}
X		olduptr = uptr;
X		openstring( line );
X		idone = puti();
X		line[stringpos()] = 0;
X		closestring();
X		newuptr = uptr;
X		uptr = olduptr;
X		outssegadr( &uptr );
X		while ( uptr.off != newuptr.off )
X			outget8();
X		if ( newuptr.off - olduptr.off < ((db_processor == 386) ? 5 : 3) )
X			outtab();					/* to column 16 (24 for 386) */
X		outtab();						/* to column 24 (32 for 386) */
X		outnlstatus = outnstr( line );
X	}
X	while ( !idone );			/* eat all prefixes */
X	return outnlstatus;
X}
X
XPRIVATE void showbreakpoints()
X{
X	struct breakentry *entry;
X	unsigned n;
X
X	for ( entry = breaktable.entry, n = 0; entry < breaktable.end; ++entry, ++n )
X	{
X		outssegadr( &entry->eadr );
X		if ( n == 6  )
X		{
X			outnl();
X			n = -1;
X		}
X	}
X	if ( n != 0 )
X		outnl();
X}
X
XPRIVATE bool_pt showentry( regs )
Xstruct regs *regs;
X{
X	openio();
X	syminit();
X	if ( verbose_entry )
X		return showregs( regs );
X	else
X	{
X		uptr.off = regs->ip;
X		uptr.seg = regs->cs;
X		return show1instruction();
X	}
X}
X
XPRIVATE void showinstructions()
X{
X	count_t row;
X
X	for ( row = 0; row < ulength; ++row )
X		if ( !show1instruction() )
X			break;
X}
X
XPRIVATE void showmem()
X{
X	u8_pt byte;
X	count_t column;
X	offset_t oldoffset;
X	count_t row;
X
X	for ( column = (db_processor == 386 ? 16 : 12); column != 0; --column )
X		/* spaces on title line above address */
X		outspace();
X	for ( column = 0; column < dwidth; ++column )
X	{
X		outh4( (u4_pt) (dptr.off + column) );
X		out2space();
X	}
X	for ( row = 0; row < dlength; ++row )
X	{
X		if ( !outnl() )
X			break;
X		outssegadr( &dptr );
X		for ( column = 0, oldoffset = dptr.off; column < dwidth; ++column )
X		{
X			outh8s( peek8( &dptr ) );
X			++dptr.off;
X		}
X		for ( column = 0, dptr.off = oldoffset; column < dwidth; ++column )
X		{
X			byte = peek8( &dptr );
X			++dptr.off;
X			if ( byte >= 0x7F || byte < ' ' )
X				byte = '.';
X			outbyte( (int) byte );
X		}
X	}
X	outnl();
X}
X
XPRIVATE void showport()
X{
X	if ( !getport_t( &inportnum ) )
X	{
X		outh16( inportnum );
X		outcolon();
X		outspace();
X	}
X	outh8( inportb( inportnum ) );
X	outnl();
X}
X
XPRIVATE bool_pt showregs( regs )
Xstruct regs *regs;
X{
X	char *nameptr;
X	static char *regnames =
X		"ax\0bx\0cx\0dx\0\nsi\0di\0bp\0\nds\0es\0sp\0ss\0\nip\0cs\0 f\0\n";
X	offset_t *regptr;
X
X	outbyte( '\r' );
X	for ( nameptr = regnames, regptr = (offset_t *) regs; *nameptr != 0; )
X	{
X		outustr( nameptr );
X		outstr( " = " );
X		if ( nameptr[1] == 's' )
X		{
X			if ( db_processor == 386 )
X			{
X				out2space();
X				out2space();
X			}
X			outh16( *(u16_t *) regptr );
X			regptr = (offset_t *) ((u16_t *) regptr + 1);
X		}
X		else if ( db_processor == 386 )
X			outh32( *regptr++ );
X		else
X			outh16( (u16_t) *regptr++ );
X		nameptr += 3;
X		if ( *nameptr == '\n' )
X		{
X			++nameptr;
X			outnl();
X		}
X		else
X			outstr( "  " );
X	}
X	uptr.off = regs->ip;
X	uptr.seg = regs->cs;
X	return show1instruction();
X}
X
X/* set up for trace of 1 instruction, maybe fake the instruction */
X
XPRIVATE bool_pt trace1( regs )
Xstruct regs *regs;
X{
X	unsigned intnum;
X
X	/*
X		The 386 clears the trap flag for software interrupts.
X		This is undesirable since we want to trace them, both to debug and to
X		get proper instruction counts.
X		So fake these instructions (real mode only).
X		Division by zero and other 386 exceptions require another method.
X		Also fake all instructions which change the interrupt flag:
X			CLI, IRET, POPF, PUSHF, STI.
X		These fakes are not quite right yet, since prefixed instructions are
X		not caught.
X		INT and IRET are extremely complicated in protected mode and it will
X		be a big job to emulate them.
X	*/
X#define BREAKPOINT_VECTOR 3
X#define CLI 0xFA
X#define INT_GENERAL 0xCD
X#define INT_OVERFLOW 0xCE
X#define INTO_VECTOR 4
X#define IRET 0xCF
X#define POPF 0x9D
X#define PUSHF 0x9C
X#define STI 0xFB
X
X	switch( peek8( (struct adr *) &regs->ip ) )
X	{
X	case CLI:
X		regs->f &= ~IF;
X		break;
X	case INT_GENERAL:
X		if ( protected )
X			goto toohard;
X		++regs->ip;
X		intnum = peek8( (struct adr *) &regs->ip );
X		++regs->ip;
X		fakeint( intnum, regs );
X		return TRUE;
X	case INT_BREAKPOINT:
X		if ( protected )
X			goto toohard;
X		fakeint( BREAKPOINT_VECTOR, regs );
X		return TRUE;
X	case INT_OVERFLOW:
X		if ( protected )
X			goto toohard;
X		fakeint( INTO_VECTOR, regs );
X		return TRUE;
X	case IRET:
X		if ( protected )
X			goto toohard;
X		regs->ip = pop( regs );
X		regs->cs = pop( regs );
X		regs->f = pop( regs );
X		return TRUE;
X	case POPF:
X		if ( protected )
X			goto toohard;
X		regs->f = pop( regs );
X		break;
X	case PUSHF:
X		if ( protected )
X			goto toohard;
X		push( regs->f, regs );
X		break;
X	case STI:
X		regs->f |= IF;
X		break;
X	default:
Xtoohard:
X		pretrace_f = regs->f;
X		if ( !ints_while_tracing )
X			regs->f &= ~IF;
X		regs->f |= TF;
X		return FALSE;
X	}
X	++regs->ip;
X	return TRUE;
X}
X
X#ifdef DOS
X
X/* various dummy routines */
X
XPUBLIC struct nlist *findsname()
X{
X	return NULL;
X}
X
XPUBLIC struct nlist *findsval()
X{
X	return NULL;
X}
X
XPUBLIC void get_con_state()
X{
X}
X
XPUBLIC void outsym()
X{
X}
X
XPUBLIC void reboot()
X{
X}
X
XPUBLIC void reset_con_state()
X{
X}
X
XPUBLIC void setproc()
X{
X}
X
XPUBLIC void syminit()
X{
X	prompt = '?';
X}
X
X#endif
X
X/* parsing routines */
X
X/* test input for being an address expression of the form [segment:;]offset */
X/* where segment and offset are numbers in the current base, fetch if so */
X
XPRIVATE bool_pt getadr( adr )
Xstruct adr *adr;
X{
X	struct adr tempadr;
X
X	if ( !getoffset_t( &tempadr.off ) )
X		return FALSE;
X	if ( ch == ':' || ch == ';' )
X	{
X		getsym();
X		tempadr.seg = tempadr.off;
X		if ( !getoffset_t( &tempadr.off ) )
X			return FALSE;
X		adr->seg = tempadr.seg;
X	}
X	adr->off = tempadr.off;
X	return TRUE;
X}
X
XPRIVATE char_pt getch()
X{
X	/* convert to unsigned char so ETOA macro works */
X	if ( (ch = UCHAR( *lineptr )) != 0 )
X		++lineptr;
X	lowch = mytolower( ch );
X	return ch;
X}
X
X/* test input for being an opcode_pt in the current base, fetch if so */
X
XPRIVATE bool_pt getopcode_pt( num )
Xopcode_pt *num;
X{
X	offset_t tempnum;
X
X	if ( !getoffset_t( &tempnum ) )
X		return FALSE;
X	*num = tempnum;
X	return TRUE;
X}
X
X/* test current char for being a digit in the current base, fetch if so */
X
XPRIVATE bool_pt getdigit( digit )
Xchar_pt *digit;
X{
X	char_pt tempdigit;
X
X	if ( (tempdigit = lowch) >= '0' && tempdigit <= '9' )
X		tempdigit = tempdigit - '0';
X	else if ( tempdigit >= 'a' && tempdigit <= 'f' )
X		tempdigit = tempdigit + (10 - 'a');
X	else
X		return FALSE;
X	if ( tempdigit >= base )
X		return FALSE;
X	*digit = tempdigit;
X	getch();
X	return TRUE;
X}
X
X/* test input for being an offset_t in the current base, fetch if so */
X
XPRIVATE bool_pt getoffset_t( num )
Xoffset_t *num;
X{
X	char_pt digit;
X	offset_t tempnum;
X
X	if ( !getdigit( &digit ) )
X		return FALSE;
X	tempnum = digit;
X	while ( getdigit( &digit ) )
X		tempnum = tempnum * base + digit;
X	*num = tempnum;
X	if ( ch == ' ' )
X		getsym();
X	return TRUE;
X}
X
XPRIVATE void getsym()
X{
X	while( getch() == ' ' )
X		;
X}
X
XPRIVATE void nextline()
X{
X		lineptr = getline();
X		if ( UCHAR( *lineptr ) <= '~' )
X			outnl();
X		getsym();
X}
END_OF_FILE
if test 17761 -ne `wc -c <'kernel/db/db.c'`; then
    echo shar: \"'kernel/db/db.c'\" unpacked with wrong size!
fi
# end of 'kernel/db/db.c'
fi
echo shar: End of archive 6 \(of 7\).
cp /dev/null ark6isdone
MISSING=""
for I in 1 2 3 4 5 6 7 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 7 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0

Bruce Evans
Internet: brucee@runx.ips.oz.au    UUCP: uunet!runx.ips.oz.au!brucee