#include "util.h"
#include "mmdf.h"
#include "ch.h"                   /* has table state strcture def       */
/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *
 *
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */

/*  READ "NETWORK" HOST NAME TABLES
 *
 *  BNF for the file is:
 *
 *      entries =   entries entry
 *      entry   =   name separator value eol
 *      name    =   {string of chars not containing a <separator>}
 *      separator=  {see the chars in _hkeyend[]}
 *      value   =   {string of chars not containing an <eol>}
 *      eol     =   {see the chars in _hvalend[]}
 *
 *      where
 *
 *          name    is a key
 *          value   is any relevant text, usually the host "address"
 *
 *  There may be any number of entries with the same value string,
 *  thereby allowing name aliases, such as UDel-EE, UDel, and UDEE.
 *
 *  The key of the first entry with a given value is taken as the
 *  "official" "name" of that "address".  (Sorry about all the
 *  quotation marks.)
 *
 *  Turns out that the local machine's alias file can also be accessed
 *  by these routines.
 *
 *  Any number of tables may be supported simultaneously, since a
 *  single table's state information structure is maintained externally,
 *  and passed with each call, through the parameter netinfo.
 *  The state structure's place for the file descriptor MUST
 *  initially be zero.
 *
 *  "Hard" failures are considered not acceptable and hence cause a
 *  call to the external routine err_abrt(); to change this policy,
 *  just change the calls to err_abrt() to be simple failure returns.
 *  The rest of the code should run "transparently".
 */

/*  Sep 81  Dave Crocker      added ch_tclose & ch_tfree, to allow
 *                            handling file descriptor overflow
 *  May 82  Dave Crocker      add ch_tread, to focus input
 *  Jun 82  Dave Crocker      split ch_tio off from ch_table
 *  Dec 82  Doug Kingston     Fixed bug in tb_read() that caused it to
 *                            ignore EOF's.   (DPK@BRL)
 *  Apr 83  Steve Kille       Change it back again
 */

extern struct ll_struct   *logptr;
extern char *tbldfldir;
extern Table **tb_list;         /* all known tables                     */
				/* SEK change syntax                    */
extern int   tb_numtables;      /* how many of them there are           */
extern char  *index();
extern char  *compress();

LOCVAR char _hkeyend[] = ": \t\n\377",
				  /* chars which delimit end of key     */
	    _hvalend[] = "\n\000\377";
				  /* chars which delimit end of value   */
LOCVAR short tb_nopen;            /* number of opened file descriptors  */

/* ******************  BASIC FILE ACTION  *************************** */

tb_open (table, fromstart)        /* gain access to a table     */
    register Table  *table;       /* table's state information          */
    int     fromstart;            /* ok to reset, if already open?      */
{
    extern int   errno;           /* in case open fails                 */
    char temppath[128];


/*  currently, there is one file (descriptor) per table.  for a large
 *  configuration, you will run out of fd's.  until/unless you run
 *  a single-file dbm-based version, there is a hack, below, which
 *  limits the number of open file-descriptors to 6.  Trying to open
 *  a 7th will cause a currently-opened one to be closed.
 */

    switch ((int)table)
    {
	case 0:
	case NOTOK:
	    return (FALSE);       /* can't open what isn't specified    */
    }
    switch ((int)(table -> tb_fp))
    {
	case 0:                   /* not opened yet                     */
/*HACK*/    table -> tb_pos = 0L;
	    if (table -> tb_file != 0)
	    {                     /* we have a path string              */
		if (tb_nopen < 6 || tb_free ())
		{
		    getfpath (table -> tb_file, tbldfldir, temppath);
				  /* add on directory portion           */
#ifdef DEBUG
		    ll_log (logptr, LLOGBTR, "opening '%s'", temppath);
#endif
		    if ((table -> tb_fp = fopen (temppath, "r")) != NULL)
		    {
			tb_nopen++;
			break;    /* all is well                        */
		    }
		}
		ll_err (logptr, (errno == ENOENT) ? LLOGPTR : LLOGTMP,
		    "Error opening %s name table '%s' (numfiles open = %d)",
				table -> tb_show, temppath, tb_nopen);
				  /* major only if file exists          */
	    }
	    table -> tb_fp = (FILE *) EOF;
				  /* no path str -> quietly fail        */
				  /* DROP ON THROUGH                    */
	case NOTOK:
	    return (FALSE);       /* failed earlier try                 */

	default:
	    if (fromstart)        /* always start from beginning?       */
		tb_seek (table, 0L);
    }
    return (TRUE);
}
/**/

tb_close (table)                  /* close  a table                     */
register Table *table;
{
    switch ((int)table)
    {
	case 0:
	case NOTOK:
	    return (FALSE);       /* can't close what isn't specified   */
    }
    switch ((int)(table -> tb_fp))
    {
	case NOTOK:
	    table -> tb_fp = 0; /* mark as free                       */
	case 0:                   /* not opened yet                     */
	    return (FALSE);       /* no close performed                 */

	default:
	    fclose (table -> tb_fp);
	    table -> tb_fp = 0; /* mark as free                       */
	    tb_nopen--;
	    return (TRUE);
    }
    /* NOTREACHED */
}

tb_free ()                       /* create a free file descriptor      */
{
/*  step from the lowest search priority to the highest, looking for a
 *  channel to close.  return success as soon as one is done.
 */
    register Table **tableptr;
#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "tb_free ()");
#endif
    for (tableptr = tb_list + tb_numtables - 1;
					tableptr >=  tb_list; tableptr--)
    {
	if (tb_close (*tableptr))
	    return (TRUE);        /* got a channel closed               */
    }
    return (FALSE);               /* couldn't get any closed            */
}

tb_seek (table, thepos)           /* reset table position               */
register Table *table;
long thepos;
{
    switch ((int)table)
    {
	case 0:
	case NOTOK:
	    return (NOTOK);       /* can't open what isn't specified    */
    }
    switch ((int)(table -> tb_fp))
    {                             /* not opened yet                     */
	case NULL:
	case EOF:
	    return (NOTOK);
    }
    fseek (table -> tb_fp, thepos, 0);
/*HACK*/ table -> tb_pos = thepos;
    return ((ferror (table -> tb_fp)) ? NOTOK : OK);
}
/* *****************  READ THE NAME TABLE  ************************** */

/*
 *  This routine used not to recognise comments, this meant that dbmbuild
 *  could skip real records in the following case.
 *  #<NL>
 *  nott:nott
 *  would be read as key = '#' value = 'nott:nott' and dbmbuild would
 *  throw it away as a comment. (JPO)
 */
tb_read (table, key, value)       /* read a table's record        */
    register Table *table;
    char *key,
	 *value;
{
    register int len;
    register FILE *fp;
    char terminator;

    fp = table -> tb_fp;

    if (feof (fp))
	return (FALSE);

    while ((len = qread (fp, key, LINESIZE, _hkeyend, '\\')) > 0)
    {
/*HACK*/    table -> tb_pos += len;
	if( key[0] == '\n' )
	    continue;	    /* blank line */
	if( key[0] == '#' )
	    if( key[len - 1] == '\n' )
		continue;   /* have read up to the newline */
	    else
	    {
		if((len = gcread(fp, value, LINESIZE, _hvalend)) > 0)
		{
/*HACK*/	    table -> tb_pos += len;
		    continue;	/* skip to end of line */
		}
		else return (FALSE);	/* EOF I guess */
	    }

    	terminator = key[len - 1];
	key[len - 1] = '\0';  /* get rid of terminator character */
	if (len > 1)
	    compress (key, key);

	if (index(_hvalend, terminator) > (char *)NULL)
 	    value[0] = '\0';		/* whole line was a key */
        else {
	    if ((len = gcread (fp, value, LINESIZE, _hvalend)) > 0)
	    {
/*HACK*/	table -> tb_pos += len;
		value[len - 1] = '\0';
		if (len > 1)
		    compress (value, value);
	    }
	    else               /* Don't worry about errors here... */
		value[0] = '\0';
        }
	return (TRUE);

    }
    if( len < 0 )        /* (len < 0) indicating IO error */
	err_abrt (RP_FIO, "Error reading %s name table '%s'",
		table -> tb_show, table -> tb_file);

    return (FALSE);             /* EOF on entry boundary,  DPK@BRL */
}
