/*++

Copyright (c) 1993-1995  Microsoft Corporation

Module Name:

   nwconv.c

Abstract:


Author:

    Arthur Hanson (arth) 27-Jul-1994

Revision History:

--*/


#include "globals.h"

LPTSTR alpsz[TOTAL_STRINGS];       // String resource array cache.
static UINT cswitch = 0;
static HCURSOR hCursor;


/////////////////////////////////////////////////////////////////////////
LPTSTR 
Lids(
   WORD idsStr
   )

/*++

Routine Description:

    Returns the requested string from the string table.  Caches the
    strings in an internal buffer.  Will return a NULL string if  the
    string can't be loaded.

Arguments:


Return Value:


--*/

{
   WORD idsString;
    static TCHAR  szEmpty[] = TEXT("");
    TCHAR Buffer[MAX_STRING_SIZE];

    WORD  nLen;
    LPTSTR lpsz;

   idsString = idsStr - IDS_STRINGBASE;
    if ((idsString == 0) ||( idsString > TOTAL_STRINGS))
        return(szEmpty);

   // -1 index as table is 0 based and 0 is not a valid string ID.
    if (alpsz[idsString-1])
        return((LPTSTR)alpsz[idsString-1]);

    if (!(nLen = (WORD) LoadString(hInst, idsStr, (LPTSTR) Buffer, MAX_STRING_SIZE)))
        return(szEmpty);

    if (!(lpsz = AllocMemory((nLen+1) * sizeof(TCHAR))))
        return(szEmpty);

    lstrcpy((LPTSTR)lpsz, (LPTSTR) Buffer);

    return (alpsz[idsString-1] = lpsz);
} // Lids


/////////////////////////////////////////////////////////////////////////
VOID 
StringTableDestroy()

/*++

Routine Description:

   Frees up all the memory allocated in the string table.

Arguments:


Return Value:


--*/

{
    int i;

    for (i=0; i < TOTAL_STRINGS ; i++ ) {
        if (alpsz[i]) {
            FreeMemory(alpsz[i]);
            alpsz[i]=NULL;
        }
    }
} // StringTableDestroy


/////////////////////////////////////////////////////////////////////////
VOID
CursorHourGlass()

/*++

Routine Description:


Arguments:


Return Value:


--*/

{
   if (!cswitch) {
      hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
      ShowCursor(TRUE);
   }

   cswitch++;

} // CursorHourGlass


/////////////////////////////////////////////////////////////////////////
VOID
CursorNormal()

/*++

Routine Description:


Arguments:


Return Value:


--*/

{

   if (cswitch == 0)
      return;

   cswitch--;

   if (!cswitch) {
      ShowCursor(FALSE);
      SetCursor(hCursor);
   }

} // Cursor Normal


/////////////////////////////////////////////////////////////////////////
BOOL
BitTest(
   int Bit, 
   BYTE *Bits
   )

/*++

Routine Description:


Arguments:


Return Value:


--*/

{
   int i, j;

   i = Bit / 8;
   j = Bit % 8;

   if ((Bits[i] >> j) & 0x01)
      return TRUE;
   else
      return FALSE;

} // BitTest


/////////////////////////////////////////////////////////////////////////
BOOL 
CenterWindow( 
   HWND hwndChild, 
   HWND hwndParent
   )

/*++

Routine Description:


Arguments:


Return Value:


--*/

{
   RECT    rChild, rParent;
   int     wChild, hChild, wParent, hParent;
   int     wScreen, hScreen, xNew, yNew;
   HDC     hdc;

   // Get the Height and Width of the child window
   GetWindowRect (hwndChild, &rChild);
   wChild = rChild.right - rChild.left;
   hChild = rChild.bottom - rChild.top;

   // Get the Height and Width of the parent window
   GetWindowRect (hwndParent, &rParent);
   wParent = rParent.right - rParent.left;
   hParent = rParent.bottom - rParent.top;

   // Get the display limits
   hdc = GetDC (hwndChild);
   wScreen = GetDeviceCaps (hdc, HORZRES);
   hScreen = GetDeviceCaps (hdc, VERTRES);
   ReleaseDC (hwndChild, hdc);

   // Calculate new X position, then adjust for screen
   xNew = rParent.left + ((wParent - wChild) /2);
   if (xNew < 0)
      xNew = 0;
   else if ((xNew+wChild) > wScreen)
      xNew = wScreen - wChild;

   // Calculate new Y position, then adjust for screen
   yNew = rParent.top  + ((hParent - hChild) /2);
   if (yNew < 0)
      yNew = 0;
   else if ((yNew+hChild) > hScreen)
      yNew = hScreen - hChild;

   // Set it, and return
   return SetWindowPos (hwndChild, NULL, xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);

} // CenterWindow


/////////////////////////////////////////////////////////////////////////
TCHAR *
lstrchr(
   LPTSTR String, 
   TCHAR c
   )

/*++

Routine Description:


Arguments:


Return Value:


--*/

{
   TCHAR *ptrChar = String;
   BOOL Found = FALSE;

   while(*ptrChar && !Found) {
      if (*ptrChar == c)
         Found = TRUE;
      else
         ptrChar++;
   }

   if (Found)
      return ptrChar;
   else
      return NULL;

} // lstrchr


/////////////////////////////////////////////////////////////////////////
BOOL
IsPath(
   LPTSTR Path
   )

/*++

Routine Description:


Arguments:


Return Value:


--*/

{
   ULONG len;
   LPTSTR ptr;

   len = lstrlen(Path);
   if (len < 2)   // must have a slash and character
      return FALSE;

   // now know path is at least 2 characters long
   ptr = Path;

   // if slash anywhere then it has to be a path
   while (*ptr)
      if (*ptr == TEXT('\\'))
         return TRUE;
      else
         ptr++;

   // no slash - unless this is a drive then it aint no path.
   if (Path[1] == TEXT(':'))
      return TRUE;

   return FALSE;

} // IsPath


/////////////////////////////////////////////////////////////////////////
LPTSTR 
lToStr(
   ULONG Number
   )

/*++

Routine Description:


Arguments:


Return Value:


--*/

{
   static TCHAR String[15];
   TCHAR tString[15];
   LPTSTR sptr, dptr;
   ULONG Count;

   sptr = String;
   dptr = tString;
   wsprintf(tString, TEXT("%lu"), Number);
   Count = lstrlen(tString);

   *sptr++ = *dptr++;
   Count--;

   while (*dptr) {
      if (!(Count % 3))
         *sptr++ = TEXT(',');

      *sptr++ = *dptr++;
      Count--;
   }
   *sptr = TEXT('\0');

   return String;
} // lToStr;


/////////////////////////////////////////////////////////////////////////
LPTSTR 
TimeToStr(
   ULONG TotTime
   )

/*++

Routine Description:


Arguments:


Return Value:


--*/

{
   static TCHAR String[10];
   ULONG Hours, Minutes, Seconds;

   Hours = TotTime / 3600;
   TotTime -= (Hours * 3600);
   Minutes = TotTime / 60;
   Seconds = TotTime - (Minutes * 60);

   wsprintf(String, TEXT("%3lu:%2lu:%2lu"), Hours, Minutes, Seconds);

   return String;
} // TimeToStr;


/////////////////////////////////////////////////////////////////////////
LPTSTR 
DateToStr(
   ULONG TotTime
   )

/*++

Routine Description:


Arguments:


Return Value:


--*/

{
   static TCHAR String[10];
   ULONG Hours, Minutes, Seconds;

   Hours = TotTime / 3600;
   TotTime -= (Hours * 3600);
   Minutes = TotTime / 60;
   Seconds = TotTime - (Minutes * 60);

   wsprintf(String, TEXT("%3lu:%2lu:%2lu"), Hours, Minutes, Seconds);

   return String;
} // DateToStr;


/*+-----------------------------------------------------------------------+
  |   Took the _splitpath and _makepath routines and converted them to    |
  |   be NT (long file name) and Unicode friendly.                        |
  +-----------------------------------------------------------------------+*/


/////////////////////////////////////////////////////////////////////////
VOID
lsplitpath(
   const TCHAR *path, 
   TCHAR *drive, 
   TCHAR *dir, 
   TCHAR *fname, 
   TCHAR *ext
   )

/*++

Routine Description:

   Splits a path name into its individual components

Arguments:

   path  - pointer to path name to be parsed
   drive - pointer to buffer for drive component, if any
   dir   - pointer to buffer for subdirectory component, if any
   fname - pointer to buffer for file base name component, if any
   ext   - pointer to buffer for file name extension component, if any

Return Value:

   drive - pointer to drive string.  Includes ':' if a drive was given.
   dir   - pointer to subdirectory string.  Includes leading and
           trailing '/' or '\', if any.
   fname - pointer to file base name
   ext   - pointer to file extension, if any.  Includes leading '.'.

--*/

{
    TCHAR *p;
    TCHAR *last_slash = NULL, *dot = NULL;
    unsigned len;

    // init these so we don't exit with bogus values
    drive[0] = TEXT('\0');
    dir[0] = TEXT('\0');
    fname[0] = TEXT('\0');
    ext[0] = TEXT('\0');

    if (path[0] == TEXT('\0'))
      return;

    /*+---------------------------------------------------------------------+
      | Assume that the path argument has the following form, where any or  |
      | all of the components may be missing.                               |
      |                                                                     |
      |  <drive><dir><fname><ext>                                           |
      |                                                                     |
      |  drive:                                                             |
      |     0 to MAX_DRIVE-1 characters, the last of which, if any, is a    |
      |     ':' or a '\' in the case of a UNC path.                         |
      |  dir:                                                               |
      |     0 to _MAX_DIR-1 characters in the form of an absolute path      |
      |     (leading '/' or '\') or relative path, the last of which, if    |
      |     any, must be a '/' or '\'.  E.g -                               |
      |                                                                     |
      |     absolute path:                                                  |
      |        \top\next\last\     ; or                                     |
      |        /top/next/last/                                              |
      |     relative path:                                                  |
      |        top\next\last\  ; or                                         |
      |        top/next/last/                                               |
      |     Mixed use of '/' and '\' within a path is also tolerated        |
      |  fname:                                                             |
      |     0 to _MAX_FNAME-1 characters not including the '.' character    |
      |  ext:                                                               |
      |     0 to _MAX_EXT-1 characters where, if any, the first must be a   |
      |     '.'                                                             |
      +---------------------------------------------------------------------+*/

    // extract drive letter and :, if any
    if ( path[0] && (path[1] == TEXT(':')) ) {
        if (drive) {
            drive[0] = path[0];
            drive[1] = path[1];
            drive[2] = TEXT('\0');
        }
        path += 2;
    }

    // if no drive then check for UNC pathname
    if (drive[0] == TEXT('\0'))
      if ((path[0] == TEXT('\\')) && (path[1] == TEXT('\\'))) {
         // got a UNC path so put server-sharename into drive
         drive[0] = path[0];
         drive[1] = path[1];
         path += 2;

         p = &drive[2];
         while ((*path != TEXT('\0')) && (*path != TEXT('\\')))
            *p++ = *path++;

         if (*path == TEXT('\0'))
            return;

         // now sitting at the share - copy this as well (copy slash first)
         *p++ = *path++;
         while ((*path != TEXT('\0')) && (*path != TEXT('\\')))
            *p++ = *path++;

         // tack on terminating NULL
         *p = TEXT('\0');
      }

    /*+---------------------------------------------------------------------+
      | extract path string, if any.  Path now points to the first character|
      | of the path, if any, or the filename or extension, if no path was   |
      | specified.  Scan ahead for the last occurence, if any, of a '/' or  |
      | '\' path separator character.  If none is found, there is no path.  |
      | We will also note the last '.' character found, if any, to aid in   |
      | handling the extension.                                             |
      +---------------------------------------------------------------------+*/

    for (last_slash = NULL, p = (TCHAR *)path; *p; p++) {
        if (*p == TEXT('/') || *p == TEXT('\\'))
            // point to one beyond for later copy
            last_slash = p + 1;
        else if (*p == TEXT('.'))
            dot = p;
    }

    if (last_slash) {

        // found a path - copy up through last_slash or max. characters allowed,
        //  whichever is smaller
        if (dir) {
            len = (USHORT) __min((last_slash - path), (_MAX_DIR - 1));
            lstrcpyn(dir, path, len + 1);
            dir[len] = TEXT('\0');
        }
        path = last_slash;
    }

    /*+---------------------------------------------------------------------+
      | extract file name and extension, if any.  Path now points to the    |
      | first character of the file name, if any, or the extension if no    |
      | file name was given.  Dot points to the '.' beginning the extension,|
      | if any.                                                             |
      +---------------------------------------------------------------------+*/

    if (dot && (dot >= path)) {
        // found the marker for an extension - copy the file name up to the
        //  '.'.
        if (fname) {
            len = (USHORT) __min((dot - path), (_MAX_FNAME - 1));
            lstrcpyn(fname, path, len + 1);
            *(fname + len) = TEXT('\0');
        }

        // now we can get the extension - remember that p still points to the
        // terminating nul character of path.
        if (ext) {
            len = (USHORT) __min((p - dot), (_MAX_EXT - 1));
            lstrcpyn(ext, dot, len + 1);
            ext[len] = TEXT('\0');
        }
    }
    else {
        // found no extension, give empty extension and copy rest of string
        // into fname.
        if (fname) {
            len = (USHORT) __min((p - path), (_MAX_FNAME - 1));
            lstrcpyn(fname, path, len + 1);
            fname[len] = TEXT('\0');
        }
        if (ext) {
            *ext = TEXT('\0');
        }
    }

} // lsplitpath


/////////////////////////////////////////////////////////////////////////
VOID
lmakepath(
   TCHAR *path, 
   const TCHAR *drive, 
   const TCHAR *dir, 
   const TCHAR *fname, 
   const TCHAR *ext
   )

/*++

Routine Description:

   create a path name from its individual components.

Arguments:

   char *path - pointer to buffer for constructed path
   char *drive - pointer to drive component, may or may not contain
       trailing ':'
   char *dir - pointer to subdirectory component, may or may not include
       leading and/or trailing '/' or '\' characters
   char *fname - pointer to file base name component
   char *ext - pointer to extension component, may or may not contain
       a leading '.'.

Return Value:

   path - pointer to constructed path name

--*/

{
    const TCHAR *p;

    /*+---------------------------------------------------------------------+
      | we assume that the arguments are in the following form (although we |
      | do not diagnose invalid arguments or illegal filenames (such as     |
      | names longer than 8.3 or with illegal characters in them)           |
      |                                                                     |
      |  drive:                                                             |
      |     A  or  A:                                                       |
      |  dir:                                                               |
      |     \top\next\last\     ; or                                        |
      |     /top/next/last/     ; or                                        |
      |                                                                     |
      |     either of the above forms with either/both the leading and      |
      |     trailing / or \ removed.  Mixed use of '/' and '\' is also      |
      |      tolerated                                                      |
      |  fname:                                                             |
      |     any valid file name                                             |
      |  ext:                                                               |
      |     any valid extension (none if empty or null )                    |
      +---------------------------------------------------------------------+*/

    // copy drive
    if (drive && *drive)
        while (*drive)
           *path++ = *drive++;

    // copy dir
    if ((p = dir) && *p) {
        do {
            *path++ = *p++;
        }
        while (*p);
        if ((*(p-1) != TEXT('/')) && (*(p-1) != TEXT('\\'))) {
            *path++ = TEXT('\\');
        }
    }

    // copy fname
    if (p = fname) {
        while (*p) {
            *path++ = *p++;
        }
    }

    // copy ext, including 0-terminator - check to see if a '.' needs to be
    // inserted.
    if (p = ext) {
        if (*p && *p != TEXT('.')) {
            *path++ = TEXT('.');
        }
        while (*path++ = *p++)
            ;
    }
    else {
        // better add the 0-terminator
        *path = TEXT('\0');
    }

} // lmakepath


#ifndef _UNICODE
#error "Function below not DBCS safe"
#endif

VOID
EscapeFormattingChars(
    LPTSTR String,
    ULONG BufferLength
    )

/*++

Routine Description:

    Escapes any formatting chars (ie. % chars) in the string so 
    if you sprintf it, you dont trap out as a result of trying to
    access bogus stack data.

Arguments:

    String - String to fix up. Escaping is done IN PLACE.
    BufferLength - Size of the buffer the string is in. We need to know
        this since we are inserting characters. BufferLength is in 
        characters, not bytes.

Return Value:

    None

--*/
{


    ULONG Length; LONG Avail ;
    LPTSTR End, Tmp = String ;

    if (!Tmp)
        return ;

    Length = lstrlen(String) ;

    //
    // Point past end of string. We use this to figure out
    // via pointer substraction how much needs to be shifted
    // down as we insert chars.
    //
    End = Tmp + Length + 1 ;     

    //
    // How much is avail for escape chars
    //
    Avail = BufferLength - (Length+1) ;  

    while (*Tmp) {

        if (*Tmp == TEXT('%')) {

            //
            // If no more space, just change to '_'.
            //
            if (Avail <= 0) {

                *Tmp = TEXT('_') ;
            }
            else {

                //
                // Move string over and add escape character.
                // This is not very efficient but we assume
                // that this is not common.
                //
                --Avail ;
                memmove(Tmp+1,
                        Tmp,
                        (UINT) (End - Tmp)) ;
                *Tmp = TEXT('%') ;
                Tmp++ ;
            }


        }

        ++Tmp ;
    }
}