Leaked source code of windows server 2003
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.

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