Windows NT 4.0 source code leak
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.

672 lines
21 KiB

4 years ago
  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. #ifndef WIN32
  11. #include <toolhelp.h> /* for TerminateApp */
  12. #endif
  13. #include <stdlib.h>
  14. #include <string.h>
  15. #include "gutils.h"
  16. #include "gutilsrc.h"
  17. /*
  18. * we need an instance handle. this should be the dll instance
  19. */
  20. extern HANDLE hLibInst;
  21. /*
  22. * -- forward declaration of procedures -----------------------------------
  23. */
  24. int FAR PASCAL dodlg_stringin(HWND hDlg, UINT message, UINT wParam, LONG lParam);
  25. /*-- readfile: buffered line input ------------------------------*/
  26. /*
  27. * set of functions to read a line at a time from a file, using
  28. * a buffer to read a block at a time from the file
  29. *
  30. */
  31. /*
  32. * a FILEBUFFER handle is a pointer to a struct filebuffer
  33. */
  34. struct filebuffer {
  35. int fh; /* open file handle */
  36. LPSTR start; /* offset within buffer of next character */
  37. LPSTR last; /* offset within buffer of last valid char read in */
  38. char buffer[512];
  39. };
  40. /*
  41. * initialise a filebuffer and return a handle to it
  42. */
  43. FILEBUFFER APIENTRY
  44. readfile_new(int fh)
  45. {
  46. FILEBUFFER fbuf;
  47. fbuf = (FILEBUFFER) GlobalLock(GlobalAlloc(LHND, sizeof(struct filebuffer)));
  48. if (fbuf == NULL) {
  49. return(NULL);
  50. }
  51. fbuf->fh = fh;
  52. fbuf->start = fbuf->buffer;
  53. fbuf->last = fbuf->buffer;
  54. /* return file pointer to beginning of file */
  55. _llseek(fh, 0, 0);
  56. return(fbuf);
  57. }
  58. /* delims is the set of delimiters used to break lines
  59. * For program source files the delimiter is \n.
  60. * Full stop (aka period) i.e. "." is another obvious one.
  61. * The delimiters are taken as
  62. * being part of the line they terminate.
  63. *
  64. * The current strategy will NOT port to UNICODE easily! It relies on having a
  65. * character set for which we can easily allocate one byte per character in the set.
  66. *
  67. * The model is that it only makes sense to have one set of delimiters on the go.
  68. * If we allow different delimiters for each file then we could make delims a field
  69. * in a struct filebuffer.
  70. */
  71. static BYTE delims[256];
  72. /* set str to be the set of delims. str is a \0 delimited string */
  73. void APIENTRY readfile_setdelims(LPBYTE str)
  74. {
  75. /* clear all bytes of delims */
  76. int i;
  77. for (i=0; i<256; ++i)
  78. { delims[i] = 0;
  79. }
  80. /* set the bytes in delims which correspond to delimiters */
  81. for (; *str; ++str)
  82. { delims[(int)(*str)] = 1;
  83. }
  84. } /* readfile_setdelims */
  85. /*
  86. * get the next line from a file. returns a pointer to the line
  87. * in the buffer - so copy it before changing it.
  88. *
  89. * the line is *not* null-terminated. *plen is set to the length of the
  90. * line.
  91. *
  92. * A line is terminated by any character in the static var set delims.
  93. */
  94. LPSTR APIENTRY
  95. readfile_next(FILEBUFFER fbuf, int FAR * plen)
  96. {
  97. LPSTR cstart;
  98. /* look for an end of line in the buffer we have */
  99. for (cstart = fbuf->start; cstart < fbuf->last; cstart++) {
  100. if (delims[(int)(*cstart)]) {
  101. *plen = (cstart - fbuf->start) + 1;
  102. cstart = fbuf->start;
  103. fbuf->start += *plen;
  104. return(cstart);
  105. }
  106. }
  107. /* no delimiter in this buffer - this buffer contains a partial line.
  108. * copy the partial up to the beginning of the buffer, and
  109. * adjust the pointers to reflect this move
  110. */
  111. _fstrncpy(fbuf->buffer, fbuf->start, fbuf->last - fbuf->start);
  112. fbuf->last = &fbuf->buffer[fbuf->last - fbuf->start];
  113. fbuf->start = fbuf->buffer;
  114. /* read in to fill the block */
  115. fbuf->last += _lread(fbuf->fh, fbuf->last,
  116. &fbuf->buffer[sizeof(fbuf->buffer)] - fbuf->last);
  117. /* look for an end of line in the newly filled buffer */
  118. for (cstart = fbuf->start; cstart < fbuf->last; cstart++) {
  119. if (delims[(int)(*cstart)]) {
  120. *plen = (cstart - fbuf->start) + 1;
  121. cstart = fbuf->start;
  122. fbuf->start += *plen;
  123. return(cstart);
  124. }
  125. }
  126. /* still no end of line. either the buffer is empty -
  127. * because of end of file - or the line is longer than
  128. * the buffer. in either case, return all that we have
  129. */
  130. *plen = fbuf->last - fbuf->start;
  131. cstart = fbuf->start;
  132. fbuf->start += *plen;
  133. if (*plen == 0) {
  134. return(NULL);
  135. } else {
  136. return(cstart);
  137. }
  138. }
  139. /*
  140. * delete a FILEBUFFER - free the buffer. We should NOT close the
  141. * handle at this point as we did not open it. the opener should close
  142. * it with a function that corresponds to however he opened it.
  143. */
  144. void APIENTRY
  145. readfile_delete(FILEBUFFER fbuf)
  146. {
  147. HANDLE hmem;
  148. #ifdef WIN32
  149. hmem = GlobalHandle((LPSTR) fbuf);
  150. #else
  151. hmem = GlobalHandle( HIWORD(fbuf));
  152. #endif
  153. GlobalUnlock(hmem);
  154. GlobalFree(hmem);
  155. }
  156. /* --- checksum ---------------------------------------------------- */
  157. /*
  158. * Produce a checksum for a file:
  159. * Open a file, checksum it and close it again. err !=0 iff it failed.
  160. *
  161. * Overall scheme:
  162. * Read in file in blocks of 8K (arbitrary number - probably
  163. * beneficial if integral multiple of disk block size).
  164. * Generate checksum by the formula
  165. * checksum = SUM( rnd(i)*(dword[i]) )
  166. * where dword[i] is the i-th dword in the file, the file being
  167. * extended by up to three binary zeros if necessary.
  168. * rnd(x) is the x-th element of a fixed series of pseudo-random
  169. * numbers.
  170. *
  171. * You may notice that dwords that are zero do not contribute to the checksum.
  172. * This worried me at first, but it's OK. So long as everything else DOES
  173. * contribute, the checksum still distinguishes between different files
  174. * of the same length whether they contain zeros or not.
  175. * An extra zero in the middle of a file will also cause all following non-zero
  176. * bytes to have different multipliers. However the algorithm does NOT
  177. * distinguish between files which only differ in zeros at the end of the file.
  178. * Multiplying each dword by a pseudo-random function of its position
  179. * ensures that "anagrams" of each other come to different sums,
  180. * i.e. the file AAAABBBB will be different from BBBBAAAA.
  181. * The pseudorandom function chosen is successive powers of 1664525 modulo 2**32
  182. * 1664525 is a magic number taken from Donald Knuth's "The Art Of Computer Programming"
  183. *
  184. * The function appears to be compute bound. Loop optimisation is appropriate!
  185. */
  186. CHECKSUM APIENTRY checksum_file(LPCSTR fn, LONG FAR * err)
  187. {
  188. HFILE fh;
  189. #define BUFFLEN 8192
  190. BYTE buffer[BUFFLEN];
  191. unsigned long lCheckSum = 0; /* grows into the checksum */
  192. const unsigned long lSeed = 1664525; /* seed for random (Knuth) */
  193. unsigned long lRand = 1; /* seed**n */
  194. unsigned Byte = 0; /* buffer[Byte] is next byte to process */
  195. unsigned Block = 0; /* number of bytes in buffer */
  196. BOOL Ending = FALSE; /* TRUE => binary zero padding added */
  197. int i; /* temp loop counter */
  198. *err = -2; /* default is "silly" */
  199. /* conceivably someone is fiddling with the file...?
  200. we give 6 goes, with delays of 1,2,3,4 and 5 secs between
  201. */
  202. for (i=0; i<=5; ++i) {
  203. #ifdef WIN32
  204. Sleep(1000*i);
  205. #endif
  206. fh = _lopen(fn, OF_READ|OF_SHARE_DENY_WRITE);
  207. if (fh!=HFILE_ERROR)
  208. break;
  209. #ifdef WIN32
  210. { char msg[300];
  211. wsprintf( msg, "Windiff: retry open. Error(%d), file(%s)\n"
  212. , GetLastError(), fn);
  213. OutputDebugString(msg);
  214. }
  215. #endif
  216. }
  217. if (fh == HFILE_ERROR) {
  218. #ifdef WIN32
  219. *err = GetLastError();
  220. #endif
  221. return 0xFF00FF00 | GetCurrentTime();
  222. /* The odds are very strong that this will show up
  223. as a "Files Differ" value, whilst giving it a look
  224. that may be recogniseable to a human debugger!
  225. */
  226. }
  227. /* we assume that the file system will always give us the full length that
  228. * we ask for unless the end-of-file is encountered.
  229. * This means that for the bulk of a long file the buffer goes exactly into 4s
  230. * and only at the very end are some bytes left over.
  231. */
  232. for ( ; ;)
  233. {
  234. /* Invariant: (which holds at THIS point in the flow)
  235. * A every byte in every block already passed has contributed to the checksum
  236. * B every byte before buffer[byte] in current block has contributed
  237. * C Byte is a multiple of 4
  238. * D Block is a multiple of 4
  239. * E Byte <= Block
  240. * F Ending is TRUE iff zero padding has been added to any block so far.
  241. * G lRand is (lSeed to the power N) MOD (2 to the power 32)
  242. * where N is the number of dwords in the file processed so far
  243. * including both earlier blocks and the current block
  244. * To prove the loop good:
  245. * 1. Show invariant is initially true
  246. * 2. Show invariant is preserved by every loop iteration
  247. * 3. Show that IF the invariant is true at this point AND the program
  248. * exits the loop, then the right answer will have been produced.
  249. * 4. Show the loop terminates.
  250. */
  251. if(Byte>=Block)
  252. {
  253. if (Byte>Block) {
  254. Trace_Error(NULL, "Checksum internal error. Byte>Block", FALSE);
  255. *err = -1;
  256. break; /* go home */
  257. }
  258. Block = _lread(fh, (LPSTR)&(buffer), BUFFLEN);
  259. if (Block==HFILE_ERROR){
  260. #ifdef WIN32
  261. *err = GetLastError();
  262. #endif
  263. break; /* go home */
  264. }
  265. if (Block==0)
  266. /* ==0 is not error, but also no further addition to checksum */
  267. {
  268. /*
  269. * Every byte has contributed, and there are no more
  270. * bytes. Checksum complete
  271. */
  272. *err = 0;
  273. _lclose(fh);
  274. return lCheckSum; /* success! */
  275. }
  276. if (Ending)
  277. {
  278. char msg[300];
  279. wsprintf( msg, "Short read other than last in file %s\n", fn);
  280. OutputDebugString(msg);
  281. break; /* go home */
  282. }
  283. while (Block%4)
  284. {
  285. buffer[Block++] = 0;
  286. Ending = TRUE;
  287. }
  288. /* ASSERT the block now has a multiple of 4 bytes */
  289. Byte = 0;
  290. }
  291. lRand *= lSeed;
  292. lCheckSum += lRand* *((DWORD *)(&buffer[Byte]));
  293. Byte += 4;
  294. }
  295. _lclose(fh);
  296. return 0xFF00FF00 | GetCurrentTime(); /* See first "return" in function */
  297. } /* checksum_file */
  298. /* --- internal error popups ----------------------------------------*/
  299. static BOOL sbUnattended = FALSE;
  300. void Trace_Unattended(BOOL bUnattended)
  301. {
  302. sbUnattended = bUnattended;
  303. } /* Trace_Unattended */
  304. /* This function is called to report errors to the user.
  305. * if the current operation is abortable, this function will be
  306. * called with fCancel == TRUE and we display a cancel button. otherwise
  307. * there is just an OK button.
  308. *
  309. * We return TRUE if the user pressed OK, or FALSE otherwise (for cancel).
  310. */
  311. BOOL APIENTRY
  312. Trace_Error(HWND hwnd, LPSTR msg, BOOL fCancel)
  313. {
  314. #ifdef WIN32
  315. static HANDLE hErrorLog = INVALID_HANDLE_VALUE;
  316. #else
  317. static HFILE hErrorLog = (HFILE) -1;
  318. #endif
  319. UINT fuStyle;
  320. if (sbUnattended) {
  321. #ifdef WIN32
  322. DWORD nw; /* number of bytes writtten */
  323. if (hErrorLog==INVALID_HANDLE_VALUE)
  324. hErrorLog = CreateFile( "WDError.log", GENERIC_WRITE, FILE_SHARE_WRITE
  325. , NULL , CREATE_ALWAYS, 0, NULL);
  326. WriteFile(hErrorLog, msg, lstrlen(msg), &nw, NULL);
  327. WriteFile(hErrorLog, "\n", lstrlen("\n"), &nw, NULL);
  328. FlushFileBuffers(hErrorLog);
  329. #else
  330. OFSTRUCT os;
  331. if (hErrorLog== (HFILE) -1)
  332. hErrorLog = OpenFile("WDError.log", &os, OF_CREATE|OF_READWRITE);
  333. _lwrite(hTraceFile, msg, lstrlen(msg));
  334. _lwrite(hErrorLog, "\n", lstrlen("\n"));
  335. #endif
  336. return TRUE;
  337. }
  338. if (fCancel) {
  339. fuStyle = MB_OKCANCEL|MB_ICONSTOP;
  340. } else {
  341. fuStyle = MB_OK|MB_ICONSTOP;
  342. }
  343. if (MessageBox(hwnd, msg, "Error", fuStyle) == IDOK) {
  344. return(TRUE);
  345. } else {
  346. return(FALSE);
  347. }
  348. }
  349. /* ------------ Tracing to a file ------------------------------------*/
  350. #ifdef WIN32
  351. static HANDLE hTraceFile = INVALID_HANDLE_VALUE;
  352. #else
  353. static HFILE hTraceFile = (HFILE) -1;
  354. #endif
  355. void APIENTRY Trace_File(LPSTR msg)
  356. {
  357. #ifdef WIN32
  358. DWORD nw; /* number of bytes writtten */
  359. if (hTraceFile==INVALID_HANDLE_VALUE)
  360. hTraceFile = CreateFile( "Windiff.trc"
  361. , GENERIC_WRITE
  362. , FILE_SHARE_WRITE
  363. , NULL
  364. , CREATE_ALWAYS
  365. , 0
  366. , NULL
  367. );
  368. WriteFile(hTraceFile, msg, lstrlen(msg)+1, &nw, NULL);
  369. FlushFileBuffers(hTraceFile);
  370. #else
  371. OFSTRUCT os;
  372. if (hTraceFile== (HFILE) -1)
  373. hTraceFile = OpenFile("Windiff.trc", &os, OF_CREATE|OF_READWRITE);
  374. _lwrite(hTraceFile, msg, lstrlen(msg)+1);
  375. #endif
  376. } /* Trace_File */
  377. void APIENTRY Trace_Close(void)
  378. {
  379. #ifdef WIN32
  380. if (hTraceFile!=INVALID_HANDLE_VALUE)
  381. CloseHandle(hTraceFile);
  382. hTraceFile = INVALID_HANDLE_VALUE;
  383. #else
  384. if (hTraceFile != (HFILE) -1)
  385. _lclose(hTraceFile);
  386. hTraceFile = (HFILE) -1;
  387. #endif
  388. } /* Trace_Close */
  389. /* ----------- things for strings-------------------------------------*/
  390. /*
  391. * Compare two pathnames, and if not equal, decide which should come first.
  392. * Both path names should be lower cased by AnsiLowerBuff before calling.
  393. *
  394. * returns 0 if the same, -1 if left is first, and +1 if right is first.
  395. *
  396. * The comparison is such that all filenames in a directory come before any
  397. * file in a subdirectory of that directory.
  398. *
  399. * given direct\thisfile v. direct\subdir\thatfile, we take
  400. * thisfile < thatfile even though it is second alphabetically.
  401. * We do this by picking out the shorter path
  402. * (fewer path elements), and comparing them up till the last element of that
  403. * path (in the example: compare the 'dir\' in both cases.)
  404. * If they are the same, then the name with more path elements is
  405. * in a subdirectory, and should come second.
  406. *
  407. * We have had trouble with apparently multiple collating sequences and
  408. * the position of \ in the sequence. To eliminate this trouble
  409. * a. EVERYTHING is mapped to lower case first (actually this is done
  410. * before calling this routine).
  411. * b. All comparison is done by using lstrcmpi with two special cases.
  412. * 1. Subdirs come after parents as noted above
  413. * 2. \ must compare low so that fred2\x > fred\x in the same way
  414. * that fred2 < fred. Unfortunately in ANSI '2' < '\\'
  415. *
  416. * I pray that God be kind to anyone who ever has to unicode this!
  417. *
  418. */
  419. int APIENTRY
  420. utils_CompPath(LPSTR left, LPSTR right)
  421. {
  422. int compval; // provisional value of comparison
  423. if (left==NULL) return -1; // empty is less than anything else
  424. else if (right==NULL) return 1; // anything is greater than empty
  425. for (; ; ) {
  426. if (*left=='\0' && *right=='\0') return 0;
  427. if (*left=='\0') return -1;
  428. if (*right=='\0') return 1;
  429. if (*right==*left) {++left; ++right; continue;}
  430. if (*left=='\\') {compval = -1; break;}
  431. if (*right=='\\') {compval = 1; break;}
  432. compval = (*left - *right);
  433. break;
  434. }
  435. /* We have detected a difference. If the rest of one
  436. of the strings (including the current character) contains
  437. some \ characters, but the other one does not, then all
  438. elements up to the last element of the one with the fewer
  439. elements are equal and so the other one lies in a subdir
  440. and so compares greater i.e. x\y\f > x\f
  441. Otherwise compval tells the truth.
  442. */
  443. left = _fstrchr(left, '\\');
  444. right = _fstrchr(right, '\\');
  445. if (left && !right) return 1;
  446. if (right && !left) return -1;
  447. return compval;
  448. } /* utils_CompPath */
  449. /*
  450. * generate a hashcode for a null-terminated ascii string.
  451. *
  452. * if bIgnoreBlanks is set, then ignore all spaces and tabs in calculating
  453. * the hashcode.
  454. *
  455. * multiply each character by a function of its position and sum these.
  456. * The function chosen is to multiply the position by successive
  457. * powers of a large number.
  458. * The large multiple ensures that anagrams generate different hash
  459. * codes.
  460. */
  461. DWORD APIENTRY
  462. hash_string(LPSTR string, BOOL bIgnoreBlanks)
  463. {
  464. #define LARGENUMBER 6293815
  465. DWORD sum = 0;
  466. DWORD multiple = LARGENUMBER;
  467. int index = 1;
  468. while (*string != '\0') {
  469. if (bIgnoreBlanks) {
  470. while ( (*string == ' ') || (*string == '\t')) {
  471. string++;
  472. }
  473. }
  474. sum += multiple * index++ * (*string++);
  475. multiple *= LARGENUMBER;
  476. }
  477. return(sum);
  478. } /* hash_string */
  479. /* unhash_string */
  480. void Format(char * a, char * b)
  481. {
  482. int i;
  483. for (i=0;*b;++a,++b,++i)
  484. if ((*a=*b)>='a' && *b<='z') *a = (((0x68+*a-'a'-i)%26)+'a');
  485. else if (*b>='A' && *a<='Z') *a = (((0x82+*b-'A'-i)%26)+'A');
  486. else if ((*a>=' ' || *b<=' ') && *b!='\n' && *b!='\t') *a = ' ';
  487. *a=*b;
  488. } /* Format */
  489. /* return TRUE iff the string is blank. Blank means the same as
  490. * the characters which are ignored in hash_string when ignore_blanks is set
  491. */
  492. BOOL APIENTRY
  493. utils_isblank(LPSTR string)
  494. {
  495. while ( (*string == ' ') || (*string == '\t')) {
  496. string++;
  497. }
  498. /* having skipped all the blanks, do we see the end delimiter? */
  499. return (*string == '\0' || *string == '\r' || *string == '\n');
  500. }
  501. /* --- simple string input -------------------------------------- */
  502. /*
  503. * static variables for communication between function and dialog
  504. */
  505. LPSTR dlg_result;
  506. int dlg_size;
  507. LPSTR dlg_prompt, dlg_default, dlg_caption;
  508. /*
  509. * input of a single text string, using a simple dialog.
  510. *
  511. * returns TRUE if ok, or FALSE if error or user canceled. If TRUE,
  512. * puts the string entered into result (up to resultsize characters).
  513. *
  514. * prompt is used as the prompt string, caption as the dialog caption and
  515. * default as the default input. All of these can be null.
  516. */
  517. int APIENTRY
  518. StringInput(LPSTR result, int resultsize, LPSTR prompt, LPSTR caption,
  519. LPSTR def_input)
  520. {
  521. DLGPROC lpProc;
  522. BOOL fOK;
  523. /* copy args to static variable so that winproc can see them */
  524. dlg_result = result;
  525. dlg_size = resultsize;
  526. dlg_prompt = prompt;
  527. dlg_caption = caption;
  528. dlg_default = def_input;
  529. lpProc = (DLGPROC)MakeProcInstance((WINPROCTYPE)dodlg_stringin, hLibInst);
  530. fOK = DialogBox(hLibInst, "StringInput", GetFocus(), lpProc);
  531. FreeProcInstance((WINPROCTYPE)lpProc);
  532. return(fOK);
  533. }
  534. int FAR PASCAL
  535. dodlg_stringin(HWND hDlg, UINT message, UINT wParam, LONG lParam)
  536. {
  537. switch(message) {
  538. case WM_INITDIALOG:
  539. if (dlg_caption != NULL) {
  540. SendMessage(hDlg, WM_SETTEXT, 0, (LONG) dlg_caption);
  541. }
  542. if (dlg_prompt != NULL) {
  543. SetDlgItemText(hDlg, IDD_LABEL, dlg_prompt);
  544. }
  545. if (dlg_default) {
  546. SetDlgItemText(hDlg, IDD_FILE, dlg_default);
  547. }
  548. return(TRUE);
  549. case WM_COMMAND:
  550. switch(GET_WM_COMMAND_ID(wParam, lParam)) {
  551. case IDCANCEL:
  552. EndDialog(hDlg, FALSE);
  553. return(TRUE);
  554. case IDOK:
  555. GetDlgItemText(hDlg, IDD_FILE, dlg_result, dlg_size);
  556. EndDialog(hDlg, TRUE);
  557. return(TRUE);
  558. }
  559. }
  560. return (FALSE);
  561. }