RCS_ID_C="$Id: amiga_generic2.c,v 3.13 1994/04/07 20:46:52 jraja Exp $";
/*
 * Copyright (c) 1993 AmiTCP/IP Group, <amitcp-group@hut.fi>
 *                    Helsinki University of Technology, Finland.
 *                    All rights reserved.
 * 
 * Created: Fri Dec 10 22:57:55 1993 too
 * Last modified: Thu Apr  7 23:46:06 1994 jraja
 *
 * HISTORY
 * $Log: amiga_generic2.c,v $
 * Revision 3.13  1994/04/07  20:46:52  jraja
 * Moved SBTC_COMPAT43 inside #ifdef notyet,
 * organized tags in the autodoc to alphapetical order.
 *
 * Revision 3.12  1994/04/02  10:47:23  jraja
 * Changed hErrno to be accessed via a pointer, added tag handling for the
 * hErrnoPtr.
 *
 * Revision 3.11  1994/03/29  12:56:35  ppessi
 * Added SBTC_COMPAT43 tag
 *
 * Revision 3.10  1994/03/22  08:37:28  jraja
 * Added tag list entry for the SBTC_FDCALLBACK (fdCallback),
 * added extern definitions for the __sys_nerr and __sys_errlist,
 * updated SocketBaseTags autodoc.
 *
 * Revision 3.9  1994/02/26  18:05:56  jraja
 * Changed socketbasetags.h to amitcp/socketbasetags.h.
 *
 * Revision 3.8  1994/02/16  06:25:51  jraja
 * Added manual page for the SocketBaseTagList().
 *
 * Revision 3.7  1994/01/20  02:26:40  jraja
 * Added errno size restriction and return value to the SetErrnoPtr().
 * Added errnoPtr handling and rest of the error lists to the
 * SocketBaseTagList().
 *
 * Revision 3.6  1994/01/18  22:55:06  jraja
 * Added some checks and '%m' functionality to the syslog().
 *
 * Revision 3.5  1994/01/18  19:22:32  jraja
 * Changed direct access to errnoPtr to baseErrno() macro (not all of them!)
 *
 * Revision 3.4  1994/01/13  07:05:24  jraja
 * Added external declarations for h_errlist and h_nerr.
 *
 * Revision 3.3  1994/01/12  07:34:26  jraja
 * Moved _getdtablesize() from amiga_generic.c to here.
 *
 * Revision 3.2  1994/01/12  07:23:28  jraja
 * Added implementation of the _SocketBaseTagList(). Moved SetDTableSize and
 * getlastfd() from amiga_generic.c to here. Removed redundant (empty) functions
 *
 * Revision 3.1  1994/01/04  14:12:28  too
 * Moved some functions from amiga_generic.c. Added GetHErrno().
 * Made cores to most AmiTCP/IP 3 functions.
 *
 */

#include <conf.h>

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/syslog.h>
#include <sys/errno.h>

#include <kern/amiga_includes.h>

#include <api/amiga_api.h>
#include <api/amiga_libcallentry.h>
#include <api/allocdatabuffer.h>

#include <api/apicalls.h>

#include <stdarg.h>

#include <amitcp/socketbasetags.h>

extern const char * const __sys_errlist[];
extern const int __sys_nerr;
extern const char * const h_errlist[];
extern const int h_nerr;
extern const char * const io_errlist[];
extern const short io_nerr;
extern const char * const sana2io_errlist[];
extern const short sana2io_nerr;
extern const char * const sana2wire_errlist[];
extern const short sana2wire_nerr;

LONG /* SAVEDS */ RAF1(_Errno,
		       struct SocketBase *, libPtr, a6)
#if 0
{
#endif
  return (LONG)readErrnoValue(libPtr);
}

LONG /* SAVEDS */ RAF3(_SetErrnoPtr,
		       struct SocketBase *,	libPtr,	a6,
		       VOID *,			err_p,	a0,
		       UBYTE,			size,	d0)
#if 0     
{
#endif
  if (size == 4 || size == 2 || size == 1) {
    if (size & 0x1 || !((ULONG)err_p & 0x1)) {	/* either odd size or address even */
      libPtr->errnoSize = size;
      libPtr->errnoPtr = err_p;
      return 0;
    }
  }

  writeErrnoValue(libPtr, EINVAL);
  return -1;
}

VOID SAVEDS RAF4(_Syslog,
		 struct SocketBase *,	libPtr, 	a6,
		 ULONG,			pri,		d0,
		 const char *,		fmt,		a0,
		 va_list,		ap,		a1)
#if 0
{
#endif
  int saved_errno;
  char fmt_cpy[1024];
  register char *p = fmt_cpy;

  CHECK_TASK_VOID();

  /* check for invalid bits or no priority set */
  if (!LOG_PRI(pri) || (pri &~ (LOG_PRIMASK|LOG_FACMASK)) ||
      !(LOG_MASK(pri) & libPtr->LogMask))
    return;

  saved_errno = readErrnoValue(libPtr);
  if (saved_errno >= __sys_nerr)
    saved_errno = 0;						/* XXX */

  /* set default facility if none specified */
  if ((pri & LOG_FACMASK) == 0)
    pri |= libPtr->LogFacility;

  /* 
   * Build the new format string
   */
  if (libPtr->LogTag) {
    p += sprintf(p, libPtr->LogTag);
  }
  if (libPtr->LogStat & LOG_PID) {
    p += sprintf(p, "[%lx]", libPtr->thisTask);
  }
  if (libPtr->LogTag) {
    *p++ = ':';
    *p++ = ' ';
  }
  /*
   * Substitute error message for %m.
   */
  {
    char ch, *t1;
    const char *t2;

    for (t1 = p; ch = *fmt; ++fmt) {
      if (ch == '%') {
	if (fmt[1] == '%') {
	  ++fmt;
	  *t1++ = ch;
	}
	else if (fmt[1] == 'm') {
	  ++fmt;
	  for (t2 = __sys_errlist[saved_errno]; *t1 = *t2++; ++t1)
	    ;
	  continue;
	}
      }
      *t1++ = ch;
    }
    *t1 = '\0';
  }

  vlog(pri, fmt_cpy, ap);
}

VOID SAVEDS RAF4(_SetSocketSignals,
		 struct SocketBase *,	libPtr, 	a6,
		 ULONG,			SIGINTR,	d0,
		 ULONG,			SIGIO,		d1,
		 ULONG,			SIGURG,		d2)
#if 0
{
#endif
  CHECK_TASK_VOID();
  /*
   * The operations below are atomic so no need to protect them
   */
  libPtr->sigIntrMask = SIGINTR;
  libPtr->sigIOMask = SIGIO;
  libPtr->sigUrgMask = SIGURG;
}

LONG /* SAVEDS */ RAF1(_getdtablesize,
		       struct SocketBase *,	libPtr, a6)
#if 0
{
#endif

  return (LONG)libPtr->dTableSize;
}

static int getLastSockFd(struct SocketBase * libPtr)
{
  int bit, lastmlong = (libPtr->dTableSize - 1) / NFDBITS;
  unsigned long *smaskp, cmask, rmask;

  for (smaskp = (ULONG *)(libPtr->dTable + libPtr->dTableSize + lastmlong);
       lastmlong >= 0; smaskp--, lastmlong--)
    if (*smaskp)
      break;

  if (lastmlong < 0)
    return -1;

  cmask = *smaskp;
  if ((rmask = cmask & 0xFFFF0000)) { bit = 16; cmask = rmask; }
  else bit = 0;
  if ((rmask = cmask & 0xFF00FF00)) { bit += 8; cmask = rmask; }
  if ((rmask = cmask & 0xF0F0F0F0)) { bit += 4; cmask = rmask; }
  if ((rmask = cmask & 0xCCCCCCCC)) { bit += 2; cmask = rmask; }
  if ((rmask = cmask & 0xAAAAAAAA)) bit += 1;

  return lastmlong * 32 + bit;
}

/*
 * Set size of descriptor tab|e
 */
static LONG
setdtablesize(struct SocketBase * libPtr, UWORD size)
{
  
  LONG oldsize = (LONG)libPtr->dTableSize;
  LONG copysize;
  struct socket ** dTable;
  int olddmasksize, copydmasksize, dmasksize;
  
  if (size < oldsize) {
    int i;

    if ((i = getLastSockFd(libPtr)) > size)
      size = i + 1;
    copysize = size;
  }
  else
    copysize = oldsize;

  olddmasksize	= (oldsize - 1) /  NFDBITS + 1;
  copydmasksize	= (copysize - 1) / NFDBITS + 1;
  dmasksize 	= (size - 1) /	   NFDBITS + 1;
  
  if ((dTable = AllocMem(size * sizeof (struct socket *) +
			 dmasksize * sizeof (fd_mask),
			 MEMF_PUBLIC|MEMF_CLEAR)) == NULL)
    return oldsize;

  aligned_bcopy(libPtr->dTable, dTable, copysize * sizeof (struct socket *));
  aligned_bcopy(libPtr->dTable + oldsize, dTable + size, 
		copydmasksize * sizeof (fd_mask));
  
  FreeMem(libPtr->dTable, oldsize * sizeof (struct socket *) +
	  olddmasksize * sizeof (fd_mask));
  
  libPtr->dTable = dTable;
  libPtr->dTableSize = size;
  
  return size;
}


#define CASE_LONG(code, baseField)\
 case (code << SBTB_CODE): /* get */ \
  *tagData = (ULONG)libPtr->baseField;\
  break;\
 case (code << SBTB_CODE) | SBTF_SET: /* set */\
  *(ULONG *)&libPtr->baseField = *tagData;\
  break

#define CASE_WORD(code, baseField)\
 case (code << SBTB_CODE): /* get */ \
  *tagData = (ULONG)libPtr->baseField;\
  break;\
 case (code << SBTB_CODE) | SBTF_SET: /* set */\
  *(UWORD *)&libPtr->baseField = (UWORD)*tagData;\
  break

#define CASE_BYTE(code, baseField)\
 case (code << SBTB_CODE): /* get */ \
  *tagData = (ULONG)libPtr->baseField;\
  break;\
 case (code << SBTB_CODE) | SBTF_SET: /* set */\
  *(UBYTE *)&libPtr->baseField = (UBYTE)*tagData;\
  break

#define CASE_FLAG(code, flag)\
  case (code << SBTB_CODE): /* get */ \
    *tagData = ((ULONG)libPtr->flags & (flag)) != 0;\
    break;\
  case (code << SBTB_CODE) | SBTF_SET: /* set */\
    if (*tagData) \
      *(UBYTE *)&libPtr->flags |= (flag);\
    else \
      *(UBYTE *)&libPtr->flags &= ~(flag);\
    break


/****** bsdsocket.library/SocketBaseTagList ***********************************

    NAME
         SocketBaseTagList - Set/Get SocketBase attributes.

    SYNOPSIS
         #include <amitcp/socketbasetags.h>

         ULONG SocketBaseTagList(struct TagItem *);

         error = SocketBaseTagList(taglist)
         D0                        A0

         error = SocketBaseTags(ULONG tag, ...);

    FUNCTION
        Set or get a list of (mostly) SocketBase instance dependent attributes
        from the AmiTCP.

    INPUTS
        These functions expect as their argument a standard tag list, one or
        several array of struct TagItem as defined in the header file
        <utility/tagitem.h>. The structure contains two fields: ti_Tag and
        ti_Data.  The ti_Tag field contains tag code, which determines what
        the SocketBaseTagList() should do with its argument, the ti_Data
        field.

        The include file <amitcp/socketbasetags.h> defines macros for base tag
        code values.  Base tag code macros begin with `SBTC_' (as Socket Base
        Tag Code).  The base tag value defines what data item the tag item
        refers.

        The tag code contains other information besides the referred data
        item.  It controls, whether the SocketBaseTagList() should set or get
        the appropriate parameter, and whether the argument of the tag in
        question is passed by value or by reference.  

        The include file <amitcp/socketbasetags.h> defines the following
        macros, which are used to construct the ti_Tag values from the base
        tag codes:

             SBTM_GETREF(code) - get by reference
             SBTM_GETVAL(code) - get by value
             SBTM_SETREF(code) - set by reference
             SBTM_SETVAL(code) - set by value

        If the actual data is stored directly into the ti_Data field, you
        should use the 'by value' macros, SBTM_GETVAL() or SBTM_SETVAL().
        However, if the ti_Data field contains a pointer to actual data, you
        should use the 'by reference' macros, SBTM_GETREF() or SBTM_SETREF().
        In either case the actual data should always be a LONG aligned to even
        address.

        According the used tag naming scheme a tag which has "PTR" suffix
        takes an pointer as its argument.  Don't mix the pointer arguments
        with 'by reference' argument passing.  It is possible to pass a
        pointer by reference (in which case the ti_Data is a pointer to the
        actual pointer).

        The list of all defined base tag codes is as follows:

             SBTC_BREAKMASK       Tag data contains the INTR signal mask.  If
                                  the calling task receives a signal in the
                                  INTR mask, the AmiTCP interrupts current
                                  function calls and returns with the error
                                  code EINTR.  The INTR mask defaults to the
                                  CTRL-C signal (SIGBREAKF_C, bit 12).

             SBTC_DTABLESIZE      Socket Descriptor Table size. This
                                  defaults to 64.

             SBTC_ERRNO           The errno value. The values are defined in
                                  <sys/errno.h>.

             SBTC_ERRNOBYTEPTR
             SBTC_ERRNOWORDPTR
             SBTC_ERRNOLONGPTR
             SBTC_ERRNOPTR(size)  Set (only) the pointer to the errno
                                  variable defined by the program.  AmiTCP
                                  defines a value for this by default, but
                                  the application must set the pointer (and
                                  the size of the errno) with one of these
                                  tags, if it wishes to access the errno
                                  variable directly.

                                  The SBTC_ERRNOPTR(size) is a macro, which
                                  expands to one of the other (BYTE, WORD or
                                  LONG) tag codes, meaning that only 1, 2
                                  and 4 are legal size values.

                                  The netlib autoinit.c sets the errno
                                  pointer for the application, if the
                                  application is linked with it.

             SBTC_ERRNOSTRPTR     Returns an error string pointer describing
                                  the errno value given on input. You can not
                                  set the error message, only get is allowed.

                                  On call the ti_Data must contain the error
                                  code number.  On return the ti_Data is
                                  assigned to the string pointer.  (*ti_Data,
                                  if passed by reference).  See the file
                                  <sys/errno.h> for symbolic definitions for
                                  the errno codes.

             SBTC_FDCALLBACK      A callback function pointer for coordination
                                  of file descriptor usage between AmiTCP and
                                  link-library.  By default no callback is
                                  called and the value of this pointer is
                                  NULL.  The prototype for the callback
                                  function is:

                                  int error = fdCallback(int fd, int action);
                                      D0                     D0      D1

                                  where

                                  error -  0 for success or one of the error
                                           codes in <sys/errno.h> in case of
                                           error. The AmiTCP API function
                                           that calls the callback usually
                                           returns the 'error' back to the
                                           caller without any further
                                           modification.

                                  fd -     file descriptor number to take
                                           'action' on.

                                  action - one of the following actions
                                           (defined in
                                           <amitcp/socketbasetags.h>):

                                           FDCB_FREE -  mark the 'fd' as
                                                        unused on the link
                                                        library structure. If
                                                        'fd' represents a
                                                        file handled by the
                                                        link library, the
                                                        error (ENOTSOCK)
                                                        should be returned.

                                           FDCB_ALLOC - mark the 'fd'
                                                        allocated as a
                                                        socket.

                                           FDCB_CHECK - check if the 'fd' is
                                                        free. If an error is
                                                        returned, the 'fd' is
                                                        marked as used in the
                                                        AmiTCP/IP structures.

                                  The AmiTCP/IP calls the callback every time
                                  a socket descriptor is allocated or freed.
                                  AmiTCP/IP uses the FDCB_CHECK before actual
                                  allocation to check that it agrees with the
                                  link library on the next free descriptor
                                  number.  Thus the link library doesn't need
                                  to tell the AmiTCP if it creates a new file
                                  handle in open(), for example.

                                  See file _chkufb.c on the net.lib sources
                                  for an example implementation of the
                                  callback function for the SAS/C.

             SBTC_HERRNO          The name resolver error code value. Get
                                  this to find out why the gethostbyname()
                                  or gethostbyaddr() failed. The values are
                                  defined in <netdb.h>

             SBTC_HERRNOSTRPTR    Returns host error string for error number
                                  in tag data.  Host error is set on
                                  unsuccesful gethostbyname() and
                                  gethostbyaddr() calls. See the file
                                  <netdb.h> for the symbolic definitions for
                                  the herrno valus.

                                  Notes for the SBTC_ERRNOSTRPTR apply also
                                  to this tag code.

             SBTC_IOERRNOSTRPTR   Returns an error string for standard
                                  AmigaOS I/O error number as defined in the
                                  header file <exec/errors.h>.  Note that the
                                  error number taken by this tag code is
                                  positive, so the error codes must be
                                  negated (to be positive).  The positive
                                  error codes depend on the particular IO
                                  device, the standard Sana-II error codes
                                  can be retrieved by the tag code
                                  SBTC_S2ERRNOSTRPTR.

                                  Notes for the SBTC_ERRNOSTRPTR apply also
                                  to this tag code.

             SBTC_LOGFACILITY     Facility code for the syslog messages as
                                  defined in the header file <sys/syslog.h>.
                                  Defaults to LOG_USER.

             SBTC_LOGMASK         Sets the filter mask of the syslog
                                  messages.  By default the mask is 0xff,
                                  meaning that all messages are passed to the
                                  log system.

             SBTC_LOGSTAT         Syslog options defined in <sys/syslog.h>.

             SBTC_LOGTAGPTR       A pointer to a string which is used by
                                  syslog() to mark individual syslog
                                  messages. This defaults to NULL, but is
                                  set to the name of the calling program by
                                  the autoinit code in netlib:autoinit.c.
                                  This is for compatibility with pre-3.0
                                  programs.

             SBTC_S2ERRNOSTRPTR   Returns an error string for a Sana-II
                                  specific I/O error code as defined in the
                                  header file <devices/sana2.h>.

                                  Notes for the SBTC_ERRNOSTRPTR apply also
                                  to this tag code.

             SBTC_S2WERRNOSTRPTR  Returns an error string for a Sana-II Wire
                                  Error code as defined in the header file
                                  <devices/sana2.h>.

                                  Notes for the SBTC_ERRNOSTRPTR apply also
                                  to this tag code.

             SBTC_SIGIOMASK       The calling task is sent the signals
                                  specified by mask in tag data when
                                  asynhronous I/O is to be notified. The
                                  default value is zero, ie. no signal is
                                  sent.

             SBTC_SIGURGMASK      The calling task is sent the signals
                                  specified by mask in tag data when urgent
                                  data for the TCP arrives. The default value
                                  is zero, ie. no signal is sent.

    RESULT 
        Returns 0 on success, and a (positive) index of the failing tag on
        error.  Note that the value 1 means _first_ TagItem, 2 the second one,
        and so on.  The return value is NOT a C-language index, which are 0
        based.

    EXAMPLES
        To be written, see net.lib sources for various examples.

    NOTES

    BUGS
        None known.

    SEE ALSO
        <netinclude:amitcp/socketbasetags.h>, <include:utility/tagitem.h>

*****************************************************************************
*
*/
#ifdef notyet
/*
             SBTC_COMPAT43        Tag data is handled as boolean.  If it is
                                  true, AmiTCP/IP uses 4.3BSD compatible
                                  sockaddr structure for this application.

                                  The unreleased AS225r2 uses also 4.3BSD-
                                  compatible sockaddr structures.

*/
#endif

ULONG SAVEDS RAF2(_SocketBaseTagList,
		  struct SocketBase *,	libPtr,		a6,
		  struct TagItem *,	tags,		a0)
#if 0     
{
#endif

  ULONG errIndex = 1;
  ULONG tag;
  ULONG *tagData;
  short tmp;
  UWORD utmp;

  static const char * const strErr = "Errlist lookup error";

  CHECK_TASK();

  while((tag = tags->ti_Tag) != TAG_END) {
    if ((LONG)tag < 0) {		/* TAG_USER is the sign bit */
      /* get pointer to the actual data */
      tagData = ((UWORD)tag & SBTF_REF) ?
	(ULONG *)tags->ti_Data : &tags->ti_Data;

      switch ((UWORD)tag & ~SBTF_REF) {

      CASE_LONG( SBTC_BREAKMASK,  sigIntrMask );

      CASE_LONG( SBTC_SIGIOMASK,  sigIOMask );

      CASE_LONG( SBTC_SIGURGMASK, sigUrgMask );

      case (SBTC_ERRNO << SBTB_CODE): /* get */ 
	*tagData = (ULONG)readErrnoValue(libPtr);
	break;
      case (SBTC_ERRNO << SBTB_CODE) | SBTF_SET: /* set */
        writeErrnoValue(libPtr, *tagData);
	break;

      case (SBTC_HERRNO << SBTB_CODE): /* get */ 
	*tagData = (ULONG)*libPtr->hErrnoPtr;
	break;
      case (SBTC_HERRNO << SBTB_CODE) | SBTF_SET: /* set */
        *libPtr->hErrnoPtr = (LONG) *tagData;
	break;

      case (SBTC_DTABLESIZE << SBTB_CODE): /* get */
	*tagData = (ULONG)libPtr->dTableSize;
	break;
      case (SBTC_DTABLESIZE << SBTB_CODE) | SBTF_SET: /* set */
	if ((tmp = (WORD)*tagData) > 0)
	  setdtablesize(libPtr, tmp);
	break;

      CASE_LONG( SBTC_FDCALLBACK,   fdCallback );

      CASE_BYTE( SBTC_LOGSTAT,      LogStat );

      CASE_LONG( SBTC_LOGTAGPTR,    LogTag );

      case (SBTC_LOGFACILITY << SBTB_CODE): /* get */
	*tagData = (ULONG)libPtr->LogFacility;
	break;
      case (SBTC_LOGFACILITY << SBTB_CODE) | SBTF_SET: /* set */
	if ((utmp = (UWORD)*tagData) != 0 && (utmp &~ LOG_FACMASK) == 0)
	  libPtr->LogFacility = utmp;
	break;

      case (SBTC_LOGMASK << SBTB_CODE): /* get */
	*tagData = (ULONG)libPtr->LogMask;
	break;
      case (SBTC_LOGMASK << SBTB_CODE) | SBTF_SET: /* set */
	if ((utmp = (UWORD)*tagData) != 0)
	  libPtr->LogMask = (UBYTE)utmp;
	break;

      case SBTC_ERRNOSTRPTR << SBTB_CODE:
	/* get index */
	utmp = (UWORD)*tagData;
	/* return string pointer */
	*tagData = (ULONG)((utmp >= __sys_nerr) ?
			   strErr : __sys_errlist[utmp]);
	break;
      case SBTC_HERRNOSTRPTR << SBTB_CODE:
	/* get index */
	utmp = (UWORD)*tagData;
	/* return string pointer */
	*tagData = (ULONG)((utmp >= h_nerr) ?
			   strErr : h_errlist[utmp]);
	break;
      case SBTC_IOERRNOSTRPTR << SBTB_CODE:
	/* get index */
	utmp = (UWORD)*tagData;
	/* return string pointer */
	*tagData = (ULONG)((utmp >= io_nerr) ? 
			   strErr : io_errlist[utmp]);
	break;
      case SBTC_S2ERRNOSTRPTR << SBTB_CODE:
	/* get index */
	utmp = (UWORD)*tagData;
	/* return string pointer */
	*tagData = (ULONG)((utmp >= sana2io_nerr) ?
			   strErr : sana2io_errlist[utmp]);
	break;
      case SBTC_S2WERRNOSTRPTR << SBTB_CODE:
	/* get index */
	utmp = (UWORD)*tagData;
	/* return string pointer */
	*tagData = (ULONG)((utmp >= sana2wire_nerr) ?
			   strErr : sana2wire_errlist[utmp]);
	break;

      case (SBTC_ERRNOBYTEPTR << SBTB_CODE) | SBTF_SET: /* set */
        if (SetErrnoPtr(libPtr, (VOID *)*tagData, 1) < 0)
	  return errIndex;
        break;
      case (SBTC_ERRNOWORDPTR << SBTB_CODE) | SBTF_SET: /* set */
        if (SetErrnoPtr(libPtr, (VOID *)*tagData, 2) < 0)
	  return errIndex;
        break;
      case (SBTC_ERRNOLONGPTR << SBTB_CODE) | SBTF_SET: /* set */
        if (SetErrnoPtr(libPtr, (VOID *)*tagData, 4) < 0)
	  return errIndex;
        break;

      CASE_LONG( SBTC_HERRNOLONGPTR, hErrnoPtr );

#ifdef notyet
      CASE_FLAG( SBTC_COMPAT43, SBFB_COMPAT43 );
#endif

      default:
	return errIndex;
      }
    }
    else {			/* TAG_USER not set */
      switch(tags->ti_Tag) {
      case TAG_IGNORE:
	break;
      case TAG_MORE:
	tags = (struct TagItem *)tags->ti_Data;
	errIndex++;
	continue;
      case TAG_SKIP:
	tags++; errIndex++;
	break;
      default:
        return errIndex;	/* fail */
      }
    }
    
    tags++; errIndex++;
  }
  
  return 0;
}

