/****************************************************************************
**  File:       pipename.c
**  Program:    pipe-handler - an AmigaDOS handler for named pipes
**  Version:    1.1
**  Author:     Ed Puckett      qix@mit-oz
**
**  Copyright 1987 by EpAc Software.  All Rights Reserved.
**
**  History:    05-Jan-87       Original Version (1.0)
**		07-Feb-87	Added conditional compilation for autoname.
*/

#include   <libraries/dos.h>
#include   <libraries/dosextens.h>
#include   <libraries/filehandler.h>
#include   <exec/exec.h>

#include   "pipelists.h"
#include   "pipename.h"
#include   "pipebuf.h"
#include   "pipecreate.h"
#include   "pipesched.h"
#include   "pipe-handler.h"



/*---------------------------------------------------------------------------
** pipename.c
** ----------
** This module contains functions related to the parsing of the pipe names.
**
** Visible Functions
** -----------------
**	int   ParsePipeName (Bname, nmp, sizep, tapnmp)
**	void  BSTRtoCstr    (BSTRp, str, maxsize)
**	void  CstrtoBSTR    (str, BSTRp, maxsize)
**	int   inrange       (x, lower, upper)
**	char  uppercase     (c)
**	char  *findchar     (str, ch)
**	void  l_strcpy      (to, from)
**	char  *strdiff      (str1, str2)
**	char  *get_autoname (newflag)     (if AUTONAME is true)
**
** Macros (in pipename.h)
** ----------------------
**	isnumeral (c)
**
** Local Functions
** ---------------
**	int  ParseNum (str, nump)
*/



/*---------------------------------------------------------------------------
** ParsePipeName() parses the string "Bname" into three parts: a pipe name,
** a size specification and a tap name.  (Bname must be the byte address of a
** BSTR, i.e., a string whose first byte is its length.)  The three parts are
** separated by the character PIPE_SPEC_CHAR (defined in pipename.h).
** Assuming that PIPE_SPEC_CHAR is '/', and that '[]' are metacharacters
** which enclose optional parts, the syntax for Bname is [D:][p][/n][/[t]].
** Here, "D" represents a device name, "p" represents a pipe name,
** "n" represents a number and "t" represents a tap name.
**      ParsePipeName() returns nonzero iff "Bname" conforms to the syntax
** and the following restrictions.
**      "D:" represents a device name.  If it occurs, it is ignored.  Notice
** that tap names which contain a ":" force a device name to be specified for
** the pipe.  Otherwise, everything up to and including the ":" in the tap
** name will be ignored.
**      *nmp returns pointing to a copy of "p", even if it is empty.  Default
** pipe names are handled by calling get_autoname().  (This is done by
** OpenPipe() if *nmp returns empty.)
**      "n" must begin with a digit.  If "n" begins with "0x", it is parsed
** as a hexadecimal number.  If it begins with "0" but not "0x", it is parsed
** as an octal number.  Otherwise, it is parsed as a decimal number.  If the
** size specifier ("/t" above) is not given, *sizep is set to DEFAULT_PIPELEN.
**     If the compile-time flag CON_TAP_ONLY is set, "t" may only be a "CON:"
** file specifier, such as "CON:10/10/400/120/TapWindow".  If CON_TAP_ONLY is
** not set, string is accepted.  If "t" is empty (but the PIPE_SPEC_CHAR was
** given), then a defualt tap name is formed by appending "p" to
** DEFAULT_TAPNAME_PREFIX.  If the tap name specifier ("/[t]" above) is not
** given, *tapnmp is set to NULL.
*/

static char  default_tapname_prefix[]  =  DEFAULT_TAPNAME_PREFIX;
static char  namebuf[sizeof (default_tapname_prefix) + PIPENAMELEN];

int  ParsePipeName (Bname, nmp, sizep, tapnmp)

BYTE   *Bname;       /* reference to BSTR name sent to handler */
char   **nmp;        /* reference to pipe name pointer */
ULONG  *sizep;       /* size longword pointer */
char   **tapnmp;     /* reference to tap name pointer, returns NULL if none */

{ char  *cp;
  int   ParseNum();


  l_strcpy (namebuf, default_tapname_prefix);

  *nmp=    namebuf + (sizeof (default_tapname_prefix) - 1);
  *sizep=  DEFAULT_PIPELEN;
  *tapnmp= NULL;

  BSTRtoCstr (Bname, *nmp, PIPENAMELEN);

  if (*(cp= findchar (*nmp, ':')) == ':')
    l_strcpy (*nmp, ++cp);     /* get rid of "devname:" prefix */

  if ( *(cp= findchar (*nmp, PIPE_SPEC_CHAR)) )     /* true if not '\0' */
    { *(cp++)= '\0';     /* terminate pipe name */

      if (isnumeral (*cp))
        { if ( (! ParseNum (cp, sizep)) || (*sizep <= 0) )
            return FALSE;

          if ( *(cp= findchar (cp, PIPE_SPEC_CHAR)) == '\0' )
            return TRUE;     /* no tap name, but successful anyway */

          ++cp;     /* skip separator */
        }

      if ( *(*tapnmp= cp) == '\0' )     /* first character of tap name */
        *tapnmp= namebuf;     /* use default prefix prepended to pipe name */
#if CON_TAP_ONLY
      else
        { if ( *(strdiff ("CON:", *tapnmp)) )     /* true if not '\0' */
            return FALSE;     /* only CON: . . . allowed */
        }
#endif CON_TAP_ONLY
    }

  return TRUE;
}



/*---------------------------------------------------------------------------
** BSTRtoCstr() converts the BSTR pointed to by "BSTRp" (a byte address) to
** a null-terminated string, storing the result in the locations pointed to
** by "str".  At most "maxsize" bytes will be stored.
*/

void  BSTRtoCstr (BSTRp, str, maxsize)

register BYTE  *BSTRp;
register char  *str;
unsigned       maxsize;

{ register int   i;
  register int   limit;


  if ((limit= *(BSTRp++)) > ((int) maxsize - 1))     /* leave room for '\0' */
    limit= (int) maxsize - 1;

  for (i= 0; i < limit; ++i)
    *(str++)= *(BSTRp++);

  *str= '\0';
}



/*---------------------------------------------------------------------------
** CstrtoBSTR() converts the null-terminated string pointed to by "str" to
** a BSTR located at the byte address "BSTRp".  At most "maxsize" bytes will
** be stored.
*/

void  CstrtoBSTR (str, BSTRp, maxsize)

register char  *str;
BYTE           *BSTRp;
unsigned       maxsize;

{ register char  *bp;
  register int   i, limit;


  bp= BSTRp + 1;

  limit= maxsize - 1;

  for (i= 0; i < limit; ++i)
    if ( (*(bp++)= *(str++)) == '\0' )
      break;

  BSTRp[0]= i;
}



/*---------------------------------------------------------------------------
** inrange() returns nonzero iff x is in the range [lower, upper].
** uppercase() returns the uppercase version of the ASCII character sent.
** These are not implemented as macros to avoid hard-to-find bugs like
** uppercase(c++), where the side-effect occurs more than once.
*/

int  inrange (x, lower, upper)

register int  x;
register int  lower;
register int  upper;

{ return  ((x >= lower) && (x <= upper));
}


char  uppercase (c)

register char  c;

{ return  (char) (inrange (c, 'a', 'z') ? (c + ('A' - 'a')) : c);
}



/*---------------------------------------------------------------------------
** The null-terminated string "str" is scanned for the character "ch".  If
** found, a pointer to its first occurrence in "str" is returned.  Otherwise,
** a pointer to the terminating '\0' in "str" is returned.
*/

char  *findchar (str, ch)

register char  *str;
register char  ch;

{ while ((*str != '\0') && (*str != ch))
    ++str;

  return str;     /* return position of ch, or end if not found */
}



/*---------------------------------------------------------------------------
** This is just like strcpy().  Its is defined here to avoid including other
** libraries.
*/

void  l_strcpy (to, from)

register char  *to;
register char  *from;

{
STRCPYLOOP:
  if (*(to++)= *(from++))
    goto STRCPYLOOP;
}



/*---------------------------------------------------------------------------
** strdiff() returns a pointer to the first difference in the two null-
** terminated strings "str1" and "str2".  If no differnce is found, or if
** "str1" is shorter than "str2", then a pointer to '\0' is returned.
** The returned pointer is to a character in "str1".
*/

char  *strdiff (str1, str2)

register char  *str1;
register char  *str2;

{ while ( *str1 && (uppercase (*str1) == uppercase (*str2)) )
    { ++str1;
      ++str2;
    }

  return str1;     /* return position of first difference, or end of str1 */
}



/*---------------------------------------------------------------------------
** get_autoname() returns a pointer to "autoname".  If "newflag" is nonzero,
** autoname is first updated so that it does not conflict with any existing
** pipe name.  This is done by looking for a block of ASCII digits in
** "autoname", and incrementing their effective value.  "autoname" MUST
** contain such a block of digits.
*/

#if AUTONAME

static char  autoname[]  =  AUTONAME_INIT;

char  *get_autoname (newflag)

BYTE  newflag;

{ char      *cp, *cpc;
  PIPEDATA  *FindPipe();


  if (newflag)     /* then create a new unique pipe name */
    { cp= findchar (autoname, '\0');

      while (! isnumeral (*cp))     /* find last numeral */
        --cp;

      do
        { ++(*cp);     /* "increment" name */

          for (cpc= cp; (! isnumeral (*cpc)); )     /* ripple carry */
            { *(cpc--)= '0';

              if (! isnumeral (*cpc))
                break;     /* no more digits */

              ++(*cpc);
            }
        }
      while (FindPipe (autoname) != NULL);     /* repeat until name is unique */
    }


  return  autoname;
}

#endif AUTONAME



/*---------------------------------------------------------------------------
** ParseNum() parses the null-terminated string pointed to by "str" into a
** number, and stores its value in *nump.  ParseNum() returns nonzero iff
** successful.  Both '\0' and PIPE_SPEC_CHAR are acceptable terminators for
** the number.
**      If the number begins with "0x", it is interpreted as hexadecimal.
** If it begins with "0" but not "0x", it is interpreted as octal.
** Otherwise, it is interpreted as decimal.
*/

static int  ParseNum (str, nump)

char   *str;
ULONG  *nump;

{ int   radix    =  10;
  char  *digits  =  "0123456789ABCDEF";
  LONG  value;


  if ((*str == '0') && (uppercase (*(str + 1)) == 'X'))
    { radix= 16;
      str += 2;
    }
  else if (*str == '0')
    { radix= 8;
      ++str;
    }

  for (*nump= 0; TRUE; ++str)
    { value= (LONG) findchar (digits, uppercase (*str)) - (LONG) digits;

      if (! inrange (value, 0, (radix - 1)))
        break;

      if (*nump > ((MAX_PIPELEN - value) / radix))
        return FALSE;

      *nump *= radix;
      *nump += value;
    }


  return  ( (*str == PIPE_SPEC_CHAR) || (*str == '\0') );
}
