/*
 * CHEST, chess analyst.  For Copyright notice read file "COPYRIGHT".
 *
 * $Source: /home/heiner/ca/chest/RCS/input.c,v $
 * $Id: input.c,v 3.25 1999/12/25 12:43:29 heiner Exp $
 *
 *	read a board and a job
 */

#include <stdio.h>
#include "types.h"
#include "str.h"
#include "board.h"
#include "xatt.h"
#include "job.h"
#include "move.h"
#include "lang.h"
#include "output.h"
#include "epdio.h"
#include "input.h"

#define INP_BUF_SIZ	4096


/*
 * Initialize a board to the empty default state.
 */
    Eximpl void
empty_board( register Board* bp )
{
    register int	pos;
    register int	i;
    register int	j;
    register Field*	p;

    bp->b_tomove = white;
    bp->b_ep     = NO_POS;
    bp->b_castle[white] = 0;
    bp->b_castle[black] = 0;
    bp->b_max_piece[white] = 0;
    bp->b_max_piece[black] = 0;
    bp->b_cur_piece[white] = 0;
    bp->b_cur_piece[black] = 0;
    bp->b_bau[white].fs_long[0] = 0;
    bp->b_bau[white].fs_long[1] = 0;
    bp->b_bau[black].fs_long[0] = 0;
    bp->b_bau[black].fs_long[1] = 0;
    bp->b_fig.fs_long[0] = 0;
    bp->b_fig.fs_long[1] = 0;
    for( i = 0 ; i < (2*MAX_PIECE) ; ++i ) {
	bp->b_piece[i] = NO_POS;
    }
    for( pos=0 ; pos<B_SIZE ; ++pos ) {
	p = &( bp->b_f[pos] );
	p->f_c    = border;
	p->f_f    = no_figure;
	F_IATTioXX(bp, p, XATT_TP_intern) = 0;
	F_DATTioXX(bp, p, XATT_TP_intern) = 0;
    }
    for( i = 0 ; i < 8 ; ++i ) {
	for( j = 0 ; j < 8 ; ++j ) {
	    bp->b_f[MK_POS(i,j)].f_c     = empty;
	    bp->b_f[MK_POS(i,j)].f_pos64 = MK_POS64(i,j);
	}
    }
    for( i=0 ; i<8 ; ++i ) {
	bp->b_packed.pb_long[i] = 0;
    }
#if WITH_ZHASH
    bp->b_zhash = 0;
#endif
    for( i=0 ; i<MAX_FIGURES ; ++i ) {
	bp->b_fig_cnt[white][i] = 0;
	bp->b_fig_cnt[black][i] = 0;
    }
			/* spare the last element in the downdate stack: */
    bp->b_ddstk.dds_ptr = &(bp->b_ddstk.dds_stack[MAX_DD_ELEMS - 1]);

    XATT_empty(bp);
}


    static void
set_nextfig( register Board* bp, Colour colour, Figure fig, Position pos )
{
    register Field*	p;

    p = &(bp->b_f[pos]);
    p->f_c   = colour;
    p->f_f   = fig;
    p->f_idx = COLOUR_IDX(colour) + bp->b_max_piece[colour];
    bp->b_piece[p->f_idx] = pos;
    bp->b_max_piece[colour] += 1;
    bp->b_cur_piece[colour] += 1;
    bp->b_fig.fs_line[LIN64(p->f_pos64)] |= (1 << COL64(p->f_pos64));
    if( fig == bauer ) {
	bp->b_bau[colour].fs_line[LIN64(p->f_pos64)] |=
						    (1 << COL64(p->f_pos64));
    }
    ins_att(bp, p);
    pacb_sync(&(bp->b_packed), p);
    ZH_UPD(bp->b_zhash, colour, fig, p->f_pos64);
    bp->b_fig_cnt[colour][fig] += 1;
}


static int	inp__lang	= LANG_NONE;

    static void
set_inp_lang( int lang )
{
    if( is_lang(lang) ) {
	inp__lang = lang;
    }
}


static FILE*	inp_file	= 0;	/* current input stream */
static Bool	inp_opened	= 0;
static long	inp_lino	= 0;	/* current line number */
static char*	inp_bufp	= 0;	/* current line buffer */
static Bool	inp_eofseen	= FALSE;	/* physical | logical */


    static void
inp_tell_input( const char* filename )
{
    if( ! f_bulkmode ) {
	printf("Input file: ");
	if( filename ) {
	    printf("'%s'\n", filename);
	}else {
	    printf("STDIN\n");
	}
	oflush();
    }
}

    Eximpl void
inp_start_input( const char* filename )
{
    if( inp_file ) {		/* check closing old input */
	if( inp_opened ) {
	    (void) fclose(inp_file);
	}
	inp_file = 0;
    }
    inp_opened  = FALSE;
    inp_lino    = 0;
    inp_bufp    = 0;
    inp_eofseen = FALSE;
    if( str_equal(filename, "") || str_equal(filename, "-") ) {
	inp_file = stdin;
	inp_tell_input((char*)0);
    }else {
	inp_file = fopen(filename, "r");
	if( ! inp_file ) {
	    inp_eofseen = TRUE;		/* do not continue to scan this input */
	    printf("Cannot open '%s' for reading, ignored.\n", filename);
	    oflush();
	}else {
	    inp_opened  = TRUE;
	    inp_tell_input(filename);
	}
    }
}


    Eximpl void
inp_tell_error( const char* what )
{
    if( !what || !*what ) {
	what = "syntax error";
    }
    printf("Input error in line %3ld: %s\n", (long)inp_lino, what);
    if( inp_bufp ) {
	printf("Input line = '%s'\n", inp_bufp);
    }
}


    static Bool
is_space( char c )
{
    return IS_SPACE(c);
}

    static const char*
skip_space( const char* p )
{
    SKIP_SPACE(p);
    return p;
}

    static void
trim_line( char* buf )
{
    if( buf ) {
	    register char*	p;
	p = buf + str_len(buf);		/* -> 0-byte */
	/*
	 * Get rid of line terminator/separator \r\n ...
	 * ... and also eat trailing white space ...
	 */
	while( (p > buf)
	    && ((p[-1] == '\n') || (p[-1] == '\r') || IS_SPACE(p[-1]))
	     ) {
	    *--p = 0;
	}
    }
}


    static Bool			/* whether recognized & stored */
cis_lang( char c, int* langp )
{
    int		lang;

    lang = lang_of_char(c);
    if( is_lang(lang) ) {
	*langp = lang;
	return TRUE;
    }
    return FALSE;
}

    static Bool			/* whether recognized & stored */
cis_colour( char c, Colour* colourp )
{
    Colour	colour;
    int		lang;
    int		mi;
    int		ma;

    if( inp__lang == LANG_NONE ) {
	mi = 1; ma = LANGS-1;
    }else {
	mi = ma = inp__lang;
    }
    for( lang=mi ; lang <= ma ; ++lang ) {
	if( (lang_chr_colour(lang, colour=white) == c)
	 || (lang_chr_colour(lang, colour=black) == c) ) {
	    *colourp = colour;
	    return TRUE;	/* yes, recognized */
	}
    }
    return FALSE;		/* not recognized */
}

    static Bool			/* whether recognized & stored */
cis_figure( char c, Figure* figurep )
{
    Figure	f;
    int		lang;
    int		mi;
    int		ma;

    if( inp__lang == LANG_NONE ) {
	mi = 1; ma = LANGS-1;
    }else {
	mi = ma = inp__lang;
    }
    for( lang=mi ; lang <= ma ; ++lang ) {
	if( (lang_chr_figure(lang, f=bauer   ) == c)
	 || (lang_chr_figure(lang, f=springer) == c)
	 || (lang_chr_figure(lang, f=laeufer ) == c)
	 || (lang_chr_figure(lang, f=turm    ) == c)
	 || (lang_chr_figure(lang, f=dame    ) == c)
	 || (lang_chr_figure(lang, f=koenig  ) == c) ) {
	    *figurep = f;
	    return TRUE;	/* yes, recognized */
	}
    }
    return FALSE;		/* not recognized */
}

    static Bool			/* whether recognized & stored */
cis_Figure( char c, Figure* figurep )
{
    Figure	f;
    int		lang;
    int		mi;
    int		ma;

    if( inp__lang == LANG_NONE ) {
	mi = 1; ma = LANGS-1;
    }else {
	mi = ma = inp__lang;
    }
    for( lang=mi ; lang <= ma ; ++lang ) {
	if( (lang_chr_Figure(lang, f=bauer   ) == c)
	 || (lang_chr_Figure(lang, f=springer) == c)
	 || (lang_chr_Figure(lang, f=laeufer ) == c)
	 || (lang_chr_Figure(lang, f=turm    ) == c)
	 || (lang_chr_Figure(lang, f=dame    ) == c)
	 || (lang_chr_Figure(lang, f=koenig  ) == c) ) {
	    *figurep = f;
	    return TRUE;	/* yes, recognized */
	}
    }
    return FALSE;		/* not recognized */
}

    static Bool			/* whether recognized & stored */
cis_castle( char c, Castle* castlep )		/* FFS: lang */
{
    static Castle	castles[] ={ castle00, castle000 };
    static const char*	c_ger ={ "kl" };	/* kurz, lang */
    static const char*	c_eng ={ "sl" };	/* short, long */
    int			inx;

    inx = str_pos(c_ger, c);
    if( inx < 0 ) {
	inx = str_pos(c_eng, c);
    }
    if( inx < 0 ) {
	return FALSE;		/* not recognized */
    }
    *castlep = castles[inx];
    return TRUE;		/* yes, recognized */
}

    static Bool			/* whether recognized & stored */
cis_column( char c, int* columnp )
{
    int		inx;

    inx = str_pos("abcdefgh", c);
    if( inx < 0 ) {
	return FALSE;		/* not recognized */
    }
    *columnp = inx;
    return TRUE;		/* yes, recognized */
}

    static Bool			/* whether recognized & stored */
cis_line( char c, int* linep )
{
    int		inx;

    inx = str_pos("12345678", c);
    if( inx < 0 ) {
	return FALSE;		/* not recognized */
    }
    *linep = inx;
    return TRUE;		/* yes, recognized */
}

    static Bool			/* whether recognized & stored */
cis_pos( char c1, char c2, Position* posp )
{
    int		column;
    int		line;

    if( ! cis_column(c1, &column)
     || ! cis_line  (c2, &line  ) ) {
	return FALSE;		/* not recognized */
    }
    *posp = (Position) MK_POS(column, line);
    return TRUE;		/* yes, recognized */
}

    static int
dec_digval( char c )
{
    return str_pos("0123456789", c);
}

    static Bool			/* whether ok (so far) */
try_set_nextfig( register Board* bp, Colour colour, Figure fig, Position pos )
{
    if( bp->b_f[pos].f_c != empty ) {	/* most frequent error */
	inp_tell_error("duplicate piece");
	return FALSE;		/* sorry */
    }
    if( bp->b_max_piece[colour] >= MAX_PIECE ) {
	inp_tell_error("too many pieces of one colour");
	return FALSE;		/* sorry */
    }
    set_nextfig(bp, colour, fig, pos);
    return TRUE;		/* ok */
}


    static Bool			/* whether ok */
chk_eo_buf( const char* buf, const char* errtxt )
{
    buf = skip_space(buf);	/* accept trailing white space */
    if( buf && buf[0] ) {	/* something unrecognized */
	inp_tell_error(errtxt);
	return FALSE;		/* sorry */
    }
    return TRUE;		/* ok, the buffer is exhausted */
}


    static Bool			/* whether ok */
grok_fys_line( const char* buf, Board* bp )
{
    /*
     * As this notation does sort the pieces by their position,
     * we have to reorder at least for the kings, which are
     * needed at the first relative piece index.
     * Hence, we first build an 8x8 intermediate representation,
     * which we then scan for different types of pieces.
     */
#define FYS_CODE(c,f)	(((((f) << 1) | ((c) & 01)) << 1) | 01)
    int8			fysb[8][8];	/* [lin][col] */
    register const char*	p;
    register int		lin;
    register int		col;
    register int		spc;
    register rColour		c;
    register int		i;
    Figure			fig = 0;	/* init for lint */
    static Figure		figlist[] ={
				    koenig,		/* MUST be first */
				    dame, turm, laeufer,
				    springer, bauer,
				    no_figure		/* MUST be last */
				};

    for( lin=0 ; lin<8 ; ++lin ) {
	for( col=0 ; col<8 ; ++col ) {
	    fysb[lin][col] = 0;
	}
    }
    p = buf;
    p = skip_space(p);	/* skip over leading spaces */
    lin = 7;
    col = 0;
    for( ; *p && !is_space(*p) ; ++p ) {
	spc = 0;
	c   = empty;
	switch( *p ) {
	 case '1':	spc = 1; break;
	 case '2':	spc = 2; break;
	 case '3':	spc = 3; break;
	 case '4':	spc = 4; break;
	 case '5':	spc = 5; break;
	 case '6':	spc = 6; break;
	 case '7':	spc = 7; break;
	 case '8':	spc = 8; break;
	 case '/':
	    if( col != 0 ) {
		inp_tell_error("misplaced / in Forsyth");
		return FALSE;
	    }
	    spc = 0;
	    break;
	 default:
	    if(       cis_Figure(*p, &fig) ) {		/* upper case */
		c   = white;
		spc = 1;
	    }else if( cis_figure(*p, &fig) ) {		/* lower case */
		c   = black;
		spc = 1;
	    }else {
		printf("@%c\n", *p);
		inp_tell_error("unrecognized char in Forsyth");
		return FALSE;
	    }
	    break;
	}
	if( (col+spc) > 8 ) {
	    inp_tell_error("spacing over line boundary");
	    return FALSE;	/* sorry */
	}
	if( c != empty ) {
	    if( lin < 0 ) {
		inp_tell_error("Forsyth has >64 positions");
		return FALSE;	/* sorry */
	    }
	    fysb[lin][col] = FYS_CODE(c,fig);
	}
	if( spc ) {
	    col += spc;
	    if( col >= 8 ) {
		col -= 8; lin -= 1;
	    }
	}
    }
    if( (lin != -1) || (col != 0) ) {
	inp_tell_error("Forsyth has <64 positions");
	return FALSE;		/* sorry */
    }
    for( i=0 ; (fig = figlist[i]) != no_figure ; ++i ) {
	for( c=0 ; c<2 ; ++c ) {
		register int		cod;
		register int		delta;
	    cod = FYS_CODE(c,fig);
	    for( col=0 ; col<8 ; ++col ) {
		if( c == white ) {
		    lin = 0; delta =  1;
		}else {
		    lin = 7; delta = -1;
		}
		for( ; (lin >=0) && (lin < 8) ; lin += delta ) {
		    if( fysb[lin][col] == cod ) {
			if( ! try_set_nextfig(bp, c, fig, MK_POS(col,lin)) ) {
			    return FALSE;	/* sorry */
			}
		    }
		}
	    }
	}
    }
    return chk_eo_buf(p, "garbage after Forsyth");
#undef FYS_CODE
}

    static Bool			/* whether ok */
grok_fen_tom( const char* buf, Board* bp )
{
    Colour	colour;

    if( ! cis_colour(*buf++, &colour) ) {
	inp_tell_error("bad colour to move in FEN");
	return FALSE;		/* sorry */
    }
    bp->b_tomove = colour;
    return chk_eo_buf(buf, "garbage after FEN colour to move");
}

    static Bool			/* whether ok */
grok_fen_castle( const char* buf, Board* bp )
{
    int		lang;

    lang = inp__lang;
    if( lang == LANG_NONE ) lang = LANG_ENGLISH;
    bp->b_castle[0] = 0;
    bp->b_castle[1] = 0;
    for( ; *buf ; ++buf ) {
	if( '-' == *buf ) {
	    /*
	     * We accept multiple and intermixed minus signes.
	     */
	}else if( lang_chr_Figure(lang, koenig) == *buf ) {
	    bp->b_castle[white] |= castle00;	/*  king side (short) */
	}else if( lang_chr_Figure(lang, dame  ) == *buf ) {
	    bp->b_castle[white] |= castle000;	/* queen side (long) */
	}else if( lang_chr_figure(lang, koenig) == *buf ) {
	    bp->b_castle[black] |= castle00;	/*  king side (short) */
	}else if( lang_chr_figure(lang, dame  ) == *buf ) {
	    bp->b_castle[black] |= castle000;	/* queen side (long) */
	}else {
	    break;
	}
    }
    return chk_eo_buf(buf, "garbage after FEN castle info");
}

    static Bool			/* whether ok */
grok_fen_ep( const char* buf, Board* bp )
{
    Position	pos;

    if( '-' == *buf ) {
	buf += 1;
    }else if( buf[0] && buf[1] && cis_pos(buf[0], buf[1], &pos) ) {
	buf += 2;
	/*
	 * FEN specifies the active target (just stepped over by pawn
	 * double step), while we need the passive target (destination
	 * of the double step).
	 * FFS: not yet checked for plausibility/legality.
	 */
	switch( LIN(pos) ) {
	 case 2: pos += MK_DELTA(0,1); break;
	 case 5: pos -= MK_DELTA(0,1); break;
	 default:
	    inp_tell_error("bad FEN e.p. info");
	    return FALSE;
	}
	bp->b_ep = pos;
    }else {
	inp_tell_error("bad FEN e.p. info");
	return FALSE;
    }
    return chk_eo_buf(buf, "garbage after FEN ep info");
}

    static Bool			/* whether ok */
grok_fen( const char* buf, Board* bp )
{
    /*
     * We expect/accept the first 4 FEN fields: board, colour, castling, ep.
     * The next 2 fields are meaningless for us: we ignore all the rest.
     */
    char	fen[INP_BUF_SIZ];	/* cannot overflow */
    const char*	ip;
    char*	op;
    char*	part[4];
    int		i;

    ip = buf;
    op = fen;
    for( i=0 ; i<4 ; ++i ) {
	ip = skip_space(ip);
	part[i] = op;
	while( *ip && ! IS_SPACE(*ip) ) {
	    *op++ = *ip++;
	}
	*op++ = '\0';			/* terminate that part */
	if( ! part[i][0] ) {
	    inp_tell_error("missing part in FEN");
	    return FALSE;
	}
    }
    return grok_fys_line  (part[0], bp)
	&& grok_fen_tom   (part[1], bp)
	&& grok_fen_castle(part[2], bp)
	&& grok_fen_ep    (part[3], bp);
}


    static Bool			/* whether job is complete */
grok_line( const char* buf, Board* bp, int* depthp )
{
    const char*	p;
    Colour	colour;
    Figure	figure;
    Position	pos;
    Castle	castle;
    int		dig;
    int		dep;
    int		lang;

    p = buf;
    p = skip_space(p);	/* skip over leading spaces */
    /*
     * The next character determines interpretation of the rest of the line:
     *  O	output language
     *  I	input language
     *  L	input & output language
     *  j	job type
     *  z	depth & colour to move
     *  c	allow castling
     *  C	FEN castling
     *  e	e.p. info
     *  E	FEN e.p. info
     *  f	Forsyth notation
     *  F	FEN
     *  .	eoj/eof
     *  #	comment
     *  >	title line
     *  % ;	copied "comment" (PGN escape & comment)
     */
    switch( *p ) {
     case 'O':		/* set output language */
	p = skip_space(++p);
	if( ! cis_lang(*p++, &lang) ) {
	    inp_tell_error("bad output language");
	    goto err;
	}
	set_out_lang(lang);
	break;
     case 'I':		/* set input language */
	p = skip_space(++p);
	if( ! cis_lang(*p++, &lang) ) {
	    inp_tell_error("bad input language");
	    goto err;
	}
	set_inp_lang(lang);
	break;
     case 'L':		/* set language (input & output) */
	p = skip_space(++p);
	if( ! cis_lang(*p++, &lang) ) {
	    inp_tell_error("bad input language");
	    goto err;
	}
	set_inp_lang(lang);
	set_out_lang(lang);
	break;
     case 'f':		/* accept Forsyth notation of board contents */
	p = skip_space(++p);
	if( ! grok_fys_line(p, bp) ) {
	    goto err;
	}
	break;
     case 'F':		/* accept FEN (first 4 fields) */
	p = skip_space(++p);
	if( ! grok_fen(p, bp) ) {
	    goto err;
	}
	break;
     default:
	if( cis_colour(*p, &colour) ) {		/* new piece */
	    ++p;
	    if( ! (   cis_figure(*p, &figure)
		   || cis_Figure(*p, &figure)) ) {
		inp_tell_error("bad figure code");
		goto err;
	    }
	    p += 1;
	    if( ! cis_pos(p[0], p[1], &pos) ) {
		inp_tell_error("bad position for piece");
		goto err;
	    }
	    p += 2;
	    if( ! try_set_nextfig(bp, colour, figure, pos) ) {
		goto err;
	    }
	    break;
	}
	inp_tell_error("bad first character");
	goto err;
     case 'j':
	++p;
	switch( *p++ ) {
	 default:
	    inp_tell_error("bad job type");
	    goto err;
	 case '\0':
	 case 'o':	/* orthodox */
	 case 'n':	set_jobtype(JT_normal); break;
	 case 'O':
	 case 'N':	set_jobtype(JT_stalemate); break;
	 case 'h':	set_jobtype(JT_helpmate); break;
	 case 'H':	set_jobtype(JT_helpstalemate); break;
	 case 's':	set_jobtype(JT_selfmate); break;
	 case 'S':	set_jobtype(JT_selfstalemate); break;
	}
	break;
     case 'z':
	++p;
	if( (dig = dec_digval(*p)) >= 0 ) {
	    ++p;
	    dep = dig;
	    while( (dig = dec_digval(*p)) >= 0 ) {
		++p;
		dep *= 10; dep += dig;
		if( dep > 999 ) {
		    inp_tell_error("job depth too large");
		    goto err;
		}
	    }
	    if( ! cis_colour(*p++, &colour) ) {
		inp_tell_error("bad colour to move");
		goto err;
	    }
	    bp->b_tomove = colour;
	    *depthp = dep;
	}else {
	    inp_tell_error("missing job-depth");
	    goto err;
	}
	break;
     case 'c':				/* allow a castling */
	++p;
	if( ! cis_colour(*p++, &colour) ) {
	    inp_tell_error("bad colour for castling");
	    goto err;
	}
	if( ! cis_castle(*p++, &castle) ) {
	    inp_tell_error("bad castling code");
	    goto err;
	}
	bp->b_castle[ colour ] |= castle;
	break;
     case 'C':				/* FEN castling */
	p = skip_space(++p);
	if( ! grok_fen_castle(p, bp) ) {
	    goto err;
	}
	break;
     case 'e':				/* ep-info */
	++p;
	if( ! cis_pos(p[0], p[1], &pos) ) {
	    inp_tell_error("bad position");
	    goto err;
	}
	bp->b_ep = pos;
	p += 2;
	break;
     case 'E':				/* FEN ep-info */
	p = skip_space(++p);
	if( ! grok_fen_ep(p, bp) ) {
	    goto err;
	}
	break;
     case '.':
	if( *++p == '.' ) {		/* ".." */
	    inp_eofseen = TRUE;
	}
	goto rdy;			/* this job is complete */
     case '\0':				/* empty line is comment */
     case '#':				/* guaranteed to be comment */
	break;
     case '>':				/* title line */
	++p;
	if( is_space(*p) ) ++p;		/* skip 1 leading space/tab */
	tit_put_line(p);
	break;
     case '%':				/* PGN escape */
     case ';':				/* PGN comment line */
	printf("%s\n", p);		/* without leading white space */
	break;
    }
    return FALSE;		/* not yet complete */
rdy:;
    return TRUE;		/* this job is complete */
err:;
    inp_eofseen = TRUE;		/* do not continue to scan this input */
    *depthp = 0;		/* do not process this input */
    return TRUE;		/* this job is complete */
}


    static Bool			/* whether job is complete */
grok_epd_line( const char* buf, Board* bp, EpdJob* ejp, int dftdep,
							int* depthp )
{
    int		rc;

    set_inp_lang(LANG_ENGLISH);		/* fixed by standard */
    set_out_lang(LANG_ENGLISH);		/* fixed by standard */
    epd_clear(ejp);
    rc = epd_fill_line(ejp, buf);
    if( rc != TRUE ) {			/* no job in this line */
	if( rc < 0 ) {			/* logical end-of-input */
	    inp_eofseen = TRUE;
	    return TRUE;	/* empty & complete: stop further reading */
	}
	return FALSE;		/* not complete: continue reading */
    }
    /*
     * Now we have the data parsed, but not yet made effective.
     */
    if( ! grok_fys_line  (ejp->fys   , bp)
     || ! grok_fen_tom   (ejp->tom   , bp)
     || ! grok_fen_castle(ejp->castle, bp)
     || ! grok_fen_ep    (ejp->ep    , bp)
      ) {
	empty_board(bp);	/* clear garbage for next try */
	return FALSE;		/* not complete: continue reading */
    }
    /*
     * The board is built.  Remains to determine the job
     * from the additional EPD data.
     */
    /* epd_print(ejp); */

    *depthp = epd_get_dep_prog(ejp, dftdep);
    if( *depthp <= 0 ) {
	empty_board(bp);	/* clear garbage for next try */
    }

#if 0
    printf("# depth = %2d, jobtype %d, attr 0x%02x, prog 0x%02x\n",
	    depth, f_jobtype, f_jobattr, f_jobprog);
#endif
    return *depthp > 0;		/* filled & complete: stop further reading */
}


/*
 * read_board()
 *	Read a board and return the depth of the wanted analysis.
 */
    Eximpl int			/* 0 | analysis depth */
read_board( Board* bp, EpdJob* ejp, int dftdep )
{
    char	buf[INP_BUF_SIZ];
    int		depth	= 0;

    empty_board(bp);
    set_jobtype(JT_normal);		/* default */
    tit_clear();
    if( inp_eofseen ) {			/* don't try to read further */
	goto out;
    }
    if( ! inp_file ) {
	inp_start_input("-");		/* stdin */
	if( ! inp_file ) {
	    goto out;
	}
    }
    inp_bufp = buf;
    if( ! f_bulkmode ) {
	printf("Reading job:\n"); oflush();
    }
    for(;;) {
	if( NULL == fgets(buf, sizeof(buf), inp_file) ) {
	    inp_eofseen = TRUE;
	    goto out;
	}
	inp_lino += 1;			/* got one more input line */
	trim_line(buf);
	if( f_bulkmode ) {		/* bulk: EPD in/out */
	    if( grok_epd_line(buf, bp, ejp, dftdep, &depth) ) {
		goto out;		/* this job is complete */
	    }
	}else {				/* normal/old input */
	    if( grok_line(buf, bp, &depth) ) {
		goto out;		/* this job is complete */
	    }
	}
    }
out:;
    inp_bufp = 0;
    return depth;
}