/* Status.c -- the status module of WizUnzip
 * Robert Heath. 1991.
 */

#include <sys\types.h>
#include <sys\stat.h>
#include <time.h>                
#include <windows.h>
#include <string.h>
#include <ctype.h>
#include <io.h>
#include <stdio.h>
#include <stdarg.h>
#include <assert.h>
#include "wizunzip.h"
#include "unzip.h"

#define STATUS_INCREMENT	512	/* incremental status data size		*/
#define MAX_H_CHARS 160			/* max horizontal chars.			*/

#define MAX_INDEX_ENTRIES 16	/* Message Window index max entries	*/

#define MAX_BUFFER_SIZE 0xffffL	/* max Message Buffer size. Must fit 
								 * within one memory segment!		*/

#define MAX_TEXTOUT_COUNT 0x7fffL /* max no. bytes TextOut() accepts */

#define STDIO_BUF_SIZE (FILNAMSIZ+LONG_FORM_FNAME_INX) /* buffer size during printf or fprintf */

static short yClient; 			/* height of client area			*/
static short yChar;				/* height of typical char.			*/
static short nVscrollPos = 0;	/* scroll position of mesg. window	*/
static short nNumLines = 0;			/* number of lines in buffer	*/
static short nVscrollMax;	/* max scroll position of mesg. window	*/
static DWORD dwStatusSize = 0L;		/* status data size 			*/
static DWORD dwBufferSize = 0L;		/* Status buffer size.  Never 
									   exceeds MAX_BUFFER_SIZE		*/
static HANDLE hGlobalStatus;		/* global mesg. handle			*/
static DWORD dwMsgWinIndex[MAX_INDEX_ENTRIES]; /* max index entries	*/
static short	nIndexEntries;			/* no. active index entries, with
									   MAX_INDEX_ENTRIES as its max. 
									   When set to 0, it's time to 
									   re-index.*/

static short nLinesPerEntry; 		/* lines per index entry	*/

/* displayed when buffer shouldn't grow or can't grow					*/
static char CLEARING_MSG[] = 	
			"Clearing Messages window to make room for more information.";


static struct KeyEntry {
	WORD	wVirtKey;
	BOOL	bCntl;
	int	iMessage;
	WORD	wRequest;
	} KeyTable[] = {
	/* vertical scroll control
	 */
	{VK_HOME,	TRUE,	WM_VSCROLL,	SB_TOP },
	{VK_END,	TRUE,	WM_VSCROLL,	SB_BOTTOM },
	{VK_PRIOR,	FALSE,	WM_VSCROLL,	SB_PAGEUP },
	{VK_NEXT,	FALSE,	WM_VSCROLL,	SB_PAGEDOWN },
	{VK_UP,		FALSE,	WM_VSCROLL,	SB_LINEUP },
	{VK_DOWN,	FALSE,	WM_VSCROLL,	SB_LINEDOWN },

	/* horizontal scroll control
	 */
	{VK_HOME,	FALSE,	WM_HSCROLL,	SB_TOP },
	{VK_END,	FALSE,	WM_HSCROLL,	SB_BOTTOM },
	{VK_PRIOR,	TRUE,	WM_HSCROLL,	SB_PAGEUP },
	{VK_NEXT,	TRUE,	WM_HSCROLL,	SB_PAGEDOWN },
	{VK_LEFT,	FALSE,	WM_HSCROLL,	SB_LINEUP },
	{VK_RIGHT,	FALSE,	WM_HSCROLL,	SB_LINEDOWN },
	} ;

#define NUMKEYS (sizeof(KeyTable)/sizeof(struct KeyEntry)) 

/* Forward Refs
 */
static void FreeStatusLog(void);

/* Globals
 */
BOOL bRealTimeMsgUpdate = TRUE; /* update messages window in real-time.
								 * Reset by callers when update can be
								 * be deferred.
								 */

/* Clears status buffer. Frees buffer.
 */
static void FreeStatusLog(void)
{
	if (hGlobalStatus)
	{
		GlobalFree(hGlobalStatus);
		hGlobalStatus = NULL;
	}
	dwStatusSize = 0L;		/* status data size 			*/
	dwBufferSize = 0L;		/* status buffer size 			*/
	nNumLines = 0;			/* number of lines in buffer	*/
	nVscrollMax = 1;
	SetScrollRange(hWndStatus, SB_VERT, 0, 1, FALSE);
	nVscrollPos = 0;
	SetScrollPos(hWndStatus, SB_VERT, nVscrollPos, TRUE);
}

/* Update Message Window Position is called after adding 
 * a number of lines to the message window without updating it.
 * The function invalidates then updates the window.
 */
void UpdateMsgWndPos(void)
{
		nVscrollPos = max(0,(nNumLines-MessageWinLines+1));	 /* set position to next to last line	*/
		SetScrollPos(hWndStatus, SB_VERT, nVscrollPos, TRUE);
		InvalidateRect(hWndStatus, NULL, TRUE);
		UpdateWindow(hWndStatus);
}

/* Add message line (or part of a line) to the global status buffer
 * that is the contents of the Message Window.
 * Assumes that global data is unlocked when called.
 */
void WriteStringToMsgWin(PSTR String, BOOL bUpdate)
{
	WriteBufferToMsgWin(String, strlen(String), bUpdate);
}

/* Add message buffer (maybe part of a line) to the global status buffer
 * that is the contents of the Message Window.
 * Assumes that global data is unlocked when called.
 */
void WriteBufferToMsgWin(LPSTR buffer, int nBufferLen, BOOL bUpdate)
{
LPSTR	lpC;				/* pointer into buffer							*/
HANDLE hGlobalStatusTmp;
LPSTR lpGlobalBuffer;			/* pointer into global buffer				*/
DWORD dwNewSize = dwStatusSize + (DWORD)nBufferLen;
int	nIncrLines = 0;				/* incremental lines in buffer			*/
int nIncompleteExistingLine = 0; /* add -1 if incomplete existing last line	*/
int nIncompleteAddedLine = 0;	/* add +1 if incomplete added last line		*/
DWORD dwRequestedSize;			/* Size needed to hold all data. Can't
								   practically exceeded MAX_BUFFER_SIZE.*/

	if (!nBufferLen)			/* if no data							*/
		return;					/* just beat it							*/

	/* count LF's in buffer	to later add to total						*/
	for (lpC = buffer; lpC != NULL && (lpC - buffer) < nBufferLen; )
	{
		/* use memchr() for speed (?) considerations					*/
		if (lpC = _fmemchr(lpC, '\n', (size_t)(nBufferLen - (lpC - buffer))))
		{
			nIncrLines++;			/* tally line found					*/
			lpC++;					/* point beyond LF for next pass 	*/
		}
	}
	if (dwNewSize > dwBufferSize)	/* if won't fit or 1st time				*/
	{
		/* Round up if necessary to nearest whole increment
		 */
		dwRequestedSize = ((dwNewSize + STATUS_INCREMENT - 1) / 
							STATUS_INCREMENT) * STATUS_INCREMENT;
		if (hGlobalStatus)	/* if buffer exists, realloc			*/
		{
				if (dwRequestedSize <= MAX_BUFFER_SIZE &&
						(hGlobalStatusTmp = GlobalReAlloc(hGlobalStatus,
									dwRequestedSize, GMEM_MOVEABLE)))
				{
					/* successful re-allocation						*/
					hGlobalStatus = hGlobalStatusTmp;
					dwBufferSize = dwRequestedSize;
				}
				else /* re-allocation failed, make last-ditch attempt! */
				{
					FreeStatusLog();		/* free own buffers		*/
                	MessageBox (hMainWnd, CLEARING_MSG,
								"Note", MB_ICONINFORMATION | MB_OK);
					WriteBufferToMsgWin(buffer, nBufferLen, bUpdate);
					return;
				}
		}
		else	/* 1st time											*/
		{
				if (hGlobalStatus = GlobalAlloc(GMEM_MOVEABLE,
									dwRequestedSize))
				{
					dwBufferSize = dwRequestedSize;	/* save it		*/
				}
				else	/* 1st allocation failed!					*/
				{
					WinAssert(hGlobalStatus);	/* DEBUG 				*/
					return;
				}
		}
	}
	/* should be easy copy of data from here							*/
	lpGlobalBuffer = GlobalLock(hGlobalStatus);
	if (lpGlobalBuffer)
	{

		/* Account for partial lines existing and being added.
		 */
		if (dwStatusSize  &&
			lpGlobalBuffer[dwStatusSize-1] != '\n')
				nIncompleteExistingLine-- ;	/* subtract 1					*/

		if (buffer[nBufferLen-1] != '\n') /* nBufferLen guaranteed >0 */
				nIncompleteAddedLine++ ;  /* add 1					*/

		/* copy data into global buffer							*/
		if (nBufferLen)	  /* map to ANSI; if 0 don't copy; 0 means 65K	*/
		{
			OemToAnsiBuff(buffer, &lpGlobalBuffer[dwStatusSize], nBufferLen);	
		}
		/* bump no. lines accounting for incomplete lines		*/
		nNumLines += (nIncrLines+nIncompleteExistingLine+nIncompleteAddedLine); 
		dwStatusSize = dwNewSize;		/* new data size counting end null	*/
		GlobalUnlock(hGlobalStatus);
		nVscrollMax = max(1, nNumLines + 2 - yClient/yChar);
		SetScrollRange(hWndStatus, SB_VERT, 0, nVscrollMax, FALSE);
		nIndexEntries = 0;	/* re-index whenever more data is added			*/
		if (bUpdate)		/* if requested to update message box			*/
		{
			nVscrollPos = max(0,(nNumLines-MessageWinLines+1));	 /* set position to next to last line	*/
			SetScrollPos(hWndStatus, SB_VERT, nVscrollPos, TRUE);
			InvalidateRect(hWndStatus, NULL, TRUE);
			UpdateWindow(hWndStatus);
		}
	}
	else
	{
		WinAssert(lpGlobalBuffer);	/* DEBUG */
	}
}



long FAR PASCAL StatusProc (HWND hWnd, unsigned iMessage, WORD wParam, LONG lParam) 
{
static short xClient ; 			/* size of client area	*/
HDC		hDC;					/* device context			*/
TEXTMETRIC	  tm;		    /* text metric structure	*/
PAINTSTRUCT ps;
#ifdef OLD_WAY
RECT	rect;
#endif
struct KeyEntry *pKE;		/* pointer to key entry		*/
LPSTR	lpStatusBuffer;			/* pointer to global mesg. buffer		*/
int		nMenuItemCount;	/* no. items in System menu	before deleting separators	*/
BOOL	bCntl;				/* control shift pressed ?				*/
static short xChar;
static short nHscrollMax;
static short nHscrollPos;
static short nMaxWidth;		/* in pixels								*/
	   	short nVscrollInc;
		short nHscrollInc;
short i, x, y, nPaintBeg, nPaintEnd;
HMENU	hSysMenu;			/* this guy's system menu					*/

	switch (iMessage) {
	case WM_CREATE:
#ifdef NEEDME
		wStatusItem = LOWORD((LONG)(((LPCREATESTRUCT)lParam)->lpCreateParams));
#endif
		hDC = GetDC(hWnd);	/* get device context */
  		hOldFont   = SelectObject ( hDC, hFixedFont);
		GetTextMetrics(hDC, &tm);
		ReleaseDC(hWnd, hDC);
		xChar = tm.tmAveCharWidth;
		yChar = tm.tmHeight + tm.tmExternalLeading; 
		nMaxWidth = MAX_H_CHARS * xChar;
		nVscrollPos = 0;					/* reset position to 0	*/
		nVscrollMax = max(1,nNumLines);
		SetScrollRange(hWnd, SB_VERT, 0, nVscrollMax, FALSE);
		SetScrollPos(hWnd, SB_VERT, 0, TRUE);
        hSysMenu = GetSystemMenu(hWnd, FALSE);
        DeleteMenu(hSysMenu, SC_SIZE, MF_BYCOMMAND); /* delete menu item */
        DeleteMenu(hSysMenu, SC_MOVE, MF_BYCOMMAND); /* delete menu item */
        DeleteMenu(hSysMenu, SC_CLOSE, MF_BYCOMMAND); /* delete menu item */
        DeleteMenu(hSysMenu, SC_TASKLIST, MF_BYCOMMAND); /* delete menu item */
		/* walk thru menu and delete all separator bars
		 */
	  	for (nMenuItemCount = GetMenuItemCount(hMenu);
			nMenuItemCount ; nMenuItemCount--)
	  	{
			if (GetMenuState(hSysMenu, nMenuItemCount-1, MF_BYPOSITION) & MF_SEPARATOR)
			{
				DeleteMenu(hSysMenu, nMenuItemCount-1, MF_BYPOSITION);
			}
		}
		return 0;

	case WM_SIZE:
		xClient = LOWORD(lParam);/* x size of client area */
		yClient = HIWORD(lParam);/* y size of client area */

		nVscrollMax = max(1, nNumLines + 2 - yClient/yChar);
		nVscrollPos = min(nVscrollPos, nVscrollMax);

		SetScrollRange(hWnd, SB_VERT, 0, nVscrollMax, FALSE);
		SetScrollPos(hWnd, SB_VERT, nVscrollPos, TRUE);

		nHscrollMax = max(0, 2 + (nMaxWidth - xClient) / xChar);
		nHscrollPos = min(nHscrollPos, nHscrollMax);

		SetScrollRange(hWnd, SB_HORZ, 0, nHscrollMax, FALSE);
		SetScrollPos(hWnd, SB_HORZ, nHscrollPos, TRUE);

		return 0;

	case WM_SYSCOMMAND:
		switch ((wParam & 0xFFF0)) { /* decode parameter type	*/
		case SC_RESTORE:	/* alert parent			*/
				PostMessage(hMainWnd, WM_COMMAND, 
					IDM_RESTORE_STATUS, 0L);
			break;
		case SC_MAXIMIZE:
				PostMessage(hMainWnd, WM_COMMAND, 					 
						IDM_MAX_STATUS, 0L);
			break;
		default:
	    	return (DefWindowProc(hWnd, iMessage, wParam, lParam));
		}
		break;
	case WM_COMMAND:
		switch (wParam) { /* decode parameter type	*/
		case IDM_CLEAR_STATUS:
			FreeStatusLog();		/* free log contents		*/
			InvalidateRect(hWndStatus, NULL, TRUE);
			break;
		default:
			break;
		}
		break;
	case WM_VSCROLL:	/* scroll bar action on list box */
		switch (wParam) { /* decode parameter type	*/
		case SB_TOP:
			nVscrollInc = -nVscrollPos;
			break;
		case SB_BOTTOM:
			nVscrollInc = nVscrollMax - nVscrollPos;
			break;
		case SB_LINEUP:
			nVscrollInc = -1;
			break;
		case SB_LINEDOWN:
			nVscrollInc = 1;
			break;
		case SB_PAGEUP:
			nVscrollInc = min(-1, -yClient/yChar);
			break;
		case SB_PAGEDOWN:
			nVscrollInc = max(1, yClient/yChar);
			break;
		case SB_THUMBPOSITION:
			nVscrollInc = LOWORD(lParam) - nVscrollPos;
			break;
		default:	/* END_SCROLL comes thru here				*/
			nVscrollInc = 0;
		} /* bottom of scroll-decoding switch	*/

		if (nVscrollInc = max(-nVscrollPos,
								min(nVscrollInc, nVscrollMax - nVscrollPos)))
		{
			nVscrollPos += nVscrollInc;
			ScrollWindow(hWnd, 0, -yChar * nVscrollInc, NULL, NULL);
			SetScrollPos(hWnd, SB_VERT, nVscrollPos, TRUE);
			UpdateWindow(hWnd);
		}
		return 0;

	case WM_HSCROLL:	/* scroll bar action on list box */
		switch (wParam) { /* decode parameter type	*/
		case SB_TOP:
			nHscrollInc = -nHscrollPos;
			break;
		case SB_BOTTOM:
			nHscrollInc = nHscrollMax - nHscrollPos;
			break;
		case SB_LINEUP:
			nHscrollInc = -1;
			break;
		case SB_LINEDOWN:
			nHscrollInc = 1;
			break;
		case SB_PAGEUP:
			nHscrollInc = -8;
			break;
		case SB_PAGEDOWN:
			nHscrollInc = 8;
			break;
		case SB_THUMBPOSITION:
			nHscrollInc = LOWORD(lParam) - nHscrollPos;
			break;
		default:
#ifdef CATCHING_END_SCROLL
			nHscrollInc = 0;
#else
	    	return (DefWindowProc(hWnd, iMessage, wParam, lParam));
#endif
		} /* bottom of scroll-decoding switch	*/

		if (nHscrollInc = max(-nHscrollPos,
								min(nHscrollInc, nHscrollMax - nHscrollPos)))
		{
			nHscrollPos += nHscrollInc;
			ScrollWindow(hWnd, -xChar * nHscrollInc, 0, NULL, NULL);
			SetScrollPos(hWnd, SB_HORZ, nHscrollPos, TRUE);
		}
		return 0;

	case WM_KEYDOWN:
		bCntl = (BOOL)(GetKeyState(VK_CONTROL) < 0 ? TRUE : FALSE);
		for (i = 0, pKE = KeyTable; i < NUMKEYS; i++, pKE++)
		{
			if (wParam == pKE->wVirtKey && bCntl == pKE->bCntl)
			{
				SendMessage(hWnd, pKE->iMessage, pKE->wRequest, 0L);
				break;
			}
		}
		break;
	case WM_PAINT:
		if (!hGlobalStatus) 		/* if nothing to paint			*/
		{
	    	return (DefWindowProc(hWnd, iMessage, wParam, lParam)); /* exit */
		}
		{
		REGISTER LPSTR lpC;			/* current char					*/
				LPSTR lpCtemp;		/* address of next '\n' in buffer */
				LPSTR lpStartC; 	/* paint starting character		*/
				LPSTR lpBegLine;	/* beginning of current line	*/
				HANDLE hNewHandle;
				DWORD	dwLineLen;	/* length of current line		*/
				short  nLinesSinceLastEntry; /* lines since last entry */
				DWORD  dwSearchLen;	/* length for _fmemchr() to search */

			lpStartC = NULL; /* paint starting character		*/
			lpStatusBuffer = GlobalLock(hGlobalStatus);
			WinAssert(lpStatusBuffer);	/* DEBUG */
			hDC = BeginPaint(hWnd, &ps);
			WinAssert(hDC);				/* DEBUG */
  			hNewHandle = SelectObject ( hDC, hFixedFont);
			WinAssert(hNewHandle);	/* DEBUG */
			nPaintBeg = max(0, nVscrollPos+ps.rcPaint.top/yChar -1);
			nPaintEnd = min(nNumLines, nVscrollPos + ps.rcPaint.bottom/yChar);
			if (nPaintBeg >= nPaintEnd)	/* if no painting to do ...		*/
			{
				EndPaint(hWnd, &ps);	/* just exit					*/
				GlobalUnlock(hGlobalStatus);	/* unlock memory		*/
				return 0;
			}
			if (!nIndexEntries)	/* re-index whenever more data is added	*/
			{
				/* Round up to make lines_per_entry encompass all
				 * possible lines in buffer.
				 */
				nLinesPerEntry = (nNumLines+MAX_INDEX_ENTRIES-1) / MAX_INDEX_ENTRIES; 
				if (!nLinesPerEntry)	/* if zero					*/ 
					nLinesPerEntry++;	/* set to 1 as minimum		*/ 

				/* Count lines from beginning of buffer to:
				 * 1) mark beginning of paint sequence (lpStartC) and
 				 * 2) periodically save buffer index in MsgWinIndex[] table.
				 */
				for (lpC = lpStatusBuffer, i = 0, nLinesSinceLastEntry = 0;
					 (DWORD)(lpC - lpStatusBuffer) < dwStatusSize ; i++)
				{
					/* We are at the 1st character position in the line  */
					if (i == nPaintBeg) /* Starting point for paint ? */
						lpStartC = lpC;	/* If so, mark starting point */

					/* Entry time ? */
					if (!nLinesSinceLastEntry++ && 
					 	nIndexEntries < MAX_INDEX_ENTRIES)
					{
						dwMsgWinIndex[nIndexEntries] = 
								(DWORD)(lpC - lpStatusBuffer); /* save index */
						nIndexEntries++;
					}
					if (nLinesSinceLastEntry >= nLinesPerEntry)
						nLinesSinceLastEntry = 0;

					/* Use _fmemchr() to find next LF.  
					 * It's probably optimized for searches.
					 */
					dwSearchLen = dwStatusSize - 
										(DWORD)(lpC - lpStatusBuffer);
					if ((lpCtemp = _fmemchr(lpC, '\n', (size_t)dwSearchLen)))
						lpC = ++lpCtemp;	/* use next char as beg of line */
 
					else /* else use lpC with incremented value			*/
						lpC += dwSearchLen;

				} /* bottom of still-counting-lines loop				*/
				WinAssert(lpStartC);	/* DEBUG 							*/
				lpC = lpStartC;		/* restore starting point			*/
				WinAssert((DWORD)lpC >= (DWORD)lpStatusBuffer &&	/* DEBUG */
				   (DWORD)lpC < (DWORD)&lpStatusBuffer[dwStatusSize]); 

			} 	/* bottom of need-to-build-index block					*/
			else	/* index is still valid								*/
			{
			short nIndexEntry; 					/* work backwards!		*/

				/* Find index of line number which is equal to or just
				 * below the starting line to paint. Work backwards
				 * thru the table. Here, "i" is the line no. corresponding
				 * to the current table index.
				 */
				for (nIndexEntry = nIndexEntries - 1,
					 i = nIndexEntry * nLinesPerEntry; 
						nIndexEntry >= 0 && 
						nPaintBeg < i ;
						nIndexEntry--, i -= nLinesPerEntry )
					;

				WinAssert(nIndexEntry >= 0);	/* DEBUG */
				WinAssert(i <= nPaintBeg);		/* DEBUG */
				/* OK, we've got a head start on the search.
				 * Start checking characters from the position found
				 * in the index table.
				 */
				for (lpC = &lpStatusBuffer[dwMsgWinIndex[nIndexEntry]];
					 i < nPaintBeg && 
#ifndef NEW_WAY
						(DWORD)(lpC - lpStatusBuffer) < dwStatusSize;
#else
						((DWORD)lpC - (DWORD)lpStatusBuffer) < dwStatusSize;
#endif
					 i++)
				{
					/* Find length of current line.  Use _fmemchr() to 
					 * find next LF.  
					 */
					dwSearchLen = dwStatusSize - 
									(DWORD)(lpC - lpStatusBuffer);
					if ((lpCtemp = _fmemchr(lpC, '\n', (size_t)dwSearchLen)) != NULL)
						lpC = ++lpCtemp; /* point to next char. past '\n'	*/

					else /* If search fails, pretend LF exists, go past it.	*/
						lpC += dwSearchLen;
 
				}
			} /* bottom of index-is-still-valid block.					*/

			/* At this point we've got the buffer address, lpC, for the 1st
			 * line at which we begin painting, nPaintBeg.
			 */
			for (i = nPaintBeg; 
				 i < nPaintEnd && 
#ifndef NEW_WAY
				 (DWORD)(lpC  - lpStatusBuffer) >=  0L &&	
				 (DWORD)(lpC  - lpStatusBuffer) < dwStatusSize ; 
#else
				 ((DWORD)lpC  - (DWORD)lpStatusBuffer) >=  0L &&	
				 ((DWORD)lpC  - (DWORD)lpStatusBuffer) < dwStatusSize ; 
#endif
				 i++)
			{
				lpBegLine = lpC;
				/* Find length of current line. Use _fmemchr() to find next LF.
				 */
				dwSearchLen = dwStatusSize - (DWORD)(lpC - lpStatusBuffer);
				if ((lpCtemp = _fmemchr(lpC, '\n', (size_t)dwSearchLen)) == NULL)
				{
					/* If search fails, pretend we found LF, we won't 
					 * display it anyway.
					 */
					lpCtemp  = lpC + dwSearchLen;
				}
				WinAssert((DWORD)lpCtemp >= (DWORD)lpBegLine);	/* should be non-negative	*/
				WinAssert((DWORD)lpCtemp >= (DWORD)lpStatusBuffer &&	/* DEBUG */
				   (DWORD)lpCtemp <= (DWORD)&lpStatusBuffer[dwStatusSize]); 

				x = xChar * (1 - nHscrollPos);
				y = yChar * (1 - nVscrollPos + i);
				dwLineLen = (DWORD)(lpCtemp - lpBegLine);/* calc length*/
				if (dwLineLen && lpBegLine[dwLineLen-1] == '\r')
					dwLineLen--; 	/* don't display '\r'	*/

				/* may be displaying long lines if binary file		*/
				if (dwLineLen > MAX_TEXTOUT_COUNT) 
					dwLineLen = MAX_TEXTOUT_COUNT;

				TabbedTextOut(hDC, x, y, lpBegLine, (int)dwLineLen, 0, NULL, 0);
				lpC = ++lpCtemp; /* point to next char. past '\n'		*/
			}
			EndPaint(hWnd, &ps);
			GlobalUnlock(hGlobalStatus);	/* unlock memory		*/
			return 0;
		}
	    return (DefWindowProc(hWnd, iMessage, wParam, lParam));
		break;
	case WM_CLOSE:			  /* message: close the window    */
		DestroyWindow(hWnd); /* close the mesg. window	*/
	    	break;
	case WM_DESTROY:
		FreeStatusLog();
		break;
	default:
	    return (DefWindowProc(hWnd, iMessage, wParam, lParam));
	}
	return 0L;
}

/* Printf buffers the current output and counts the number of lines
 * within it.  It makes sure there is enough space in the global
 * buffer, then copies the buffered data to the global buffer.
 * It then triggers a repaint of the status buffer.
 */
int _FAR_ _cdecl printf(const char _FAR_ *format, ...)
{
va_list argptr;
int	LinesInBuffer = 0;
HANDLE hMemory;
PSTR psBuffer;

	va_start(argptr, format);
    hMemory = LocalAlloc(LMEM_MOVEABLE, STDIO_BUF_SIZE);
	WinAssert(hMemory);
	if (!hMemory)
	{
		return 0;
	}
    psBuffer = (PSTR)LocalLock(hMemory);
	WinAssert(psBuffer);	/* DEBUG */
	vsprintf(psBuffer, format, argptr);
	va_end(argptr);	
	WinAssert(strlen(psBuffer) < STDIO_BUF_SIZE);	/* raise STDIO_BUF_SIZE ?	*/
	WriteStringToMsgWin(psBuffer, bRealTimeMsgUpdate);
  	LocalUnlock(hMemory);                 /* unlock it to Windows */
  	LocalFree(hMemory);                 /* Returns it to Windows */
	return 0;
}

/* fprintf clone for code in unzip.c, etc. 
 */
int _FAR_ _cdecl fprintf(FILE _FAR_ *file, const char _FAR_ *format, ...)
{
va_list argptr;
int	LinesInBuffer = 0;
HANDLE hMemory;
PSTR psBuffer;

	va_start(argptr, format);
    hMemory = LocalAlloc(LMEM_MOVEABLE, STDIO_BUF_SIZE);
	WinAssert(hMemory);
	if (!hMemory)
	{
		return 0;
	}
    psBuffer = (PSTR)LocalLock(hMemory);
	WinAssert(psBuffer);	/* DEBUG */
	vsprintf(psBuffer, format, argptr);
	va_end(argptr);	
	WinAssert(strlen(psBuffer) < STDIO_BUF_SIZE);	/* raise STDIO_BUF_SIZE ?	*/
	WriteStringToMsgWin(psBuffer, bRealTimeMsgUpdate);
  	LocalUnlock(hMemory);                 /* unlock it to Windows */
  	LocalFree(hMemory);                 /* Returns it to Windows */
}

void _FAR_ _cdecl perror(const char _FAR_ *parm1)
{
	printf(parm1);
}
