home *** CD-ROM | disk | FTP | other *** search
/ CD Actual 14 / CDACTUAL.iso / cdactual / demobin / share / program / c / WUNZ13SR.ZIP / STATUS.C < prev    next >
Encoding:
C/C++ Source or Header  |  1992-07-02  |  24.2 KB  |  637 lines

  1. /* Status.c -- the status module of WizUnzip
  2.  * Robert Heath. 1991.
  3.  */
  4.  
  5. #include <sys\types.h>
  6. #include <sys\stat.h>
  7. #include <time.h>                
  8. #include <string.h>
  9. #include <ctype.h>
  10. #include <io.h>
  11. #include <stdio.h>
  12. #include <stdarg.h>
  13. #include "wizunzip.h"
  14. #include "unzip.h"
  15.  
  16. #define STATUS_INCREMENT    512 /* incremental status data size     */
  17. #define MAX_H_CHARS 160         /* max horizontal chars.            */
  18.  
  19. #define MAX_INDEX_ENTRIES 16    /* Message Window index max entries */
  20.  
  21. #define cchBufferMax 0xffffL    /* max Message Buffer size. Must fit 
  22.                                  * within one memory segment!       */
  23.  
  24. #define cchTextOutMax 0x7fffL /* max no. bytes TextOut() accepts */
  25.  
  26. #define STDIO_BUF_SIZE (FILNAMSIZ+LONG_FORM_FNAME_INX) /* buffer size during printf or fprintf */
  27.  
  28. static short yClient;               /* height of client area */
  29. static short nVscrollPos = 0;       /* scroll position of mesg. window  */
  30. static short nNumLines = 0;         /* number of lines in buffer */
  31. static short nVscrollMax;           /* max scroll position of mesg. window  */
  32. static DWORD dwStatusSize = 0L;     /* status data size */
  33. static DWORD dwBufferSize = 0L;     /* Status buffer size.  Never 
  34.                                        exceeds cchBufferMax     */
  35. static HANDLE hStatusBuffer;        /* global mesg. handle  */
  36. static DWORD rgidwMsgWin[MAX_INDEX_ENTRIES]; /* max index entries   */
  37. static short    cMsgWinEntries;         /* no. active index entries, with
  38.                                        MAX_INDEX_ENTRIES as its max. 
  39.                                        When set to 0, it's time to 
  40.                                        re-index.*/
  41.  
  42. static short nLinesPerEntry;        /* lines per index entry    */
  43.  
  44. /* displayed when buffer shouldn't grow or can't grow                   */
  45. static char __based(__segname("STRINGS_TEXT")) szClearBufferMsg[] =     
  46.             "Clearing Messages window to make room for more information.";
  47.  
  48.  
  49. struct KeyEntry
  50. {
  51.     WORD    wVirtKey;
  52.     BOOL    bCntl;
  53.     WORD    wMessage;
  54.     WORD    wRequest;
  55. } __based(__segname("STRINGS_TEXT")) KeyTable[] = 
  56. {
  57.     /* vertical scroll control */
  58.     {VK_HOME,   TRUE,   WM_VSCROLL, SB_TOP },
  59.     {VK_END,    TRUE,   WM_VSCROLL, SB_BOTTOM },
  60.     {VK_PRIOR,  FALSE,  WM_VSCROLL, SB_PAGEUP },
  61.     {VK_NEXT,   FALSE,  WM_VSCROLL, SB_PAGEDOWN },
  62.     {VK_UP,     FALSE,  WM_VSCROLL, SB_LINEUP },
  63.     {VK_DOWN,   FALSE,  WM_VSCROLL, SB_LINEDOWN },
  64.  
  65.     /* horizontal scroll control */
  66.     {VK_HOME,   FALSE,  WM_HSCROLL, SB_TOP },
  67.     {VK_END,    FALSE,  WM_HSCROLL, SB_BOTTOM },
  68.     {VK_PRIOR,  TRUE,   WM_HSCROLL, SB_PAGEUP },
  69.     {VK_NEXT,   TRUE,   WM_HSCROLL, SB_PAGEDOWN },
  70.     {VK_LEFT,   FALSE,  WM_HSCROLL, SB_LINEUP },
  71.     {VK_RIGHT,  FALSE,  WM_HSCROLL, SB_LINEDOWN },
  72. } ;
  73.  
  74. #define NUMKEYS (sizeof(KeyTable)/sizeof(struct KeyEntry)) 
  75.  
  76. /* Forward Refs
  77.  */
  78. static void FreeStatusLog(void);
  79.  
  80. /* Globals */
  81. BOOL bRealTimeMsgUpdate = TRUE; /* update messages window in real-time.
  82.                                  * Reset by callers when update can be
  83.                                  * be deferred.
  84.                                  */
  85.  
  86. /* Clears status buffer. Frees buffer.
  87.  */
  88. static void FreeStatusLog(void)
  89. {
  90.     if (hStatusBuffer)
  91.     {
  92.         GlobalFree(hStatusBuffer);
  93.         hStatusBuffer = (HANDLE)0;
  94.     }
  95.     dwStatusSize = 0L;      /* status data size             */
  96.     dwBufferSize = 0L;      /* status buffer size           */
  97.     nNumLines = 0;          /* number of lines in buffer    */
  98.     nVscrollMax = 1;
  99.     SetScrollRange(hWndStatus, SB_VERT, 0, 1, FALSE);
  100.     nVscrollPos = 0;
  101.     SetScrollPos(hWndStatus, SB_VERT, nVscrollPos, TRUE);
  102. }
  103.  
  104. /* Update Message Window Position is called after adding 
  105.  * a number of lines to the message window without updating it.
  106.  * The function invalidates then updates the window.
  107.  */
  108. void UpdateMsgWndPos(void)
  109. {
  110.     nVscrollPos = max(0,(nNumLines-cLinesMessageWin+1));     /* set position to next to last line   */
  111.     SetScrollPos(hWndStatus, SB_VERT, nVscrollPos, TRUE);
  112.     InvalidateRect(hWndStatus, NULL, TRUE);
  113.     UpdateWindow(hWndStatus);
  114. }
  115.  
  116. /* Add message line (or part of a line) to the global status buffer
  117.  * that is the contents of the Message Window.
  118.  * Assumes that global data is unlocked when called.
  119.  */
  120. void WriteStringToMsgWin(PSTR psz, BOOL bUpdate)
  121. {
  122.     WriteBufferToMsgWin(psz, strlen(psz), bUpdate);
  123. }
  124.  
  125. /* Add message buffer (maybe part of a line) to the global status buffer
  126.  * that is the contents of the Message Window.
  127.  * Assumes that global data is unlocked when called.
  128.  */
  129. void WriteBufferToMsgWin(LPSTR pszBuffer, int nBufferLen, BOOL bUpdate)
  130. {
  131.     LPSTR   lpszT;              /* pointer into buffer                          */
  132.     HANDLE hStatusBufferTmp;
  133.     LPSTR lpGlobalBuffer;           /* pointer into global buffer               */
  134.     DWORD dwNewSize = dwStatusSize + (DWORD)nBufferLen;
  135.     int nIncrLines = 0;             /* incremental lines in buffer          */
  136.     int nIncompleteExistingLine = 0; /* add -1 if incomplete existing last line */
  137.     int nIncompleteAddedLine = 0;   /* add +1 if incomplete added last line     */
  138.     DWORD dwRequestedSize;          /* Size needed to hold all data. Can't
  139.                                        practically exceeded cchBufferMax.*/
  140.  
  141.     if (!nBufferLen)    /* if no data       */
  142.         return;         /* just beat it     */
  143.  
  144.     /* count LF's in buffer to later add to total                       */
  145.     for (lpszT = pszBuffer; lpszT != NULL && (lpszT - pszBuffer) < nBufferLen; )
  146.     {
  147.         /* use memchr() for speed (?) considerations                    */
  148.         if (lpszT = _fmemchr(lpszT, '\n', (size_t)(nBufferLen - (lpszT - pszBuffer))))
  149.         {
  150.             nIncrLines++;   /* tally line found */
  151.             lpszT++;        /* point beyond LF for next pass */
  152.         }
  153.     }
  154.     if (dwNewSize > dwBufferSize)   /* if won't fit or 1st time */
  155.     {
  156.         /* Round up if necessary to nearest whole increment */
  157.         dwRequestedSize = ((dwNewSize + STATUS_INCREMENT - 1) / 
  158.                             STATUS_INCREMENT) * STATUS_INCREMENT;
  159.         if (hStatusBuffer)  /* if buffer exists, realloc */
  160.         {
  161.             if (dwRequestedSize <= cchBufferMax &&
  162.                     (hStatusBufferTmp = GlobalReAlloc(hStatusBuffer,
  163.                                 dwRequestedSize, GMEM_MOVEABLE)))
  164.             {
  165.                 /* successful re-allocation */
  166.                 hStatusBuffer = hStatusBufferTmp;
  167.                 dwBufferSize = dwRequestedSize;
  168.             }
  169.             else /* re-allocation failed, make last-ditch attempt! */
  170.             {
  171.                 FreeStatusLog();        /* free own buffers */
  172.                 MessageBox (hWndMain, szClearBufferMsg,
  173.                             "Note", MB_ICONINFORMATION | MB_OK);
  174.                 WriteBufferToMsgWin(pszBuffer, nBufferLen, bUpdate);
  175.                 return;
  176.             }
  177.         }
  178.         else    /* 1st time */
  179.         {
  180.             if (hStatusBuffer = GlobalAlloc(GMEM_MOVEABLE,
  181.                                 dwRequestedSize))
  182.             {
  183.                 dwBufferSize = dwRequestedSize; /* save it      */
  184.             }
  185.             else    /* 1st allocation failed! */
  186.             {
  187.                 WinAssert(hStatusBuffer);
  188.                 return;
  189.             }
  190.         }
  191.     }
  192.     /* should be easy copy of data from here */
  193.     lpGlobalBuffer = GlobalLock(hStatusBuffer);
  194.     if (lpGlobalBuffer)
  195.     {
  196.  
  197.         /* Account for partial lines existing and being added. */
  198.         if (dwStatusSize  &&
  199.             lpGlobalBuffer[dwStatusSize-1] != '\n')
  200.                 nIncompleteExistingLine-- ; /* subtract 1                   */
  201.  
  202.         if (pszBuffer[nBufferLen-1] != '\n') /* nBufferLen guaranteed >0 */
  203.                 nIncompleteAddedLine++ ;  /* add 1                  */
  204.  
  205.         /* copy data into global buffer                         */
  206.         if (nBufferLen)   /* map to ANSI; if 0 don't copy; 0 means 65K  */
  207.         {
  208.             OemToAnsiBuff(pszBuffer, &lpGlobalBuffer[dwStatusSize], nBufferLen);    
  209.         }
  210.         /* bump no. lines accounting for incomplete lines       */
  211.         nNumLines += (nIncrLines+nIncompleteExistingLine+nIncompleteAddedLine); 
  212.         dwStatusSize = dwNewSize;       /* new data size counting end null  */
  213.         GlobalUnlock(hStatusBuffer);
  214.         nVscrollMax = max(1, nNumLines + 2 - yClient/dyChar);
  215.         SetScrollRange(hWndStatus, SB_VERT, 0, nVscrollMax, FALSE);
  216.         cMsgWinEntries = 0; /* re-index whenever more data is added         */
  217.         if (bUpdate)        /* if requested to update message box           */
  218.         {
  219.             nVscrollPos = max(0,(nNumLines-cLinesMessageWin+1));     /* set position to next to last line   */
  220.             SetScrollPos(hWndStatus, SB_VERT, nVscrollPos, TRUE);
  221.             InvalidateRect(hWndStatus, NULL, TRUE);
  222.             UpdateWindow(hWndStatus);
  223.         }
  224.     }
  225.     else
  226.     {
  227.         WinAssert(lpGlobalBuffer);
  228.     }
  229. }
  230.  
  231.  
  232.  
  233. long FAR PASCAL StatusProc(HWND hWnd, WORD wMessage, WORD wParam, LONG lParam) 
  234. {
  235.     short xClient ;             /* size of client area  */
  236.     HDC     hDC;                /* device context       */
  237.     PAINTSTRUCT ps;
  238.     struct KeyEntry __far *pKE;     /* pointer to key entry     */
  239.     LPSTR   lpStatusBuffer;     /* pointer to global msg. buffer */
  240.     int     nMenuItemCount;     /* no. items in System menu before deleting separators  */
  241.     BOOL    bCntl;              /* control shift pressed ?  */
  242.     static short nHscrollMax;
  243.     static short nHscrollPos;
  244.     static short nMaxWidth;     /* in pixels    */
  245.     short nVscrollInc;
  246.     short nHscrollInc;
  247.     short i, x, y, nPaintBeg, nPaintEnd;
  248.     HMENU   hSysMenu;           /* this guy's system menu   */
  249.  
  250.     switch (wMessage)
  251.     {
  252.     case WM_CREATE:
  253.         nMaxWidth = MAX_H_CHARS * dxChar;
  254.         nVscrollPos = 0;
  255.         nVscrollMax = max(1,nNumLines);
  256.         SetScrollRange(hWnd, SB_VERT, 0, nVscrollMax, FALSE);
  257.         SetScrollPos(hWnd, SB_VERT, 0, TRUE);
  258.  
  259.         /* Remove system menu items to limit user actions on status window */
  260.         hSysMenu = GetSystemMenu(hWnd, FALSE);
  261.         DeleteMenu(hSysMenu, SC_SIZE, MF_BYCOMMAND);
  262.         DeleteMenu(hSysMenu, SC_MOVE, MF_BYCOMMAND);
  263.         DeleteMenu(hSysMenu, SC_CLOSE, MF_BYCOMMAND);
  264.         DeleteMenu(hSysMenu, SC_TASKLIST, MF_BYCOMMAND);
  265.  
  266.         /* walk thru menu and delete all separator bars */
  267.         for (nMenuItemCount = GetMenuItemCount(hMenu);
  268.             nMenuItemCount ; nMenuItemCount--)
  269.         {
  270.             if (GetMenuState(hSysMenu, nMenuItemCount-1, MF_BYPOSITION) & MF_SEPARATOR)
  271.             {
  272.                 DeleteMenu(hSysMenu, nMenuItemCount-1, MF_BYPOSITION);
  273.             }
  274.         }
  275.         return 0;
  276.  
  277.     case WM_SIZE:
  278.         xClient = LOWORD(lParam);/* x size of client area */
  279.         yClient = HIWORD(lParam);/* y size of client area */
  280.  
  281.         nVscrollMax = max(1, nNumLines + 2 - yClient/dyChar);
  282.         nVscrollPos = min(nVscrollPos, nVscrollMax);
  283.  
  284.         SetScrollRange(hWnd, SB_VERT, 0, nVscrollMax, FALSE);
  285.         SetScrollPos(hWnd, SB_VERT, nVscrollPos, TRUE);
  286.  
  287.         nHscrollMax = max(0, 2 + (nMaxWidth - xClient) / dxChar);
  288.         nHscrollPos = min(nHscrollPos, nHscrollMax);
  289.  
  290.         SetScrollRange(hWnd, SB_HORZ, 0, nHscrollMax, FALSE);
  291.         SetScrollPos(hWnd, SB_HORZ, nHscrollPos, TRUE);
  292.  
  293.         return 0;
  294.  
  295.     case WM_SYSCOMMAND:
  296.         switch ((wParam & 0xFFF0))
  297.         {
  298.         case SC_RESTORE:    /* alert parent         */
  299.             PostMessage(hWndMain, WM_COMMAND, IDM_RESTORE_STATUS, 0L);
  300.             break;
  301.         case SC_MAXIMIZE:
  302.             PostMessage(hWndMain, WM_COMMAND, IDM_MAX_STATUS, 0L);
  303.             break;
  304.         default:
  305.             return DefWindowProc(hWnd, wMessage, wParam, lParam);
  306.         }
  307.         break;
  308.     case WM_COMMAND:
  309.         if (wParam == IDM_CLEAR_STATUS)
  310.         {
  311.             FreeStatusLog();
  312.             InvalidateRect(hWndStatus, NULL, TRUE);
  313.         }
  314.         break;
  315.     case WM_VSCROLL:    /* scroll bar action on list box */
  316.         switch (wParam)
  317.         {
  318.         case SB_TOP:
  319.             nVscrollInc = -nVscrollPos;
  320.             break;
  321.         case SB_BOTTOM:
  322.             nVscrollInc = nVscrollMax - nVscrollPos;
  323.             break;
  324.         case SB_LINEUP:
  325.             nVscrollInc = -1;
  326.             break;
  327.         case SB_LINEDOWN:
  328.             nVscrollInc = 1;
  329.             break;
  330.         case SB_PAGEUP:
  331.             nVscrollInc = min(-1, -yClient/dyChar);
  332.             break;
  333.         case SB_PAGEDOWN:
  334.             nVscrollInc = max(1, yClient/dyChar);
  335.             break;
  336.         case SB_THUMBPOSITION:
  337.             nVscrollInc = LOWORD(lParam) - nVscrollPos;
  338.             break;
  339.         default:    /* END_SCROLL comes thru here               */
  340.             nVscrollInc = 0;
  341.         }
  342.  
  343.         if (nVscrollInc = max(-nVscrollPos,
  344.                                 min(nVscrollInc, nVscrollMax - nVscrollPos)))
  345.         {
  346.             nVscrollPos += nVscrollInc;
  347.             ScrollWindow(hWnd, 0, -dyChar * nVscrollInc, NULL, NULL);
  348.             SetScrollPos(hWnd, SB_VERT, nVscrollPos, TRUE);
  349.             UpdateWindow(hWnd);
  350.         }
  351.         return 0;
  352.  
  353.     case WM_HSCROLL:    /* scroll bar action on list box */
  354.         switch (wParam)
  355.         {
  356.         case SB_TOP:
  357.             nHscrollInc = -nHscrollPos;
  358.             break;
  359.         case SB_BOTTOM:
  360.             nHscrollInc = nHscrollMax - nHscrollPos;
  361.             break;
  362.         case SB_LINEUP:
  363.             nHscrollInc = -1;
  364.             break;
  365.         case SB_LINEDOWN:
  366.             nHscrollInc = 1;
  367.             break;
  368.         case SB_PAGEUP:
  369.             nHscrollInc = -8;
  370.             break;
  371.         case SB_PAGEDOWN:
  372.             nHscrollInc = 8;
  373.             break;
  374.         case SB_THUMBPOSITION:
  375.             nHscrollInc = LOWORD(lParam) - nHscrollPos;
  376.             break;
  377.         default:
  378.             return DefWindowProc(hWnd, wMessage, wParam, lParam);
  379.         }
  380.  
  381.         if (nHscrollInc = max(-nHscrollPos,
  382.                                 min(nHscrollInc, nHscrollMax - nHscrollPos)))
  383.         {
  384.             nHscrollPos += nHscrollInc;
  385.             ScrollWindow(hWnd, -dxChar * nHscrollInc, 0, NULL, NULL);
  386.             SetScrollPos(hWnd, SB_HORZ, nHscrollPos, TRUE);
  387.         }
  388.         return 0;
  389.  
  390.     case WM_KEYDOWN:
  391.         bCntl = (BOOL)(GetKeyState(VK_CONTROL) < 0 ? TRUE : FALSE);
  392.         for (i = 0, pKE = KeyTable; i < NUMKEYS; i++, pKE++)
  393.         {
  394.             if ((wParam == pKE->wVirtKey) && (bCntl == pKE->bCntl))
  395.             {
  396.                 SendMessage(hWnd, pKE->wMessage, pKE->wRequest, 0L);
  397.                 break;
  398.             }
  399.         }
  400.         break;
  401.     case WM_PAINT:
  402.         if (!hStatusBuffer)         /* if nothing to paint  */
  403.         {
  404.             return DefWindowProc(hWnd, wMessage, wParam, lParam);
  405.         }
  406.         {
  407.             REGISTER LPSTR lpsz;            /* current char                 */
  408.             LPSTR lpszNextLF;       /* address of next '\n' in buffer */
  409.             LPSTR lpszStart;    /* paint starting character     */
  410.             LPSTR lpszLineCur;  /* beginning of current line    */
  411.             HANDLE hNew;
  412.             DWORD   cchLine;    /* length of current line       */
  413.             short  nLinesSinceLastEntry; /* lines since last entry */
  414.             DWORD  dwSearchLen; /* length for _fmemchr() to search */
  415.  
  416.             lpszStart = NULL; /* paint starting character       */
  417.             lpStatusBuffer = GlobalLock(hStatusBuffer);
  418.             WinAssert(lpStatusBuffer);  /* DEBUG */
  419.             hDC = BeginPaint(hWnd, &ps);
  420.             WinAssert(hDC);             /* DEBUG */
  421.             hNew = SelectObject( hDC, hFixedFont);
  422.             WinAssert(hNew);
  423.             nPaintBeg = max(0, nVscrollPos+ps.rcPaint.top/dyChar -1);
  424.             nPaintEnd = min(nNumLines, nVscrollPos + ps.rcPaint.bottom/dyChar);
  425.             if (nPaintBeg >= nPaintEnd) /* if no painting to do ...     */
  426.             {
  427.                 EndPaint(hWnd, &ps);
  428.                 GlobalUnlock(hStatusBuffer);
  429.                 return 0;
  430.             }
  431.             if (!cMsgWinEntries)    /* re-index whenever more data is added */
  432.             {
  433.                 /* Round up to make lines_per_entry encompass all
  434.                  * possible lines in buffer.
  435.                  */
  436.                 nLinesPerEntry = (nNumLines+MAX_INDEX_ENTRIES-1) / MAX_INDEX_ENTRIES; 
  437.                 if (!nLinesPerEntry)    /* if zero                  */ 
  438.                     nLinesPerEntry++;   /* set to 1 as minimum      */ 
  439.  
  440.                 /* Count lines from beginning of buffer to:
  441.                  * 1) mark beginning of paint sequence (lpszStart) and
  442.                  * 2) periodically save buffer index in MsgWinIndex[] table.
  443.                  */
  444.                 for (lpsz = lpStatusBuffer, i = 0, nLinesSinceLastEntry = 0;
  445.                      (DWORD)(lpsz - lpStatusBuffer) < dwStatusSize ; i++)
  446.                 {
  447.                     /* We are at the 1st character position in the line  */
  448.                     if (i == nPaintBeg) /* Starting point for paint ? */
  449.                         lpszStart = lpsz;   /* If so, mark starting point */
  450.  
  451.                     /* Entry time ? */
  452.                     if (!nLinesSinceLastEntry++ && 
  453.                         cMsgWinEntries < MAX_INDEX_ENTRIES)
  454.                     {
  455.                         rgidwMsgWin[cMsgWinEntries] = 
  456.                                 (DWORD)(lpsz - lpStatusBuffer); /* save index */
  457.                         cMsgWinEntries++;
  458.                     }
  459.  
  460.                     if (nLinesSinceLastEntry >= nLinesPerEntry)
  461.                         nLinesSinceLastEntry = 0;
  462.  
  463.                     /* Use _fmemchr() to find next LF.  
  464.                      * It's probably optimized for searches.
  465.                      */
  466.                     dwSearchLen = dwStatusSize - 
  467.                                         (DWORD)(lpsz - lpStatusBuffer);
  468.                     if ((lpszNextLF = _fmemchr(lpsz, '\n', (size_t)dwSearchLen)))
  469.                         lpsz = ++lpszNextLF;    /* use next char as beg of line */
  470.                     else /* use lpsz with incremented value */
  471.                         lpsz += dwSearchLen;
  472.  
  473.                 } /* bottom of still-counting-lines loop */
  474.  
  475.                 WinAssert(lpszStart);
  476.                 lpsz = lpszStart;       /* restore starting point           */
  477.                 WinAssert((DWORD)lpsz >= (DWORD)lpStatusBuffer &&
  478.                    (DWORD)lpsz < (DWORD)&lpStatusBuffer[dwStatusSize]); 
  479.  
  480.             }   /* bottom of need-to-build-index block  */
  481.             else    /* index is still valid */
  482.             {
  483.                 short nIndexEntry;
  484.  
  485.                 /* Find index of line number which is equal to or just
  486.                  * below the starting line to paint. Work backwards
  487.                  * thru the table. Here, "i" is the line no. corresponding
  488.                  * to the current table index.
  489.                  */
  490.                 for (nIndexEntry = cMsgWinEntries - 1,
  491.                      i = nIndexEntry * nLinesPerEntry; 
  492.                         nIndexEntry >= 0 && 
  493.                         nPaintBeg < i ;
  494.                         nIndexEntry--, i -= nLinesPerEntry )
  495.                 {
  496.                     ;
  497.                 }
  498.  
  499.                 WinAssert(nIndexEntry >= 0);
  500.                 WinAssert(i <= nPaintBeg);
  501.  
  502.                 /* OK, we've got a head start on the search.
  503.                  * Start checking characters from the position found
  504.                  * in the index table.
  505.                  */
  506.                 for (lpsz = &lpStatusBuffer[rgidwMsgWin[nIndexEntry]];
  507.                      i < nPaintBeg && 
  508.                         (DWORD)(lpsz - lpStatusBuffer) < dwStatusSize;
  509.                      ++i)
  510.                 {
  511.                     /* Find length of current line.  Use _fmemchr() to 
  512.                      * find next LF.  
  513.                      */
  514.                     dwSearchLen = dwStatusSize - 
  515.                                     (DWORD)(lpsz - lpStatusBuffer);
  516.                     if ((lpszNextLF = _fmemchr(lpsz, '\n', (size_t)dwSearchLen)) != NULL)
  517.                         lpsz = ++lpszNextLF; /* point to next char. past '\n'   */
  518.  
  519.                     else /* If search fails, pretend LF exists, go past it. */
  520.                         lpsz += dwSearchLen;
  521.  
  522.                 }
  523.             } /* bottom of index-is-still-valid block. */
  524.  
  525.             /* At this point we've got the buffer address, lpsz, for the 1st
  526.              * line at which we begin painting, nPaintBeg.
  527.              */
  528.             for (i = nPaintBeg; 
  529.                  (i < nPaintEnd) && 
  530.                  ((DWORD)(lpsz  - lpStatusBuffer) >=  0L) &&    
  531.                  ((DWORD)(lpsz  - lpStatusBuffer) < dwStatusSize) ; 
  532.                  ++i)
  533.             {
  534.                 lpszLineCur = lpsz;
  535.                 /* Find length of current line. Use _fmemchr() to find next LF.
  536.                  */
  537.                 dwSearchLen = dwStatusSize - (DWORD)(lpsz - lpStatusBuffer);
  538.                 if ((lpszNextLF = _fmemchr(lpsz, '\n', (size_t)dwSearchLen)) == NULL)
  539.                 {
  540.                     /* If search fails, pretend we found LF, we won't 
  541.                      * display it anyway.
  542.                      */
  543.                     lpszNextLF = lpsz + dwSearchLen;
  544.                 }
  545.                 WinAssert((DWORD)lpszNextLF >= (DWORD)lpszLineCur); /* should be non-negative   */
  546.                 WinAssert((DWORD)lpszNextLF >= (DWORD)lpStatusBuffer && /* DEBUG */
  547.                    (DWORD)lpszNextLF <= (DWORD)&lpStatusBuffer[dwStatusSize]); 
  548.  
  549.                 x = dxChar * (1 - nHscrollPos);
  550.                 y = dyChar * (1 - nVscrollPos + i);
  551.                 cchLine = (DWORD)(lpszNextLF - lpszLineCur);/* calc length*/
  552.                 /* don't display '\r'   */
  553.                 if (cchLine && lpszLineCur[cchLine-1] == '\r')
  554.                     cchLine--;
  555.  
  556.                 /* may be displaying long lines if binary file */
  557.                 if (cchLine > cchTextOutMax) 
  558.                     cchLine = cchTextOutMax;
  559.  
  560.                 TabbedTextOut(hDC, x, y, lpszLineCur, (int)cchLine, 0, NULL, 0);
  561.                 lpsz = ++lpszNextLF; /* point to char. past '\n' */
  562.             }
  563.             EndPaint(hWnd, &ps);
  564.             GlobalUnlock(hStatusBuffer);
  565.             return 0;
  566.         }
  567.         break;
  568.     case WM_CLOSE:
  569.         DestroyWindow(hWnd);
  570.         break;
  571.     case WM_DESTROY:
  572.         FreeStatusLog();
  573.         break;
  574.     default:
  575.         return DefWindowProc(hWnd, wMessage, wParam, lParam);
  576.     }
  577.     return 0L;
  578. }
  579.  
  580. /* Printf buffers the current output and counts the number of lines
  581.  * within it.  It makes sure there is enough space in the global
  582.  * buffer, then copies the buffered data to the global buffer.
  583.  * It then triggers a repaint of the status buffer.
  584.  */
  585. int __far __cdecl printf(const char *format, ...)
  586. {
  587.     va_list argptr;
  588.     HANDLE hMemory;
  589.     PSTR pszBuffer;
  590.  
  591.     va_start(argptr, format);
  592.     hMemory = LocalAlloc(LMEM_MOVEABLE, STDIO_BUF_SIZE);
  593.     WinAssert(hMemory);
  594.     if (!hMemory)
  595.     {
  596.         return 0;
  597.     }
  598.     pszBuffer = (PSTR)LocalLock(hMemory);
  599.     WinAssert(pszBuffer);
  600.     vsprintf(pszBuffer, format, argptr);
  601.     va_end(argptr); 
  602.     WinAssert(strlen(pszBuffer) < STDIO_BUF_SIZE);  /* raise STDIO_BUF_SIZE ?   */
  603.     WriteStringToMsgWin(pszBuffer, bRealTimeMsgUpdate);
  604.     LocalUnlock(hMemory);
  605.     LocalFree(hMemory);
  606.     return 0;
  607. }
  608.  
  609. /* fprintf clone for code in unzip.c, etc. */
  610. int __far __cdecl fprintf(FILE *file, const char *format, ...)
  611. {
  612.     va_list argptr;
  613.     HANDLE hMemory;
  614.     PSTR pszBuffer;
  615.  
  616.     va_start(argptr, format);
  617.     hMemory = LocalAlloc(LMEM_MOVEABLE, STDIO_BUF_SIZE);
  618.     WinAssert(hMemory);
  619.     if (!hMemory)
  620.     {
  621.         return 0;
  622.     }
  623.     pszBuffer = (PSTR)LocalLock(hMemory);
  624.     WinAssert(pszBuffer);
  625.     vsprintf(pszBuffer, format, argptr);
  626.     va_end(argptr); 
  627.     WinAssert(strlen(pszBuffer) < STDIO_BUF_SIZE);  /* raise STDIO_BUF_SIZE ?   */
  628.     WriteStringToMsgWin(pszBuffer, bRealTimeMsgUpdate);
  629.     LocalUnlock(hMemory);
  630.     LocalFree(hMemory);
  631. }
  632.  
  633. void __far __cdecl perror(const char *parm1)
  634. {
  635.     printf(parm1);
  636. }
  637.