/* Status.c -- the status module of WizUnzip * Robert Heath. 1991. */ #include #include #include #include #include #include #include #include #include #include #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); }