/* $Id: handlenames.cpp,v 1.5 2002-04-29 17:05:29 sandervl Exp $ */

/*
 * Win32 Handle Manager Object Namespace for OS/2
 *
 * 2001/11/23 Patrick Haller <patrick.haller@innotek.de>
 *
 * Project Odin Software License can be found in LICENSE.TXT
 *
 */

//#undef DEBUG_LOCAL
//#define DEBUG_LOCAL


/*****************************************************************************
 * Remark                                                                    *
 *****************************************************************************

 */


/*****************************************************************************
 * Includes                                                                  *
 *****************************************************************************/

#define INCL_DOSSEMAPHORES
#define INCL_DOSERRORS
#include <os2.h>

#include <stdlib.h>
#include <string.h>

#include <ccollection.h>
#include "handlenames.h"
#include <vmutex.h>


/*****************************************************************************
 * Defines                                                                   *
 *****************************************************************************/


/*****************************************************************************
 * Structures                                                                *
 *****************************************************************************/

typedef struct tagHandleName
{
  // the real "defined" target name (which can directly passed on
  // to the device handlers or filesystems or ...
  PSZ   pszTarget;
  ULONG ulTargetLength;
  
  // the alias prefix
  PSZ   pszSymbolicLink;
  ULONG ulSymbolicLinkLength;
} HANDLENAME, *PHANDLENAME;


class HandleNames
{
  protected:
    VMutex       mtxHandleNameMgr;
    CLinearList* pSymbolicLinks;
  
    PHANDLENAME findSymbolicLink(PSZ pszSymbolicLink,
                                 BOOL fCaseInsensitive);
  
    PHANDLENAME findSymbolicLinkExact(PSZ pszSymbolicLink);
  
  public:
    HandleNames(void);
    ~HandleNames();
  
    BOOL addSymbolicLink(PSZ pszSymbolicLink,
                         PSZ pszTarget);
  
    BOOL removeSymbolicLink(PSZ pszSymbolicLink);
  
    BOOL removeTarget(PSZ pszTarget);
  
    BOOL resolveName(PSZ pszName, 
                     PSZ pszTarget, 
                     ULONG ulTargetLength, 
                     BOOL fCaseInsensitive);
};


/*****************************************************************************
 * Process Global Structures                                                 *
 *****************************************************************************/


static HandleNames  handleNameMgr;
static HandleNames* pHandleNameMgr = &handleNameMgr;


/*****************************************************************************
 * Local Prototypes                                                          *
 *****************************************************************************/



/*****************************************************************************
 * Name      : HandleNames::HandleNames
 * Purpose   : Constructor for handle name mapper
 * Parameters: 
 * Variables :
 * Result    : 
 * Remark    :
 * Status    :
 *
 * Author    : Patrick Haller [2001/11/23]
 *****************************************************************************/

HandleNames::HandleNames(void)
{
  // Note: as handleNameMgr is a static object, the
  // destructor will never be called, this memory is leaked.
  pSymbolicLinks = new CLinearList();
}


/*****************************************************************************
 * Name      : HandleNames::~HandleNames
 * Purpose   : destructor for handle name mapper
 * Parameters: 
 * Variables :
 * Result    : 
 * Remark    :
 * Status    :
 *
 * Author    : Patrick Haller [2001/11/23]
 *****************************************************************************/

HandleNames::~HandleNames()
{
  delete pSymbolicLinks;
}


/*****************************************************************************
 * Name      : HandleNames::findSymbolicLink
 * Purpose   : find the table entry with the specified symbolic link
 * Parameters: PSZ  pszSymbolicLink - the link to scan for
 *             BOOL fCaseInsensitive - TRUE for a case-insensitive lookup
 * Variables :
 * Result    : 
 * Remark    : The comparison here is not meant to be "identity" but
 *             "startsWith" because for the name resolver, the first
 *             symbolic link that the specified name starts with is used
 *             for name resolution.
 *             So the idea is to prevent ambiguous symbolic link names here.
 *
 *             It is expected to enter this method in "locked" state
 * Status    :
 *
 * Author    : Patrick Haller [2001/11/23]
 *****************************************************************************/

PHANDLENAME HandleNames::findSymbolicLink(PSZ pszSymbolicLink, 
                                          BOOL fCaseInsensitive)
{
  PLINEARLISTENTRY pLE = pSymbolicLinks->getFirst();
  while (pLE)
  {
    PHANDLENAME pHandleName = (PHANDLENAME)pLE->pObject;
    
    int iLen = strlen(pHandleName->pszSymbolicLink);
    
    if (fCaseInsensitive)
    {
      // Note: pszSymbolicLink must either terminate at [iLen] or
      // have a delimiter.
      if ( (pszSymbolicLink[iLen] == '\\') ||
           (pszSymbolicLink[iLen] == 0) )
        if (strnicmp(pHandleName->pszSymbolicLink, pszSymbolicLink, iLen) == 0)
          return pHandleName;
    }
    else
    {
      if ( (pszSymbolicLink[iLen] == '\\') ||
           (pszSymbolicLink[iLen] == 0) )
        if (strncmp(pHandleName->pszSymbolicLink, pszSymbolicLink, iLen) == 0)
          return pHandleName;
    }
    
    
    // skip to the next entry
    pLE = pSymbolicLinks->getNext(pLE);
  }
  
  // not found
  return NULL;
}


/*****************************************************************************
 * Name      : HandleNames::findSymbolicLinkExact
 * Purpose   : find the table entry with the specified symbolic link
 * Parameters: PSZ  pszSymbolicLink - the link to scan for
 *             BOOL fCaseInsensitive - TRUE for a case-insensitive lookup
 * Variables :
 * Result    : 
 * Remark    : The comparison here is not meant to be "identity" but
 *             "startsWith" because for the name resolver, the first
 *             symbolic link that the specified name starts with is used
 *             for name resolution.
 *             So the idea is to prevent ambiguous symbolic link names here.
 *
 *             It is expected to enter this method in "locked" state
 * Status    :
 *
 * Author    : Patrick Haller [2001/11/23]
 *****************************************************************************/

PHANDLENAME HandleNames::findSymbolicLinkExact(PSZ pszSymbolicLink)
{
  PLINEARLISTENTRY pLE = pSymbolicLinks->getFirst();
  while (pLE)
  {
    PHANDLENAME pHandleName = (PHANDLENAME)pLE->pObject;
    
    if (strcmp(pHandleName->pszSymbolicLink, pszSymbolicLink) == 0)
      return pHandleName;
    
    // skip to the next entry
    pLE = pSymbolicLinks->getNext(pLE);
  }
  
  // not found
  return NULL;
}


/*****************************************************************************
 * Name      : HandleNames::addSymbolicLink
 * Purpose   : add a symbolic link definition to the table
 * Parameters: PSZ pszSymbolicLink - the name of the symbolic link
 *             PSZ pszTarget       - the name of the device/name to link on
 * Variables :
 * Result    : TRUE if successful
 *             FALSE if otherwise (same link already exists)
 * Remark    :
 * Status    :
 *
 * Author    : Patrick Haller [2001/11/23]
 *****************************************************************************/

BOOL HandleNames::addSymbolicLink(PSZ pszSymbolicLink, 
                                  PSZ pszTarget)
{
  BOOL rc = TRUE;
  
  mtxHandleNameMgr.enter();
  
  // 1 - find symbolic link with same name
  PHANDLENAME pHandleName = findSymbolicLinkExact(pszSymbolicLink);
  
  // 2 - if found 
  if (NULL != pHandleName)
  {
    // 2.1 - and targets are different, return FALSE
    if (strcmp(pszTarget, pHandleName->pszTarget) == 0)
      rc = TRUE;
    
    // 2.2 - and targets are identical, return TRUE
    else
      rc = FALSE;
  }
  else
  {
    // 3 - add definition to table
    pHandleName = (PHANDLENAME)malloc( sizeof(HANDLENAME) );
    if (NULL == pHandleName)
      rc = FALSE;
    else
    {
      pHandleName->pszSymbolicLink = strdup(pszSymbolicLink);
      if (NULL == pHandleName->pszSymbolicLink)
      {
        free (pHandleName);
        rc = FALSE;
      }
      else
      {
        pHandleName->pszTarget = strdup(pszTarget);
        if (NULL == pHandleName->pszTarget)
        {
          free (pHandleName->pszSymbolicLink);
          free (pHandleName);
          rc = FALSE;
        }
        else
        {
          // fill in these values
          pHandleName->ulTargetLength = strlen(pszTarget);
          pHandleName->ulSymbolicLinkLength = strlen(pszSymbolicLink);
          
          // OK, finally add to the list
          pSymbolicLinks->addFirst(pHandleName);
        }
      }
    }
  }
  
  mtxHandleNameMgr.leave();
  
  return rc;
}


/*****************************************************************************
 * Name      : 
 * Purpose   : 
 * Parameters: 
 * Variables :
 * Result    : TRUE if successful, FALSE if otherwise
 * Remark    :
 * Status    :
 *
 * Author    : Patrick Haller [2001/11/23]
 *****************************************************************************/

BOOL HandleNames::removeSymbolicLink(PSZ pszSymbolicLink)
{
  BOOL rc = TRUE;
  
  mtxHandleNameMgr.enter();
  
  // 1 - find symbolic name
  PHANDLENAME pHandleName = findSymbolicLinkExact(pszSymbolicLink);
  if (NULL == pHandleName)
    rc = FALSE;
  else
  {
    // 2 - remove the link
    pSymbolicLinks->removeObject(pHandleName);
    
    if (NULL != pHandleName->pszSymbolicLink )
      free( pHandleName->pszSymbolicLink );
    
    if (NULL != pHandleName->pszTarget )
      free( pHandleName->pszTarget );
    
    free( pHandleName );
  }
  
  mtxHandleNameMgr.leave();
  
  return rc;
}


/*****************************************************************************
 * Name      : HandleNames::removeTarget
 * Purpose   : remove all links to the specified target
 * Parameters: PSZ pszTarget - the name of the target to remove
 * Variables :
 * Result    : TRUE if successful, FALSE if otherwise
 * Remark    :
 * Status    :
 *
 * Author    : Patrick Haller [2001/11/23]
 *****************************************************************************/

BOOL HandleNames::removeTarget(PSZ pszTarget)
{
  BOOL rc = FALSE;
  
  mtxHandleNameMgr.enter();
  
  // iterate over all registered symbolic links
  PLINEARLISTENTRY pLE = pSymbolicLinks->getFirst();
  while (pLE)
  {
    PHANDLENAME pHandleName = (PHANDLENAME)pLE->pObject;
    
    // check the name
    if (strcmp(pszTarget, pHandleName->pszTarget) == 0)
    {
      pSymbolicLinks->removeElement(pLE);
      
      // at least one removal succeeded
      rc = TRUE;
    }
  }
  
  mtxHandleNameMgr.leave();
  
  return rc;
}


/*****************************************************************************
 * Name      : HandleNames::resolveName
 * Purpose   : resolve the specified name according to the symbolic links
 *             until we reach the "final" name
 * Parameters: PSZ   pszName
 *             PSZ   pszTarget
 *             ULONG ulTargetLength
 *             BOOL  fCaseInsensitive
 * Variables :
 * Result    : FALSE if name was not modified, TRUE if name was resolved
 * Remark    : This is a very easy, cheesy implementation of a pathname 
 *             cracker. Should be sufficient at the moment though.
 * Status    :
 *
 * Author    : Patrick Haller [2001/11/23]
 *****************************************************************************/

BOOL HandleNames::resolveName(PSZ pszName, 
                              PSZ pszTarget, 
                              ULONG ulTargetLength,
                              BOOL fCaseInsensitive)
{
  BOOL rc = FALSE;
  
  mtxHandleNameMgr.enter();
  
  // scan through the names (case-insensitive)
  PHANDLENAME pHandleName = findSymbolicLink(pszName, fCaseInsensitive);
  if (NULL != pHandleName)
  {
    // rebuild the target name
    int iNameLength     = strlen(pszName);
    
    // first copy the resolved target name fragment
    strncpy(pszTarget,
            pHandleName->pszTarget,
            ulTargetLength);
    
    // now append the rest of the specified name with the
    // now resolved symbolic cut away
    if (ulTargetLength != pHandleName->ulTargetLength)
      strncpy(pszTarget + pHandleName->ulTargetLength,
              pszName + pHandleName->ulSymbolicLinkLength,
              ulTargetLength - pHandleName->ulTargetLength);
    
    // tell caller the name has been resolved
    // (is different from the source name)
    rc = TRUE;
  }
  
  mtxHandleNameMgr.leave();
  
  return rc;
}




/*****************************************************************************
 * Exported Wrapper Functions
 *****************************************************************************/
 
BOOL HandleNamesResolveName(PSZ pszName,
                            PSZ pszTarget,
                            ULONG ulTargetLength,
                            BOOL fCaseInsensitive)
{
  return pHandleNameMgr->resolveName(pszName,
                                     pszTarget,
                                     ulTargetLength,
                                     fCaseInsensitive);
}


BOOL HandleNamesAddSymbolicLink(PSZ pszSymbolicLink,
                                PSZ pszTarget)
{
  return pHandleNameMgr->addSymbolicLink(pszSymbolicLink,
                                         pszTarget);
}
  

BOOL HandleNamesRemoveSymbolicLink(PSZ pszSymbolicLink)
{
  return pHandleNameMgr->removeSymbolicLink(pszSymbolicLink);
}
  

BOOL HandleNamesRemoveTarget(PSZ pszTarget)
{
  return pHandleNameMgr->removeTarget(pszTarget);
}
