Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

996 lines
28 KiB

  1. /*
  2. * utils.c
  3. *
  4. *
  5. * some standard file-reading, hashing and checksum routines.
  6. *
  7. * Geraint Davies, July 92
  8. */
  9. #include <windows.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <winnls.h>
  13. #include "gutils.h"
  14. #include "gutilsrc.h"
  15. const WCHAR c_wchMagic = 0xfeff; // magic marker for Unicode files
  16. /*
  17. * we need an instance handle. this should be the dll instance
  18. */
  19. extern HANDLE hLibInst;
  20. /*
  21. * -- forward declaration of procedures -----------------------------------
  22. */
  23. INT_PTR dodlg_stringin(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
  24. /*-- readfile: buffered line input ------------------------------*/
  25. /*
  26. * set of functions to read a line at a time from a file, using
  27. * a buffer to read a block at a time from the file
  28. *
  29. */
  30. /*
  31. * a FILEBUFFER handle is a pointer to a struct filebuffer
  32. */
  33. struct filebuffer {
  34. int fh; /* open file handle */
  35. LPSTR start; /* offset within buffer of next character */
  36. LPSTR last; /* offset within buffer of last valid char read in */
  37. char buffer[BUFFER_SIZE];
  38. BOOL fUnicode; /* TRUE if the file is Unicode */
  39. WCHAR wzBuffer[MAX_LINE_LENGTH];
  40. LPWSTR pwzStart;
  41. LPWSTR pwzLast;
  42. };
  43. typedef enum {
  44. CT_LEAD = 0,
  45. CT_TRAIL = 1,
  46. CT_ANK = 2,
  47. CT_INVALID = 3,
  48. } DBCSTYPE;
  49. DBCSTYPE
  50. DBCScharType(
  51. LPTSTR str,
  52. int index
  53. )
  54. {
  55. /*
  56. TT .. ??? maybe LEAD or TRAIL
  57. FT .. second == LEAD
  58. FF .. second == ANK
  59. TF .. ??? maybe ANK or TRAIL
  60. */
  61. // (chrisant) this was really broken to use lstrlen here; readfile_next
  62. // uses this on fbuf->buffer which is explicitly NOT null-terminated.
  63. if ( index >= 0 /*|| index <= lstrlen(str)*/ ) { // EOS is valid parameter.
  64. LPTSTR pos = str + index;
  65. DBCSTYPE candidate = (IsDBCSLeadByte( *pos-- ) ? CT_LEAD : CT_ANK);
  66. BOOL maybeTrail = FALSE;
  67. for ( ; pos >= str; pos-- ) {
  68. if ( !IsDBCSLeadByte( *pos ) )
  69. break;
  70. maybeTrail ^= 1;
  71. }
  72. return maybeTrail ? CT_TRAIL : candidate;
  73. }
  74. return CT_INVALID;
  75. }
  76. /*
  77. * initialise a filebuffer and return a handle to it
  78. */
  79. FILEBUFFER
  80. APIENTRY
  81. readfile_new(
  82. int fh,
  83. BOOL *pfUnicode
  84. )
  85. {
  86. FILEBUFFER fbuf;
  87. UINT cbRead;
  88. WCHAR wchMagic;
  89. *pfUnicode = FALSE;
  90. fbuf = (FILEBUFFER) GlobalLock(GlobalAlloc(LHND, sizeof(struct filebuffer)));
  91. if (fbuf == NULL) {
  92. return(NULL);
  93. }
  94. fbuf->fh = fh;
  95. fbuf->start = fbuf->buffer;
  96. fbuf->last = fbuf->buffer;
  97. fbuf->fUnicode = FALSE;
  98. /* return file pointer to beginning of file */
  99. _llseek(fh, 0, 0);
  100. cbRead = _lread(fh, &wchMagic, sizeof(wchMagic));
  101. if (cbRead == 2 && c_wchMagic == wchMagic)
  102. {
  103. fbuf->fUnicode = TRUE;
  104. *pfUnicode = TRUE;
  105. fbuf->pwzStart = fbuf->wzBuffer;
  106. fbuf->pwzLast = fbuf->wzBuffer;
  107. }
  108. else
  109. {
  110. _llseek(fh, 0, 0);
  111. }
  112. return(fbuf);
  113. }
  114. /* delims is the set of delimiters used to break lines
  115. * For program source files the delimiter is \n.
  116. * Full stop (aka period) i.e. "." is another obvious one.
  117. * The delimiters are taken as
  118. * being part of the line they terminate.
  119. *
  120. * The current strategy will NOT port to UNICODE easily! It relies on having a
  121. * character set for which we can easily allocate one byte per character in the set.
  122. *
  123. * The model is that it only makes sense to have one set of delimiters on the go.
  124. * If we allow different delimiters for each file then we could make delims a field
  125. * in a struct filebuffer.
  126. */
  127. static BYTE delims[256];
  128. /* set str to be the set of delims. str is a \0 delimited string */
  129. void
  130. APIENTRY
  131. readfile_setdelims(
  132. LPBYTE str
  133. )
  134. {
  135. /* clear all bytes of delims */
  136. int i;
  137. for (i=0; i<256; ++i) {
  138. delims[i] = 0;
  139. }
  140. /* set the bytes in delims which correspond to delimiters */
  141. for (; *str; ++str) {delims[(int)(*str)] = 1;
  142. }
  143. } /* readfile_setdelims */
  144. static BOOL FFindEOL(FILEBUFFER fbuf, LPSTR *ppszLine, int *pcch, LPWSTR *ppwzLine, int *pcwch)
  145. {
  146. LPSTR psz;
  147. LPWSTR pwz;
  148. if (fbuf->fUnicode)
  149. {
  150. for (pwz = fbuf->pwzStart; pwz < fbuf->pwzLast; pwz++)
  151. {
  152. if (!*pwz)
  153. *pwz = '.';
  154. //$ review: (chrisant) not strictly correct, but easiest for now
  155. // to get unicode up and limping.
  156. if (*pwz < 256 && delims[*pwz])
  157. {
  158. *pcwch = (UINT)(pwz - fbuf->pwzStart) + 1;
  159. *ppwzLine = fbuf->pwzStart;
  160. fbuf->pwzStart += *pcwch;
  161. // notice we fall thru and let the loop below actually return
  162. break;
  163. }
  164. }
  165. }
  166. for (psz = fbuf->start; psz < fbuf->last; psz = CharNext(psz))
  167. {
  168. if (!*psz)
  169. *psz = '.';
  170. if (delims[*psz])
  171. {
  172. *pcch = (UINT)(psz - fbuf->start) + 1;
  173. *ppszLine = fbuf->start;
  174. fbuf->start += *pcch;
  175. return TRUE;
  176. }
  177. }
  178. return FALSE;
  179. }
  180. /*
  181. * get the next line from a file. returns a pointer to the line
  182. * in the buffer - so copy it before changing it.
  183. *
  184. * the line is *not* null-terminated. *plen is set to the length of the
  185. * line.
  186. *
  187. * A line is terminated by any character in the static var set delims.
  188. */
  189. LPSTR APIENTRY
  190. readfile_next(
  191. FILEBUFFER fbuf,
  192. int * plen,
  193. LPWSTR *ppwz,
  194. int *pcwch
  195. )
  196. {
  197. LPSTR cstart;
  198. UINT cbFree;
  199. UINT cbRead;
  200. //$ FUTURE: (chrisant) THIS DOES NOT HANDLE UNICODE 3.0 SURROGATE PAIRS
  201. // CORRECTLY YET.
  202. *ppwz = NULL;
  203. *pcwch = 0;
  204. /* look for an end of line in the buffer we have */
  205. if (FFindEOL(fbuf, &cstart, plen, ppwz, pcwch))
  206. {
  207. return cstart;
  208. }
  209. /* no delimiter in this buffer - this buffer contains a partial line.
  210. * copy the partial up to the beginning of the buffer, and
  211. * adjust the pointers to reflect this move
  212. */
  213. if (fbuf->fUnicode)
  214. {
  215. memmove(fbuf->wzBuffer, fbuf->pwzStart, (LPBYTE)fbuf->pwzLast - (LPBYTE)fbuf->pwzStart);
  216. fbuf->pwzLast = fbuf->wzBuffer + (fbuf->pwzLast - fbuf->pwzStart);
  217. fbuf->pwzStart = fbuf->wzBuffer;
  218. }
  219. memmove(fbuf->buffer, fbuf->start, (LPBYTE)fbuf->last - (LPBYTE)fbuf->start);
  220. fbuf->last = fbuf->buffer + (fbuf->last - fbuf->start);
  221. fbuf->start = fbuf->buffer;
  222. /* read in to fill the block */
  223. if (fbuf->fUnicode)
  224. {
  225. // HACK: for unicode files, we'll read in the unicode and convert it
  226. // to ansi. we try to be clever by converting to ACP, then converting
  227. // back to unicode, and comparing the two unicode strings. for any
  228. // wchars that are not identical, we replace them with 5-byte hex
  229. // codes of the format xFFFF.
  230. char szACP[MAX_LINE_LENGTH * sizeof(WCHAR)];
  231. WCHAR wzRoundtrip[MAX_LINE_LENGTH];
  232. UINT cchAnsi;
  233. UINT cchWide;
  234. UINT cchRoundtrip;
  235. LPWSTR pwzOrig;
  236. LPCWSTR pwzRoundtrip;
  237. LPSTR pszACP;
  238. cbFree = sizeof(fbuf->wzBuffer) - (UINT)((LPBYTE)fbuf->pwzLast - (LPBYTE)fbuf->pwzStart);
  239. cbRead = _lread(fbuf->fh, fbuf->pwzLast, cbFree);
  240. //$ FUTURE: (chrisant) what if we read an odd number of bytes? how
  241. // will that impact the _llseek(... -1 ...) calls near the bottom of
  242. // this function?
  243. // wide to ansi
  244. cchWide = cbRead / 2;
  245. cchAnsi = WideCharToMultiByte(GetACP(),
  246. 0,
  247. fbuf->pwzLast,
  248. cchWide,
  249. szACP,
  250. DimensionOf(szACP),
  251. NULL,
  252. NULL);
  253. // round trip, to find chars not in ACP
  254. cchRoundtrip = MultiByteToWideChar(GetACP(),
  255. 0,
  256. szACP,
  257. cchAnsi,
  258. wzRoundtrip,
  259. DimensionOf(wzRoundtrip));
  260. // find non-ACP chars
  261. pwzOrig = fbuf->pwzLast;
  262. pwzRoundtrip = wzRoundtrip;
  263. pszACP = szACP;
  264. while (cchWide && cchRoundtrip)
  265. {
  266. if (*pwzOrig == *pwzRoundtrip)
  267. {
  268. // copy the DBCS representation into the buffer
  269. if (IsDBCSLeadByte(*pszACP))
  270. *(fbuf->last++) = *(pszACP++);
  271. *(fbuf->last++) = *(pszACP++);
  272. }
  273. else
  274. {
  275. // copy a hexized representation into the buffer
  276. static const char rgHex[] = "0123456789ABCDEF";
  277. *(fbuf->last++) = 'x';
  278. *(fbuf->last++) = rgHex[((*pwzOrig) >> 12) & 0xf];
  279. *(fbuf->last++) = rgHex[((*pwzOrig) >> 8) & 0xf];
  280. *(fbuf->last++) = rgHex[((*pwzOrig) >> 4) & 0xf];
  281. *(fbuf->last++) = rgHex[((*pwzOrig) >> 0) & 0xf];
  282. if (IsDBCSLeadByte(*pszACP))
  283. pszACP++;
  284. pszACP++;
  285. }
  286. ++pwzOrig;
  287. ++pwzRoundtrip;
  288. --cchWide;
  289. --cchRoundtrip;
  290. }
  291. fbuf->pwzLast = pwzOrig;
  292. }
  293. else
  294. {
  295. cbFree = sizeof(fbuf->buffer) - (UINT)((LPBYTE)fbuf->last - (LPBYTE)fbuf->start);
  296. cbRead = _lread(fbuf->fh, fbuf->last, cbFree);
  297. if (cbRead == HFILE_ERROR)
  298. {
  299. cbRead = 0;
  300. }
  301. else if (DBCScharType(fbuf->last, cbRead-1) == CT_LEAD)
  302. {
  303. cbRead--;
  304. *(fbuf->last + cbRead) = '\0';
  305. _llseek(fbuf->fh,-1,FILE_CURRENT);
  306. }
  307. fbuf->last += cbRead;
  308. }
  309. /* look for an end of line in the newly filled buffer */
  310. if (FFindEOL(fbuf, &cstart, plen, ppwz, pcwch))
  311. {
  312. return cstart;
  313. }
  314. /* still no end of line. either the buffer is empty -
  315. * because of end of file - or the line is longer than
  316. * the buffer. in either case, return all that we have
  317. */
  318. if (fbuf->fUnicode)
  319. {
  320. *pcwch = (UINT)(fbuf->pwzLast - fbuf->pwzStart);
  321. *ppwz = fbuf->pwzStart;
  322. fbuf->pwzStart += *pcwch;
  323. }
  324. *plen = (int)(fbuf->last - fbuf->start);
  325. cstart = fbuf->start;
  326. fbuf->start += *plen;
  327. if (*plen == 0) {
  328. return(NULL);
  329. } else {
  330. return(cstart);
  331. }
  332. }
  333. /*
  334. * delete a FILEBUFFER - free the buffer. We should NOT close the
  335. * handle at this point as we did not open it. the opener should close
  336. * it with a function that corresponds to however he opened it.
  337. */
  338. void APIENTRY
  339. readfile_delete(
  340. FILEBUFFER fbuf
  341. )
  342. {
  343. HANDLE hmem;
  344. hmem = GlobalHandle((LPSTR) fbuf);
  345. GlobalUnlock(hmem);
  346. GlobalFree(hmem);
  347. }
  348. /* --- checksum ---------------------------------------------------- */
  349. /*
  350. * Produce a checksum for a file:
  351. * Open a file, checksum it and close it again. err !=0 iff it failed.
  352. *
  353. * Overall scheme:
  354. * Read in file in blocks of 8K (arbitrary number - probably
  355. * beneficial if integral multiple of disk block size).
  356. * Generate checksum by the formula
  357. * checksum = SUM( rnd(i)*(dword[i]) )
  358. * where dword[i] is the i-th dword in the file, the file being
  359. * extended by up to three binary zeros if necessary.
  360. * rnd(x) is the x-th element of a fixed series of pseudo-random
  361. * numbers.
  362. *
  363. * You may notice that dwords that are zero do not contribute to the checksum.
  364. * This worried me at first, but it's OK. So long as everything else DOES
  365. * contribute, the checksum still distinguishes between different files
  366. * of the same length whether they contain zeros or not.
  367. * An extra zero in the middle of a file will also cause all following non-zero
  368. * bytes to have different multipliers. However the algorithm does NOT
  369. * distinguish between files which only differ in zeros at the end of the file.
  370. * Multiplying each dword by a pseudo-random function of its position
  371. * ensures that "anagrams" of each other come to different sums,
  372. * i.e. the file AAAABBBB will be different from BBBBAAAA.
  373. * The pseudorandom function chosen is successive powers of 1664525 modulo 2**32
  374. * 1664525 is a magic number taken from Donald Knuth's "The Art Of Computer Programming"
  375. *
  376. * The function appears to be compute bound. Loop optimisation is appropriate!
  377. */
  378. CHECKSUM
  379. APIENTRY
  380. checksum_file(
  381. LPCSTR fn,
  382. LONG * err
  383. )
  384. {
  385. HFILE fh;
  386. #define BUFFLEN 8192
  387. BYTE buffer[BUFFLEN];
  388. unsigned long lCheckSum = 0; /* grows into the checksum */
  389. const unsigned long lSeed = 1664525; /* seed for random (Knuth) */
  390. unsigned long lRand = 1; /* seed**n */
  391. unsigned Byte = 0; /* buffer[Byte] is next byte to process */
  392. unsigned Block = 0; /* number of bytes in buffer */
  393. BOOL Ending = FALSE; /* TRUE => binary zero padding added */
  394. int i; /* temp loop counter */
  395. *err = -2; /* default is "silly" */
  396. /* conceivably someone is fiddling with the file...?
  397. we give 6 goes, with delays of 1,2,3,4 and 5 secs between
  398. */
  399. for (i=0; i<=5; ++i) {
  400. Sleep(1000*i);
  401. fh = _lopen(fn, OF_READ|OF_SHARE_DENY_WRITE);
  402. if (fh!=HFILE_ERROR)
  403. break;
  404. {
  405. char msg[300];
  406. wsprintf( msg, "Windiff: retry open. Error(%d), file(%s)\n"
  407. , GetLastError(), fn);
  408. OutputDebugString(msg);
  409. }
  410. }
  411. if (fh == HFILE_ERROR) {
  412. *err = GetLastError();
  413. return 0xFF00FF00 | GetCurrentTime();
  414. /* The odds are very strong that this will show up
  415. as a "Files Differ" value, whilst giving it a look
  416. that may be recogniseable to a human debugger!
  417. */
  418. }
  419. /* we assume that the file system will always give us the full length that
  420. * we ask for unless the end-of-file is encountered.
  421. * This means that for the bulk of a long file the buffer goes exactly into 4s
  422. * and only at the very end are some bytes left over.
  423. */
  424. for ( ; ;) {
  425. /* Invariant: (which holds at THIS point in the flow)
  426. * A every byte in every block already passed has contributed to the checksum
  427. * B every byte before buffer[byte] in current block has contributed
  428. * C Byte is a multiple of 4
  429. * D Block is a multiple of 4
  430. * E Byte <= Block
  431. * F Ending is TRUE iff zero padding has been added to any block so far.
  432. * G lRand is (lSeed to the power N) MOD (2 to the power 32)
  433. * where N is the number of dwords in the file processed so far
  434. * including both earlier blocks and the current block
  435. * To prove the loop good:
  436. * 1. Show invariant is initially true
  437. * 2. Show invariant is preserved by every loop iteration
  438. * 3. Show that IF the invariant is true at this point AND the program
  439. * exits the loop, then the right answer will have been produced.
  440. * 4. Show the loop terminates.
  441. */
  442. if (Byte>=Block) {
  443. if (Byte>Block) {
  444. Trace_Error(NULL, "Checksum internal error. Byte>Block", FALSE);
  445. *err = -1;
  446. break; /* go home */
  447. }
  448. Block = _lread(fh, (LPSTR)&(buffer), BUFFLEN);
  449. if (Block==HFILE_ERROR) {
  450. *err = GetLastError();
  451. break; /* go home */
  452. }
  453. if (Block==0)
  454. /* ==0 is not error, but also no further addition to checksum */
  455. {
  456. /*
  457. * Every byte has contributed, and there are no more
  458. * bytes. Checksum complete
  459. */
  460. *err = 0;
  461. _lclose(fh);
  462. return lCheckSum; /* success! */
  463. }
  464. if (Ending) {
  465. char msg[300];
  466. wsprintf( msg, "Short read other than last in file %s\n", fn);
  467. OutputDebugString(msg);
  468. break; /* go home */
  469. }
  470. while (Block%4) {
  471. buffer[Block++] = 0;
  472. Ending = TRUE;
  473. }
  474. /* ASSERT the block now has a multiple of 4 bytes */
  475. Byte = 0;
  476. }
  477. lRand *= lSeed;
  478. lCheckSum += lRand* *((DWORD *)(&buffer[Byte]));
  479. Byte += 4;
  480. }
  481. _lclose(fh);
  482. return 0xFF00FF00 | GetCurrentTime(); /* See first "return" in function */
  483. } /* checksum_file */
  484. /* --- internal error popups ----------------------------------------*/
  485. static BOOL sbUnattended = FALSE;
  486. void
  487. Trace_Unattended(
  488. BOOL bUnattended
  489. )
  490. {
  491. sbUnattended = bUnattended;
  492. } /* Trace_Unattended */
  493. /* This function is called to report errors to the user.
  494. * if the current operation is abortable, this function will be
  495. * called with fCancel == TRUE and we display a cancel button. otherwise
  496. * there is just an OK button.
  497. *
  498. * We return TRUE if the user pressed OK, or FALSE otherwise (for cancel).
  499. */
  500. BOOL APIENTRY
  501. Trace_Error(
  502. HWND hwnd,
  503. LPSTR msg,
  504. BOOL fCancel
  505. )
  506. {
  507. static HANDLE hErrorLog = INVALID_HANDLE_VALUE;
  508. UINT fuStyle;
  509. if (sbUnattended) {
  510. DWORD nw; /* number of bytes writtten */
  511. if (hErrorLog==INVALID_HANDLE_VALUE)
  512. hErrorLog = CreateFile( "WDError.log", GENERIC_WRITE, FILE_SHARE_WRITE
  513. , NULL , CREATE_ALWAYS, 0, NULL);
  514. WriteFile(hErrorLog, msg, lstrlen(msg), &nw, NULL);
  515. WriteFile(hErrorLog, "\n", lstrlen("\n"), &nw, NULL);
  516. FlushFileBuffers(hErrorLog);
  517. return TRUE;
  518. }
  519. if (fCancel) {
  520. fuStyle = MB_OKCANCEL|MB_ICONSTOP;
  521. } else {
  522. fuStyle = MB_OK|MB_ICONSTOP;
  523. }
  524. if (MessageBox(hwnd, msg, NULL, fuStyle) == IDOK) {
  525. return(TRUE);
  526. } else {
  527. return(FALSE);
  528. }
  529. }
  530. /* ------------ Tracing to a file ------------------------------------*/
  531. static HANDLE hTraceFile = INVALID_HANDLE_VALUE;
  532. void
  533. APIENTRY
  534. Trace_File(
  535. LPSTR msg
  536. )
  537. {
  538. DWORD nw; /* number of bytes writtten */
  539. if (hTraceFile==INVALID_HANDLE_VALUE)
  540. hTraceFile = CreateFile( "Windiff.trc"
  541. , GENERIC_WRITE
  542. , FILE_SHARE_WRITE
  543. , NULL
  544. , CREATE_ALWAYS
  545. , 0
  546. , NULL
  547. );
  548. WriteFile(hTraceFile, msg, lstrlen(msg)+1, &nw, NULL);
  549. FlushFileBuffers(hTraceFile);
  550. } /* Trace_File */
  551. void
  552. APIENTRY
  553. Trace_Close(
  554. void
  555. )
  556. {
  557. if (hTraceFile!=INVALID_HANDLE_VALUE)
  558. CloseHandle(hTraceFile);
  559. hTraceFile = INVALID_HANDLE_VALUE;
  560. } /* Trace_Close */
  561. /* ----------- things for strings-------------------------------------*/
  562. /*
  563. * Compare two pathnames, and if not equal, decide which should come first.
  564. * Both path names should be lower cased by AnsiLowerBuff before calling.
  565. *
  566. * returns 0 if the same, -1 if left is first, and +1 if right is first.
  567. *
  568. * The comparison is such that all filenames in a directory come before any
  569. * file in a subdirectory of that directory.
  570. *
  571. * given direct\thisfile v. direct\subdir\thatfile, we take
  572. * thisfile < thatfile even though it is second alphabetically.
  573. * We do this by picking out the shorter path
  574. * (fewer path elements), and comparing them up till the last element of that
  575. * path (in the example: compare the 'dir\' in both cases.)
  576. * If they are the same, then the name with more path elements is
  577. * in a subdirectory, and should come second.
  578. *
  579. * We have had trouble with apparently multiple collating sequences and
  580. * the position of \ in the sequence. To eliminate this trouble
  581. * a. EVERYTHING is mapped to lower case first (actually this is done
  582. * before calling this routine).
  583. * b. All comparison is done by using lstrcmpi with two special cases.
  584. * 1. Subdirs come after parents as noted above
  585. * 2. \ must compare low so that fred2\x > fred\x in the same way
  586. * that fred2 < fred. Unfortunately in ANSI '2' < '\\'
  587. *
  588. * I pray that God be kind to anyone who ever has to unicode this!
  589. *
  590. */
  591. int APIENTRY
  592. utils_CompPath(
  593. LPSTR left,
  594. LPSTR right
  595. )
  596. {
  597. int compval; // provisional value of comparison
  598. if (left==NULL) return -1; // empty is less than anything else
  599. else if (right==NULL) return 1; // anything is greater than empty
  600. for (; ; ) {
  601. if (*left=='\0' && *right=='\0') return 0;
  602. if (*left=='\0') return -1;
  603. if (*right=='\0') return 1;
  604. if (IsDBCSLeadByte(*left) || IsDBCSLeadByte(*right)) {
  605. if (*right != *left) {
  606. compval = (*left - *right);
  607. break;
  608. }
  609. ++left;
  610. ++right;
  611. if (*right != *left) {
  612. compval = (*left - *right);
  613. break;
  614. }
  615. ++left;
  616. ++right;
  617. } else {
  618. if (*right==*left) {++left; ++right; continue;}
  619. if (*left=='\\') {compval = -1; break;}
  620. if (*right=='\\') {compval = 1; break;}
  621. compval = (*left - *right);
  622. break;
  623. }
  624. }
  625. /* We have detected a difference. If the rest of one
  626. of the strings (including the current character) contains
  627. some \ characters, but the other one does not, then all
  628. elements up to the last element of the one with the fewer
  629. elements are equal and so the other one lies in a subdir
  630. and so compares greater i.e. x\y\f > x\f
  631. Otherwise compval tells the truth.
  632. */
  633. left = My_mbschr(left, '\\');
  634. right = My_mbschr(right, '\\');
  635. if (left && !right) return 1;
  636. if (right && !left) return -1;
  637. return compval;
  638. } /* utils_CompPath */
  639. /*
  640. * generate a hashcode for a null-terminated ascii string.
  641. *
  642. * if bIgnoreBlanks is set, then ignore all spaces and tabs in calculating
  643. * the hashcode.
  644. *
  645. * multiply each character by a function of its position and sum these.
  646. * The function chosen is to multiply the position by successive
  647. * powers of a large number.
  648. * The large multiple ensures that anagrams generate different hash
  649. * codes.
  650. */
  651. DWORD APIENTRY
  652. hash_string(
  653. LPSTR string,
  654. BOOL bIgnoreBlanks
  655. )
  656. {
  657. #define LARGENUMBER 6293815
  658. DWORD sum = 0;
  659. DWORD multiple = LARGENUMBER;
  660. int index = 1;
  661. while (*string != '\0') {
  662. if (bIgnoreBlanks) {
  663. while ( (*string == ' ') || (*string == '\t')) {
  664. string++;
  665. }
  666. }
  667. sum += multiple * index++ * (*string++);
  668. multiple *= LARGENUMBER;
  669. }
  670. return(sum);
  671. } /* hash_string */
  672. /* unhash_string */
  673. void
  674. Format(
  675. char * a,
  676. char * b
  677. )
  678. {
  679. int i;
  680. for (i=0;*b;++a,++b,++i)
  681. if ((*a=*b)>='a' && *b<='z') *a = (((0x68+*a-'a'-i)%26)+'a');
  682. else if (*b>='A' && *a<='Z') *a = (((0x82+*b-'A'-i)%26)+'A');
  683. else if ((*a>=' ' || *b<=' ') && *b!='\n' && *b!='\t') *a = ' ';
  684. *a=*b;
  685. } /* Format */
  686. /* return TRUE iff the string is blank. Blank means the same as
  687. * the characters which are ignored in hash_string when ignore_blanks is set
  688. */
  689. BOOL APIENTRY
  690. utils_isblank(
  691. LPSTR string
  692. )
  693. {
  694. while ( (*string == ' ') || (*string == '\t')) {
  695. string++;
  696. }
  697. /* having skipped all the blanks, do we see the end delimiter? */
  698. return (*string == '\0' || *string == '\r' || *string == '\n');
  699. }
  700. /* --- simple string input -------------------------------------- */
  701. /*
  702. * static variables for communication between function and dialog
  703. */
  704. LPSTR dlg_result;
  705. int dlg_size;
  706. LPSTR dlg_prompt, dlg_default, dlg_caption;
  707. /*
  708. * input of a single text string, using a simple dialog.
  709. *
  710. * returns TRUE if ok, or FALSE if error or user canceled. If TRUE,
  711. * puts the string entered into result (up to resultsize characters).
  712. *
  713. * prompt is used as the prompt string, caption as the dialog caption and
  714. * default as the default input. All of these can be null.
  715. */
  716. int APIENTRY
  717. StringInput(
  718. LPSTR result,
  719. int resultsize,
  720. LPSTR prompt,
  721. LPSTR caption,
  722. LPSTR def_input
  723. )
  724. {
  725. DLGPROC lpProc;
  726. BOOL fOK;
  727. /* copy args to static variable so that winproc can see them */
  728. dlg_result = result;
  729. dlg_size = resultsize;
  730. dlg_prompt = prompt;
  731. dlg_caption = caption;
  732. dlg_default = def_input;
  733. lpProc = (DLGPROC)MakeProcInstance((WINPROCTYPE)dodlg_stringin, hLibInst);
  734. fOK = (BOOL) DialogBox(hLibInst, "StringInput", GetFocus(), lpProc);
  735. FreeProcInstance((WINPROCTYPE)lpProc);
  736. return(fOK);
  737. }
  738. INT_PTR
  739. dodlg_stringin(
  740. HWND hDlg,
  741. UINT message,
  742. WPARAM wParam,
  743. LPARAM lParam
  744. )
  745. {
  746. switch (message) {
  747. case WM_INITDIALOG:
  748. if (dlg_caption != NULL) {
  749. SendMessage(hDlg, WM_SETTEXT, 0, (LPARAM) dlg_caption);
  750. }
  751. if (dlg_prompt != NULL) {
  752. SetDlgItemText(hDlg, IDD_LABEL, dlg_prompt);
  753. }
  754. if (dlg_default) {
  755. SetDlgItemText(hDlg, IDD_FILE, dlg_default);
  756. }
  757. return(TRUE);
  758. case WM_COMMAND:
  759. switch (GET_WM_COMMAND_ID(wParam, lParam)) {
  760. case IDCANCEL:
  761. EndDialog(hDlg, FALSE);
  762. return(TRUE);
  763. case IDOK:
  764. GetDlgItemText(hDlg, IDD_FILE, dlg_result, dlg_size);
  765. EndDialog(hDlg, TRUE);
  766. return(TRUE);
  767. }
  768. }
  769. return (FALSE);
  770. }
  771. /***************************************************************************
  772. * Function: My_mbspbrk
  773. *
  774. * Purpose:
  775. *
  776. * DBCS version of strpbrk
  777. *
  778. */
  779. PUCHAR
  780. My_mbspbrk(
  781. PUCHAR psz,
  782. PUCHAR pszSep
  783. )
  784. {
  785. PUCHAR pszSepT;
  786. while (*psz != '\0') {
  787. pszSepT = pszSep;
  788. while (*pszSepT != '\0') {
  789. if (*pszSepT == *psz) {
  790. return psz;
  791. }
  792. pszSepT = CharNext(pszSepT);
  793. }
  794. psz = CharNext(psz);
  795. }
  796. return NULL;
  797. }
  798. /***************************************************************************
  799. * Function: My_mbschr
  800. *
  801. * Purpose:
  802. *
  803. * DBCS version of strchr
  804. *
  805. */
  806. LPSTR
  807. My_mbschr(
  808. LPCSTR psz,
  809. unsigned short uiSep
  810. )
  811. {
  812. while (*psz != '\0' && *psz != uiSep) {
  813. psz = CharNext(psz);
  814. }
  815. return (LPSTR)(*psz == uiSep ? psz : NULL);
  816. }
  817. /***************************************************************************
  818. * Function: My_mbsncpy
  819. *
  820. * Purpose:
  821. *
  822. * DBCS version of strncpy
  823. *
  824. */
  825. LPSTR
  826. My_mbsncpy(
  827. LPSTR psz1,
  828. LPCSTR psz2,
  829. size_t nLength
  830. )
  831. {
  832. int nLen = (int)nLength;
  833. LPTSTR pszSv = psz1;
  834. while (0 < nLen) {
  835. if (*psz2 == '\0') {
  836. *psz1++ = '\0';
  837. nLen--;
  838. } else if (IsDBCSLeadByte(*psz2)) {
  839. if (nLen == 1) {
  840. *psz1 = '\0';
  841. } else {
  842. *psz1++ = *psz2++;
  843. *psz1++ = *psz2++;
  844. }
  845. nLen -= 2;
  846. } else {
  847. *psz1++ = *psz2++;
  848. nLen--;
  849. }
  850. }
  851. return pszSv;
  852. }
  853. /***************************************************************************
  854. * Function: LoadRcString
  855. *
  856. * Purpose: Loads a resource string from string table and returns a pointer
  857. * to the string.
  858. *
  859. * Parameters: wID - resource string id
  860. *
  861. */
  862. LPTSTR
  863. APIENTRY
  864. LoadRcString(
  865. UINT wID
  866. )
  867. {
  868. static TCHAR szBuf[512];
  869. LoadString((HANDLE)GetModuleHandle(NULL),wID,szBuf,sizeof(szBuf));
  870. return szBuf;
  871. }