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( ®s ) )
X traceremainder = 0;
X if ( traceremainder != 0 )
X closeio();
X }
X if ( traceremainder != 0 )
X {
X if ( trace1( ®s ) )
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 *) ®s.ip ) == NULL )
X ++regs.ip;
X disarmbreakpoints();
X showentry( ®s );
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 *) ®s.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 *) ®s.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( ®s );
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( ®s ) )
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 *) ®s->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 *) ®s->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 *) ®s->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 *) ®s->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