/* * npfile.c - Routines for file i/o for notepad * Copyright (C) 1984-2001 Microsoft Inc. */ #include "precomp.h" HANDLE hFirstMem; CHAR BOM_UTF8[3]= {(BYTE) 0xEF, (BYTE) 0xBB, (BYTE)0xBF}; //**************************************************************** // // ReverseEndian // // Purpose: copies unicode character from one endian source // to another. // // may work on lpDst == lpSrc // VOID ReverseEndian( PTCHAR lpDst, PTCHAR lpSrc, DWORD nChars ) { DWORD cnt; for( cnt=0; cnt < nChars; cnt++,lpDst++,lpSrc++ ) { *lpDst= (TCHAR) (((*lpSrc<<8) & 0xFF00) + ((*lpSrc>>8)&0xFF)); } } //***************************************************************** // // AnsiWriteFile() // // Purpose : To simulate the effects of _lwrite() in a Unicode // environment by converting to ANSI buffer and // writing out the ANSI text. // Returns : TRUE is successful, FALSE if not // GetLastError() will have the error code. // //***************************************************************** BOOL AnsiWriteFile(HANDLE hFile, // file to write to UINT uCodePage, // code page to convert unicode to DWORD dwFlags, // flags for WideCharToMultiByte conversion LPVOID lpBuffer, // unicode buffer DWORD nChars) // number of unicode chars { LPSTR lpAnsi; // pointer to allocate buffer BOOL fDefCharUsed; // flag that conversion wasn't perfect BOOL* pfDefCharUsed; // pointer to flag DWORD nBytesWritten; // number of bytes written BOOL bStatus; // status from conversion and writefile DWORD nBytes; // number of ascii character to produce // // WideCharToMultiByte fails to convert zero characters. // If we get a request to write 0 chars, just return. We are done. // if( nChars == 0 ) { return( TRUE ); } pfDefCharUsed= NULL; if( uCodePage != CP_UTF8 ) { pfDefCharUsed= &fDefCharUsed; } // // Calculate number of bytes to write // The caller calculated the number of bytes to write // but it might be wrong because the user approved data loss. // Do the calculation here (again) in case the dwFlags are different. // FEATURE: We could optimize this in the case where the dwFlags did not // change. // nBytes= WideCharToMultiByte( uCodePage, // code page dwFlags, // performance and mapping flags (LPWSTR) lpBuffer, // wide char buffer nChars, // chars in wide char buffer NULL, // output buffer 0, // size of output buffer NULL, // char to sub. for unmapped chars (use default) pfDefCharUsed); // flag to set if default char used if( nBytes == 0 ) { return( FALSE ); } // // Allocate buffer to convert to // lpAnsi= (LPSTR) LocalAlloc( LPTR, nBytes + 1 ); if( !lpAnsi ) { SetLastError( ERROR_NOT_ENOUGH_MEMORY ); return (FALSE); } bStatus= WideCharToMultiByte( uCodePage, // code page dwFlags, // performance and mapping flags (LPWSTR) lpBuffer, // wide char buffer nChars, // chars in wide char buffer lpAnsi, // resultant ascii string nBytes, // size of ascii string buffer NULL, // char to sub. for unmapped chars (use default) pfDefCharUsed); // flag to set if default char used if( bStatus ) { bStatus= WriteFile( hFile, lpAnsi, nBytes, &nBytesWritten, NULL ); } LocalFree( lpAnsi ); return( bStatus ); } // end of AnsiWriteFile() // Routines to deal with the soft EOL formatting. // // MLE Actually inserts characters into the text being under edit, so they // have to be removed before saving the file. // // It turns out that MLE will get confused if the current line is bigger than // the current file, so we will reset the cursor to 0,0 to keep it from looking stupid. // Should be fixed in MLE, but... // static DWORD dwStartSel; // saved start of selection static DWORD dwEndSel; // saved end of selection VOID ClearFmt(VOID) { if( fWrap ) { if( fMLE_is_broken ) { GotoAndScrollInView( 1 ); } else { SendMessage( hwndEdit, EM_GETSEL, (WPARAM) &dwStartSel, (LPARAM) &dwEndSel ); SendMessage( hwndEdit, EM_SETSEL, (WPARAM) 0, (LPARAM) 0 ); } SendMessage( hwndEdit, EM_FMTLINES, (WPARAM)FALSE, 0 ); // remove soft EOLs } } VOID RestoreFmt(VOID) { UINT CharIndex; if( fWrap ) { if( fMLE_is_broken ) { NpReCreate( ES_STD ); // slow but it works } else { SendMessage( hwndEdit, EM_FMTLINES, (WPARAM)TRUE, 0 ); // add soft EOLs SendMessage( hwndEdit, EM_SETSEL, (WPARAM) dwStartSel, (LPARAM) dwEndSel); } } } /* Save notepad file to disk. szFileSave points to filename. fSaveAs is TRUE iff we are being called from SaveAsDlgProc. This implies we must open file on current directory, whether or not it already exists there or somewhere else in our search path. Assumes that text exists within hwndEdit. 30 July 1991 Clark Cyr */ BOOL FAR SaveFile (HWND hwndParent, TCHAR *szFileSave, BOOL fSaveAs ) { LPTSTR lpch; UINT nChars; BOOL flag; BOOL fNew = FALSE; BOOL fDefCharUsed = FALSE; BOOL* pfDefCharUsed; static WCHAR wchBOM = BYTE_ORDER_MARK; static WCHAR wchRBOM= REVERSE_BYTE_ORDER_MARK; HLOCAL hEText; // handle to MLE text DWORD nBytesWritten; // number of bytes written DWORD nAsciiLength; // length of equivalent ascii file UINT cpTemp= CP_ACP; // code page to convert to DWORD dwFlags; // flags for WideCharToMultiByte /* If saving to an existing file, make sure correct disk is in drive */ if (!fSaveAs) { fp= CreateFile( szFileSave, // name of file GENERIC_READ|GENERIC_WRITE, // access mode FILE_SHARE_READ, // share mode NULL, // security descriptor OPEN_EXISTING, // how to create FILE_ATTRIBUTE_NORMAL, // file attributes NULL); // hnd of file with attrs } else { // Carefully open the file. Do not truncate it if it exists. // set the fNew flag if it had to be created. // We do all this in case of failures later in the process. fp= CreateFile( szFileSave, // name of file GENERIC_READ|GENERIC_WRITE, // access mode FILE_SHARE_READ|FILE_SHARE_WRITE, // share mode NULL, // security descriptor OPEN_ALWAYS, // how to create FILE_ATTRIBUTE_NORMAL, // file attributes NULL); // hnd of file with attrs if( fp != INVALID_HANDLE_VALUE ) { fNew= (GetLastError() != ERROR_ALREADY_EXISTS ); } } if( fp == INVALID_HANDLE_VALUE ) { AlertBox( hwndParent, szNN, szCREATEERR, szFileSave, MB_APPLMODAL | MB_OK | MB_ICONEXCLAMATION); return FALSE; } // if wordwrap, remove soft carriage returns // Also move the cursor to a safe place to get around MLE bugs if( fWrap ) { ClearFmt(); } /* Must get text length after formatting */ nChars = (UINT)SendMessage (hwndEdit, WM_GETTEXTLENGTH, 0, (LPARAM)0); hEText= (HANDLE) SendMessage( hwndEdit, EM_GETHANDLE, 0,0 ); if( !hEText || !(lpch= (LPTSTR) LocalLock(hEText) )) { AlertUser_FileFail( szFileSave ); goto CleanUp; } // Determine the SaveAs file type, and write the appropriate BOM. // If the filetype is UTF-8 or Ansi, do the conversion. switch(g_ftSaveAs) { case FT_UNICODE: WriteFile( fp, &wchBOM, ByteCountOf(1), &nBytesWritten, NULL ); flag= WriteFile(fp, lpch, ByteCountOf(nChars), &nBytesWritten, NULL); break; case FT_UNICODEBE: WriteFile( fp, &wchRBOM, ByteCountOf(1), &nBytesWritten, NULL ); ReverseEndian( lpch, lpch,nChars ); flag= WriteFile(fp, lpch, ByteCountOf(nChars), &nBytesWritten, NULL); ReverseEndian( lpch, lpch, nChars ); break; // If it UTF-8, write the BOM (3 bytes), set the code page and fall // through to the default case. case FT_UTF8: WriteFile( fp, &BOM_UTF8, 3, &nBytesWritten, NULL ); // fall through to convert and write the file default: if (g_ftSaveAs != FT_UTF8) { // // Always use the current locale code page to do the translation // If the user changes locales, they will need to know what locale // this version of the file was saved with. Since we don't save that // information, the user may be in trouble. Unicode would save his bacon. // cpTemp= GetACP(); pfDefCharUsed= &fDefCharUsed; dwFlags= WC_NO_BEST_FIT_CHARS; } else { cpTemp= CP_UTF8; pfDefCharUsed= NULL; // these must be NULL and 0 for this code page dwFlags= 0; } nAsciiLength= WideCharToMultiByte( cpTemp, dwFlags, (LPWSTR)lpch, nChars, NULL, 0, NULL, // if no conversion, use default char pfDefCharUsed); // If we can't round-trip the character, warn the user. // If we don't use WC_NO_BEST_FIT_CHARS, WCTMB() will convert some characters // without setting the fDefCharUsed (for example alt-233) // This fixes ntbug9:367586 if( fDefCharUsed || (nChars && (nAsciiLength==0) ) ) { if ( AlertBox( hwndParent, szNN, szErrUnicode, szFileSave, MB_APPLMODAL|MB_OKCANCEL|MB_ICONEXCLAMATION) == IDCANCEL) goto CleanUp; // User has approved loss of data, so try conversion with flags // that allow loss. ntbug9: 435042 dwFlags= 0; } flag= AnsiWriteFile( fp, cpTemp, dwFlags, lpch, nChars ); break; } if (!flag) { SetCursor(hStdCursor); /* display normal cursor */ AlertUser_FileFail( szFileSave ); CleanUp: SetCursor( hStdCursor ); CloseHandle (fp); fp=INVALID_HANDLE_VALUE; if( hEText ) LocalUnlock( hEText ); if (fNew) DeleteFile (szFileSave); /* if wordwrap, insert soft carriage returns */ if (fWrap) { RestoreFmt(); } return FALSE; } else { SetEndOfFile (fp); g_ftOpenedAs = g_ftSaveAs; SendMessage (hwndEdit, EM_SETMODIFY, FALSE, 0L); SetTitle (szFileSave); fUntitled = FALSE; } CloseHandle (fp); fp=INVALID_HANDLE_VALUE; if( hEText ) LocalUnlock( hEText ); /* if wordwrap, insert soft carriage returns */ if (fWrap) { RestoreFmt(); } /* Display the hour glass cursor */ SetCursor(hStdCursor); return TRUE; } // end of SaveFile() /* Read contents of file from disk. * Do any conversions required. * File is already open, referenced by handle fp * Close the file when done. * If typeFlag>=0, then use it as filetype, otherwise do automagic guessing. */ BOOL FAR LoadFile (TCHAR * sz, INT typeFlag ) { UINT len, i, nChars; LPTSTR lpch=NULL; LPTSTR lpBuf; LPSTR lpBufAfterBOM; BOOL fLog=FALSE; TCHAR* p; TCHAR szSave[MAX_PATH]; /* Private copy of current filename */ BOOL bUnicode=FALSE; /* true if file detected as unicode */ BOOL bUTF8=FALSE; /* true if file detected as UTF-8 */ DWORD nBytesRead; // number of bytes read BY_HANDLE_FILE_INFORMATION fiFileInfo; BOOL bStatus; // boolean status HLOCAL hNewEdit=NULL; // new handle for edit buffer HANDLE hMap; // file mapping handle TCHAR szNullFile[2]; // fake null mapped file INT cpTemp = CP_ACP; NP_FILETYPE ftOpenedAs=FT_UNKNOWN; if( fp == INVALID_HANDLE_VALUE ) { AlertUser_FileFail( sz ); return (FALSE); } // // Get size of file // We use this heavy duty GetFileInformationByHandle API // because it finds bugs. It takes longer, but it only is // called at user interaction time. // bStatus= GetFileInformationByHandle( fp, &fiFileInfo ); len= (UINT) fiFileInfo.nFileSizeLow; // NT may delay giving this status until the file is accessed. // i.e. the open succeeds, but operations may fail on damaged files. if( !bStatus ) { AlertUser_FileFail( sz ); CloseHandle( fp ); fp=INVALID_HANDLE_VALUE; return( FALSE ); } // If the file is too big, fail now. // -1 not valid because we need a zero on the end. // If the file is too big, fail now. // -1 not valid because we need a zero on the end. // // bug# 168148: silently fails to open 2.4 gig text file on win64 // Caused by trying to convert ascii file to unicode which overflowed // the dword length handled by multibytetowidechar conversion. // Since no one will be happy with the performance of the MLE with // a file this big, we will just refuse to open it now. // // For example, on a Pentium 173 MHz with 192 Megs o'RAM (Tecra 8000) // I got these results: // // size CPU-time // 0 .12 // 1 .46 // 2 .77 // 3 1.041 // 4 1.662 // 5 2.092 // 6 2.543 // 7 3.023 // 8 3.534 // 9 4.084 // 10 4.576 // 16 8.371 // 32 23.142 // 64 74.426 // // Curve fitting these numbers to cpu-time=a+b*size+c*size*size // we get a really good fit with cpu= .24+.28*size+.013*size*size // // For 1 gig, this works out to be 3.68 hours. 2 gigs=14.6 hours // // And the user isn't going to be happy with adding or deleting characters // with the MLE control. It wants to keep the memory stuctures uptodate // at all times. // // Going to richedit isn't a near term solution either: // // size CPU-time // 2 3.8 // 4 9.0 // 6 21.9 // 8 30.4 // 10 65.3 // 16 1721 or >3.5 hours (it was still running when I killed it) // // // feature: should we only bail if not unicode? // if( len >=0x40000000 || fiFileInfo.nFileSizeHigh != 0 ) { AlertBox( hwndNP, szNN, szFTL, sz, MB_APPLMODAL | MB_OK | MB_ICONEXCLAMATION); CloseHandle (fp); fp=INVALID_HANDLE_VALUE; return (FALSE); } SetCursor(hWaitCursor); // physical I/O takes time // // Create a file mapping so we don't page the file to // the pagefile. This is a big win on small ram machines. // if( len != 0 ) { lpBuf= NULL; hMap= CreateFileMapping( fp, NULL, PAGE_READONLY, 0, len, NULL ); if( hMap ) { lpBuf= MapViewOfFile( hMap, FILE_MAP_READ, 0,0,len); CloseHandle( hMap ); } } else // file mapping doesn't work on zero length files { lpBuf= (LPTSTR) &szNullFile; *lpBuf= 0; // null terminate } CloseHandle( fp ); fp=INVALID_HANDLE_VALUE; if( lpBuf == NULL ) { SetCursor( hStdCursor ); AlertUser_FileFail( sz ); return( FALSE ); } // // protect access to the mapped file with a try/except so we // can detect I/O errors. // // // WARNING: be very very careful. This code is pretty fragile. // Files across the network, or RSM files (tape) may throw excepts // at random points in this code. Anywhere the code touches the // memory mapped file can cause an AV. Make sure variables are // in consistent state if an exception is thrown. Be very careful // with globals. __try { /* Determine the file type and number of characters * If the user overrides, use what is specified. * Otherwise, we depend on 'IsTextUnicode' getting it right. * If it doesn't, bug IsTextUnicode. */ lpBufAfterBOM= (LPSTR) lpBuf; if( typeFlag == FT_UNKNOWN ) { switch(*lpBuf) { case BYTE_ORDER_MARK: bUnicode= TRUE; ftOpenedAs= FT_UNICODE; // don't count the BOM. nChars= len / sizeof(TCHAR) -1; break; case REVERSE_BYTE_ORDER_MARK: bUnicode= TRUE; ftOpenedAs= FT_UNICODEBE; // don't count the BOM. nChars= len / sizeof(TCHAR) -1; break; // UTF bom has 3 bytes; if it doesn't have UTF BOM just fall through .. case BOM_UTF8_HALF: if (len > 2 && ((BYTE) *(((LPSTR)lpBuf)+2) == BOM_UTF8_2HALF) ) { bUTF8= TRUE; cpTemp= CP_UTF8; ftOpenedAs= FT_UTF8; // Ignore the first three bytes. lpBufAfterBOM= (LPSTR)lpBuf + 3; len -= 3; break; } default: // Is the file unicode without BOM ? if ((bUnicode= IsInputTextUnicode((LPSTR) lpBuf, len))) { ftOpenedAs= FT_UNICODE; nChars= len / sizeof(TCHAR); } else { // Is the file UTF-8 even though it doesn't have UTF-8 BOM. if ((bUTF8= IsTextUTF8((LPSTR) lpBuf, len))) { ftOpenedAs= FT_UTF8; cpTemp= CP_UTF8; } // well, not it must be an ansi file! else { ftOpenedAs= FT_ANSI; cpTemp= CP_ACP; } } break; } } else { switch(typeFlag) { case FT_UNICODE: bUnicode= TRUE; ftOpenedAs= FT_UNICODE; nChars= len / sizeof(TCHAR); // don't count the BOM. if (*lpBuf == BYTE_ORDER_MARK) nChars--; break; case FT_UNICODEBE: bUnicode= TRUE; ftOpenedAs= FT_UNICODE; nChars= len / sizeof(TCHAR); // don't count the BOM. if (*lpBuf == REVERSE_BYTE_ORDER_MARK) nChars--; break; case FT_UTF8: bUTF8= TRUE; cpTemp= CP_UTF8; ftOpenedAs= FT_UTF8; if (len > 2 && ((*lpBuf == BOM_UTF8_HALF) && ((BYTE) *(((LPSTR)lpBuf)+2) == BOM_UTF8_2HALF)) ) { // Ignore the first three bytes. lpBufAfterBOM= (LPSTR)lpBuf + 3; len -= 3; break; } break; case FT_ANSI: default: ftOpenedAs= FT_ANSI; cpTemp= CP_ACP; break; } } // find out no. of chars present in the string. if (!bUnicode) { nChars = MultiByteToWideChar (cpTemp, 0, (LPSTR)lpBufAfterBOM, len, NULL, 0); } // // Don't display text until all done. // SendMessage (hwndEdit, WM_SETREDRAW, (WPARAM)FALSE, (LPARAM)0); // Reset selection to 0 SendMessage(hwndEdit, EM_SETSEL, 0, 0L); SendMessage(hwndEdit, EM_SCROLLCARET, 0, 0); // resize the edit buffer // if we can't resize the memory, inform the user if (!(hNewEdit= LocalReAlloc(hEdit,ByteCountOf(nChars + 1),LMEM_MOVEABLE))) { /* Bug 7441: New() causes szFileName to be set to "Untitled". Save a * copy of the filename to pass to AlertBox. * 17 November 1991 Clark R. Cyr */ lstrcpy(szSave, sz); New(FALSE); /* Display the hour glass cursor */ SetCursor(hStdCursor); AlertBox( hwndNP, szNN, szFTL, szSave, MB_APPLMODAL | MB_OK | MB_ICONEXCLAMATION); if( lpBuf != (LPTSTR) &szNullFile ) { UnmapViewOfFile( lpBuf ); } // let user see old text SendMessage (hwndEdit, WM_SETREDRAW, (WPARAM)FALSE, (LPARAM)0); return FALSE; } /* Transfer file from temporary buffer to the edit buffer */ lpch= (LPTSTR) LocalLock(hNewEdit); if( bUnicode ) { /* skip the Byte Order Mark */ if (*lpBuf == BYTE_ORDER_MARK) { CopyMemory (lpch, lpBuf + 1, ByteCountOf(nChars)); } else if( *lpBuf == REVERSE_BYTE_ORDER_MARK ) { ReverseEndian( lpch, lpBuf+1, nChars ); } else { CopyMemory (lpch, lpBuf, ByteCountOf(nChars)); } } else { nChars = MultiByteToWideChar (cpTemp, 0, (LPSTR)lpBufAfterBOM, len, (LPWSTR)lpch, nChars); } g_ftOpenedAs= ftOpenedAs; // got everything; update global safe now } __except(EXCEPTION_EXECUTE_HANDLER) { AlertBox( hwndNP, szNN, szDiskError, sz, MB_APPLMODAL | MB_OK | MB_ICONEXCLAMATION ); nChars= 0; // don't deal with it. } /* Free file mapping */ if( lpBuf != (LPTSTR) &szNullFile ) { UnmapViewOfFile( lpBuf ); } if( lpch ) { // Fix any NUL character that came in from the file to be spaces. for (i = 0, p = lpch; i < nChars; i++, p++) { if( *p == (TCHAR) 0 ) *p= TEXT(' '); } // null terminate it. Safe even if nChars==0 because it is 1 TCHAR bigger *(lpch+nChars)= (TCHAR) 0; /* zero terminate the thing */ // Set 'fLog' if first characters in file are ".LOG" fLog= *lpch++ == TEXT('.') && *lpch++ == TEXT('L') && *lpch++ == TEXT('O') && *lpch == TEXT('G'); } if( hNewEdit ) { LocalUnlock( hNewEdit ); // now it is safe to set the global edit handle hEdit= hNewEdit; } lstrcpy( szFileName, sz ); SetTitle( sz ); fUntitled= FALSE; /* Pass handle to edit control. This is more efficient than WM_SETTEXT * which would require twice the buffer space. */ /* Bug 7443: If EM_SETHANDLE doesn't have enough memory to complete things, * it will send the EN_ERRSPACE message. If this happens, don't put up the * out of memory notification, put up the file to large message instead. * 17 November 1991 Clark R. Cyr */ dwEmSetHandle = SETHANDLEINPROGRESS; SendMessage (hwndEdit, EM_SETHANDLE, (WPARAM)hEdit, (LPARAM)0); if (dwEmSetHandle == SETHANDLEFAILED) { SetCursor(hStdCursor); dwEmSetHandle = 0; AlertBox( hwndNP, szNN, szFTL, sz,MB_APPLMODAL|MB_OK|MB_ICONEXCLAMATION); New (FALSE); SendMessage (hwndEdit, WM_SETREDRAW, (WPARAM)TRUE, (LPARAM)0); return (FALSE); } dwEmSetHandle = 0; PostMessage (hwndEdit, EM_LIMITTEXT, (WPARAM)CCHNPMAX, 0L); /* If file starts with ".LOG" go to end and stamp date time */ if (fLog) { SendMessage( hwndEdit, EM_SETSEL, (WPARAM)nChars, (LPARAM)nChars); SendMessage( hwndEdit, EM_SCROLLCARET, 0, 0); InsertDateTime(TRUE); } /* Move vertical thumb to correct position */ SetScrollPos (hwndNP, SB_VERT, (int) SendMessage (hwndEdit, WM_VSCROLL, EM_GETTHUMB, 0L), TRUE); /* Now display text */ SendMessage( hwndEdit, WM_SETREDRAW, (WPARAM)TRUE, (LPARAM)0 ); InvalidateRect( hwndEdit, (LPRECT)NULL, TRUE ); UpdateWindow( hwndEdit ); SetCursor(hStdCursor); return( TRUE ); } // end of LoadFile() /* New Command - reset everything */ void FAR New (BOOL fCheck) { HANDLE hTemp; TCHAR* pSz; if (!fCheck || CheckSave (FALSE)) { SendMessage( hwndEdit, WM_SETTEXT, (WPARAM)0, (LPARAM)TEXT("") ); fUntitled= TRUE; lstrcpy( szFileName, szUntitled ); SetTitle(szFileName ); SendMessage( hwndEdit, EM_SETSEL, 0, 0L ); SendMessage( hwndEdit, EM_SCROLLCARET, 0, 0 ); // resize of 1 NULL character i.e. zero length hTemp= LocalReAlloc( hEdit, sizeof(TCHAR), LMEM_MOVEABLE ); if( hTemp ) { hEdit= hTemp; } // null terminate the buffer. LocalReAlloc won't do it // because in all cases it is not growing which is the // only time it would zero out anything. pSz= LocalLock( hEdit ); *pSz= TEXT('\0'); LocalUnlock( hEdit ); SendMessage (hwndEdit, EM_SETHANDLE, (WPARAM)hEdit, 0L); szSearch[0] = (TCHAR) 0; } } // end of New() /* If sz does not have extension, append ".txt" * This function is useful for getting to undecorated filenames * that setup apps use. DO NOT CHANGE the extension. Too many setup * apps depend on this functionality. */ void FAR AddExt( TCHAR* sz ) { TCHAR* pch1; int ch; DWORD dwSize; dwSize= lstrlen(sz); pch1= sz + dwSize; // point to end ch= *pch1; while( ch != TEXT('.') && ch != TEXT('\\') && ch != TEXT(':') && pch1 > sz) { // // backup one character. Do NOT use CharPrev because // it sometimes doesn't actually backup. Some Thai // tone marks fit this category but there seems to be others. // This is safe since it will stop at the beginning of the // string or on delimiters listed above. bug# 139374 2/13/98 // // pch1= (TCHAR*)CharPrev (sz, pch1); pch1--; // back up ch= *pch1; } if( *pch1 != TEXT('.') ) { if( dwSize + sizeof(".txt") <= MAX_PATH ) { // avoid buffer overruns lstrcat( sz, TEXT(".txt") ); } } } /* AlertUser_FileFail( LPTSTR szFileName ) * * szFileName is the name of file that was attempted to open. * Some sort of failure on file open. Alert the user * with some monologue box. At least give him decent * error messages. */ VOID FAR AlertUser_FileFail( LPTSTR szFileName ) { TCHAR msg[256]; // buffer to format message into DWORD dwStatus; // status from FormatMessage UINT style= MB_APPLMODAL | MB_OK | MB_ICONEXCLAMATION; // Check GetLastError to see why we failed dwStatus= FormatMessage( FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), GetUserDefaultLangID(), msg, // where message will end up CharSizeOf(msg), NULL ); if( dwStatus ) { MessageBox( hwndNP, msg, szNN, style ); } else { AlertBox( hwndNP, szNN, szDiskError, szFileName, style ); } }