windows-nt/Source/XPSP1/NT/sdktools/gutils/utils.c

997 lines
28 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*
* utils.c
*
*
* some standard file-reading, hashing and checksum routines.
*
* Geraint Davies, July 92
*/
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <winnls.h>
#include "gutils.h"
#include "gutilsrc.h"
const WCHAR c_wchMagic = 0xfeff; // magic marker for Unicode files
/*
* we need an instance handle. this should be the dll instance
*/
extern HANDLE hLibInst;
/*
* -- forward declaration of procedures -----------------------------------
*/
INT_PTR dodlg_stringin(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
/*-- readfile: buffered line input ------------------------------*/
/*
* set of functions to read a line at a time from a file, using
* a buffer to read a block at a time from the file
*
*/
/*
* a FILEBUFFER handle is a pointer to a struct filebuffer
*/
struct filebuffer {
int fh; /* open file handle */
LPSTR start; /* offset within buffer of next character */
LPSTR last; /* offset within buffer of last valid char read in */
char buffer[BUFFER_SIZE];
BOOL fUnicode; /* TRUE if the file is Unicode */
WCHAR wzBuffer[MAX_LINE_LENGTH];
LPWSTR pwzStart;
LPWSTR pwzLast;
};
typedef enum {
CT_LEAD = 0,
CT_TRAIL = 1,
CT_ANK = 2,
CT_INVALID = 3,
} DBCSTYPE;
DBCSTYPE
DBCScharType(
LPTSTR str,
int index
)
{
/*
TT .. ??? maybe LEAD or TRAIL
FT .. second == LEAD
FF .. second == ANK
TF .. ??? maybe ANK or TRAIL
*/
// (chrisant) this was really broken to use lstrlen here; readfile_next
// uses this on fbuf->buffer which is explicitly NOT null-terminated.
if ( index >= 0 /*|| index <= lstrlen(str)*/ ) { // EOS is valid parameter.
LPTSTR pos = str + index;
DBCSTYPE candidate = (IsDBCSLeadByte( *pos-- ) ? CT_LEAD : CT_ANK);
BOOL maybeTrail = FALSE;
for ( ; pos >= str; pos-- ) {
if ( !IsDBCSLeadByte( *pos ) )
break;
maybeTrail ^= 1;
}
return maybeTrail ? CT_TRAIL : candidate;
}
return CT_INVALID;
}
/*
* initialise a filebuffer and return a handle to it
*/
FILEBUFFER
APIENTRY
readfile_new(
int fh,
BOOL *pfUnicode
)
{
FILEBUFFER fbuf;
UINT cbRead;
WCHAR wchMagic;
*pfUnicode = FALSE;
fbuf = (FILEBUFFER) GlobalLock(GlobalAlloc(LHND, sizeof(struct filebuffer)));
if (fbuf == NULL) {
return(NULL);
}
fbuf->fh = fh;
fbuf->start = fbuf->buffer;
fbuf->last = fbuf->buffer;
fbuf->fUnicode = FALSE;
/* return file pointer to beginning of file */
_llseek(fh, 0, 0);
cbRead = _lread(fh, &wchMagic, sizeof(wchMagic));
if (cbRead == 2 && c_wchMagic == wchMagic)
{
fbuf->fUnicode = TRUE;
*pfUnicode = TRUE;
fbuf->pwzStart = fbuf->wzBuffer;
fbuf->pwzLast = fbuf->wzBuffer;
}
else
{
_llseek(fh, 0, 0);
}
return(fbuf);
}
/* delims is the set of delimiters used to break lines
* For program source files the delimiter is \n.
* Full stop (aka period) i.e. "." is another obvious one.
* The delimiters are taken as
* being part of the line they terminate.
*
* The current strategy will NOT port to UNICODE easily! It relies on having a
* character set for which we can easily allocate one byte per character in the set.
*
* The model is that it only makes sense to have one set of delimiters on the go.
* If we allow different delimiters for each file then we could make delims a field
* in a struct filebuffer.
*/
static BYTE delims[256];
/* set str to be the set of delims. str is a \0 delimited string */
void
APIENTRY
readfile_setdelims(
LPBYTE str
)
{
/* clear all bytes of delims */
int i;
for (i=0; i<256; ++i) {
delims[i] = 0;
}
/* set the bytes in delims which correspond to delimiters */
for (; *str; ++str) {delims[(int)(*str)] = 1;
}
} /* readfile_setdelims */
static BOOL FFindEOL(FILEBUFFER fbuf, LPSTR *ppszLine, int *pcch, LPWSTR *ppwzLine, int *pcwch)
{
LPSTR psz;
LPWSTR pwz;
if (fbuf->fUnicode)
{
for (pwz = fbuf->pwzStart; pwz < fbuf->pwzLast; pwz++)
{
if (!*pwz)
*pwz = '.';
//$ review: (chrisant) not strictly correct, but easiest for now
// to get unicode up and limping.
if (*pwz < 256 && delims[*pwz])
{
*pcwch = (UINT)(pwz - fbuf->pwzStart) + 1;
*ppwzLine = fbuf->pwzStart;
fbuf->pwzStart += *pcwch;
// notice we fall thru and let the loop below actually return
break;
}
}
}
for (psz = fbuf->start; psz < fbuf->last; psz = CharNext(psz))
{
if (!*psz)
*psz = '.';
if (delims[*psz])
{
*pcch = (UINT)(psz - fbuf->start) + 1;
*ppszLine = fbuf->start;
fbuf->start += *pcch;
return TRUE;
}
}
return FALSE;
}
/*
* get the next line from a file. returns a pointer to the line
* in the buffer - so copy it before changing it.
*
* the line is *not* null-terminated. *plen is set to the length of the
* line.
*
* A line is terminated by any character in the static var set delims.
*/
LPSTR APIENTRY
readfile_next(
FILEBUFFER fbuf,
int * plen,
LPWSTR *ppwz,
int *pcwch
)
{
LPSTR cstart;
UINT cbFree;
UINT cbRead;
//$ FUTURE: (chrisant) THIS DOES NOT HANDLE UNICODE 3.0 SURROGATE PAIRS
// CORRECTLY YET.
*ppwz = NULL;
*pcwch = 0;
/* look for an end of line in the buffer we have */
if (FFindEOL(fbuf, &cstart, plen, ppwz, pcwch))
{
return cstart;
}
/* no delimiter in this buffer - this buffer contains a partial line.
* copy the partial up to the beginning of the buffer, and
* adjust the pointers to reflect this move
*/
if (fbuf->fUnicode)
{
memmove(fbuf->wzBuffer, fbuf->pwzStart, (LPBYTE)fbuf->pwzLast - (LPBYTE)fbuf->pwzStart);
fbuf->pwzLast = fbuf->wzBuffer + (fbuf->pwzLast - fbuf->pwzStart);
fbuf->pwzStart = fbuf->wzBuffer;
}
memmove(fbuf->buffer, fbuf->start, (LPBYTE)fbuf->last - (LPBYTE)fbuf->start);
fbuf->last = fbuf->buffer + (fbuf->last - fbuf->start);
fbuf->start = fbuf->buffer;
/* read in to fill the block */
if (fbuf->fUnicode)
{
// HACK: for unicode files, we'll read in the unicode and convert it
// to ansi. we try to be clever by converting to ACP, then converting
// back to unicode, and comparing the two unicode strings. for any
// wchars that are not identical, we replace them with 5-byte hex
// codes of the format xFFFF.
char szACP[MAX_LINE_LENGTH * sizeof(WCHAR)];
WCHAR wzRoundtrip[MAX_LINE_LENGTH];
UINT cchAnsi;
UINT cchWide;
UINT cchRoundtrip;
LPWSTR pwzOrig;
LPCWSTR pwzRoundtrip;
LPSTR pszACP;
cbFree = sizeof(fbuf->wzBuffer) - (UINT)((LPBYTE)fbuf->pwzLast - (LPBYTE)fbuf->pwzStart);
cbRead = _lread(fbuf->fh, fbuf->pwzLast, cbFree);
//$ FUTURE: (chrisant) what if we read an odd number of bytes? how
// will that impact the _llseek(... -1 ...) calls near the bottom of
// this function?
// wide to ansi
cchWide = cbRead / 2;
cchAnsi = WideCharToMultiByte(GetACP(),
0,
fbuf->pwzLast,
cchWide,
szACP,
DimensionOf(szACP),
NULL,
NULL);
// round trip, to find chars not in ACP
cchRoundtrip = MultiByteToWideChar(GetACP(),
0,
szACP,
cchAnsi,
wzRoundtrip,
DimensionOf(wzRoundtrip));
// find non-ACP chars
pwzOrig = fbuf->pwzLast;
pwzRoundtrip = wzRoundtrip;
pszACP = szACP;
while (cchWide && cchRoundtrip)
{
if (*pwzOrig == *pwzRoundtrip)
{
// copy the DBCS representation into the buffer
if (IsDBCSLeadByte(*pszACP))
*(fbuf->last++) = *(pszACP++);
*(fbuf->last++) = *(pszACP++);
}
else
{
// copy a hexized representation into the buffer
static const char rgHex[] = "0123456789ABCDEF";
*(fbuf->last++) = 'x';
*(fbuf->last++) = rgHex[((*pwzOrig) >> 12) & 0xf];
*(fbuf->last++) = rgHex[((*pwzOrig) >> 8) & 0xf];
*(fbuf->last++) = rgHex[((*pwzOrig) >> 4) & 0xf];
*(fbuf->last++) = rgHex[((*pwzOrig) >> 0) & 0xf];
if (IsDBCSLeadByte(*pszACP))
pszACP++;
pszACP++;
}
++pwzOrig;
++pwzRoundtrip;
--cchWide;
--cchRoundtrip;
}
fbuf->pwzLast = pwzOrig;
}
else
{
cbFree = sizeof(fbuf->buffer) - (UINT)((LPBYTE)fbuf->last - (LPBYTE)fbuf->start);
cbRead = _lread(fbuf->fh, fbuf->last, cbFree);
if (cbRead == HFILE_ERROR)
{
cbRead = 0;
}
else if (DBCScharType(fbuf->last, cbRead-1) == CT_LEAD)
{
cbRead--;
*(fbuf->last + cbRead) = '\0';
_llseek(fbuf->fh,-1,FILE_CURRENT);
}
fbuf->last += cbRead;
}
/* look for an end of line in the newly filled buffer */
if (FFindEOL(fbuf, &cstart, plen, ppwz, pcwch))
{
return cstart;
}
/* still no end of line. either the buffer is empty -
* because of end of file - or the line is longer than
* the buffer. in either case, return all that we have
*/
if (fbuf->fUnicode)
{
*pcwch = (UINT)(fbuf->pwzLast - fbuf->pwzStart);
*ppwz = fbuf->pwzStart;
fbuf->pwzStart += *pcwch;
}
*plen = (int)(fbuf->last - fbuf->start);
cstart = fbuf->start;
fbuf->start += *plen;
if (*plen == 0) {
return(NULL);
} else {
return(cstart);
}
}
/*
* delete a FILEBUFFER - free the buffer. We should NOT close the
* handle at this point as we did not open it. the opener should close
* it with a function that corresponds to however he opened it.
*/
void APIENTRY
readfile_delete(
FILEBUFFER fbuf
)
{
HANDLE hmem;
hmem = GlobalHandle((LPSTR) fbuf);
GlobalUnlock(hmem);
GlobalFree(hmem);
}
/* --- checksum ---------------------------------------------------- */
/*
* Produce a checksum for a file:
* Open a file, checksum it and close it again. err !=0 iff it failed.
*
* Overall scheme:
* Read in file in blocks of 8K (arbitrary number - probably
* beneficial if integral multiple of disk block size).
* Generate checksum by the formula
* checksum = SUM( rnd(i)*(dword[i]) )
* where dword[i] is the i-th dword in the file, the file being
* extended by up to three binary zeros if necessary.
* rnd(x) is the x-th element of a fixed series of pseudo-random
* numbers.
*
* You may notice that dwords that are zero do not contribute to the checksum.
* This worried me at first, but it's OK. So long as everything else DOES
* contribute, the checksum still distinguishes between different files
* of the same length whether they contain zeros or not.
* An extra zero in the middle of a file will also cause all following non-zero
* bytes to have different multipliers. However the algorithm does NOT
* distinguish between files which only differ in zeros at the end of the file.
* Multiplying each dword by a pseudo-random function of its position
* ensures that "anagrams" of each other come to different sums,
* i.e. the file AAAABBBB will be different from BBBBAAAA.
* The pseudorandom function chosen is successive powers of 1664525 modulo 2**32
* 1664525 is a magic number taken from Donald Knuth's "The Art Of Computer Programming"
*
* The function appears to be compute bound. Loop optimisation is appropriate!
*/
CHECKSUM
APIENTRY
checksum_file(
LPCSTR fn,
LONG * err
)
{
HFILE fh;
#define BUFFLEN 8192
BYTE buffer[BUFFLEN];
unsigned long lCheckSum = 0; /* grows into the checksum */
const unsigned long lSeed = 1664525; /* seed for random (Knuth) */
unsigned long lRand = 1; /* seed**n */
unsigned Byte = 0; /* buffer[Byte] is next byte to process */
unsigned Block = 0; /* number of bytes in buffer */
BOOL Ending = FALSE; /* TRUE => binary zero padding added */
int i; /* temp loop counter */
*err = -2; /* default is "silly" */
/* conceivably someone is fiddling with the file...?
we give 6 goes, with delays of 1,2,3,4 and 5 secs between
*/
for (i=0; i<=5; ++i) {
Sleep(1000*i);
fh = _lopen(fn, OF_READ|OF_SHARE_DENY_WRITE);
if (fh!=HFILE_ERROR)
break;
{
char msg[300];
wsprintf( msg, "Windiff: retry open. Error(%d), file(%s)\n"
, GetLastError(), fn);
OutputDebugString(msg);
}
}
if (fh == HFILE_ERROR) {
*err = GetLastError();
return 0xFF00FF00 | GetCurrentTime();
/* The odds are very strong that this will show up
as a "Files Differ" value, whilst giving it a look
that may be recogniseable to a human debugger!
*/
}
/* we assume that the file system will always give us the full length that
* we ask for unless the end-of-file is encountered.
* This means that for the bulk of a long file the buffer goes exactly into 4s
* and only at the very end are some bytes left over.
*/
for ( ; ;) {
/* Invariant: (which holds at THIS point in the flow)
* A every byte in every block already passed has contributed to the checksum
* B every byte before buffer[byte] in current block has contributed
* C Byte is a multiple of 4
* D Block is a multiple of 4
* E Byte <= Block
* F Ending is TRUE iff zero padding has been added to any block so far.
* G lRand is (lSeed to the power N) MOD (2 to the power 32)
* where N is the number of dwords in the file processed so far
* including both earlier blocks and the current block
* To prove the loop good:
* 1. Show invariant is initially true
* 2. Show invariant is preserved by every loop iteration
* 3. Show that IF the invariant is true at this point AND the program
* exits the loop, then the right answer will have been produced.
* 4. Show the loop terminates.
*/
if (Byte>=Block) {
if (Byte>Block) {
Trace_Error(NULL, "Checksum internal error. Byte>Block", FALSE);
*err = -1;
break; /* go home */
}
Block = _lread(fh, (LPSTR)&(buffer), BUFFLEN);
if (Block==HFILE_ERROR) {
*err = GetLastError();
break; /* go home */
}
if (Block==0)
/* ==0 is not error, but also no further addition to checksum */
{
/*
* Every byte has contributed, and there are no more
* bytes. Checksum complete
*/
*err = 0;
_lclose(fh);
return lCheckSum; /* success! */
}
if (Ending) {
char msg[300];
wsprintf( msg, "Short read other than last in file %s\n", fn);
OutputDebugString(msg);
break; /* go home */
}
while (Block%4) {
buffer[Block++] = 0;
Ending = TRUE;
}
/* ASSERT the block now has a multiple of 4 bytes */
Byte = 0;
}
lRand *= lSeed;
lCheckSum += lRand* *((DWORD *)(&buffer[Byte]));
Byte += 4;
}
_lclose(fh);
return 0xFF00FF00 | GetCurrentTime(); /* See first "return" in function */
} /* checksum_file */
/* --- internal error popups ----------------------------------------*/
static BOOL sbUnattended = FALSE;
void
Trace_Unattended(
BOOL bUnattended
)
{
sbUnattended = bUnattended;
} /* Trace_Unattended */
/* This function is called to report errors to the user.
* if the current operation is abortable, this function will be
* called with fCancel == TRUE and we display a cancel button. otherwise
* there is just an OK button.
*
* We return TRUE if the user pressed OK, or FALSE otherwise (for cancel).
*/
BOOL APIENTRY
Trace_Error(
HWND hwnd,
LPSTR msg,
BOOL fCancel
)
{
static HANDLE hErrorLog = INVALID_HANDLE_VALUE;
UINT fuStyle;
if (sbUnattended) {
DWORD nw; /* number of bytes writtten */
if (hErrorLog==INVALID_HANDLE_VALUE)
hErrorLog = CreateFile( "WDError.log", GENERIC_WRITE, FILE_SHARE_WRITE
, NULL , CREATE_ALWAYS, 0, NULL);
WriteFile(hErrorLog, msg, lstrlen(msg), &nw, NULL);
WriteFile(hErrorLog, "\n", lstrlen("\n"), &nw, NULL);
FlushFileBuffers(hErrorLog);
return TRUE;
}
if (fCancel) {
fuStyle = MB_OKCANCEL|MB_ICONSTOP;
} else {
fuStyle = MB_OK|MB_ICONSTOP;
}
if (MessageBox(hwnd, msg, NULL, fuStyle) == IDOK) {
return(TRUE);
} else {
return(FALSE);
}
}
/* ------------ Tracing to a file ------------------------------------*/
static HANDLE hTraceFile = INVALID_HANDLE_VALUE;
void
APIENTRY
Trace_File(
LPSTR msg
)
{
DWORD nw; /* number of bytes writtten */
if (hTraceFile==INVALID_HANDLE_VALUE)
hTraceFile = CreateFile( "Windiff.trc"
, GENERIC_WRITE
, FILE_SHARE_WRITE
, NULL
, CREATE_ALWAYS
, 0
, NULL
);
WriteFile(hTraceFile, msg, lstrlen(msg)+1, &nw, NULL);
FlushFileBuffers(hTraceFile);
} /* Trace_File */
void
APIENTRY
Trace_Close(
void
)
{
if (hTraceFile!=INVALID_HANDLE_VALUE)
CloseHandle(hTraceFile);
hTraceFile = INVALID_HANDLE_VALUE;
} /* Trace_Close */
/* ----------- things for strings-------------------------------------*/
/*
* Compare two pathnames, and if not equal, decide which should come first.
* Both path names should be lower cased by AnsiLowerBuff before calling.
*
* returns 0 if the same, -1 if left is first, and +1 if right is first.
*
* The comparison is such that all filenames in a directory come before any
* file in a subdirectory of that directory.
*
* given direct\thisfile v. direct\subdir\thatfile, we take
* thisfile < thatfile even though it is second alphabetically.
* We do this by picking out the shorter path
* (fewer path elements), and comparing them up till the last element of that
* path (in the example: compare the 'dir\' in both cases.)
* If they are the same, then the name with more path elements is
* in a subdirectory, and should come second.
*
* We have had trouble with apparently multiple collating sequences and
* the position of \ in the sequence. To eliminate this trouble
* a. EVERYTHING is mapped to lower case first (actually this is done
* before calling this routine).
* b. All comparison is done by using lstrcmpi with two special cases.
* 1. Subdirs come after parents as noted above
* 2. \ must compare low so that fred2\x > fred\x in the same way
* that fred2 < fred. Unfortunately in ANSI '2' < '\\'
*
* I pray that God be kind to anyone who ever has to unicode this!
*
*/
int APIENTRY
utils_CompPath(
LPSTR left,
LPSTR right
)
{
int compval; // provisional value of comparison
if (left==NULL) return -1; // empty is less than anything else
else if (right==NULL) return 1; // anything is greater than empty
for (; ; ) {
if (*left=='\0' && *right=='\0') return 0;
if (*left=='\0') return -1;
if (*right=='\0') return 1;
if (IsDBCSLeadByte(*left) || IsDBCSLeadByte(*right)) {
if (*right != *left) {
compval = (*left - *right);
break;
}
++left;
++right;
if (*right != *left) {
compval = (*left - *right);
break;
}
++left;
++right;
} else {
if (*right==*left) {++left; ++right; continue;}
if (*left=='\\') {compval = -1; break;}
if (*right=='\\') {compval = 1; break;}
compval = (*left - *right);
break;
}
}
/* We have detected a difference. If the rest of one
of the strings (including the current character) contains
some \ characters, but the other one does not, then all
elements up to the last element of the one with the fewer
elements are equal and so the other one lies in a subdir
and so compares greater i.e. x\y\f > x\f
Otherwise compval tells the truth.
*/
left = My_mbschr(left, '\\');
right = My_mbschr(right, '\\');
if (left && !right) return 1;
if (right && !left) return -1;
return compval;
} /* utils_CompPath */
/*
* generate a hashcode for a null-terminated ascii string.
*
* if bIgnoreBlanks is set, then ignore all spaces and tabs in calculating
* the hashcode.
*
* multiply each character by a function of its position and sum these.
* The function chosen is to multiply the position by successive
* powers of a large number.
* The large multiple ensures that anagrams generate different hash
* codes.
*/
DWORD APIENTRY
hash_string(
LPSTR string,
BOOL bIgnoreBlanks
)
{
#define LARGENUMBER 6293815
DWORD sum = 0;
DWORD multiple = LARGENUMBER;
int index = 1;
while (*string != '\0') {
if (bIgnoreBlanks) {
while ( (*string == ' ') || (*string == '\t')) {
string++;
}
}
sum += multiple * index++ * (*string++);
multiple *= LARGENUMBER;
}
return(sum);
} /* hash_string */
/* unhash_string */
void
Format(
char * a,
char * b
)
{
int i;
for (i=0;*b;++a,++b,++i)
if ((*a=*b)>='a' && *b<='z') *a = (((0x68+*a-'a'-i)%26)+'a');
else if (*b>='A' && *a<='Z') *a = (((0x82+*b-'A'-i)%26)+'A');
else if ((*a>=' ' || *b<=' ') && *b!='\n' && *b!='\t') *a = ' ';
*a=*b;
} /* Format */
/* return TRUE iff the string is blank. Blank means the same as
* the characters which are ignored in hash_string when ignore_blanks is set
*/
BOOL APIENTRY
utils_isblank(
LPSTR string
)
{
while ( (*string == ' ') || (*string == '\t')) {
string++;
}
/* having skipped all the blanks, do we see the end delimiter? */
return (*string == '\0' || *string == '\r' || *string == '\n');
}
/* --- simple string input -------------------------------------- */
/*
* static variables for communication between function and dialog
*/
LPSTR dlg_result;
int dlg_size;
LPSTR dlg_prompt, dlg_default, dlg_caption;
/*
* input of a single text string, using a simple dialog.
*
* returns TRUE if ok, or FALSE if error or user canceled. If TRUE,
* puts the string entered into result (up to resultsize characters).
*
* prompt is used as the prompt string, caption as the dialog caption and
* default as the default input. All of these can be null.
*/
int APIENTRY
StringInput(
LPSTR result,
int resultsize,
LPSTR prompt,
LPSTR caption,
LPSTR def_input
)
{
DLGPROC lpProc;
BOOL fOK;
/* copy args to static variable so that winproc can see them */
dlg_result = result;
dlg_size = resultsize;
dlg_prompt = prompt;
dlg_caption = caption;
dlg_default = def_input;
lpProc = (DLGPROC)MakeProcInstance((WINPROCTYPE)dodlg_stringin, hLibInst);
fOK = (BOOL) DialogBox(hLibInst, "StringInput", GetFocus(), lpProc);
FreeProcInstance((WINPROCTYPE)lpProc);
return(fOK);
}
INT_PTR
dodlg_stringin(
HWND hDlg,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
switch (message) {
case WM_INITDIALOG:
if (dlg_caption != NULL) {
SendMessage(hDlg, WM_SETTEXT, 0, (LPARAM) dlg_caption);
}
if (dlg_prompt != NULL) {
SetDlgItemText(hDlg, IDD_LABEL, dlg_prompt);
}
if (dlg_default) {
SetDlgItemText(hDlg, IDD_FILE, dlg_default);
}
return(TRUE);
case WM_COMMAND:
switch (GET_WM_COMMAND_ID(wParam, lParam)) {
case IDCANCEL:
EndDialog(hDlg, FALSE);
return(TRUE);
case IDOK:
GetDlgItemText(hDlg, IDD_FILE, dlg_result, dlg_size);
EndDialog(hDlg, TRUE);
return(TRUE);
}
}
return (FALSE);
}
/***************************************************************************
* Function: My_mbspbrk
*
* Purpose:
*
* DBCS version of strpbrk
*
*/
PUCHAR
My_mbspbrk(
PUCHAR psz,
PUCHAR pszSep
)
{
PUCHAR pszSepT;
while (*psz != '\0') {
pszSepT = pszSep;
while (*pszSepT != '\0') {
if (*pszSepT == *psz) {
return psz;
}
pszSepT = CharNext(pszSepT);
}
psz = CharNext(psz);
}
return NULL;
}
/***************************************************************************
* Function: My_mbschr
*
* Purpose:
*
* DBCS version of strchr
*
*/
LPSTR
My_mbschr(
LPCSTR psz,
unsigned short uiSep
)
{
while (*psz != '\0' && *psz != uiSep) {
psz = CharNext(psz);
}
return (LPSTR)(*psz == uiSep ? psz : NULL);
}
/***************************************************************************
* Function: My_mbsncpy
*
* Purpose:
*
* DBCS version of strncpy
*
*/
LPSTR
My_mbsncpy(
LPSTR psz1,
LPCSTR psz2,
size_t nLength
)
{
int nLen = (int)nLength;
LPTSTR pszSv = psz1;
while (0 < nLen) {
if (*psz2 == '\0') {
*psz1++ = '\0';
nLen--;
} else if (IsDBCSLeadByte(*psz2)) {
if (nLen == 1) {
*psz1 = '\0';
} else {
*psz1++ = *psz2++;
*psz1++ = *psz2++;
}
nLen -= 2;
} else {
*psz1++ = *psz2++;
nLen--;
}
}
return pszSv;
}
/***************************************************************************
* Function: LoadRcString
*
* Purpose: Loads a resource string from string table and returns a pointer
* to the string.
*
* Parameters: wID - resource string id
*
*/
LPTSTR
APIENTRY
LoadRcString(
UINT wID
)
{
static TCHAR szBuf[512];
LoadString((HANDLE)GetModuleHandle(NULL),wID,szBuf,sizeof(szBuf));
return szBuf;
}