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.

2525 lines
84 KiB

  1. // FINDSTR (used to be QGREP), June 1992
  2. //
  3. // Modification History:
  4. //
  5. // Aug 1990 PeteS Created.
  6. // 1990 DaveGi Ported to Cruiser
  7. // 31-Oct-1990 W-Barry Removed the #ifdef M_I386 'cause this
  8. // code will never see 16bit again.
  9. // June 1992 t-petes Added recursive file search in subdirs.
  10. // Used file mapping instead of multi-thread.
  11. // Disabled internal switches.
  12. // Internatioanlized display messages.
  13. // Made switches case-insensitive.
  14. // 05/08/93 v-junm Added Japanese search support.
  15. // 06/03/93 v-junm Added Bilingual Message support>
  16. /* About FILEMAP support:
  17. * The file mapping object is used to speed up string searches. The new
  18. * file mapping method is coded as #ifdef-#else-#endif to show the
  19. * changes needed to be made. The old code(non-filemapping) has a read
  20. * buffer like this:
  21. *
  22. * filbuf[] = {.....................................}
  23. * ^ ^
  24. * BegPtr EndPtr
  25. *
  26. * This means there are some spare space before BegPtr and after EndPtr
  27. * for the search algorithm to work its way. The old code also
  28. * occasionally modifies filbuf[](like filbuf[i] = '\n';).
  29. *
  30. * The new code(filemapping) must avoid doing all of the above because
  31. * there are no spare space before BegPtr or after EndPtr when mapping
  32. * view of the file which is opened as read-only.
  33. */
  34. #include <nt.h>
  35. #include <ntrtl.h>
  36. #include <nturtl.h>
  37. #include <stdio.h>
  38. #include <time.h>
  39. #include <stdlib.h>
  40. #include <string.h>
  41. #include <fcntl.h>
  42. #include <io.h>
  43. #include <windows.h>
  44. #include <ctype.h>
  45. #include <assert.h>
  46. #include <locale.h>
  47. #include <stdarg.h>
  48. #include "fsmsg.h"
  49. #define FILBUFLEN (SECTORLEN*2)
  50. #define ISCOT 0x0002 // Handle is console output
  51. #define LG2SECLEN 10 // Log base two of sector length
  52. #define LNOLEN 12 // Maximum line number length
  53. #define MAXSTRLEN 128 // Maximum search string length
  54. #define OUTBUFLEN (SECTORLEN*2) // Output buffer length
  55. #define PATHLEN (MAX_PATH+2) // Path buffer length
  56. #define SECTORLEN (1 << LG2SECLEN) // Sector length
  57. #define STKLEN 512 // Stack length in bytes
  58. #define TRTABLEN 256 // Translation table length
  59. #define s_text(x) (((char *)(x)) - ((x)->s_must)) // Text field access macro
  60. #define EOS ('\r') // End of string
  61. #define CURRENT_DIRECTORY_MAX_LENGTH 512
  62. #define MAX_SLASH_C_OPTION 100
  63. // Bit flag definitions
  64. #define SHOWNAME 0x01 // Print filename
  65. #define NAMEONLY 0x02 // Print filename only
  66. #define LINENOS 0x04 // Print line numbers
  67. #define BEGLINE 0x08 // Match at beginning of line
  68. #define ENDLINE 0x10 // Match at end of line
  69. #define DEBUG 0x20 // Print debugging output
  70. #define TIMER 0x40 // Time execution
  71. #define SEEKOFF 0x80 // Print seek offsets
  72. #define PRINTABLE_ONLY 0x100 // Skip files with non-printable characters
  73. #define OFFLINE_FILES 0x200 // Do not skip offline files
  74. #define DISPLAYBUFFER_SIZE 4096
  75. // Type definitions
  76. typedef struct stringnode {
  77. struct stringnode *s_alt; // List of alternates
  78. struct stringnode *s_suf; // List of suffixes
  79. int s_must; // Length of portion that must match
  80. }
  81. STRINGNODE; // String node
  82. typedef ULONG CBIO; // I/O byte count
  83. typedef ULONG PARM; // Generic parameter
  84. typedef CBIO *PCBIO; // Pointer to I/O byte count
  85. typedef PARM *PPARM; // Pointer to generic parameter
  86. // Global data
  87. char *BaseByteAddress = NULL; // File mapping base address
  88. BOOL bStdIn = FALSE; // Std-input file flag
  89. BOOL bLargeFile = FALSE; // Dealing with non-memory mapped file
  90. #ifdef FE_SB
  91. BOOL IsDBCSCodePage = TRUE;
  92. #endif
  93. char filbuf[FILBUFLEN*2L + 12];
  94. char outbuf[OUTBUFLEN*2];
  95. char td1[TRTABLEN] = { 0 };
  96. unsigned cchmin = (unsigned)-1; // Minimum string length
  97. unsigned chmax = 0; // Maximum character
  98. unsigned chmin = (unsigned)-1; // Minimum character
  99. char transtab[TRTABLEN] = { 0 };
  100. STRINGNODE *stringlist[TRTABLEN/2];
  101. int casesen = 1; // Assume case-sensitivity
  102. long cbfile; // Number of bytes in file
  103. static int clists = 1; // One is first available index
  104. int flags; // Flags
  105. unsigned lineno; // Current line number
  106. char *program; // Program name
  107. int status = 1; // Assume failure
  108. int strcnt = 0; // String count
  109. char target[MAXSTRLEN]; // Last string added
  110. int targetlen; // Length of last string added
  111. unsigned waste; // Wasted storage in heap
  112. int arrc; // I/O return code for DOSREAD
  113. char asyncio; // Asynchronous I/O flag
  114. int awrc = TRUE; // I/O return code for DOSWRITE
  115. char *bufptr[] = { filbuf + 4, filbuf + FILBUFLEN + 8 };
  116. CBIO cbread; // Bytes read by DOSREAD
  117. CBIO cbwrite; // Bytes written by DOSWRITE
  118. char *obuf[] = { outbuf, outbuf + OUTBUFLEN };
  119. int ocnt[] = { OUTBUFLEN, OUTBUFLEN };
  120. int oi = 0; // Output buffer index
  121. char *optr[] = { outbuf, outbuf + OUTBUFLEN };
  122. char pmode; // Protected mode flag
  123. WORD wAttrib = 0; // filename color
  124. CONSOLE_SCREEN_BUFFER_INFO csbi = {0}; // Our default screen info
  125. CRITICAL_SECTION critSection;
  126. BOOLEAN fExiting = FALSE;
  127. BOOLEAN fOfflineSkipped = FALSE; // Whether offline files were skipped
  128. // External functions and forward references
  129. void printmessage(FILE *fp, DWORD messagegID, ...);
  130. // Message display function for internationalization
  131. int filematch(char *pszfile, char **ppszpat, int cpat, int fsubdirs);
  132. #ifdef FE_SB
  133. // Function to check if a certain location in a string is the second byte
  134. // of a DBCS character.
  135. int IsTailByte( unsigned const char *, const int );
  136. int _mbsnicmp( const unsigned char *, const unsigned char *, int, BOOL * );
  137. unsigned char *_mbslwr( unsigned char * );
  138. char *_mbsrchr( const char *, int );
  139. #endif
  140. void addexpr( char *, int ); // See QMATCH.C
  141. void addstring( char *, int ); // See below
  142. int countlines( char *, char * );
  143. char *findexpr( unsigned char *, char *); // See QMATCH.C
  144. char *findlist( unsigned char *, char * );
  145. char *findone( unsigned char *buffer, char *bufend );
  146. void flush1buf( void ); // See below
  147. void flush1nobuf( void ); // See below
  148. int grepbuffer( char *, char *, char * ); // See below
  149. int isexpr( unsigned char *, int ); // See QMATCH.C
  150. void matchstrings( char *, char *, int, int *, int * );
  151. int preveol( char * );
  152. int strncspn( char *, char *, int );
  153. int strnspn( char *, char *, int );
  154. char *strnupr( char *pch, int cch );
  155. void write1buf( char *, int, WORD ); // See below
  156. void (*addstr)( char *, int ) = NULL;
  157. char *(*find)( unsigned char *, char * ) = NULL;
  158. void (*flush1)( void ) = flush1buf;
  159. int (*grep)( char *, char *, char * ) = grepbuffer;
  160. void (*write1)( char *, int, WORD ) = write1buf;
  161. void write1nobuf( char *, int, WORD );
  162. int
  163. has_wild_cards(
  164. char* p
  165. )
  166. {
  167. if (!p)
  168. return 0;
  169. for (; *p; p++) {
  170. if (*p == '?' || *p == '*') {
  171. return 1;
  172. }
  173. }
  174. return 0;
  175. }
  176. void
  177. error(
  178. DWORD messageID
  179. )
  180. {
  181. printmessage(stderr, messageID, program);
  182. // Print message
  183. exit(2); // Die
  184. }
  185. char *
  186. alloc(
  187. unsigned size
  188. )
  189. {
  190. char *cp; // Char pointer
  191. if ((cp = (char *) malloc(size)) == NULL) { // If allocation fails
  192. printmessage(stderr, MSG_FINDSTR_OUT_OF_MEMORY, program);
  193. // Write error message
  194. exit(2); // Die
  195. }
  196. return(cp); // Return pointer to buffer
  197. }
  198. void
  199. freenode(
  200. STRINGNODE *x
  201. )
  202. {
  203. register STRINGNODE *y; // Pointer to next node in list
  204. while(x != NULL) { // While not at end of list
  205. if (x->s_suf != NULL)
  206. freenode(x->s_suf); // Free suffix list if not end
  207. else
  208. --strcnt; // Else decrement string count
  209. y = x; // Save pointer
  210. x = x->s_alt; // Move down the list
  211. free((char *)((INT_PTR) s_text(y) & ~(sizeof(void *) - 1)));
  212. // Free the node
  213. }
  214. }
  215. STRINGNODE *
  216. newnode(
  217. char *s,
  218. int n
  219. )
  220. {
  221. register STRINGNODE *newNode; // Pointer to new node
  222. char *t; // String pointer
  223. int d; // rounds to a dword boundary
  224. d = n & (sizeof(void *) - 1) ? sizeof(void *) - (n & (sizeof(void *) - 1)) : 0; // offset to next dword past n
  225. t = alloc(sizeof(STRINGNODE) + n + d);
  226. // Allocate string node
  227. t += d; // END of string word-aligned
  228. strncpy(t, s, n); // Copy string text
  229. newNode = (STRINGNODE *)(t + n); // Set pointer to node
  230. newNode->s_alt = NULL; // No alternates yet
  231. newNode->s_suf = NULL; // No suffixes yet
  232. newNode->s_must = n; // Set string length
  233. return(newNode); // Return pointer to new node
  234. }
  235. STRINGNODE *
  236. reallocnode(
  237. STRINGNODE *node,
  238. char *s,
  239. int n
  240. )
  241. {
  242. register char *cp; // Char pointer
  243. assert(n <= node->s_must); // Node must not grow
  244. waste += (unsigned)(node->s_must - n);
  245. // Add in wasted space
  246. assert(sizeof(char *) == sizeof(int));
  247. // Optimizer should eliminate this
  248. cp = (char *)((INT_PTR) s_text(node) & ~(sizeof(void *) - 1));
  249. // Point to start of text
  250. node->s_must = n; // Set new length
  251. if (n & (sizeof(void *) - 1))
  252. cp += sizeof(void *) - (n & (sizeof(void *) - 1)); // Adjust non dword-aligned string
  253. memmove(cp, s, n); // Copy new text
  254. cp += n; // Skip over new text
  255. memmove(cp, node, sizeof(STRINGNODE));// Copy the node
  256. return((STRINGNODE *) cp); // Return pointer to moved node
  257. }
  258. /*** maketd1 - add entry for TD1 shift table
  259. *
  260. * This function fills in the TD1 table for the given
  261. * search string. The idea is adapted from Daniel M.
  262. * Sunday's QuickSearch algorithm as described in an
  263. * article in the August 1990 issue of "Communications
  264. * of the ACM". As described, the algorithm is suitable
  265. * for single-string searches. The idea to extend it for
  266. * multiple search strings is mine and is described below.
  267. *
  268. * Think of searching for a match as shifting the search
  269. * pattern p of length n over the source text s until the
  270. * search pattern is aligned with matching text or until
  271. * the end of the source text is reached.
  272. *
  273. * At any point when we find a mismatch, we know
  274. * we will shift our pattern to the right in the
  275. * source text at least one position. Thus,
  276. * whenever we find a mismatch, we know the character
  277. * s[n] will figure in our next attempt to match.
  278. *
  279. * For some character c, TD1[c] is the 1-based index
  280. * from right to left of the first occurrence of c
  281. * in p. Put another way, it is the count of places
  282. * to shift p to the right on s so that the rightmost
  283. * c in p is aligned with s[n]. If p does not contain
  284. * c, then TD1[c] = n + 1, meaning we shift p to align
  285. * p[0] with s[n + 1] and try our next match there.
  286. *
  287. * Computing TD1 for a single string is easy:
  288. *
  289. * memset(TD1, n + 1, sizeof TD1);
  290. * for (i = 0; i < n; ++i) {
  291. * TD1[p[i]] = n - i;
  292. * }
  293. *
  294. * Generalizing this computation to a case where there
  295. * are multiple strings of differing lengths is trickier.
  296. * The key is to generate a TD1 that is as conservative
  297. * as necessary, meaning that no shift value can be larger
  298. * than one plus the length of the shortest string for
  299. * which you are looking. The other key is to realize
  300. * that you must treat each string as though it were only
  301. * as long as the shortest string. This is best illustrated
  302. * with an example. Consider the following two strings:
  303. *
  304. * DYNAMIC PROCEDURE
  305. * 7654321 927614321
  306. *
  307. * The numbers under each letter indicate the values of the
  308. * TD1 entries if we computed the array for each string
  309. * separately. Taking the union of these two sets, and taking
  310. * the smallest value where there are conflicts would yield
  311. * the following TD1:
  312. *
  313. * DYNAMICPODURE
  314. * 7654321974321
  315. *
  316. * Note that TD1['P'] equals 9; since n, the length of our
  317. * shortest string is 7, we know we should not have any
  318. * shift value larger than 8. If we clamp our shift values
  319. * to this value, then we get
  320. *
  321. * DYNAMICPODURE
  322. * 7654321874321
  323. *
  324. * Already, this looks fishy, but let's try it out on
  325. * s = "DYNAMPROCEDURE". We know we should match on
  326. * the trailing procedure, but watch:
  327. *
  328. * DYNAMPROCEDURE
  329. * ^^^^^^^|
  330. *
  331. * Since DYNAMPR doesn't match one of our search strings,
  332. * we look at TD1[s[n]] == TD1['O'] == 7. Applying this
  333. * shift, we get
  334. *
  335. * DYNAMPROCEDURE
  336. * ^^^^^^^
  337. *
  338. * As you can see, by shifting 7, we have gone too far, and
  339. * we miss our match. When computing TD1 for "PROCEDURE",
  340. * we must take only the first 7 characters, "PROCEDU".
  341. * Any trailing characters can be ignored (!) since they
  342. * have no effect on matching the first 7 characters of
  343. * the string. Our modified TD1 then becomes
  344. *
  345. * DYNAMICPODURE
  346. * 7654321752163
  347. *
  348. * When applied to s, we get TD1[s[n]] == TD1['O'] == 5,
  349. * leaving us with
  350. *
  351. * DYNAMPROCEDURE
  352. * ^^^^^^^
  353. * which is just where we need to be to match on "PROCEDURE".
  354. *
  355. * Going to this algorithm has speeded qgrep up on multi-string
  356. * searches from 20-30%. The all-C version with this algorithm
  357. * became as fast or faster than the C+ASM version of the old
  358. * algorithm. Thank you, Daniel Sunday, for your inspiration!
  359. *
  360. * Note: if we are case-insensitive, then we expect the input
  361. * string to be upper-cased on entry to this routine.
  362. *
  363. * Pete Stewart, August 14, 1990.
  364. */
  365. void
  366. maketd1(
  367. unsigned char *pch,
  368. unsigned cch,
  369. unsigned cchstart
  370. )
  371. {
  372. unsigned ch, ch1; // Character
  373. unsigned i; // String index
  374. unsigned char s[2];
  375. s[1] = 0;
  376. if ((cch += cchstart) > cchmin)
  377. cch = cchmin; // Use smaller count
  378. for (i = cchstart; i < cch; ++i) { // Examine each char left to right
  379. ch = *pch++; // Get the character
  380. for (;;) { // Loop to set up entries
  381. if (ch < chmin)
  382. chmin = ch; // Remember if smallest
  383. if (ch > chmax)
  384. chmax = ch; // Remember if largest
  385. if (cchmin - i < (unsigned) td1[ch])
  386. td1[ch] = (unsigned char)(cchmin - i);
  387. // Set value if smaller than previous
  388. if (casesen || !isalpha(ch) || islower(ch))
  389. break; // Exit loop if done
  390. ch1 = ch;
  391. s[0] = (char)ch;
  392. ch = (unsigned char)(_strlwr((char*)s))[0]; // Force to lower case
  393. if (ch1 == s[0]) // Lower case is the same to previous.
  394. break; // Exit loop if done
  395. }
  396. }
  397. }
  398. static int
  399. newstring(
  400. unsigned char *s,
  401. int n
  402. )
  403. {
  404. register STRINGNODE *cur; // Current string
  405. register STRINGNODE **pprev; // Pointer to previous link
  406. STRINGNODE *newNode; // New string
  407. int i; // Index
  408. int j; // Count
  409. int k; // Count
  410. unsigned char c[2];
  411. c[1] = 0;
  412. if ( (unsigned)n < cchmin)
  413. cchmin = n; // Remember length of shortest string
  414. if ((i = (UCHAR)transtab[*s]) == 0) { // If no existing list
  415. // We have to start a new list
  416. if ((i = clists++) >= TRTABLEN/2)
  417. error(MSG_FINDSTR_TOO_MANY_STRING_LISTS); //"Too many string lists");
  418. // Die if too many string lists
  419. stringlist[i] = NULL; // Initialize
  420. transtab[*s] = (char) i; // Set pointer to new list
  421. if (!casesen && isalpha(*s)) {
  422. c[0] = *s;
  423. if ((unsigned char)(_strlwr((char*)c))[0] != *s ||
  424. (unsigned char)(_strupr((char*)c))[0] != *s)
  425. transtab[c[0]] = (char) i; // Set pointer for other case
  426. }
  427. }
  428. else
  429. if (stringlist[i] == NULL)
  430. return(0); // Check for existing 1-byte string
  431. if (--n == 0) { // If 1-byte string
  432. freenode(stringlist[i]); // Free any existing stuff
  433. stringlist[i] = NULL; // No record here
  434. ++strcnt; // We have a new string
  435. return(1); // String added
  436. }
  437. ++s; // Skip first char
  438. pprev = stringlist + i; // Get pointer to link
  439. cur = *pprev; // Get pointer to node
  440. while(cur != NULL) { // Loop to traverse match tree
  441. i = (n > cur->s_must)? cur->s_must: n;
  442. // Find minimum of string lengths
  443. matchstrings((char *)s, s_text(cur), i, &j, &k);
  444. // Compare the strings
  445. if (j == 0) { // If complete mismatch
  446. if (k < 0)
  447. break; // Break if insertion point found
  448. pprev = &(cur->s_alt); // Get pointer to alternate link
  449. cur = *pprev; // Follow the link
  450. } else if (i == j) { // Else if strings matched
  451. if (i == n) { // If new is prefix of current
  452. cur = *pprev = reallocnode(cur, s_text(cur), n);
  453. // Shorten text of node
  454. if (cur->s_suf != NULL) { // If there are suffixes
  455. freenode(cur->s_suf);
  456. // Suffixes no longer needed
  457. cur->s_suf = NULL;
  458. ++strcnt; // Account for this string
  459. }
  460. return(1); // String added
  461. }
  462. pprev = &(cur->s_suf); // Get pointer to suffix link
  463. if ((cur = *pprev) == NULL) return(0);
  464. // Done if current is prefix of new
  465. s += i; // Skip matched portion
  466. n -= i;
  467. } else { // Else partial match
  468. // We must split an existing node.
  469. // This is the trickiest case.
  470. newNode = newnode(s_text(cur) + j, cur->s_must - j);
  471. // Unmatched part of current string
  472. cur = *pprev = reallocnode(cur, s_text(cur), j);
  473. // Set length to matched portion
  474. newNode->s_suf = cur->s_suf; // Current string's suffixes
  475. if (k < 0) { // If new preceded current
  476. cur->s_suf = newnode((char *)s + j, n - j);
  477. // FIrst suffix is new string
  478. cur->s_suf->s_alt = newNode;// Alternate is part of current
  479. } else { // Else new followed current
  480. newNode->s_alt = newnode((char *)(s + j), n - j);
  481. // Unmatched new string is alternate
  482. cur->s_suf = newNode; // New suffix list
  483. }
  484. ++strcnt; // One more string
  485. return(1); // String added
  486. }
  487. }
  488. *pprev = newnode((char *)s, n); // Set pointer to new node
  489. (*pprev)->s_alt = cur; // Attach alternates
  490. ++strcnt; // One more string
  491. return(1); // String added
  492. }
  493. void
  494. addstring(
  495. char *s,
  496. int n
  497. )
  498. {
  499. int endline; // Match-at-end-of-line flag
  500. register char *pch; // Char pointer
  501. endline = flags & ENDLINE; // Initialize flag
  502. pch = target; // Initialize pointer
  503. while(n-- > 0) { // While not at end of string
  504. switch(*pch = *s++) { // Switch on character
  505. case '\\': // Escape
  506. if (n > 0 && !isalnum(*s)) { // If next character "special"
  507. --n; // Decrement counter
  508. *pch = *s++; // Copy next character
  509. }
  510. ++pch; // Increment pointer
  511. break;
  512. default: // All others
  513. if (IsDBCSLeadByte(*pch)) {
  514. --n;
  515. ++pch; // Increment pointer
  516. *pch = *s++;
  517. }
  518. ++pch; // Increment pointer
  519. break;
  520. }
  521. }
  522. if (endline)
  523. *pch++ = EOS; // Add end character if needed
  524. targetlen = (int)(pch - target); // Compute target string length
  525. if (!casesen)
  526. strnupr(target, targetlen); // Force to upper case if necessary
  527. newstring((unsigned char *)target, targetlen); // Add string
  528. }
  529. int
  530. addstrings(
  531. char *buffer,
  532. char *bufend,
  533. char *seplist
  534. )
  535. {
  536. int len; // String length
  537. char tmpbuf[MAXSTRLEN+2];
  538. while(buffer < bufend) { // While buffer not empty
  539. len = strnspn(buffer, seplist, (int)(bufend - buffer));
  540. // Count leading separators
  541. if ((buffer += len) >= bufend) {
  542. break; // Skip leading separators
  543. }
  544. len = strncspn(buffer, seplist, (int)(bufend - buffer));
  545. // Get length of search string
  546. //
  547. // We need to verify the length of the string before we call
  548. // isexpr since the size of the buffer used is BUFLEN = 256
  549. //
  550. if (len >= MAXSTRLEN)
  551. error(MSG_FINDSTR_SEARCH_STRING_TOO_LONG);
  552. if (addstr == NULL) {
  553. addstr = isexpr( (unsigned char *) buffer, len ) ? addexpr : addstring;
  554. // Select search string type
  555. }
  556. memcpy(tmpbuf, buffer, len);
  557. tmpbuf[len] = '\n';
  558. tmpbuf[len+1] = 0;
  559. if ( addstr == addexpr || (flags & BEGLINE) ||
  560. findlist((unsigned char *)tmpbuf, tmpbuf + len + 1) == NULL) {
  561. // If no match within string
  562. (*addstr)(buffer, len); // Add string to list
  563. }
  564. buffer += len; // Skip the string
  565. }
  566. return(0); // Keep looking
  567. }
  568. int
  569. enumlist(
  570. STRINGNODE *node,
  571. int cchprev
  572. )
  573. {
  574. int strcnt; // String count
  575. strcnt = 0; // Initialize
  576. while(node != NULL) { // While not at end of list
  577. maketd1((unsigned char *)s_text(node), node->s_must, cchprev);
  578. // Make TD1 entries
  579. #if DBG
  580. if (flags & DEBUG) { // If verbose output wanted
  581. int i; // Counter
  582. for(i = 0; i < cchprev; ++i)
  583. fputc(' ', stderr); // Indent line
  584. fwrite(s_text(node), sizeof(char), node->s_must, stderr);
  585. // Write this portion
  586. fprintf(stderr, "\n"); // Newline
  587. }
  588. #endif
  589. strcnt += (node->s_suf != NULL) ?
  590. enumlist(node->s_suf, cchprev + node->s_must): 1;
  591. // Recurse to do suffixes
  592. node = node->s_alt; // Do next alternate in list
  593. }
  594. return (strcnt ? strcnt: 1); // Return string count
  595. }
  596. int
  597. enumstrings()
  598. {
  599. unsigned char ch; // Character
  600. unsigned i; // Index
  601. int strcnt; // String count
  602. strcnt = 0; // Initialize
  603. for(i = 0; i < TRTABLEN; ++i) { // Loop through translation table
  604. if (casesen || !isalpha(i) || !islower(i)) {
  605. // If case sensitive or not lower
  606. if (transtab[i] == 0)
  607. continue; // Skip null entries
  608. ch = (char) i; // Get character
  609. maketd1((unsigned char *)&ch, 1, 0); // Make TD1 entry
  610. #if DBG
  611. if (flags & DEBUG)
  612. fprintf(stderr, "%c\n", i); // Print the first byte
  613. #endif
  614. strcnt += enumlist(stringlist[transtab[i]], 1);
  615. // Enumerate the list
  616. }
  617. }
  618. return (strcnt); // Return string count
  619. }
  620. HANDLE
  621. openfile(
  622. char *name
  623. )
  624. {
  625. HANDLE fd;
  626. DWORD attr;
  627. attr = GetFileAttributes(name);
  628. if (attr != (DWORD) -1 && (attr & FILE_ATTRIBUTE_DIRECTORY))
  629. return (HANDLE)-1;
  630. // Skip offline files unless instructed otherwise
  631. if (attr != (DWORD) -1 && (attr & FILE_ATTRIBUTE_OFFLINE) && !(flags & OFFLINE_FILES)) {
  632. fOfflineSkipped = TRUE;
  633. return (HANDLE)-1;
  634. }
  635. if ((fd = CreateFile(name,
  636. GENERIC_READ,
  637. FILE_SHARE_READ | FILE_SHARE_WRITE,
  638. NULL,
  639. OPEN_EXISTING,
  640. FILE_FLAG_OPEN_NO_RECALL,
  641. NULL)) == (HANDLE)-1) {
  642. printmessage(stderr, MSG_FINDSTR_CANNOT_OPEN_FILE, program, name);
  643. }
  644. return( fd ); // Return file descriptor
  645. }
  646. void
  647. startread(
  648. HANDLE fd,
  649. char *buffer,
  650. int buflen
  651. )
  652. {
  653. if (bStdIn || bLargeFile) {
  654. arrc = ReadFile(fd,(PVOID)buffer, buflen, &cbread, NULL);
  655. }
  656. }
  657. int
  658. finishread()
  659. {
  660. return(arrc ? cbread : -1); // Return number of bytes read
  661. }
  662. void
  663. startwrite( HANDLE fd, char *buffer, int buflen)
  664. {
  665. awrc = WriteFile(fd,(PVOID)buffer, buflen, &cbwrite, NULL);
  666. return;
  667. }
  668. int
  669. finishwrite()
  670. {
  671. return(awrc ? cbwrite : -1); // Return number of bytes written
  672. }
  673. BOOL
  674. CtrlHandler(DWORD CtrlType)
  675. {
  676. // We'll handle Ctrl-C events
  677. switch(CtrlType) {
  678. case CTRL_C_EVENT:
  679. case CTRL_BREAK_EVENT:
  680. if (csbi.wAttributes) {
  681. EnterCriticalSection(&critSection);
  682. fExiting = TRUE;
  683. SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
  684. csbi.wAttributes);
  685. LeaveCriticalSection(&critSection);
  686. }
  687. break;
  688. }
  689. // Deal with all other events as normal
  690. return (FALSE);
  691. }
  692. void
  693. write1nobuf(
  694. char *buffer,
  695. int buflen,
  696. WORD wAttributes
  697. )
  698. {
  699. int nT;
  700. CBIO cb; // Count of bytes written
  701. BOOL fCR;
  702. BOOL fLF;
  703. char buf[STKLEN];
  704. char *szT;
  705. static HANDLE hConOut = INVALID_HANDLE_VALUE;
  706. int remaining_length;
  707. // Get the console screen buffer info if we haven't yet.
  708. if (hConOut == INVALID_HANDLE_VALUE) {
  709. hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
  710. InitializeCriticalSection(&critSection);
  711. SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE);
  712. }
  713. if (wAttributes) {
  714. EnterCriticalSection(&critSection);
  715. // if not exiting, highlight the output
  716. if (!fExiting)
  717. SetConsoleTextAttribute(hConOut, wAttributes);
  718. LeaveCriticalSection(&critSection);
  719. if (fExiting)
  720. ExitProcess(2);
  721. }
  722. remaining_length = buflen;
  723. while (remaining_length) {
  724. buflen = (int)min(sizeof(buf) / sizeof(buf[0]), remaining_length);
  725. szT = buf;
  726. if (IsDBCSCodePage) {
  727. memcpy(buf, buffer, buflen);
  728. } else {
  729. for(nT = 0; nT < buflen; nT++) {
  730. *(szT++) = ((isprint((unsigned char)buffer[nT]) ||
  731. isspace((unsigned char)buffer[nT])) ?
  732. buffer[nT] : '.');
  733. }
  734. }
  735. if (!WriteFile(hConOut, (PVOID)buf, buflen, &cb, NULL)
  736. || (cb != (CBIO)(buflen)))
  737. {
  738. SetConsoleTextAttribute(hConOut, csbi.wAttributes);
  739. error(MSG_FINDSTR_WRITE_ERROR); // Die if write fails
  740. }
  741. remaining_length -= buflen;
  742. buffer += buflen;
  743. }
  744. if (wAttributes)
  745. SetConsoleTextAttribute(hConOut, csbi.wAttributes);
  746. }
  747. void
  748. write1buf(
  749. char *buffer,
  750. int buflen,
  751. WORD wAttributes
  752. )
  753. {
  754. register int cb; // Byte count
  755. while(buflen > 0) { // While bytes remain
  756. if (!awrc) { // If previous write failed
  757. printmessage(stderr, MSG_FINDSTR_WRITE_ERROR, program); // Print error message
  758. exit(2); // Die
  759. }
  760. if ((cb = ocnt[oi]) == 0) { // If buffer full
  761. startwrite( GetStdHandle( STD_OUTPUT_HANDLE ), obuf[oi], OUTBUFLEN );
  762. // Write the buffer
  763. ocnt[oi] = OUTBUFLEN; // Reset count and pointer
  764. optr[oi] = obuf[oi];
  765. oi ^= 1; // Switch buffers
  766. cb = ocnt[oi]; // Get space remaining
  767. }
  768. if (cb > buflen)
  769. cb = buflen; // Get minimum
  770. memmove(optr[oi], buffer, cb); // Copy bytes to buffer
  771. ocnt[oi] -= cb; // Update buffer length and pointers
  772. optr[oi] += cb;
  773. buflen -= cb;
  774. buffer += cb;
  775. }
  776. }
  777. void
  778. flush1nobuf(
  779. void
  780. )
  781. {
  782. ;
  783. }
  784. void
  785. flush1buf(
  786. void
  787. )
  788. {
  789. register int cb; // Byte count
  790. if ((cb = OUTBUFLEN - ocnt[oi]) > 0) { // If buffer not empty
  791. startwrite( GetStdHandle( STD_OUTPUT_HANDLE ), obuf[oi], cb ); // Start write
  792. if (finishwrite() != cb) { // If write failed
  793. printmessage(stderr, MSG_FINDSTR_WRITE_ERROR, program); // Print error message
  794. exit(2); // Die
  795. }
  796. }
  797. }
  798. int
  799. grepbuffer(
  800. char *startbuf,
  801. char *endbuf,
  802. char *name
  803. )
  804. {
  805. char *cp; // Buffer pointer
  806. char *lastmatch; // Last matching line
  807. int linelen; // Line length
  808. int namlen = 0; // Length of name
  809. char lnobuf[LNOLEN]; // Line number buffer
  810. char nambuf[PATHLEN]; // Name buffer
  811. cp = startbuf; // Initialize to start of buffer
  812. lastmatch = cp; // No previous match yet
  813. while((cp = (*find)((unsigned char *)cp, endbuf)) != NULL) {
  814. // While matches are found
  815. --cp; // Back up to previous character
  816. // Take care of '\n' as an artificial newline before line 1.
  817. if ((flags & BEGLINE) && (bStdIn || bLargeFile || cp >= BaseByteAddress) && *cp != '\n' ) {
  818. // If begin line conditions not met
  819. cp += strncspn(cp, "\n", (int)(endbuf - cp)) + 1;
  820. // Skip line
  821. continue; // Keep looking
  822. }
  823. status = 0; // Match found
  824. if (flags & NAMEONLY)
  825. return(1); // Return if filename only wanted
  826. cp -= preveol(cp) - 1; // Point at start of line
  827. if (flags & SHOWNAME) { // If name wanted
  828. if (namlen == 0) { // If name not formatted yet
  829. namlen = sprintf(nambuf, "%s:", name);
  830. // Format name if not done already
  831. }
  832. (*write1)(nambuf, namlen, wAttrib); // Show name
  833. }
  834. if (flags & LINENOS) { // If line number wanted
  835. lineno += countlines(lastmatch, cp);
  836. // Count lines since last match
  837. (*write1)(lnobuf, sprintf(lnobuf, "%u:", lineno), wAttrib);
  838. // Print line number
  839. lastmatch = cp; // New last match
  840. }
  841. if (flags & SEEKOFF) { // If seek offset wanted
  842. (*write1)(lnobuf, sprintf(lnobuf, "%lu:",
  843. cbfile + (long)(cp - startbuf)), wAttrib);
  844. // Print seek offset
  845. }
  846. linelen = strncspn(cp, "\n", (int)(endbuf - cp)) + 1;
  847. // Calculate line length
  848. if (linelen > endbuf - cp) {
  849. linelen = (int)(endbuf - cp);
  850. }
  851. (*write1)(cp, linelen, 0); // Print the line
  852. cp += linelen; // Skip the line
  853. }
  854. lineno += countlines(lastmatch, endbuf);
  855. // Count remaining lines in buffer
  856. return(0); // Keep searching
  857. }
  858. void
  859. showv(
  860. char *name,
  861. char *startbuf,
  862. char *lastmatch,
  863. char *thismatch
  864. )
  865. {
  866. register int linelen;
  867. int namlen = 0; // Length of name
  868. char lnobuf[LNOLEN]; // Line number buffer
  869. char nambuf[PATHLEN];// Name buffer
  870. if (flags & (SHOWNAME | LINENOS | SEEKOFF)) {
  871. while(lastmatch < thismatch) {
  872. if (flags & SHOWNAME) { // If name wanted
  873. if (namlen == 0) { // If name not formatted yet
  874. namlen = sprintf(nambuf, "%s:", name);
  875. // Format name if not done already
  876. }
  877. (*write1)(nambuf, namlen, wAttrib);
  878. // Write the name
  879. }
  880. if (flags & LINENOS) // If line numbers wanted
  881. {
  882. (*write1)(lnobuf, sprintf(lnobuf, "%u:", lineno++), wAttrib);
  883. // Print the line number
  884. }
  885. if (flags & SEEKOFF) { // If seek offsets wanted
  886. (*write1)(lnobuf, sprintf(lnobuf, "%lu:",
  887. cbfile + (long)(lastmatch - startbuf)), wAttrib);
  888. // Print the line number
  889. }
  890. linelen = strncspn(lastmatch, "\n", (int)(thismatch - lastmatch));
  891. // If there's room for the '\n' then pull it in. Otherwise
  892. // the buffer doesn't have a '\n' within the range here.
  893. if (linelen < thismatch - lastmatch) {
  894. linelen++;
  895. }
  896. (*write1)(lastmatch, linelen, 0);
  897. lastmatch += linelen;
  898. }
  899. }
  900. else
  901. (*write1)(lastmatch, (int)(thismatch - lastmatch), 0);
  902. }
  903. int
  904. grepvbuffer(
  905. char *startbuf,
  906. char *endbuf,
  907. char *name
  908. )
  909. {
  910. char *cp; // Buffer pointer
  911. char *lastmatch; // Pointer to line after last match
  912. cp = startbuf; // Initialize to start of buffer
  913. lastmatch = cp;
  914. while((cp = (*find)((unsigned char *)cp, endbuf)) != NULL) {
  915. --cp; // Back up to previous character
  916. // Take care of '\n' as an artificial newline before line 1.
  917. if ((flags & BEGLINE) && (bStdIn || bLargeFile || cp >= BaseByteAddress) && *cp != '\n') {
  918. // If begin line conditions not met
  919. cp += strncspn(cp, "\n", (int)(endbuf - cp)) + 1;
  920. // Skip line
  921. continue; // Keep looking
  922. }
  923. cp -= preveol(cp) - 1; // Point at start of line
  924. if (cp > lastmatch) { // If we have lines without matches
  925. status = 0; // Lines without matches found
  926. if (flags & NAMEONLY) return(1);
  927. // Skip rest of file if NAMEONLY
  928. showv(name, startbuf, lastmatch, cp);
  929. // Show from last match to this
  930. }
  931. cp += strncspn(cp, "\n", (int)(endbuf - cp)) + 1;
  932. // Skip over line with match
  933. lastmatch = cp; // New "last" match
  934. ++lineno; // Increment line count
  935. }
  936. if (endbuf > lastmatch) { // If we have lines without matches
  937. status = 0; // Lines without matches found
  938. if (flags & NAMEONLY)
  939. return(1); // Skip rest of file if NAMEONLY
  940. showv(name, startbuf, lastmatch, endbuf);
  941. // Show buffer tail
  942. }
  943. return(0); // Keep searching file
  944. }
  945. void
  946. qgrep(
  947. int (*grep)( char *, char *, char * ),
  948. char *name,
  949. HANDLE fd
  950. )
  951. {
  952. register int cb; // Byte count
  953. char *cp; // Buffer pointer
  954. char *endbuf; // End of buffer
  955. int taillen; // Length of buffer tail
  956. int bufi; // Buffer index
  957. HANDLE MapHandle; // File mapping handle
  958. BOOL grep_result;
  959. cbfile = 0L; // File empty so far
  960. lineno = 1; // File starts on line 1
  961. taillen = 0; // No buffer tail yet
  962. bufi = 0; // Initialize buffer index
  963. cp = bufptr[0]; // Initialize to start of buffer
  964. bStdIn = (fd == GetStdHandle(STD_INPUT_HANDLE));
  965. // If fd is not std-input, use file mapping object method.
  966. if (!bStdIn) {
  967. DWORD cbread_high;
  968. if ((((cbread = (CBIO)GetFileSize(fd, &cbread_high)) == -1) && (GetLastError() != NO_ERROR)) ||
  969. (cbread == 0 && cbread_high == 0)) {
  970. return; // skip the file
  971. }
  972. if (cbread_high) {
  973. bLargeFile = TRUE; // too large to map and even if it succeed in mapping like under ia64, it
  974. // will probably fail in pointer arithmetics
  975. } else {
  976. MapHandle = CreateFileMapping(fd,
  977. NULL,
  978. PAGE_READONLY,
  979. 0L,
  980. 0L,
  981. NULL);
  982. if (MapHandle == NULL) {
  983. printmessage(stderr, MSG_FINDSTR_CANNOT_CREATE_FILE_MAPPING, program);
  984. return;
  985. }
  986. BaseByteAddress = (char *) MapViewOfFile(MapHandle,
  987. FILE_MAP_READ,
  988. 0L,
  989. 0L,
  990. 0);
  991. CloseHandle(MapHandle);
  992. if (BaseByteAddress == NULL) {
  993. bLargeFile = TRUE; // use alternate method
  994. } else {
  995. cp = bufptr[0] = BaseByteAddress;
  996. arrc = TRUE;
  997. }
  998. }
  999. }
  1000. if (bStdIn || bLargeFile) {
  1001. // Reset buffer pointers since they might have been changed.
  1002. cp = bufptr[0] = filbuf + 4;
  1003. arrc = ReadFile(fd, (PVOID)cp, FILBUFLEN, &cbread, NULL);
  1004. }
  1005. if (flags & PRINTABLE_ONLY) {
  1006. unsigned char *s;
  1007. unsigned long n;
  1008. s = (unsigned char *)cp;
  1009. n = cbread;
  1010. while (--n) {
  1011. if (*s < ' ') {
  1012. // If not backspace, tab, CR, LF, FF or Ctrl-Z then not a printable character.
  1013. if (strchr("\b\t\v\r\n\f\032", *s) == NULL) {
  1014. goto skipfile;
  1015. }
  1016. }
  1017. s += 1;
  1018. }
  1019. }
  1020. // Note: if FILEMAP && !bStdIn, 'while' is executed once(taillen is 0).
  1021. while((cb = finishread()) + taillen > 0) {
  1022. // While search incomplete
  1023. if (bStdIn || bLargeFile) {
  1024. if (cb == -1) { // If buffer tail is all that's left
  1025. *cp++ = '\r'; // Add end of line sequence
  1026. *cp++ = '\n';
  1027. endbuf = cp; // Note end of buffer
  1028. taillen = 0; // Set tail length to zero
  1029. } else { // Else start next read
  1030. taillen = preveol(cp + cb - 1); // Find length of partial line
  1031. endbuf = cp + cb - taillen; // Get pointer to end of buffer
  1032. cp = bufptr[bufi ^ 1]; // Pointer to other buffer
  1033. memmove(cp, endbuf, taillen); // Copy tail to head of other buffer
  1034. cp += taillen; // Skip over tail
  1035. if (taillen > (FILBUFLEN/2)) {
  1036. if (taillen >= FILBUFLEN) {
  1037. char tmp[15];
  1038. cbfile += taillen;
  1039. taillen = 0;
  1040. cp = bufptr[bufi^1];
  1041. startread(fd, cp, FILBUFLEN);
  1042. _ultoa((unsigned long)lineno, tmp, 10);
  1043. printmessage(stderr, MSG_FINDSTR_LINE_TOO_LONG, program, tmp);
  1044. } else
  1045. startread(fd, cp, (FILBUFLEN - taillen));
  1046. } else
  1047. startread(fd, cp, (FILBUFLEN - taillen) & (~0 << LG2SECLEN));
  1048. // Start next read
  1049. }
  1050. } else {
  1051. endbuf = cp + cb - taillen; // Get pointer to end of buffer
  1052. // Cause 'while' to terminate(since no next read is needed.)
  1053. cbread = 0;
  1054. arrc = TRUE;
  1055. }
  1056. __try {
  1057. grep_result = (*grep)(bufptr[bufi], endbuf, name);
  1058. } __except( GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR ) {
  1059. printmessage(stderr, MSG_FINDSTR_READ_ERROR, program, name);
  1060. break;
  1061. }
  1062. if (grep_result) { // If rest of file can be skipped
  1063. (*write1)(name, strlen(name), 0);
  1064. // Write file name
  1065. (*write1)("\r\n", 2, 0); // Write newline sequence
  1066. if (!bStdIn && !bLargeFile) {
  1067. if (BaseByteAddress != NULL)
  1068. UnmapViewOfFile(BaseByteAddress);
  1069. }
  1070. return; // Skip rest of file
  1071. }
  1072. cbfile += (long)(endbuf - bufptr[bufi]);
  1073. // Increment count of bytes in file
  1074. bufi ^= 1; // Switch buffers
  1075. }
  1076. skipfile:
  1077. if (!bStdIn && !bLargeFile) {
  1078. if (BaseByteAddress != NULL)
  1079. UnmapViewOfFile(BaseByteAddress);
  1080. }
  1081. }
  1082. char *
  1083. rmpath(
  1084. char *name
  1085. )
  1086. {
  1087. char *cp; // Char pointer
  1088. if (name[0] != '\0' && name[1] == ':')
  1089. name += 2; // Skip drive spec if any
  1090. cp = name; // Point to start
  1091. while(*name != '\0') { // While not at end
  1092. ++name; // Skip to next character
  1093. if (name[-1] == '/' || name[-1] == '\\') cp = name;
  1094. // Point past path separator
  1095. }
  1096. return(cp); // Return pointer to name
  1097. }
  1098. void
  1099. prepend_path(
  1100. char* file_name,
  1101. char* path
  1102. )
  1103. {
  1104. int path_len;
  1105. char* last;
  1106. // First figure out how much of the path to take.
  1107. // Check for the last occurance of '\' if there is one.
  1108. #ifdef FE_SB
  1109. // DBCS tailbytes can contain '\' character. Use MBCS function.
  1110. last = _mbsrchr(path, '\\');
  1111. #else
  1112. last = strrchr(path, '\\');
  1113. #endif
  1114. if (last) {
  1115. path_len = (int)(last - path) + 1;
  1116. } else if (path[1] == ':') {
  1117. path_len = 2;
  1118. } else {
  1119. path_len = 0;
  1120. }
  1121. memmove(file_name + path_len, file_name, strlen(file_name) + 1);
  1122. memmove(file_name, path, path_len);
  1123. }
  1124. void
  1125. ConvertAppToOem(
  1126. unsigned argc,
  1127. char* argv[]
  1128. )
  1129. /*++
  1130. Routine Description:
  1131. Converts the command line from ANSI to OEM, and force the app
  1132. to use OEM APIs
  1133. Arguments:
  1134. argc - Standard C argument count.
  1135. argv - Standard C argument strings.
  1136. Return Value:
  1137. None.
  1138. --*/
  1139. {
  1140. unsigned i;
  1141. for( i=0; i<argc; i++ ) {
  1142. CharToOem( argv[i], argv[i] );
  1143. }
  1144. SetFileApisToOEM();
  1145. }
  1146. int __cdecl
  1147. main(
  1148. int argc,
  1149. char **argv
  1150. )
  1151. {
  1152. char *cp;
  1153. char *cpaddstrings[MAX_SLASH_C_OPTION];
  1154. int add_string_count = 0;
  1155. char *dirlist = NULL;
  1156. HANDLE fd;
  1157. FILE *fi;
  1158. int fsubdirs; // Search subdirectories
  1159. int i;
  1160. int j;
  1161. char *inpfile = NULL;
  1162. char *strfile = NULL;
  1163. unsigned long tstart; // Start time
  1164. char filnam[MAX_PATH+1];
  1165. WIN32_FIND_DATA find_data;
  1166. HANDLE find_handle;
  1167. #ifdef FE_SB
  1168. LANGID LangId;
  1169. #endif
  1170. char *locale;
  1171. BOOLEAN option_L_specified = FALSE;
  1172. BOOLEAN option_R_specified = FALSE;
  1173. ConvertAppToOem( argc, argv );
  1174. tstart = clock(); // Get start time
  1175. GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
  1176. // Default color: just add intensity
  1177. wAttrib = csbi.wAttributes | FOREGROUND_INTENSITY;
  1178. memset(cpaddstrings, 0, sizeof(cpaddstrings));
  1179. #ifdef FE_SB
  1180. //
  1181. // Set TEB's language ID to correspond to the console output code page. This
  1182. // will ensure the correct language message is displayed when FormatMessage is
  1183. // called.
  1184. //
  1185. switch (GetConsoleOutputCP()) {
  1186. case 932:
  1187. LangId = MAKELANGID( LANG_JAPANESE, SUBLANG_DEFAULT );
  1188. break;
  1189. case 949:
  1190. LangId = MAKELANGID( LANG_KOREAN, SUBLANG_KOREAN );
  1191. break;
  1192. case 936:
  1193. LangId = MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED );
  1194. break;
  1195. case 950:
  1196. LangId = MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL );
  1197. break;
  1198. default:
  1199. LangId = PRIMARYLANGID(LANGIDFROMLCID( GetUserDefaultLCID() ));
  1200. if (LangId == LANG_JAPANESE ||
  1201. LangId == LANG_KOREAN ||
  1202. LangId == LANG_CHINESE ) {
  1203. LangId = MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US );
  1204. }
  1205. else {
  1206. LangId = MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT );
  1207. }
  1208. IsDBCSCodePage = FALSE;
  1209. break;
  1210. }
  1211. SetThreadLocale( MAKELCID(LangId, SORT_DEFAULT) );
  1212. if ((locale = setlocale(LC_ALL, ".OCP")) == NULL) {
  1213. UINT Codepage;
  1214. if (Codepage = GetConsoleOutputCP()) {
  1215. char achCodepage[10];
  1216. wsprintfA(achCodepage, ".%3.4d", Codepage);
  1217. if ((locale = setlocale(LC_ALL, achCodepage)) == NULL) {
  1218. error(MSG_FINDSTR_UNABLE_TO_SET_LOCALE);
  1219. }
  1220. } else
  1221. error(MSG_FINDSTR_UNABLE_TO_SET_LOCALE);
  1222. }
  1223. #endif
  1224. asyncio = pmode = 1; // Do asynchronous I/O
  1225. // program = rmpath(argv[0]); // Set program name
  1226. program ="FINDSTR";
  1227. memset(td1, 1, TRTABLEN); // Set up TD1 for startup
  1228. flags = 0;
  1229. _setmode(_fileno(stdout), O_BINARY); // No linefeed translation on output
  1230. _setmode(_fileno(stderr), O_BINARY); // No linefeed translation on output
  1231. fsubdirs = 0;
  1232. for(i = 1; i < argc && (argv[i][0] == '/' || argv[i][0] == '-'); ++i)
  1233. {
  1234. for(cp = &argv[i][1]; *cp != '\0'; ++cp)
  1235. {
  1236. switch(*cp)
  1237. {
  1238. case '?':
  1239. printmessage(stdout, MSG_FINDSTR_USAGE, NULL); // Verbose usage message
  1240. exit(0);
  1241. case 'b':
  1242. case 'B':
  1243. flags |= BEGLINE;
  1244. break;
  1245. case 'e':
  1246. case 'E':
  1247. flags |= ENDLINE;
  1248. break;
  1249. case 'i':
  1250. case 'I':
  1251. casesen = 0; // case-insensitive search
  1252. break;
  1253. case 'l':
  1254. case 'L':
  1255. addstr = addstring; // Treat strings literally
  1256. option_L_specified = TRUE;
  1257. break;
  1258. case 'm':
  1259. case 'M':
  1260. flags |= NAMEONLY;
  1261. break;
  1262. case 'n':
  1263. case 'N':
  1264. flags |= LINENOS;
  1265. break;
  1266. case 'o':
  1267. case 'O':
  1268. // Check whether this is an /o or /offline switch
  1269. if (0 == _stricmp(cp, "OFFLINE")) {
  1270. flags |= OFFLINE_FILES;
  1271. cp += (lstrlen( "OFFLINE" ) - 1);
  1272. } else if (0 == _stricmp(cp, "OFF")) {
  1273. flags |= OFFLINE_FILES;
  1274. cp += (lstrlen( "OFF" ) - 1);
  1275. } else {
  1276. flags |= SEEKOFF;
  1277. }
  1278. break;
  1279. case 'p':
  1280. case 'P':
  1281. flags |= PRINTABLE_ONLY;
  1282. break;
  1283. case 'r':
  1284. case 'R':
  1285. addstr = addexpr; // Add expression to list
  1286. option_R_specified = TRUE;
  1287. break;
  1288. case 's':
  1289. case 'S':
  1290. fsubdirs = 1;
  1291. break;
  1292. case 'v':
  1293. case 'V':
  1294. grep = grepvbuffer;
  1295. break;
  1296. case 'x':
  1297. case 'X':
  1298. flags |= BEGLINE | ENDLINE;
  1299. break;
  1300. #if DBG
  1301. case 'd':
  1302. // This is kinda cheezy, but I didn't want to change the
  1303. // debug flag as it's been here a while and I couldn't come
  1304. // up with a different flag for the dirlist, so...
  1305. if (*(cp + 1) == ':')
  1306. {
  1307. *cp-- = 'D';
  1308. break;
  1309. }
  1310. flags |= DEBUG;
  1311. break;
  1312. case 't':
  1313. flags |= TIMER;
  1314. break;
  1315. #endif
  1316. default:
  1317. {
  1318. int cch;
  1319. char chSwitch;
  1320. char tmp[3];
  1321. chSwitch = *cp;
  1322. if (*(cp + 1) == ':')
  1323. {
  1324. if (!*(cp + 2))
  1325. {
  1326. tmp[0]=chSwitch;
  1327. tmp[1]='\0';
  1328. printmessage(stderr, MSG_FINDSTR_ARGUMENT_MISSING, program, tmp);
  1329. exit(2);
  1330. }
  1331. cp += 2; // Point to string
  1332. cch = lstrlen(cp);
  1333. switch(chSwitch)
  1334. {
  1335. case 'd':
  1336. case 'D':
  1337. dirlist = cp;
  1338. cp += cch - 1;
  1339. continue;
  1340. case 'c':
  1341. case 'C':
  1342. // Add it after we've gone through all the flags
  1343. // don't add it now as things may change with
  1344. // later flags
  1345. addstr = addstring; // Treat strings literally
  1346. if (add_string_count >= MAX_SLASH_C_OPTION) {
  1347. error(MSG_FINDSTR_TOO_MANY_SLASH_C_OPTION);
  1348. }
  1349. cpaddstrings[add_string_count++] = cp;
  1350. cp += cch - 1;
  1351. continue;
  1352. case 'g':
  1353. case 'G': // Patterns in file
  1354. case 'f':
  1355. case 'F': // Names of files to search in file
  1356. if (chSwitch == 'f' || chSwitch == 'F')
  1357. inpfile = cp;
  1358. else
  1359. strfile = cp;
  1360. cp += cch - 1;
  1361. continue;
  1362. case 'a':
  1363. case 'A':
  1364. wAttrib = 0;
  1365. for(; *cp && isxdigit(*cp); ++cp) {
  1366. int digit = (int) (*cp <= TEXT('9'))
  1367. ? (int)*cp - (int)'0'
  1368. : (int)tolower(*cp)-(int)'W';
  1369. wAttrib = (wAttrib << 4) + digit;
  1370. }
  1371. cp--;
  1372. continue;
  1373. default:
  1374. cp += cch - 1;
  1375. // break out and spit out the switch ignored msg
  1376. break;
  1377. }
  1378. }
  1379. tmp[0]='/';
  1380. tmp[1]=chSwitch;
  1381. tmp[2]='\0';
  1382. printmessage(stderr, MSG_FINDSTR_SWITCH_IGNORED, program, tmp);
  1383. break;
  1384. }
  1385. }
  1386. }
  1387. } // for( i=1; )
  1388. if (option_L_specified && option_R_specified)
  1389. error(MSG_FINDSTR_CONFLICTING_OPTIONS_LR);
  1390. else if (option_L_specified)
  1391. addstr = addstring;
  1392. else if (option_R_specified)
  1393. addstr = addexpr;
  1394. // Explicit string (no separators). Add string "as is"
  1395. if (add_string_count) {
  1396. for (j=0; j<add_string_count && cpaddstrings[j]; j++)
  1397. addstrings( cpaddstrings[j], cpaddstrings[j] + lstrlen(cpaddstrings[j]), "" );
  1398. }
  1399. if (i == argc && strcnt == 0 && strfile == NULL)
  1400. error(MSG_FINDSTR_BAD_COMMAND_LINE);
  1401. bufptr[0][-1] = bufptr[1][-1] = '\n'; // Mark beginnings with newline
  1402. // Note: 4-Dec-90 w-barry Since there currently exists no method to query a
  1403. // handle with the Win32 api (no equivalent to
  1404. // DosQueryHType() ), the following piece of code
  1405. // replaces the commented section.
  1406. if (_isatty(_fileno(stdout))) { // If stdout is a device
  1407. write1 = write1nobuf; // Use unbuffered output
  1408. flush1 = flush1nobuf;
  1409. }
  1410. // /*
  1411. // * Check type of handle for std. out.
  1412. // */
  1413. // if (DosQueryHType(fileno(stdout),(PPARM) &j,(PPARM) &fd) != NO_ERROR)
  1414. // {
  1415. // error("Standard output bad handle");
  1416. // }
  1417. // // Die if error
  1418. // if (j != 0 && (fd & ISCOT)) // If handle is console output
  1419. //#else
  1420. // filbuf[3] = '\n'; // Mark beginning with newline
  1421. // if (isatty(fileno(stdout))) // If stdout is a device
  1422. //#endif
  1423. // {
  1424. // write1 = write1nobuf; // Use unbuffered output
  1425. // flush1 = flush1nobuf;
  1426. // }
  1427. if (strfile != NULL) { // If strings from file
  1428. if ((strcmp(strfile, "/") != 0) && (strcmp(strfile, "-") != 0)) {
  1429. // If strings not from std. input
  1430. if ( ( fd = CreateFile( strfile,
  1431. GENERIC_READ,
  1432. 0,
  1433. NULL,
  1434. OPEN_EXISTING,
  1435. 0,
  1436. NULL ) ) == (HANDLE)-1 )
  1437. { // If open fails
  1438. printmessage(stderr, MSG_FINDSTR_CANNOT_READ_STRINGS, program, strfile);
  1439. exit(2); // Die
  1440. }
  1441. }else {
  1442. fd = GetStdHandle( STD_INPUT_HANDLE ); // Else use std. input
  1443. }
  1444. qgrep( addstrings, "\r\n", fd );// Do the work
  1445. if ( fd != GetStdHandle( STD_INPUT_HANDLE ) ) {
  1446. CloseHandle( fd ); // Close strings file
  1447. }
  1448. } else if (strcnt == 0) { // Else if strings on command line
  1449. cp = argv[i++]; // Set pointer to strings
  1450. addstrings(cp, cp + strlen(cp), " \t");
  1451. // Add strings to list
  1452. }
  1453. if (strcnt == 0)
  1454. error(MSG_FINDSTR_NO_SEARCH_STRINGS); // Die if no strings
  1455. if (addstr != addexpr) { // If not using expressions
  1456. memset(td1, cchmin + 1, TRTABLEN);// Initialize table
  1457. find = findlist; // Assume finding many
  1458. if ((j = enumstrings()) != strcnt) {
  1459. char t1[15], t2[15];
  1460. _itoa(j, t1, 10);
  1461. _itoa(strcnt, t2, 10);
  1462. printmessage(stderr, MSG_FINDSTR_STRING_COUNT_ERROR, t1, t2);
  1463. }
  1464. // Enumerate strings and verify count
  1465. #if DBG
  1466. if (flags & DEBUG) { // If debugging output wanted
  1467. fprintf(stderr, "%u bytes wasted in heap\n", waste);
  1468. // Print storage waste
  1469. assert(chmin <= chmax); // Must have some entries
  1470. fprintf(stderr, "chmin = %u, chmax = %u, cchmin = %u\n", chmin, chmax, cchmin);
  1471. // Print range info
  1472. for (j = (int)chmin; j <= (int)chmax; ++j) {
  1473. // Loop to print TD1 table
  1474. if ( td1[j] <= (char)cchmin ) { // If not maximum shift
  1475. if (isascii(j) && isprint(j))
  1476. fprintf(stderr, "'%c'=%2u ", j, td1[j]); // Show literally if printable
  1477. else
  1478. fprintf(stderr, "\\%02x=%2u ", j, td1[j]); // Else show hex value
  1479. }
  1480. }
  1481. fprintf(stderr, "\n");
  1482. }
  1483. #endif
  1484. if (strcnt == 1 && casesen)
  1485. find = findone; // Find one case-sensitive string
  1486. } else if (find == NULL) {
  1487. find = findexpr; // Else find expressions
  1488. }
  1489. if (inpfile != NULL) { // If file list from file
  1490. flags |= SHOWNAME; // Always show name of file
  1491. if ((strcmp(inpfile, "/") != 0) && (strcmp(inpfile, "-") != 0)) {
  1492. if ((fi = fopen(inpfile, "r")) == NULL) {
  1493. // If open fails
  1494. printmessage(stderr, MSG_FINDSTR_CANNOT_READ_FILE_LIST, program, inpfile);
  1495. exit(2); // Error exit
  1496. }
  1497. } else
  1498. fi = stdin; // Else read file list from stdin
  1499. while(fgets(filnam, MAX_PATH+1, fi) != NULL) {
  1500. // While there are names
  1501. filnam[strcspn(filnam, "\r\n")] = '\0'; // Null-terminate the name
  1502. if ((fd = openfile(filnam)) == (HANDLE)-1) {
  1503. continue; // Skip file if it cannot be opened
  1504. }
  1505. qgrep(grep, filnam, fd); // Do the work
  1506. CloseHandle( fd );
  1507. }
  1508. if (fi != stdin)
  1509. fclose(fi); // Close the list file
  1510. } else if (i == argc) {
  1511. flags &= ~(NAMEONLY | SHOWNAME);
  1512. qgrep( grep, NULL, GetStdHandle( STD_INPUT_HANDLE ) );
  1513. }
  1514. if (argc > i + 1 || fsubdirs || has_wild_cards(argv[i]))
  1515. flags |= SHOWNAME;
  1516. if (dirlist && *dirlist) {
  1517. char *dir;
  1518. char *dirend = (char *)-1;
  1519. char *original_current_directory = NULL;
  1520. DWORD size;
  1521. size = GetCurrentDirectory(0, NULL);
  1522. if (size) {
  1523. original_current_directory = (PCHAR)malloc(size);
  1524. if (original_current_directory == NULL) {
  1525. printmessage(stderr, MSG_FINDSTR_OUT_OF_MEMORY, program);
  1526. exit(2);
  1527. }
  1528. size = GetCurrentDirectory(size, original_current_directory);
  1529. }
  1530. if (!size) {
  1531. free(original_current_directory);
  1532. printmessage(stderr, MSG_FINDSTR_UNABLE_TO_GET_CURRENT_DIRECTORY, program);
  1533. exit(2);
  1534. }
  1535. for(dir = dirlist; dirend; dir = dirend + 1) {
  1536. if (dirend = strchr(dir, ';'))
  1537. *dirend = 0;
  1538. if (*dir) {
  1539. (*write1)(" ", 2, wAttrib); // Indent a couple of spaces
  1540. (*write1)(dir, lstrlen(dir), wAttrib); // Show name
  1541. (*write1)(":\r\n", 3, wAttrib); // Write newline sequence
  1542. if (!SetCurrentDirectory(original_current_directory)) {
  1543. free(original_current_directory);
  1544. printmessage(stderr, MSG_FINDSTR_CANNOT_OPEN_FILE, program,
  1545. original_current_directory);
  1546. exit(2);
  1547. }
  1548. if (!SetCurrentDirectory(dir)) {
  1549. printmessage(stderr, MSG_FINDSTR_CANNOT_OPEN_FILE, program, dir);
  1550. } else {
  1551. while (filematch(filnam, argv + i, argc - i, fsubdirs) >= 0) {
  1552. #ifdef FE_SB
  1553. // _mbslwr((unsigned char *)filnam);
  1554. #else
  1555. // _strlwr(filnam);
  1556. #endif
  1557. if ((fd = openfile(filnam)) != (HANDLE)-1) {
  1558. qgrep(grep, filnam, fd);
  1559. CloseHandle( fd );
  1560. }
  1561. }
  1562. }
  1563. }
  1564. }
  1565. free(original_current_directory);
  1566. }
  1567. else if (fsubdirs && argc > i) { // If directory search wanted
  1568. while (filematch(filnam, argv + i, argc - i, fsubdirs) >= 0) {
  1569. #ifdef FE_SB
  1570. // _mbslwr((unsigned char *)filnam);
  1571. #else
  1572. // _strlwr(filnam);
  1573. #endif
  1574. if ((fd = openfile(filnam)) == (HANDLE)-1) {
  1575. continue;
  1576. }
  1577. qgrep(grep, filnam, fd);
  1578. CloseHandle( fd );
  1579. }
  1580. } else { // Else search files specified
  1581. for(; i < argc; ++i) {
  1582. #ifdef FE_SB
  1583. // _mbslwr((unsigned char *) argv[i]);
  1584. #else
  1585. // _strlwr(argv[i]);
  1586. #endif
  1587. find_handle = FindFirstFile(argv[i], &find_data);
  1588. if (find_handle == INVALID_HANDLE_VALUE) {
  1589. printmessage(stderr, MSG_FINDSTR_CANNOT_OPEN_FILE, program, argv[i]);
  1590. continue;
  1591. }
  1592. do {
  1593. #ifdef FE_SB
  1594. // _mbslwr((unsigned char *)find_data.cFileName);
  1595. #else
  1596. // _strlwr(find_data.cFileName);
  1597. #endif
  1598. prepend_path(find_data.cFileName, argv[i]);
  1599. fd = openfile(find_data.cFileName);
  1600. if (fd != INVALID_HANDLE_VALUE) {
  1601. qgrep(grep, find_data.cFileName, fd);
  1602. CloseHandle( fd );
  1603. }
  1604. } while (FindNextFile(find_handle, &find_data));
  1605. }
  1606. }
  1607. (*flush1)();
  1608. #if DBG
  1609. if ( flags & TIMER ) { // If timing wanted
  1610. unsigned long tend;
  1611. tend = clock();
  1612. tstart = tend - tstart; // Get time in milliseconds
  1613. fprintf(stderr, "%lu.%03lu seconds\n", ( tstart / CLK_TCK ), ( tstart % CLK_TCK ) );
  1614. // Print total elapsed time
  1615. }
  1616. #endif
  1617. // Print warning in case that offline files were skipped
  1618. if (fOfflineSkipped) {
  1619. printmessage(stderr, MSG_FINDSTR_OFFLINE_FILE_SKIPPED, program);
  1620. }
  1621. return( status );
  1622. } // main
  1623. char * findsub( unsigned char *, char * );
  1624. char * findsubi( unsigned char *, char * );
  1625. char * (*flworker[])(unsigned char *, char *) = { // Table of workers
  1626. findsubi,
  1627. findsub
  1628. };
  1629. char *
  1630. strnupr(
  1631. char *pch,
  1632. int cch
  1633. )
  1634. {
  1635. char c[2];
  1636. #ifdef FE_SB
  1637. int max = cch;
  1638. c[1] = 0;
  1639. for ( cch = 0; cch < max; cch++ ) {
  1640. #else
  1641. c[1] = 0;
  1642. while (cch-- > 0) { // Convert string to upper case
  1643. #endif
  1644. if (isalpha((unsigned char)pch[cch])) {
  1645. c[0] = pch[cch];
  1646. pch[cch] = (_strupr(c))[0];
  1647. }
  1648. #ifdef FE_SB
  1649. else if (IsDBCSCodePage && IsDBCSLeadByte(pch[cch]))
  1650. cch++;
  1651. #endif
  1652. }
  1653. return(pch);
  1654. }
  1655. /*
  1656. * This is an implementation of the QuickSearch algorith described
  1657. * by Daniel M. Sunday in the August 1990 issue of CACM. The TD1
  1658. * table is computed before this routine is called.
  1659. */
  1660. char *
  1661. findone(
  1662. unsigned char *buffer,
  1663. char *bufend
  1664. )
  1665. {
  1666. #ifdef FE_SB // Starting position of string for checking 2nd bytes of DBCS characters.
  1667. unsigned char *bufferhead = buffer;
  1668. #endif
  1669. if ((bufend -= targetlen - 1) <= (char *) buffer)
  1670. return((char *) 0); // Fail if buffer too small
  1671. while (buffer < (unsigned char *) bufend) { // While space remains
  1672. int cch; // Character count
  1673. register char *pch1; // Char pointer
  1674. register char *pch2; // Char pointer
  1675. pch1 = target; // Point at pattern
  1676. pch2 = (char *) buffer; // Point at buffer
  1677. #ifdef FE_SB
  1678. // If buffer points to the 2nd byte of a DBCS character,
  1679. // skip to next compare position.
  1680. if ( !IsTailByte( bufferhead, (int)(buffer - bufferhead) ) ) {
  1681. #endif
  1682. for (cch = targetlen; cch > 0; --cch) {
  1683. // Loop to try match
  1684. if (*pch1++ != *pch2++)
  1685. break; // Exit loop on mismatch
  1686. }
  1687. if (cch == 0)
  1688. return((char *)buffer); // Return pointer to match
  1689. #ifdef FE_SB
  1690. }
  1691. #endif
  1692. if (buffer + 1 < (unsigned char *) bufend) // Make sure buffer[targetlen] is valid.
  1693. buffer += ((unsigned char)td1[buffer[targetlen]]); // Skip ahead
  1694. else
  1695. break;
  1696. }
  1697. return((char *) 0); // No match
  1698. }
  1699. int
  1700. preveol(
  1701. char *s
  1702. )
  1703. {
  1704. register char *cp; // Char pointer
  1705. cp = s + 1; // Initialize pointer
  1706. if (!bStdIn && !bLargeFile) {
  1707. while((--cp >= BaseByteAddress) && (*cp != '\n'))
  1708. ; // Find previous end-of-line
  1709. } else {
  1710. while(*--cp != '\n') ; // Find previous end-of-line
  1711. }
  1712. return (int)(s - cp); // Return distance to match
  1713. }
  1714. int
  1715. countlines(
  1716. char *start,
  1717. char *finish
  1718. )
  1719. {
  1720. register int count; // Line count
  1721. for(count = 0; start < finish; ) {
  1722. // Loop to count lines
  1723. if (*start++ == '\n')
  1724. ++count; // Increment count if linefeed found
  1725. }
  1726. return(count); // Return count
  1727. }
  1728. char *
  1729. findlist(
  1730. unsigned char *buffer,
  1731. char *bufend
  1732. )
  1733. {
  1734. char *match; // Pointer to matching string
  1735. // Avoid writting to bufend. bufend[-1] is something(such as '\n') that is not
  1736. // part of search and will cause the search to stop.
  1737. match = (*flworker[casesen])(buffer, bufend); // Call worker
  1738. return(match); // Return matching string
  1739. }
  1740. char *
  1741. findsub(
  1742. unsigned char *buffer,
  1743. char *bufend
  1744. )
  1745. {
  1746. register char *cp; // Char pointer
  1747. STRINGNODE *s; // String node pointer
  1748. int i; // Index
  1749. #ifdef FE_SB // Head of buffer for checking if a certain offset is the 2nd byte of a DBCS character.
  1750. unsigned char *bufhead = buffer;
  1751. #endif
  1752. char *real_bufend = bufend;
  1753. if (cchmin != (unsigned)-1 &&
  1754. cchmin != 0 &&
  1755. (bufend -= cchmin - 1) < (char *) buffer)
  1756. return((char *) 0); // Compute effective buffer length
  1757. while(buffer < (unsigned char *) bufend) { // Loop to find match
  1758. #ifdef FE_SB
  1759. // Search cannot start at the second byte of a DBCS character,
  1760. // so check for it and skip it if it is a second byte.
  1761. if ((i = (UCHAR)transtab[*buffer]) != 0 &&
  1762. !IsTailByte( bufhead, (int)(buffer-bufhead) ) ) {
  1763. #else
  1764. if ((i = (UCHAR)transtab[*buffer]) != 0) {
  1765. #endif
  1766. // If valid first character
  1767. if ((s = stringlist[i]) == 0) {
  1768. return((char *)buffer); // Check for 1-byte match
  1769. }
  1770. for(cp = (char *) buffer + 1; (real_bufend - cp) >= s->s_must; ) { // Loop to search list
  1771. if ((i = _strncoll(cp, s_text(s), s->s_must)) == 0) {
  1772. // If portions match
  1773. cp += s->s_must; // Skip matching portion
  1774. if ((s = s->s_suf) == 0)
  1775. return((char *)buffer); // Return match if end of list
  1776. continue; // Else continue
  1777. }
  1778. if (i < 0 || (s = s->s_alt) == 0) {
  1779. break; // Break if not in this list
  1780. }
  1781. }
  1782. }
  1783. if (buffer + 1 < (unsigned char *) bufend) // Make sure buffer[cchmin] is valid.
  1784. if (cchmin == (unsigned)-1)
  1785. buffer++;
  1786. else
  1787. buffer += ((unsigned char)td1[buffer[cchmin]]); // Shift as much as possible
  1788. else
  1789. break;
  1790. }
  1791. return((char *) 0); // No match
  1792. }
  1793. char *
  1794. findsubi(
  1795. unsigned char *buffer,
  1796. char *bufend
  1797. )
  1798. {
  1799. register char *cp; // Char pointer
  1800. STRINGNODE *s; // String node pointer
  1801. int i; // Index
  1802. #ifdef FE_SB
  1803. // Keep head of buffer for checking if a certain offset is the 2nd byte of
  1804. // a DBCS character.
  1805. unsigned char *bufhead = buffer;
  1806. #endif
  1807. if (cchmin != (unsigned)-1 &&
  1808. cchmin != 0 &&
  1809. (bufend -= cchmin - 1) < (char *) buffer)
  1810. return((char *) 0); // Compute effective buffer length
  1811. while(buffer < (unsigned char *) bufend) { // Loop to find match
  1812. #ifdef FE_SB
  1813. // Search cannot start at the second byte of a DBCS character, so check for it
  1814. // and skip it if it is a second byte.
  1815. if ((i = (UCHAR)transtab[*buffer]) != 0 &&
  1816. !IsTailByte( bufhead, (int)(buffer-bufhead) ) ) {
  1817. // If valid first character
  1818. BOOL TailByte; // Flag to check if 1st char is leadbyte.
  1819. #else
  1820. if ((i = (UCHAR)transtab[*buffer]) != 0) { // If valid first character
  1821. #endif
  1822. if ((s = stringlist[i]) == 0)
  1823. return((char *) buffer); // Check for 1-byte match
  1824. #ifdef FE_SB
  1825. // Same leadbytes with tailbytes such as 0x41 and 0x61 will become the same
  1826. // character, so become aware of it and use the multibyte function.
  1827. //
  1828. // Check if buffer+1 is a tailbyte character.
  1829. //
  1830. TailByte = IsTailByte(buffer, 1);
  1831. for(cp = (char *) buffer + 1; ; ) { // Loop to search list
  1832. if ((i = _mbsnicmp((unsigned char *)cp, (unsigned char *) s_text(s), s->s_must, &TailByte)) == 0) {
  1833. #else
  1834. for(cp = (char *) buffer + 1; ; ) { // Loop to search list
  1835. if ((i = memicmp(cp, s_text(s), s->s_must)) == 0) {
  1836. #endif
  1837. // If portions match
  1838. cp += s->s_must; // Skip matching portion
  1839. if ((s = s->s_suf) == 0)
  1840. return((char *) buffer); // Return match if end of list
  1841. continue; // And continue
  1842. }
  1843. if (i < 0 || (s = s->s_alt) == 0)
  1844. break; // Break if not in this list
  1845. }
  1846. }
  1847. if (buffer + 1 < (unsigned char *) bufend) // Make sure buffer[cchmin] is valid.
  1848. if (cchmin == (unsigned)-1)
  1849. buffer++;
  1850. else
  1851. buffer += ((unsigned char)td1[buffer[cchmin]]); // Shift as much as possible
  1852. else
  1853. break;
  1854. }
  1855. return((char *) 0); // No match
  1856. }
  1857. int
  1858. strnspn(
  1859. char *s,
  1860. char *t,
  1861. int n
  1862. )
  1863. /*
  1864. Description:
  1865. Finds the position of the first character in s of length n that is not
  1866. in the character set t.
  1867. Argument:
  1868. s - string to search from.
  1869. t - character set to search for
  1870. n - length of s
  1871. Returns:
  1872. Returns the offset of the first character in s that is not in t
  1873. */
  1874. {
  1875. register char *s1; // String pointer
  1876. register char *t1; // String pointer
  1877. for(s1 = s; n-- != 0; ++s1) { // While not at end of s
  1878. for(t1 = t; *t1 != '\0'; ++t1) { // While not at end of t
  1879. if (*s1 == *t1)
  1880. break; // Break if match found
  1881. }
  1882. if (*t1 == '\0')
  1883. break; // Break if no match found
  1884. }
  1885. return (int)(s1 - s); // Return length
  1886. }
  1887. int
  1888. strncspn(
  1889. char *s,
  1890. char *t,
  1891. int n
  1892. )
  1893. /*
  1894. Description:
  1895. Finds the position of the first occurence of characters in t in string
  1896. s of length n.
  1897. Argument:
  1898. s - string to search from.
  1899. t - character set to search for
  1900. n - length of s
  1901. Returns:
  1902. Returns first offset position in s that consists of characters in t
  1903. Returns length of s if not found.
  1904. */
  1905. {
  1906. register char *s1; // String pointer
  1907. register char *t1; // String pointer
  1908. for(s1 = s; n-- != 0; ++s1) { // While not at end of s
  1909. for(t1 = t; *t1 != '\0'; ++t1) { // While not at end of t
  1910. if (*s1 == *t1)
  1911. return (int)(s1 - s); // Return if match found
  1912. }
  1913. }
  1914. return (int)(s1 - s); // Return length
  1915. }
  1916. void
  1917. matchstrings(
  1918. char *s1,
  1919. char *s2,
  1920. int len,
  1921. int *nmatched,
  1922. int *leg
  1923. )
  1924. {
  1925. register char *cp; // Char pointer
  1926. register int (__cdecl *cmp)(const char*, const char*, size_t); // Comparison function pointer
  1927. cmp = casesen ? _strncoll: _strnicoll; // Set pointer
  1928. if ((*leg = (*cmp)(s1, s2, len)) != 0) { // If strings don't match
  1929. for(cp = s1; (*cmp)(cp, s2++, 1) == 0; ++cp)
  1930. ;
  1931. // Find mismatch
  1932. *nmatched = (int)(cp - s1); // Return number matched
  1933. }
  1934. else *nmatched = len; // Else all matched
  1935. }
  1936. void
  1937. printmessage (
  1938. FILE* fp,
  1939. DWORD messageID,
  1940. ...
  1941. )
  1942. {
  1943. char messagebuffer[DISPLAYBUFFER_SIZE];
  1944. WCHAR widemessagebuffer[DISPLAYBUFFER_SIZE];
  1945. ULONG len;
  1946. NTSTATUS status;
  1947. va_list ap;
  1948. va_start(ap, messageID);
  1949. if (len = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE,
  1950. NULL,
  1951. messageID,
  1952. 0,
  1953. messagebuffer,
  1954. DISPLAYBUFFER_SIZE,
  1955. &ap)) {
  1956. // the messagebuffer should be null terminated
  1957. status = RtlMultiByteToUnicodeN(widemessagebuffer,
  1958. DISPLAYBUFFER_SIZE*sizeof(WCHAR),
  1959. &len,
  1960. messagebuffer,
  1961. len);
  1962. // the widemessagebuffer is not null terminated but len tells us how long
  1963. if (NT_SUCCESS(status)) {
  1964. status = RtlUnicodeToOemN(messagebuffer, DISPLAYBUFFER_SIZE-1, &len, widemessagebuffer, len);
  1965. // the messagebuffer is not null terminated but len tells us how long
  1966. if (NT_SUCCESS(status)) {
  1967. messagebuffer[len] = 0;
  1968. fprintf(fp, "%s", messagebuffer);
  1969. } else {
  1970. DbgPrint("Failure to convert Unicode to Oem: %d\n", GetLastError());
  1971. }
  1972. } else {
  1973. DbgPrint("Failure to convert MultiByte to Unicode: %d\n", GetLastError());
  1974. }
  1975. } else {
  1976. DbgPrint("FormatMessage failed: %d\n", GetLastError());
  1977. }
  1978. va_end(ap);
  1979. }
  1980. #ifdef FE_SB
  1981. int
  1982. IsTailByte(
  1983. unsigned const char *text,
  1984. const int offset
  1985. )
  1986. /*
  1987. Description:
  1988. This routine checks to see if the byte at the offset location is a
  1989. tail byte of a DBCS character. The offset is calculated such that the
  1990. first location has a value of 0.
  1991. Argument:
  1992. text - Points to a MBCS text string.
  1993. offset - zero base offset to check character is a tailbyte of a DBCS
  1994. character.
  1995. Returns:
  1996. TRUE - offset position is a tailbyte character.
  1997. FALSE - otherwise.
  1998. Modifications:
  1999. v-junm: 05/06/93 - Original.
  2000. */
  2001. {
  2002. int i = offset;
  2003. if ( !IsDBCSCodePage )
  2004. return( FALSE );
  2005. for ( ; i; i-- )
  2006. if ( !IsDBCSLeadByte ( text[i-1] ) )
  2007. break;
  2008. return( ( offset - i ) % 2 );
  2009. }
  2010. char *
  2011. _mbsrchr(
  2012. const char *string,
  2013. int c
  2014. )
  2015. /*
  2016. Description:
  2017. This function is a DBCS enabled version of the STRRCHR function
  2018. included in the MS C/C++ library. What DBCS enabled means is that
  2019. the SBCS character 'c' is found in a MBCS string 'string'. 'c' is
  2020. a SBCS character that cannot be contained in the tailbyte of a DBCS
  2021. character.
  2022. Argument:
  2023. string - Points to a MBCS text string.
  2024. offset - Character to find in string.
  2025. Returns:
  2026. Returns a pointer to the last occurance of c in string, or a NULL
  2027. pointer if c is not found.
  2028. Modifications:
  2029. v-junm: 05/06/93 - Original.
  2030. */
  2031. {
  2032. register int i = strlen( string );
  2033. for (; i >= 0; i-- ) {
  2034. if ( ( *(string + i) == (char)c ) && !IsTailByte( (unsigned char *) string, i ) )
  2035. return( (char*)(string + i) );
  2036. }
  2037. return ( NULL );
  2038. }
  2039. unsigned char *
  2040. _mbslwr(
  2041. unsigned char *s
  2042. )
  2043. /*
  2044. Description:
  2045. This function is a DBCS aware version of the strlwr function
  2046. included in the MS C/C++ library. SBCS alphabets contained in
  2047. the tailbyte of a DBCS character is not affected in the conversion.
  2048. Argument:
  2049. s - String to converted to lower case.
  2050. Returns:
  2051. Returns a string that was converted to lower case.
  2052. Modifications:
  2053. v-junm: 05/06/93 - Original.
  2054. */
  2055. {
  2056. //
  2057. // If NonJP code page, use original routine.
  2058. //
  2059. if ( !IsDBCSCodePage )
  2060. return( (unsigned char *) _strlwr( (char *) s ) );
  2061. //
  2062. // While not end of string convert to lower case.
  2063. //
  2064. for( ; *s; s++ ) {
  2065. //
  2066. // if Leadbyte and next character is not NULL
  2067. // skip tailbyte
  2068. // else if uppercase character
  2069. // convert it to lowercase
  2070. //
  2071. if ( IsDBCSLeadByte( *s ) && *(s+1) )
  2072. s++;
  2073. else if ( *s >= 0x41 && *s <= 0x5a )
  2074. *s = *s + 0x20;
  2075. }
  2076. return( s );
  2077. }
  2078. int
  2079. _mbsnicmp(
  2080. const unsigned char *s1,
  2081. const unsigned char *s2,
  2082. int n,
  2083. BOOL *TailByte
  2084. )
  2085. /*
  2086. Description:
  2087. This is similar to a DBCS aware version of the memicmp function
  2088. contained in the MS C/C++ library. The only difference is that
  2089. an additional parameter is passed which indicates if the first
  2090. character is a tailbyte of a DBCS character.
  2091. Argument:
  2092. s1 - string 1 to compare.
  2093. s2 - string 2 to compare.
  2094. n - maximum number of bytes to compare.
  2095. TailByte - flag to indicate first character in s1 and s2 is a tailbyte
  2096. of a DBCS character.
  2097. Returns:
  2098. RetVal < 0 - s1 < s2
  2099. RetVal = 0 - s1 == s2
  2100. RetVal > 0 - s1 > s2
  2101. Modifications:
  2102. v-junm: 05/06/93 - Original.
  2103. */
  2104. {
  2105. BOOL tail = *TailByte;
  2106. int i;
  2107. *TailByte = FALSE;
  2108. for( ; n; n--, s1++, s2++ ) {
  2109. if ( *s1 == *s2 ) {
  2110. if ( tail == FALSE && IsDBCSLeadByte( *s1 ) )
  2111. tail = TRUE;
  2112. else
  2113. tail = FALSE;
  2114. continue;
  2115. }
  2116. else if ( !tail ) {
  2117. i = _strnicoll((char *)s1, (char *)s2, 1);
  2118. if (i == 0)
  2119. continue;
  2120. return i;
  2121. }
  2122. return( *s1 - *s2 );
  2123. }
  2124. *TailByte = tail;
  2125. return( 0 );
  2126. }
  2127. #endif