/*
 * COPYRIGHT:         See COPYING in the top level directory
 * PROJECT:           ReactOS system libraries
 * FILE:              lib/rtl/nls.c
 * PURPOSE:           National Language Support (NLS) functions
 * PROGRAMMERS:       Emanuele Aliberti
 */

/* INCLUDES *****************************************************************/

#include <rtl.h>

#define NDEBUG
#include <debug.h>

/* GLOBALS *******************************************************************/

PUSHORT NlsUnicodeUpcaseTable = NULL;
PUSHORT NlsUnicodeLowercaseTable = NULL;

USHORT NlsAnsiCodePage = 0; /* exported */
BOOLEAN NlsMbCodePageTag = FALSE; /* exported */
PWCHAR NlsAnsiToUnicodeTable = NULL;
PCHAR NlsUnicodeToAnsiTable = NULL;
PWCHAR NlsDbcsUnicodeToAnsiTable = NULL;
PUSHORT NlsLeadByteInfo = NULL; /* exported */


USHORT NlsOemCodePage = 0;
BOOLEAN NlsMbOemCodePageTag = FALSE; /* exported */
PWCHAR NlsOemToUnicodeTable = NULL;
PCHAR NlsUnicodeToOemTable =NULL;
PWCHAR NlsDbcsUnicodeToOemTable = NULL;
PUSHORT _NlsOemLeadByteInfo = NULL; /* exported */

USHORT NlsOemDefaultChar = '\0';
USHORT NlsUnicodeDefaultChar = 0;


#define NlsOemLeadByteInfo              _NlsOemLeadByteInfo
#define INIT_FUNCTION

/* FUNCTIONS *****************************************************************/

/*
 * @unimplemented
 */
NTSTATUS NTAPI
RtlCustomCPToUnicodeN(IN PCPTABLEINFO CustomCP,
                      PWCHAR UnicodeString,
                      ULONG UnicodeSize,
                      PULONG ResultSize,
                      PCHAR CustomString,
                      ULONG CustomSize)
{
   ULONG Size = 0;
   ULONG i;

   if (CustomCP->DBCSCodePage == 0)
   {
      /* single-byte code page */
      if (CustomSize > (UnicodeSize / sizeof(WCHAR)))
         Size = UnicodeSize / sizeof(WCHAR);
      else
         Size = CustomSize;

      if (ResultSize != NULL)
         *ResultSize = Size * sizeof(WCHAR);

      for (i = 0; i < Size; i++)
      {
         *UnicodeString = CustomCP->MultiByteTable[(UCHAR)*CustomString];
         UnicodeString++;
         CustomString++;
      }
   }
   else
   {
      /* multi-byte code page */
      /* FIXME */
      ASSERT(FALSE);
   }

   return(STATUS_SUCCESS);
}



WCHAR NTAPI
RtlDowncaseUnicodeChar (IN WCHAR Source)
{
   USHORT Offset;

   if (Source < L'A')
      return Source;

   if (Source <= L'Z')
      return Source + (L'a' - L'A');

   if (Source < 0x80)
      return Source;

   Offset = ((USHORT)Source >> 8);
   DPRINT("Offset: %hx\n", Offset);

   Offset = NlsUnicodeLowercaseTable[Offset];
   DPRINT("Offset: %hx\n", Offset);

   Offset += (((USHORT)Source & 0x00F0) >> 4);
   DPRINT("Offset: %hx\n", Offset);

   Offset = NlsUnicodeLowercaseTable[Offset];
   DPRINT("Offset: %hx\n", Offset);

   Offset += ((USHORT)Source & 0x000F);
   DPRINT("Offset: %hx\n", Offset);

   Offset = NlsUnicodeLowercaseTable[Offset];
   DPRINT("Offset: %hx\n", Offset);

   DPRINT("Result: %hx\n", Source + (SHORT)Offset);

   return Source + (SHORT)Offset;
}




/*
 * @implemented
 */
VOID NTAPI
RtlGetDefaultCodePage(OUT PUSHORT AnsiCodePage,
                      OUT PUSHORT OemCodePage)
{
   *AnsiCodePage = NlsAnsiCodePage;
   *OemCodePage = NlsOemCodePage;
}




/*
 * @implemented
 */
VOID NTAPI
RtlInitCodePageTable(IN PUSHORT TableBase,
                     OUT PCPTABLEINFO CodePageTable)
{
   PNLS_FILE_HEADER NlsFileHeader;

   DPRINT("RtlInitCodePageTable() called\n");

   NlsFileHeader = (PNLS_FILE_HEADER)TableBase;

   /* Copy header fields first */
   CodePageTable->CodePage = NlsFileHeader->CodePage;
   CodePageTable->MaximumCharacterSize = NlsFileHeader->MaximumCharacterSize;
   CodePageTable->DefaultChar = NlsFileHeader->DefaultChar;
   CodePageTable->UniDefaultChar = NlsFileHeader->UniDefaultChar;
   CodePageTable->TransDefaultChar = NlsFileHeader->TransDefaultChar;
   CodePageTable->TransUniDefaultChar = NlsFileHeader->TransUniDefaultChar;

   RtlCopyMemory(&CodePageTable->LeadByte,
                 &NlsFileHeader->LeadByte,
                 MAXIMUM_LEADBYTES);

   /* Offset to wide char table is after the header */
   CodePageTable->WideCharTable = TableBase + NlsFileHeader->HeaderSize + 1 +
                                  TableBase[NlsFileHeader->HeaderSize];

   /* Then multibyte table (256 wchars) follows */
   CodePageTable->MultiByteTable = TableBase + NlsFileHeader->HeaderSize + 1;

   /* Check the presence of glyph table (256 wchars) */
   if (!CodePageTable->MultiByteTable[256])
      CodePageTable->DBCSRanges = CodePageTable->MultiByteTable + 256 + 1;
   else
      CodePageTable->DBCSRanges = CodePageTable->MultiByteTable + 256 + 1 + 256;

   /* Is this double-byte code page? */
   if (*CodePageTable->DBCSRanges)
   {
      CodePageTable->DBCSCodePage = 1;
      CodePageTable->DBCSOffsets = CodePageTable->DBCSRanges + 1;
   }
   else
   {
      CodePageTable->DBCSCodePage = 0;
      CodePageTable->DBCSOffsets = NULL;
   }
}




/*
 * @implemented
 */
VOID NTAPI
RtlInitNlsTables(IN PUSHORT AnsiTableBase,
                 IN PUSHORT OemTableBase,
                 IN PUSHORT CaseTableBase,
                 OUT PNLSTABLEINFO NlsTable)
{
   DPRINT("RtlInitNlsTables()called\n");

   if (AnsiTableBase == NULL ||
         OemTableBase == NULL ||
         CaseTableBase == NULL)
      return;

   RtlInitCodePageTable (AnsiTableBase,
                         &NlsTable->AnsiTableInfo);

   RtlInitCodePageTable (OemTableBase,
                         &NlsTable->OemTableInfo);

   NlsTable->UpperCaseTable = (PUSHORT)CaseTableBase + 2;
   NlsTable->LowerCaseTable = (PUSHORT)CaseTableBase + *((PUSHORT)CaseTableBase + 1) + 2;
}


/*
 * @unimplemented
 */
NTSTATUS NTAPI
RtlMultiByteToUnicodeN(
   OUT PWCHAR UnicodeString,
   IN ULONG UnicodeSize,
   OUT PULONG ResultSize,
   IN PCSTR MbString,
   IN ULONG MbSize)
{
   ULONG Size = 0;
   ULONG i;

   PAGED_CODE_RTL();

   if (NlsMbCodePageTag == FALSE)
   {
      /* single-byte code page */
      if (MbSize > (UnicodeSize / sizeof(WCHAR)))
         Size = UnicodeSize / sizeof(WCHAR);
      else
         Size = MbSize;

      if (ResultSize != NULL)
         *ResultSize = Size * sizeof(WCHAR);

      for (i = 0; i < Size; i++)
         UnicodeString[i] = NlsAnsiToUnicodeTable[(UCHAR)MbString[i]];
   }
   else
   {
      /* multi-byte code page */
      /* FIXME */

      UCHAR Char;
      USHORT LeadByteInfo;
      PCSTR MbEnd = MbString + MbSize;

      for (i = 0; i < UnicodeSize / sizeof(WCHAR) && MbString < MbEnd; i++)
      {
         Char = *(PUCHAR)MbString++;

         if (Char < 0x80)
         {
            *UnicodeString++ = Char;
            continue;
         }

         LeadByteInfo = NlsLeadByteInfo[Char];

         if (!LeadByteInfo)
         {
            *UnicodeString++ = NlsAnsiToUnicodeTable[Char];
            continue;
         }

         if (MbString < MbEnd)
            *UnicodeString++ = NlsLeadByteInfo[LeadByteInfo + *(PUCHAR)MbString++];
      }

      if (ResultSize != NULL)
         *ResultSize = i * sizeof(WCHAR);
   }

   return STATUS_SUCCESS;
}



/*
 * @implemented
 */
NTSTATUS
NTAPI
RtlMultiByteToUnicodeSize(PULONG UnicodeSize,
                          PCSTR MbString,
                          ULONG MbSize)
{
    ULONG Length = 0;

    PAGED_CODE_RTL();

    if (!NlsMbCodePageTag)
    {
        /* single-byte code page */
        *UnicodeSize = MbSize * sizeof (WCHAR);
    }
    else
    {
        /* multi-byte code page */
        /* FIXME */

        while (MbSize--)
        {
            UCHAR Char = *(PUCHAR)MbString++;

            if (Char >= 0x80 && NlsLeadByteInfo[Char])
            {
                if (MbSize)
                {
                    /* Move on */
                    MbSize--;
                    MbString++;
                }
            }

            /* Increase returned size */
            Length++;
        }

        /* Return final size */
        *UnicodeSize = Length * sizeof(WCHAR);
    }

    /* Success */
    return STATUS_SUCCESS;
}



/*
 * @unimplemented
 */
NTSTATUS NTAPI
RtlOemToUnicodeN (PWCHAR UnicodeString,
                  ULONG UnicodeSize,
                  PULONG ResultSize,
                  PCCH OemString,
                  ULONG OemSize)
{
   ULONG Size = 0;
   ULONG i;

   if (NlsMbOemCodePageTag == FALSE)
   {
      /* single-byte code page */
      if (OemSize > (UnicodeSize / sizeof(WCHAR)))
         Size = UnicodeSize / sizeof(WCHAR);
      else
         Size = OemSize;

      if (ResultSize != NULL)
         *ResultSize = Size * sizeof(WCHAR);

      for (i = 0; i < Size; i++)
      {
         *UnicodeString = NlsOemToUnicodeTable[(UCHAR)*OemString];
         UnicodeString++;
         OemString++;
      }
   }
   else
   {
      /* multi-byte code page */
      /* FIXME */

      UCHAR Char;
      USHORT OemLeadByteInfo;
      PCCH OemEnd = OemString + OemSize;

      for (i = 0; i < UnicodeSize / sizeof(WCHAR) && OemString < OemEnd; i++)
      {
         Char = *(PUCHAR)OemString++;

         if (Char < 0x80)
         {
            *UnicodeString++ = Char;
            continue;
         }

         OemLeadByteInfo = NlsOemLeadByteInfo[Char];

         if (!OemLeadByteInfo)
         {
            *UnicodeString++ = NlsOemToUnicodeTable[Char];
            continue;
         }

         if (OemString < OemEnd)
            *UnicodeString++ =
               NlsOemLeadByteInfo[OemLeadByteInfo + *(PUCHAR)OemString++];
      }

      if (ResultSize != NULL)
         *ResultSize = i * sizeof(WCHAR);
   }

   return STATUS_SUCCESS;
}



/*
 * @implemented
 */
VOID NTAPI
RtlResetRtlTranslations(IN PNLSTABLEINFO NlsTable)
{
   DPRINT("RtlResetRtlTranslations() called\n");

   /* Set ANSI data */
   NlsAnsiToUnicodeTable = (PWCHAR)NlsTable->AnsiTableInfo.MultiByteTable; /* Real type is PUSHORT */
   NlsUnicodeToAnsiTable = NlsTable->AnsiTableInfo.WideCharTable;
   NlsDbcsUnicodeToAnsiTable = (PWCHAR)NlsTable->AnsiTableInfo.WideCharTable;
   NlsMbCodePageTag = (NlsTable->AnsiTableInfo.DBCSCodePage != 0);
   NlsLeadByteInfo = NlsTable->AnsiTableInfo.DBCSOffsets;
   NlsAnsiCodePage = NlsTable->AnsiTableInfo.CodePage;
   DPRINT("Ansi codepage %hu\n", NlsAnsiCodePage);

   /* Set OEM data */
   NlsOemToUnicodeTable = (PWCHAR)NlsTable->OemTableInfo.MultiByteTable; /* Real type is PUSHORT */
   NlsUnicodeToOemTable = NlsTable->OemTableInfo.WideCharTable;
   NlsDbcsUnicodeToOemTable = (PWCHAR)NlsTable->OemTableInfo.WideCharTable;
   NlsMbOemCodePageTag = (NlsTable->OemTableInfo.DBCSCodePage != 0);
   NlsOemLeadByteInfo = NlsTable->OemTableInfo.DBCSOffsets;
   NlsOemCodePage = NlsTable->OemTableInfo.CodePage;
   DPRINT("Oem codepage %hu\n", NlsOemCodePage);

   /* Set Unicode case map data */
   NlsUnicodeUpcaseTable = NlsTable->UpperCaseTable;
   NlsUnicodeLowercaseTable = NlsTable->LowerCaseTable;

   /* set the default characters for RtlpDidUnicodeToOemWork */
   NlsOemDefaultChar = NlsTable->OemTableInfo.DefaultChar;
   NlsUnicodeDefaultChar = NlsTable->OemTableInfo.TransDefaultChar;
}



/*
 * @unimplemented
 */
NTSTATUS NTAPI
RtlUnicodeToCustomCPN(IN PCPTABLEINFO CustomCP,
                      PCHAR CustomString,
                      ULONG CustomSize,
                      PULONG ResultSize,
                      PWCHAR UnicodeString,
                      ULONG UnicodeSize)
{
   ULONG Size = 0;
   ULONG i;

   if (CustomCP->DBCSCodePage == 0)
   {
      /* single-byte code page */
      if (UnicodeSize > (CustomSize * sizeof(WCHAR)))
         Size = CustomSize;
      else
         Size = UnicodeSize / sizeof(WCHAR);

      if (ResultSize != NULL)
         *ResultSize = Size;

      for (i = 0; i < Size; i++)
      {
         *CustomString = ((PCHAR)CustomCP->WideCharTable)[*UnicodeString];
         CustomString++;
         UnicodeString++;
      }
   }
   else
   {
      /* multi-byte code page */
      /* FIXME */
      ASSERT(FALSE);
   }

   return STATUS_SUCCESS;
}



/*
 * @unimplemented
 */
NTSTATUS NTAPI
RtlUnicodeToMultiByteN (PCHAR MbString,
                        ULONG MbSize,
                        PULONG ResultSize,
                        PWCHAR UnicodeString,
                        ULONG UnicodeSize)
{
   ULONG Size = 0;
   ULONG i;

   PAGED_CODE_RTL();

   if (NlsMbCodePageTag == FALSE)
   {
      /* single-byte code page */
      Size =  (UnicodeSize > (MbSize * sizeof (WCHAR)))
                 ? MbSize
	         : (UnicodeSize / sizeof (WCHAR));

      if (ResultSize != NULL)
      {
         *ResultSize = Size;
      }

      for (i = 0; i < Size; i++)
      {
         *MbString++ = NlsUnicodeToAnsiTable[*UnicodeString++];
      }
   }
   else
   {
      /* multi-byte code page */
      /* FIXME */

      USHORT WideChar;
      USHORT MbChar;

      for (i = MbSize, Size = UnicodeSize / sizeof(WCHAR); i && Size; i--, Size--)
      {
         WideChar = *UnicodeString++;

         if (WideChar < 0x80)
         {
            *MbString++ = LOBYTE(WideChar);
            continue;
         }

         MbChar = NlsDbcsUnicodeToAnsiTable[WideChar];

         if (!HIBYTE(MbChar))
         {
            *MbString++ = LOBYTE(MbChar);
            continue;
         }

         if (i >= 2)
         {
            *MbString++ = HIBYTE(MbChar);
            *MbString++ = LOBYTE(MbChar);
            i--;
         }
         else break;
      }

      if (ResultSize != NULL)
         *ResultSize = MbSize - i;
   }

   return STATUS_SUCCESS;
}

/*
 * @implemented
 */
NTSTATUS
NTAPI
RtlUnicodeToMultiByteSize(PULONG MbSize,
                          PCWCH UnicodeString,
                          ULONG UnicodeSize)
{
    ULONG UnicodeLength = UnicodeSize / sizeof(WCHAR);
    ULONG MbLength = 0;

    if (!NlsMbCodePageTag)
    {
        /* single-byte code page */
        *MbSize = UnicodeLength;
    }
    else
    {
        /* multi-byte code page */
        /* FIXME */

        while (UnicodeLength--)
        {
            USHORT WideChar = *UnicodeString++;

            if (WideChar >= 0x80 && HIBYTE(NlsDbcsUnicodeToAnsiTable[WideChar]))
            {
                MbLength += sizeof(WCHAR);
            }
            else
            {
                MbLength++;
            }
        }

        *MbSize = MbLength;
    }

    /* Success */
    return STATUS_SUCCESS;
}

/*
 * @unimplemented
 */
NTSTATUS NTAPI
RtlUnicodeToOemN (PCHAR OemString,
                  ULONG OemSize,
                  PULONG ResultSize,
                  PCWCH UnicodeString,
                  ULONG UnicodeSize)
{
   ULONG Size = 0;
   ULONG i;

   if (NlsMbOemCodePageTag == FALSE)
   {
      /* single-byte code page */
      if (UnicodeSize > (OemSize * sizeof(WCHAR)))
         Size = OemSize;
      else
         Size = UnicodeSize / sizeof(WCHAR);

      if (ResultSize != NULL)
         *ResultSize = Size;

      for (i = 0; i < Size; i++)
      {
         *OemString = NlsUnicodeToOemTable[*UnicodeString];
         OemString++;
         UnicodeString++;
      }
   }
   else
   {
      /* multi-byte code page */
      /* FIXME */

      USHORT WideChar;
      USHORT OemChar;

      for (i = OemSize, Size = UnicodeSize / sizeof(WCHAR); i && Size; i--, Size--)
      {
         WideChar = *UnicodeString++;

         if (WideChar < 0x80)
         {
            *OemString++ = LOBYTE(WideChar);
            continue;
         }

         OemChar = NlsDbcsUnicodeToOemTable[WideChar];

         if (!HIBYTE(OemChar))
         {
            *OemString++ = LOBYTE(OemChar);
            continue;
         }

         if (i >= 2)
         {
            *OemString++ = HIBYTE(OemChar);
            *OemString++ = LOBYTE(OemChar);
            i--;
         }
         else break;
      }

      if (ResultSize != NULL)
         *ResultSize = OemSize - i;
   }

   return STATUS_SUCCESS;
}




/*
 * @implemented
 */
WCHAR NTAPI
RtlUpcaseUnicodeChar(IN WCHAR Source)
{
   USHORT Offset;

   if (Source < 'a')
      return Source;

   if (Source <= 'z')
      return (Source - ('a' - 'A'));

   Offset = ((USHORT)Source >> 8) & 0xFF;
   Offset = NlsUnicodeUpcaseTable[Offset];

   Offset += ((USHORT)Source >> 4) & 0xF;
   Offset = NlsUnicodeUpcaseTable[Offset];

   Offset += ((USHORT)Source & 0xF);
   Offset = NlsUnicodeUpcaseTable[Offset];

   return Source + (SHORT)Offset;
}



/*
 * @unimplemented
 */
NTSTATUS NTAPI
RtlUpcaseUnicodeToCustomCPN (IN PCPTABLEINFO CustomCP,
                             PCHAR CustomString,
                             ULONG CustomSize,
                             PULONG ResultSize,
                             PWCHAR UnicodeString,
                             ULONG UnicodeSize)
{
   WCHAR UpcaseChar;
   ULONG Size = 0;
   ULONG i;

   if (CustomCP->DBCSCodePage == 0)
   {
      /* single-byte code page */
      if (UnicodeSize > (CustomSize * sizeof(WCHAR)))
         Size = CustomSize;
      else
         Size = UnicodeSize / sizeof(WCHAR);

      if (ResultSize != NULL)
         *ResultSize = Size;

      for (i = 0; i < Size; i++)
      {
         UpcaseChar = RtlUpcaseUnicodeChar(*UnicodeString);
         *CustomString = ((PCHAR)CustomCP->WideCharTable)[UpcaseChar];
         CustomString++;
         UnicodeString++;
      }
   }
   else
   {
      /* multi-byte code page */
      /* FIXME */
      ASSERT(FALSE);
   }

   return STATUS_SUCCESS;
}


/*
 * @unimplemented
 */
NTSTATUS NTAPI
RtlUpcaseUnicodeToMultiByteN (PCHAR MbString,
                              ULONG MbSize,
                              PULONG ResultSize,
                              PCWCH UnicodeString,
                              ULONG UnicodeSize)
{
   WCHAR UpcaseChar;
   ULONG Size = 0;
   ULONG i;

   if (NlsMbCodePageTag == FALSE)
   {
      /* single-byte code page */
      if (UnicodeSize > (MbSize * sizeof(WCHAR)))
         Size = MbSize;
      else
         Size = UnicodeSize / sizeof(WCHAR);

      if (ResultSize != NULL)
         *ResultSize = Size;

      for (i = 0; i < Size; i++)
      {
         UpcaseChar = RtlUpcaseUnicodeChar(*UnicodeString);
         *MbString = NlsUnicodeToAnsiTable[UpcaseChar];
         MbString++;
         UnicodeString++;
      }
   }
   else
   {
      /* multi-byte code page */
      /* FIXME */
      ASSERT(FALSE);
   }

   return STATUS_SUCCESS;
}


/*
 * @unimplemented
 */
NTSTATUS NTAPI
RtlUpcaseUnicodeToOemN (PCHAR OemString,
                        ULONG OemSize,
                        PULONG ResultSize,
                        PCWCH UnicodeString,
                        ULONG UnicodeSize)
{
   WCHAR UpcaseChar;
   ULONG Size = 0;
   ULONG i;

   ASSERT(NlsUnicodeToOemTable != NULL);

   if (NlsMbOemCodePageTag == FALSE)
   {
      /* single-byte code page */
      if (UnicodeSize > (OemSize * sizeof(WCHAR)))
         Size = OemSize;
      else
         Size = UnicodeSize / sizeof(WCHAR);

      if (ResultSize != NULL)
         *ResultSize = Size;

      for (i = 0; i < Size; i++)
      {
         UpcaseChar = RtlUpcaseUnicodeChar(*UnicodeString);
         *OemString = NlsUnicodeToOemTable[UpcaseChar];
         OemString++;
         UnicodeString++;
      }
   }
   else
   {
      /* multi-byte code page */
      /* FIXME */

      USHORT WideChar;
      USHORT OemChar;

      for (i = OemSize, Size = UnicodeSize / sizeof(WCHAR); i && Size; i--, Size--)
      {
         WideChar = RtlUpcaseUnicodeChar(*UnicodeString++);

         if (WideChar < 0x80)
         {
            *OemString++ = LOBYTE(WideChar);
            continue;
         }

         OemChar = NlsDbcsUnicodeToOemTable[WideChar];

         if (!HIBYTE(OemChar))
         {
            *OemString++ = LOBYTE(OemChar);
            continue;
         }

         if (i >= 2)
         {
            *OemString++ = HIBYTE(OemChar);
            *OemString++ = LOBYTE(OemChar);
            i--;
         }
         else break;
      }

      if (ResultSize != NULL)
         *ResultSize = OemSize - i;
   }

   return STATUS_SUCCESS;
}



/*
 * @unimplemented
 */
CHAR NTAPI
RtlUpperChar (IN CHAR Source)
{
   WCHAR Unicode;
   CHAR Destination;

   /* Check for simple ANSI case */
   if (Source <= 'z')
   {
       /* Check for simple downcase a-z case */
       if (Source >= 'a')
       {
           /* Just XOR with the difference */
           return Source ^ ('a' - 'A');
       }
       else
       {
           /* Otherwise return the same char, it's already upcase */
           return Source;
       }
   }
   else
   {
        if (NlsMbCodePageTag == FALSE)
        {
            /* single-byte code page */

            /* ansi->unicode */
            Unicode = NlsAnsiToUnicodeTable[(UCHAR)Source];

            /* upcase conversion */
            Unicode = RtlUpcaseUnicodeChar (Unicode);

            /* unicode -> ansi */
            Destination = NlsUnicodeToAnsiTable[(USHORT)Unicode];
        }
        else
        {
            /* multi-byte code page */
            /* FIXME */
            Destination = Source;
        }
   }

   return Destination;
}

/* EOF */
