#if UNIX
#  include <signal.h>
#endif
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>

#include "adltypes.h"
#include "adlprog.h"
#include "adldef.h"
#include "vstring.h"
#include "virtmem.h"

#include "debugsym.c"

#define NUMCODE	(hdr.codeindex.numobjs * 512)

#undef max
#undef min
#define max(a,b) (a > b ? a : b)
#define min(a,b) (a < b ? a : b)

char
    *inname;			/* Input file name			*/
FILE
    *infile;			/* Input file				*/

struct pagetab
    codetab;			/* Virtual memory table			*/

address
    *sortedrouts,		/* Sorted list of routine entry points	*/
    bot, top;			/* Beginning and ending of action range	*/
int16
    blocked,			/* Semaphore for breaker		*/
    was_signaled,		/* Were we signaled?			*/
    my_nums;			/* Temporary for virtual strings	*/
struct symbol
    *symtab;			/* Symbol table				*/
char
    curr_name[ 80 ],		/* Current file name			*/
    curr_string[ 1024 ];	/* Current line in current file		*/
FILE
    *curr_file;			/* File descriptor of above		*/
int
    print_lines = 0,		/* Should we print lines?		*/
    curr_line;			/* Current line number			*/


struct	header		hdr;		/* Actual header		*/
struct	verbrec		*verbspace;	/* Verb contents		*/
struct	objrec		*objspace;	/* Object contents		*/
struct	preprec		*prepspace;	/* Preposition contents		*/
struct	vp_syn		*verbsyn;	/* Verb synonyms		*/
int16			*varspace;	/* Stack & variables		*/
int16			*nounspace;	/* Noun indices			*/
address			*routspace;	/* Routine indexes		*/
int32			*str_tab;	/* String table			*/

void
    init( void ),
    loadarray( void **a, struct adldir *d ),
    sortrouts( void ),
    sym_decode( void ),
    adldebug( void ),
    eatEOL( void ),
    getargs( address lower, address upper ),
    dumpsymbols( int16 t ),
    dumpdir( void ),
    dumpglobs( void ),
    dumpinstr( void ),
    print_one( int16 num ),
    dumpstrings( void ),
    dumpnouns( void ),
    dumpobjs( void ),
    dumprouts( void ),
    dumpverbs( void ),
    dohelp( void );
char
    *print_type( int type ),
    printone( address *addr );
int
    mygetchar( void ),
    eatwhite( int ch ),
    find_leg( void ),
    findone( int type ),
    find_sym( int t, int16 v );

int main( int argc, char **argv )
{
    if( argc != 2 ) {
	/* Too few command line arguments */
	printf( "Usage: %s dungeon.\n", argv[ 0 ] );
	exit( -1 );
    }
    inname = argv[ 1 ];		/* Save the input file name		*/
    init();			/* Initialize the structures		*/
    adldebug();			/* Do the debugging			*/
    return 0;
}

void breaker( void )
{
    if( !blocked ) {
	blocked = was_signaled = 1;
	puts( "\n" );
    }
}

void init( void )
{
    /* Open the file */
    infile = fopen( inname, "rb+" );
    if( !infile ) {
	printf( "Error opening file %s\n", inname );
	exit( -1 );
    }

    /* Read the header structure */
    fseek( infile, 0L, 0 );
    fread( &hdr, sizeof( struct header ), 1, infile );
    if( hdr.magic != M_ADL ) {
	printf( "%s : not an ADL datafile.\n", inname );
	exit( -1 );
    }

    /* Load the arrays of things */
    loadarray( (void **)&varspace,	&hdr.varindex );
    NUMROUT++;
    loadarray( (void **)&routspace,	&hdr.routindex );
    NUMROUT--;
    loadarray( (void **)&symtab,		&hdr.symindex );
    loadarray( (void **)&verbspace,	&hdr.verbindex );
    loadarray( (void **)&objspace,	&hdr.objindex );
    loadarray( (void **)&nounspace,	&hdr.nounindex );
    loadarray( (void **)&str_tab,	&hdr.strtabindex );

    /* Initialize the virtual memory routines */
    vsinit( infile, hdr.strindex.ptr, 1, (char *)NULL, (char *)NULL, &my_nums,
	    str_tab );
    vm_init( infile, hdr.codeindex.ptr, &codetab, 0 );

    /* Print the copyright messages */
    fputs( "ADL debugger - Version 2.1 - April 28, 1987\n", stderr );
    fputs( "Copyright 1985, 1986, 1987 by Ross Cunniff\n", stderr );
    fputs( "All rights reserved.\n", stderr );
    fflush( stderr );

    /* Sort the routine entry points, and decode the symbol table */
    sortrouts();
    sym_decode();

    /* All done.  Print the prompt and enable the break catcher. */
    fputs( "Type ? for help.\n", stderr );
    fflush( stderr );
}


void loadarray( void **a, struct adldir *d )
{
    if( d->numobjs * d->objsize ) {
	fseek( infile, d->ptr, 0 );
	*a = malloc( d->numobjs * d->objsize );
	if( *a == (char *)0 ) {
	    fprintf( stderr, "Out of memory.\n" );
	    exit( -1 );
	}
	fread( *a, d->numobjs, d->objsize, infile );
    }
}


void sortrouts( void )
{
    int16
	i, j;			/* Loop counters			*/
    char
	*used;

    /* Tell the user what we're doing */
    fputs( "Sorting routines...", stderr );
    fflush( stderr );

    /* Get memory for the sorted routines and zero it out */
    sortedrouts = (address *)malloc( sizeof( address ) * (NUMROUT + 1) );
    used = malloc( sizeof( char ) * (NUMROUT + 1) );
    if( (sortedrouts == (address *)0) || (used == (char *)0) ) {
	fprintf( stderr, "Out of memory.\n" );
	exit( -1 );
    }

    for( i = 0; i <= NUMROUT; i++ ) {
	used[ i ] = sortedrouts[ i ] = 0;
    }

    /* Use a simple selection sort. (yeah, yeah, O(N**2), but I'm lazy) */
    for( i = 1; i <= NUMROUT; i++ ) {
	for( j = 0; j < NUMROUT; j++ ) {
	    if(		!used[ j ] &&
		(routspace[ j ] > routspace[ sortedrouts[ NUMROUT - i ] ]) )
	    {
		sortedrouts[ NUMROUT - i ] = j;
	    }
	}
	used[ sortedrouts[ NUMROUT - i ] ] = 1;
    }

    routspace[ NUMROUT ] = NUMCODE + 1;

    /* All done! */
    fputs( "Done.\n", stderr );
    fflush( stderr );
    sortedrouts[ NUMROUT ] = NUMROUT;
    free( used );
}


void sym_decode( void )
{
    int16
	i, j;

    /* Tell the user what we're doing */
    fputs( "Decoding symbol table...", stderr );
    fflush( stderr );

    /* Un-encode the symbol table, by inverting each character */
    for( i = 1; i < NUMSYM; i++ ) {
	if( !*symtab[ i ].name ) {
	    /* This name is null - make it a question mark */
	    symtab[ i ].name[ 0 ] = '?';
	    symtab[ i ].name[ 1 ] = '\0';
	}
	else {
	    for( j = 0; symtab[ i ].name[ j ]; j++ ) {
		symtab[ i ].name[ j ] ^= CODE_CHAR;
	    }
	}
    }

    /* All done! */
    fputs( "Done.\n", stderr );
    fflush( stderr );
}


void adldebug( void )
{
    int
	prompt = 1;
    static char
	*print_styles[3] = {
	    "assembly only",
	    "mixed assembly and source",
	    "source only"
	};

    while( 1 ) {
	was_signaled = 0;
	blocked = 0;
	if( prompt ) {
	    fflush( stdout );
	    fputs( "> ", stderr );
	    fflush( stderr );
	}
	prompt = 1;
	switch( getchar() ) {
	/* Execute the user's command */
	case 'a' : eatEOL();		      dumpsymbols( ADJEC );	break;
	case 'd' : eatEOL();		      dumpdir();		break;
	case 'g' : getargs( 0, NUMVAR - 1 );  dumpglobs();		break;
	case 'i' : getargs( 1, NUMCODE - 1 ); dumpinstr();		break;
	case 'm' : getargs( 1, NUMSTR - 1 );  dumpstrings();		break;
	case 'n' : getargs( 0, NUMNOUN - 1 ); dumpnouns();		break;
	case 'o' : getargs( 0, NUMOBJ - 1 );  dumpobjs();		break;
	case 'p' :
	    eatEOL();
	    print_lines = (print_lines+1) % 3;
	    printf("Printing %s\n", print_styles[print_lines]);
	    break;
	case 'r' : getargs( 1, NUMROUT - 1 ); dumprouts();		break;
	case 's' : getargs( 0, NUMSYM - 1 );  dumpsymbols( 0 );		break;
	case 'v' : getargs( 0, NUMVERB - 1 ); dumpverbs();		break;
	case '?' : eatEOL();		      dohelp();			break;
	case EOF :
	case 'q' : exit( 0 );
	case '\t':
	case ' ' : prompt = 0;
	case '\n': break;
	default  : eatEOL();		  puts( "\007" );
	}
    }
}


void eatEOL( void )
{
    while( getchar() != '\n' ) {
	/* NOTHING */
    }
}


void getargs( address lower, address upper )
{
    int
	ch;

    bot = top = 0;
    ch = eatwhite( ' ' );
    if( ch == '$' ) {
	bot = upper;
	ch = mygetchar();
    }
    else {
	while( isdigit( ch ) ) {
	    bot = 10L * bot + (ch - '0');
	    ch = mygetchar();
	}
    }
    bot = min( max( bot, lower), upper );
    ch = eatwhite( ch );
    if( ch == '\n' ) {
	top = bot;
	return;
    }
    else if( ch != '-' ) {
	top = bot - 1;
	while( ch != '\n' ) {
	    ch = mygetchar();
	}
	return;
    }

    ch = eatwhite( ' ' );
    if( ch == '$' ) {
	top = upper;
    }
    else {
	while( isdigit( ch ) ) {
	    top = 10L * top + (ch - '0');
	    ch = mygetchar();
	}
    }
    top = min( max( top, lower ), upper );
    while( ch != '\n' ) {
	ch = mygetchar();
    }
}


int mygetchar( void )
{
    int
	ch;

    if( (ch = getchar()) == EOF ) {
	exit( 0 );
    }
    return ch;
}


int eatwhite( int ch )
{
    while( (ch == ' ') || (ch == '\t') )
	ch = mygetchar();
    return ch;
}


void dumpsymbols( int16 t )
{
    int
	i;

    if( t ) {
	bot = 1; top = NUMSYM - 1;
    }
    for( i = bot; i <= top; i++ ) {
	checkbreak( breaker );
	if( was_signaled ) {
	    return;
	}
	if( t && symtab[ i ].type != t ) {
	    continue;
	}
	printf( "symtab[ %4d ].type = %s .val = %4d, .name = \"%s\"\n", i,
		print_type( symtab[ i ].type ),
		symtab[ i ].val, symtab[ i ].name );
    }
}


char *print_type( int type )
{
    switch( type ) {
    case NOUN     : return "NOUN,    ";
    case VERB     : return "VERB,    ";
    case ADJEC    : return "ADJEC,   ";
    case PREP     : return "PREP,    ";
    case ROUTINE  : return "ROUTINE, ";
    case STRING   : return "STRING,  ";
    case CONST    : return "CONST,   ";
    case VAR      : return "GLOBAL,  ";
    case ARGUMENT : return "ARGUMENT,";
    case NOUN_SYN : return "NOUN_SYN,";
    case ARTICLE  : return "ARTICLE, ";
    default	  : return "UNKNOWN, ";
    }
}


void dumpdir( void )
{
    printf( "File name is %s\n", inname );
    printf( "ADL id is %d\n", hdr.adlid );
    printf( "%8d bytes in instructions\n",
  	hdr.codeindex.numobjs * hdr.codeindex.objsize );
    printf( "%8ld bytes in messages\n\n",
  	(int32)((int32)hdr.strindex.numobjs * (int32)hdr.strindex.objsize ) );
    printf( "%8d bytes in %8d string table entries\n",
  	4*hdr.strtabindex.numobjs, hdr.strtabindex.numobjs );
    printf( "%8d bytes in %8d symbols",
 	NUMSYM*hdr.symindex.objsize, NUMSYM );
    printf( " (%d legal)\n", find_leg() );
    printf( "%8d bytes in %8d verbs\n",
	NUMVERB*hdr.verbindex.objsize, NUMVERB );
    printf( "%8d bytes in %8d objects\n", NUMOBJ*hdr.objindex.objsize, NUMOBJ );
    printf( "%8d bytes in %8d nouns\n",
	NUMNOUN*hdr.nounindex.objsize, NUMNOUN );
    printf( "%8d bytes in %8d routines\n",
	NUMROUT*hdr.routindex.objsize,NUMROUT);
    printf( "%8d bytes in %8d globals\n",
	NUMVAR * hdr.varindex.objsize, NUMVAR );
    printf( "%8d adjectives\n", findone( ADJEC ) );
    printf( "%8d articles\n", findone( ART ) );
}


int find_leg( void )
{
    int
	i, num_leg;

    num_leg = 0;
    for( i = 0; i < NUMSYM; i++ ) {
	if( *symtab[ i ].name != '?') {
	    num_leg++;
	}
    }
    return num_leg;
}


int findone( int type )
{
    int
	i, num;

    num = 0;
    for( i = 1; i < NUMSYM; i++ ) {
	if( symtab[ i ].type == type ) {
	    num++;
	}
    }
    return num;
}


void dumpglobs( void )
{
    int
	i;

    fputs( "Globals:\n", stdout );
    for( i = bot; i <= top; i++ ) {
	checkbreak( breaker );
	if( was_signaled ) {
	    return;
	}
	printf( "\tVAR( %d ) = %d\n", i, varspace[ i ] );
    }
}


void dumpinstr( void )
{
    long
	i,		/* Loop counter				*/
	lastrout;	/* Last routine we've seen		*/
    address
	t;

    /* Find which routine this instruction is in */
    lastrout = 0;
    while(	(((long)routspace[ sortedrouts[ lastrout ] ]) <= ((long)bot)) &&
		(((long)lastrout) < ((long)NUMROUT)) )
    {
	lastrout++;
    }
    lastrout--;

    /* Print the instructions */
    printf( "ROUTINE %d + %ld:\n", sortedrouts[ lastrout ],
	   (long)bot - (long)routspace[ sortedrouts[ lastrout ] ] );
    for( i = bot; i <= top; /* NULL */ ) {
	checkbreak( breaker );
	if( was_signaled ) {
	    return;
	}
	if( ((long)i) >= ((long)routspace[ sortedrouts[ lastrout + 1 ] ]) ) {
	    /* We crossed a routine boundary */
	    if( lastrout < NUMROUT ) {
		lastrout++;
		printf( "ROUTINE %d:\n", sortedrouts[ lastrout ] );
	    }
	    else {
		return;
	    }
	}
	t = i;
	(void)printone( &t );
	i = t;
    }
}


char printone( address *addr )
{
    char
	opr, *s, *temp_name;
    address
	num,
	opnd;

    opr = vm_get8( (int32)(*addr), &codetab );
    if( opr & PUSHN ) {
	/* The high order bit is set - this is a PUSH NEG */
	if( opr & 0x7F ) {
	    opnd = 0xFF00 | opr;	/* Perform the sign extension */
	}
	else {
	    opnd = 0;
	}
	opr = num = 1;

	if( print_lines < 2 ) {
            s = "";
            if ((-opnd) < (sizeof(adlBuiltins) / sizeof(char *))) {
                s = adlBuiltins[-opnd];
            }
	    printf( "\t\t%08d : PUSH     %d   %s\n", *addr, opnd, s );
	}
    }
    else if( opr & (PUSHARG | PUSHLOCL | CALL) ) {
	switch( opr & (PUSHARG | PUSHLOCL | CALL) ) {
	case PUSHARG  : s = "PUSHARG  "; break;
	case PUSHLOCL : s = "PUSHLOCL "; break;
	case CALL     : s = "CALL     "; break;
	}
	if( print_lines < 2 ) {
	    printf( "\t\t%08d : %s%d\n", *addr, s, opr & 0x01F );
	}
	opr = num = 1;
    }
    else if( opr & (PUSHS | JMP | JMPZ) ) {
	switch( opr & (PUSHS | JMP | JMPZ) ) {
	case PUSHS:
	    s = "PUSH     ";
	    opnd = (opr & 0x07) << 8;
	    opnd |= vm_get8( (int32)(*addr + 1), &codetab ) & 0xFF;
	    if( opnd > 1023 )
		/* Sign extend the thing */
		opnd = opnd - 2048;
	    num = 2;
	    break;
	case JMP  :
	    s = "JMP      ";
	    opnd = vm_get16( (int32)(*addr) + 1, &codetab ) & 0x0FFFF;
#if LONG_ADDRESS
	    opnd |= ((address)opr & 0x07) << 16;
#endif
	    num = 3;
	    break;
	case JMPZ :
	    s = "JMPZ     ";
	    opnd = vm_get16( (int32)(*addr) + 1, &codetab ) & 0x0FFFF;
#if LONG_ADDRESS
	    opnd |= ((address)opr & 0x07) << 16;
#endif
	    num = 3;
	    break;
	}
	if( print_lines < 2 ) {
#if LONG_ADDRESS
	    printf( "\t\t%08ld : %s%ld\n", *addr, s, opnd );
#else
	    printf( "\t\t%08d : %s%d\n", *addr, s, opnd );
#endif
	}
	opr = 1;
    }
    else {
	switch( opr ) {
	case NOP    : s = "NOP      "; num = 1; break;
	case PUSHME : s = "PUSHME   "; num = 1; break;
	case POP    : s = "POP      "; num = 1; break;
	case RET    : s = "RET      "; num = 1; break;
	case FILEN  : s = "FILEN    "; num = 3; break;
	case LINEN  : s = "LINEN    "; num = 3; break;
	case PUSH   : s = "PUSH     "; num = 3; break;
	default     : s = "ILLEGAL  "; num = 0; opr = -1;
	}
	if( num <= 1 ) {
	    if( print_lines < 2 ) {
		printf( "\t\t%08ld : %s\n", (long) *addr, s );
	    }
	}
	else {
	    opnd = vm_get16( (int32)(*addr + 1), &codetab );
	    if( print_lines ) {
		if( opr == FILEN ) {
		    temp_name = virtstr( opnd );
		    if( strcmp( temp_name, curr_name ) != 0 ) {
			printf( "***FILE %s***\n", temp_name );
			if( curr_file != (FILE *)0 ) {
			    fclose( curr_file );
			}
			curr_file = fopen( temp_name, "r" );
			strcpy( curr_name, temp_name );
			curr_line = 0;
		    }
		}
		else if( opr == LINEN ) {
		    print_one( opnd );
		}
		else if( print_lines < 2 ) {
		    printf( "\t\t%08ld : %s%d\n", (long) *addr, s, opnd );
		}
	    }
	    else {
		printf( "\t\t%08ld : %s%d\n", (long) *addr, s, opnd );
	    }
	}
    }
    *addr += num;
    return opr;
}


void print_one( int16 num )
{
    if( curr_file == (FILE *)0 ) {
	return;
    }

    if( curr_line > num ) {
	curr_line = 0;
	fseek( curr_file, 0L, 0 );
    }
    if( curr_line == num ) {
	/*fputs( curr_string, stdout );*/
    }
    if( curr_line < num ) {
	while( curr_line < num ) {
	    fgets( curr_string, 1024, curr_file );
	    curr_line++;
	}
	fputs( curr_string, stdout );
    }
}


void dumpstrings( void )
{
    int16
	i;

    for( i = bot; i <= top; i++ ) {
	checkbreak( breaker );
	if( was_signaled ) {
	    return;
	}
	printf( "Virtstr( %d ) = %s\n", i, virtstr( i ) );
    }
}


void dumpnouns( void )
{
    int16
	i;

    fputs( "Nouns:\n", stdout );
    for( i = bot; i <= top; i++ ) {
	checkbreak( breaker );
	if( was_signaled ) {
	    return;
	}
	printf( "\tNOUN %s\n", symtab[ find_sym( NOUN, i ) ].name );
	printf( "\t\tnounspace[ %5d ] = %d\n", i, nounspace[ i ] );
    }
}


int find_sym( int t, int16 v )
{
    int
	i;

    for( i = 0; i < NUMSYM; i++ ) {
	if( symtab[ i ].type == t && symtab[ i ].val == v ) {
	    return i;
	}
    }
    return 0;
}


void dumpobjs( void )
{
    int16
	i, j, t;

    fputs( "Objects:\n", stdout );
    for( i = bot; i <= top; i++ ) {
	checkbreak( breaker );
	if( was_signaled ) {
	    return;
	}

	/* Print the object name */
	t = objspace[ i ].adj;
	if( t < 0 ) {
	    /* This modifier is a verb */
	    printf(	"\tOBJECT %s %s", symtab[ find_sym( VERB, -t ) ].name,
			symtab[ find_sym( NOUN, objspace[ i ].noun ) ].name );
	}
	else if( t > 0 ) {
	    /* This modifier is an adjective */
	    printf(	"\tOBJECT %s %s", symtab[ find_sym( ADJEC, t ) ].name,
			symtab[ find_sym( NOUN, objspace[ i ].noun ) ].name );
	}
	else {
	    /* This object has no modifier */
	    printf( "\tOBJECT %s",
			symtab[ find_sym( NOUN, objspace[ i ].noun ) ].name );
	}
	printf( "\t{val is %d}\n", i );

	/* Print the object properties */
	printf( "\t\tLOC        = %d\n", objspace[ i ].loc );
	printf( "\t\tCONT       = %d\n", objspace[ i ].cont );
	printf( "\t\tLINK       = %d\n", objspace[ i ].link );
	printf( "\t\tOTHERS     = %d\n", objspace[ i ].others );
	printf( "\t\tBITPROPS   = %08x\n", objspace[ i ].bitprops);
	for( j = 0; j < _LD - (NUMBITS + 1); j++ ) {
	    printf( "\t\tPROP[ %d ] = %d\n", j + (NUMBITS + 1),
			objspace[i].props[j]);
	}
	printf( "\t\tLDESC      = %d\n", objspace[i].props[_LD-(NUMBITS + 1)] );
	printf( "\t\tSDESC      = %d\n", objspace[i].props[_SD-(NUMBITS + 1)] );
	printf( "\t\tACTION     = %d\n", objspace[i].props[_ACT-(NUMBITS + 1)]);
	fputs( "\n", stdout );
    }
    fputs( "\n", stdout );
}


void dumprouts( void )
{
    address
	j;
    int16
	i, rname;
    char
	t;

    fputs( "Routines:\n", stdout );
    for( i = bot; i <= top; i++ ) {
	rname = find_sym( ROUTINE, i );
	if( rname ) {
	    printf( "\tROUTINE %s:\n", symtab[ rname ].name );
	}
	else {
	    printf( "\tROUTINE #%d:\n", i );
	}
	j = routspace[ i ];
	if( !j ) {
	    fputs( "\t\tNULL\n", stdout );
	}
	else {
	    t = printone( &j );
	    while( (t != RET) && (t >= 0) ) {
		checkbreak( breaker );
		if( was_signaled )
		    return;
		t = printone( &j );
	    }
	}
	fputs( "\n", stdout );
    }
}


void dumpverbs( void )
{
  int16
	i, v;

  fputs( "Verbs:\n", stdout );
    for( i = bot; i <= top; i++ ) {
	v = find_sym( VERB, i );
	checkbreak( breaker );
	if( was_signaled ) {
	    return;
	}
	printf( "\tVERB %s; { val is %d }\n", symtab[ v ].name, i );
	printf( "\t\tPREACT = %d\n", verbspace[ i ].preact );
	printf( "\t\tACTION = %d\n", verbspace[ i ].postact );
	fputs( "\n", stdout );
    }
    fputs( "\n", stdout );
}


void dohelp( void )
{
    puts( "Commands available:" );
    puts( "  a       -- print out all adjectives" );
    puts( "  d       -- print out debugging information" );
    puts( "  g RANGE -- print out globals in RANGE" );
    puts( "  i RANGE -- print out instructions in RANGE" );
    puts( "  m RANGE -- print out virtual strings in RANGE" );
    puts( "  n RANGE -- print out nouns in RANGE" );
    puts( "  o RANGE -- print out objects in RANGE" );
    puts( "  p       -- toggles printing of source lines" );
    puts( "  q       -- quit adldebug" );
    puts( "  r RANGE -- print out routines in RANGE" );
    puts( "  s RANGE -- print out symbols in RANGE" );
    puts( "  v RANGE -- print out verbs in RANGE" );
    puts( "  ?       -- print out this list" );
    puts( "\nRANGE is either NUMBER, or NUMBER-NUMBER, where NUMBER" );
    puts( "is either a number or the character '$' (representing the" );
    puts( "largest possible value)." );
}

/*** EOF adldebug.c ***/
