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

2520 lines
82 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. if (addstr == NULL) {
  547. addstr = isexpr( (unsigned char *) buffer, len ) ? addexpr : addstring;
  548. // Select search string type
  549. }
  550. if (len >= MAXSTRLEN)
  551. error(MSG_FINDSTR_SEARCH_STRING_TOO_LONG);
  552. memcpy(tmpbuf, buffer, len);
  553. tmpbuf[len] = '\n';
  554. tmpbuf[len+1] = 0;
  555. if ( addstr == addexpr || (flags & BEGLINE) ||
  556. findlist((unsigned char *)tmpbuf, tmpbuf + len + 1) == NULL) {
  557. // If no match within string
  558. (*addstr)(buffer, len); // Add string to list
  559. }
  560. buffer += len; // Skip the string
  561. }
  562. return(0); // Keep looking
  563. }
  564. int
  565. enumlist(
  566. STRINGNODE *node,
  567. int cchprev
  568. )
  569. {
  570. int strcnt; // String count
  571. strcnt = 0; // Initialize
  572. while(node != NULL) { // While not at end of list
  573. maketd1((unsigned char *)s_text(node), node->s_must, cchprev);
  574. // Make TD1 entries
  575. #if DBG
  576. if (flags & DEBUG) { // If verbose output wanted
  577. int i; // Counter
  578. for(i = 0; i < cchprev; ++i)
  579. fputc(' ', stderr); // Indent line
  580. fwrite(s_text(node), sizeof(char), node->s_must, stderr);
  581. // Write this portion
  582. fprintf(stderr, "\n"); // Newline
  583. }
  584. #endif
  585. strcnt += (node->s_suf != NULL) ?
  586. enumlist(node->s_suf, cchprev + node->s_must): 1;
  587. // Recurse to do suffixes
  588. node = node->s_alt; // Do next alternate in list
  589. }
  590. return (strcnt ? strcnt: 1); // Return string count
  591. }
  592. int
  593. enumstrings()
  594. {
  595. unsigned char ch; // Character
  596. unsigned i; // Index
  597. int strcnt; // String count
  598. strcnt = 0; // Initialize
  599. for(i = 0; i < TRTABLEN; ++i) { // Loop through translation table
  600. if (casesen || !isalpha(i) || !islower(i)) {
  601. // If case sensitive or not lower
  602. if (transtab[i] == 0)
  603. continue; // Skip null entries
  604. ch = (char) i; // Get character
  605. maketd1((unsigned char *)&ch, 1, 0); // Make TD1 entry
  606. #if DBG
  607. if (flags & DEBUG)
  608. fprintf(stderr, "%c\n", i); // Print the first byte
  609. #endif
  610. strcnt += enumlist(stringlist[transtab[i]], 1);
  611. // Enumerate the list
  612. }
  613. }
  614. return (strcnt); // Return string count
  615. }
  616. HANDLE
  617. openfile(
  618. char *name
  619. )
  620. {
  621. HANDLE fd;
  622. DWORD attr;
  623. attr = GetFileAttributes(name);
  624. if (attr != (DWORD) -1 && (attr & FILE_ATTRIBUTE_DIRECTORY))
  625. return (HANDLE)-1;
  626. // Skip offline files unless instructed otherwise
  627. if (attr != (DWORD) -1 && (attr & FILE_ATTRIBUTE_OFFLINE) && !(flags & OFFLINE_FILES)) {
  628. fOfflineSkipped = TRUE;
  629. return (HANDLE)-1;
  630. }
  631. if ((fd = CreateFile(name,
  632. GENERIC_READ,
  633. FILE_SHARE_READ | FILE_SHARE_WRITE,
  634. NULL,
  635. OPEN_EXISTING,
  636. FILE_FLAG_OPEN_NO_RECALL,
  637. NULL)) == (HANDLE)-1) {
  638. printmessage(stderr, MSG_FINDSTR_CANNOT_OPEN_FILE, program, name);
  639. }
  640. return( fd ); // Return file descriptor
  641. }
  642. void
  643. startread(
  644. HANDLE fd,
  645. char *buffer,
  646. int buflen
  647. )
  648. {
  649. if (bStdIn || bLargeFile) {
  650. arrc = ReadFile(fd,(PVOID)buffer, buflen, &cbread, NULL);
  651. }
  652. }
  653. int
  654. finishread()
  655. {
  656. return(arrc ? cbread : -1); // Return number of bytes read
  657. }
  658. void
  659. startwrite( HANDLE fd, char *buffer, int buflen)
  660. {
  661. awrc = WriteFile(fd,(PVOID)buffer, buflen, &cbwrite, NULL);
  662. return;
  663. }
  664. int
  665. finishwrite()
  666. {
  667. return(awrc ? cbwrite : -1); // Return number of bytes written
  668. }
  669. BOOL
  670. CtrlHandler(DWORD CtrlType)
  671. {
  672. // We'll handle Ctrl-C events
  673. switch(CtrlType) {
  674. case CTRL_C_EVENT:
  675. case CTRL_BREAK_EVENT:
  676. if (csbi.wAttributes) {
  677. EnterCriticalSection(&critSection);
  678. fExiting = TRUE;
  679. SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
  680. csbi.wAttributes);
  681. LeaveCriticalSection(&critSection);
  682. }
  683. break;
  684. }
  685. // Deal with all other events as normal
  686. return (FALSE);
  687. }
  688. void
  689. write1nobuf(
  690. char *buffer,
  691. int buflen,
  692. WORD wAttributes
  693. )
  694. {
  695. int nT;
  696. CBIO cb; // Count of bytes written
  697. BOOL fCR;
  698. BOOL fLF;
  699. char buf[STKLEN];
  700. char *szT;
  701. static HANDLE hConOut = INVALID_HANDLE_VALUE;
  702. int remaining_length;
  703. // Get the console screen buffer info if we haven't yet.
  704. if (hConOut == INVALID_HANDLE_VALUE) {
  705. hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
  706. InitializeCriticalSection(&critSection);
  707. SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE);
  708. }
  709. if (wAttributes) {
  710. EnterCriticalSection(&critSection);
  711. // if not exiting, highlight the output
  712. if (!fExiting)
  713. SetConsoleTextAttribute(hConOut, wAttributes);
  714. LeaveCriticalSection(&critSection);
  715. if (fExiting)
  716. ExitProcess(2);
  717. }
  718. remaining_length = buflen;
  719. while (remaining_length) {
  720. buflen = (int)min(sizeof(buf) / sizeof(buf[0]), remaining_length);
  721. szT = buf;
  722. if (IsDBCSCodePage) {
  723. memcpy(buf, buffer, buflen);
  724. } else {
  725. for(nT = 0; nT < buflen; nT++) {
  726. *(szT++) = ((isprint((unsigned char)buffer[nT]) ||
  727. isspace((unsigned char)buffer[nT])) ?
  728. buffer[nT] : '.');
  729. }
  730. }
  731. if (!WriteFile(hConOut, (PVOID)buf, buflen, &cb, NULL)
  732. || (cb != (CBIO)(buflen)))
  733. {
  734. SetConsoleTextAttribute(hConOut, csbi.wAttributes);
  735. error(MSG_FINDSTR_WRITE_ERROR); // Die if write fails
  736. }
  737. remaining_length -= buflen;
  738. buffer += buflen;
  739. }
  740. if (wAttributes)
  741. SetConsoleTextAttribute(hConOut, csbi.wAttributes);
  742. }
  743. void
  744. write1buf(
  745. char *buffer,
  746. int buflen,
  747. WORD wAttributes
  748. )
  749. {
  750. register int cb; // Byte count
  751. while(buflen > 0) { // While bytes remain
  752. if (!awrc) { // If previous write failed
  753. printmessage(stderr, MSG_FINDSTR_WRITE_ERROR, program); // Print error message
  754. exit(2); // Die
  755. }
  756. if ((cb = ocnt[oi]) == 0) { // If buffer full
  757. startwrite( GetStdHandle( STD_OUTPUT_HANDLE ), obuf[oi], OUTBUFLEN );
  758. // Write the buffer
  759. ocnt[oi] = OUTBUFLEN; // Reset count and pointer
  760. optr[oi] = obuf[oi];
  761. oi ^= 1; // Switch buffers
  762. cb = ocnt[oi]; // Get space remaining
  763. }
  764. if (cb > buflen)
  765. cb = buflen; // Get minimum
  766. memmove(optr[oi], buffer, cb); // Copy bytes to buffer
  767. ocnt[oi] -= cb; // Update buffer length and pointers
  768. optr[oi] += cb;
  769. buflen -= cb;
  770. buffer += cb;
  771. }
  772. }
  773. void
  774. flush1nobuf(
  775. void
  776. )
  777. {
  778. ;
  779. }
  780. void
  781. flush1buf(
  782. void
  783. )
  784. {
  785. register int cb; // Byte count
  786. if ((cb = OUTBUFLEN - ocnt[oi]) > 0) { // If buffer not empty
  787. startwrite( GetStdHandle( STD_OUTPUT_HANDLE ), obuf[oi], cb ); // Start write
  788. if (finishwrite() != cb) { // If write failed
  789. printmessage(stderr, MSG_FINDSTR_WRITE_ERROR, program); // Print error message
  790. exit(2); // Die
  791. }
  792. }
  793. }
  794. int
  795. grepbuffer(
  796. char *startbuf,
  797. char *endbuf,
  798. char *name
  799. )
  800. {
  801. char *cp; // Buffer pointer
  802. char *lastmatch; // Last matching line
  803. int linelen; // Line length
  804. int namlen = 0; // Length of name
  805. char lnobuf[LNOLEN]; // Line number buffer
  806. char nambuf[PATHLEN]; // Name buffer
  807. cp = startbuf; // Initialize to start of buffer
  808. lastmatch = cp; // No previous match yet
  809. while((cp = (*find)((unsigned char *)cp, endbuf)) != NULL) {
  810. // While matches are found
  811. --cp; // Back up to previous character
  812. // Take care of '\n' as an artificial newline before line 1.
  813. if ((flags & BEGLINE) && (bStdIn || bLargeFile || cp >= BaseByteAddress) && *cp != '\n' ) {
  814. // If begin line conditions not met
  815. cp += strncspn(cp, "\n", (int)(endbuf - cp)) + 1;
  816. // Skip line
  817. continue; // Keep looking
  818. }
  819. status = 0; // Match found
  820. if (flags & NAMEONLY)
  821. return(1); // Return if filename only wanted
  822. cp -= preveol(cp) - 1; // Point at start of line
  823. if (flags & SHOWNAME) { // If name wanted
  824. if (namlen == 0) { // If name not formatted yet
  825. namlen = sprintf(nambuf, "%s:", name);
  826. // Format name if not done already
  827. }
  828. (*write1)(nambuf, namlen, wAttrib); // Show name
  829. }
  830. if (flags & LINENOS) { // If line number wanted
  831. lineno += countlines(lastmatch, cp);
  832. // Count lines since last match
  833. (*write1)(lnobuf, sprintf(lnobuf, "%u:", lineno), wAttrib);
  834. // Print line number
  835. lastmatch = cp; // New last match
  836. }
  837. if (flags & SEEKOFF) { // If seek offset wanted
  838. (*write1)(lnobuf, sprintf(lnobuf, "%lu:",
  839. cbfile + (long)(cp - startbuf)), wAttrib);
  840. // Print seek offset
  841. }
  842. linelen = strncspn(cp, "\n", (int)(endbuf - cp)) + 1;
  843. // Calculate line length
  844. if (linelen > endbuf - cp) {
  845. linelen = (int)(endbuf - cp);
  846. }
  847. (*write1)(cp, linelen, 0); // Print the line
  848. cp += linelen; // Skip the line
  849. }
  850. lineno += countlines(lastmatch, endbuf);
  851. // Count remaining lines in buffer
  852. return(0); // Keep searching
  853. }
  854. void
  855. showv(
  856. char *name,
  857. char *startbuf,
  858. char *lastmatch,
  859. char *thismatch
  860. )
  861. {
  862. register int linelen;
  863. int namlen = 0; // Length of name
  864. char lnobuf[LNOLEN]; // Line number buffer
  865. char nambuf[PATHLEN];// Name buffer
  866. if (flags & (SHOWNAME | LINENOS | SEEKOFF)) {
  867. while(lastmatch < thismatch) {
  868. if (flags & SHOWNAME) { // If name wanted
  869. if (namlen == 0) { // If name not formatted yet
  870. namlen = sprintf(nambuf, "%s:", name);
  871. // Format name if not done already
  872. }
  873. (*write1)(nambuf, namlen, wAttrib);
  874. // Write the name
  875. }
  876. if (flags & LINENOS) // If line numbers wanted
  877. {
  878. (*write1)(lnobuf, sprintf(lnobuf, "%u:", lineno++), wAttrib);
  879. // Print the line number
  880. }
  881. if (flags & SEEKOFF) { // If seek offsets wanted
  882. (*write1)(lnobuf, sprintf(lnobuf, "%lu:",
  883. cbfile + (long)(lastmatch - startbuf)), wAttrib);
  884. // Print the line number
  885. }
  886. linelen = strncspn(lastmatch, "\n", (int)(thismatch - lastmatch));
  887. // If there's room for the '\n' then pull it in. Otherwise
  888. // the buffer doesn't have a '\n' within the range here.
  889. if (linelen < thismatch - lastmatch) {
  890. linelen++;
  891. }
  892. (*write1)(lastmatch, linelen, 0);
  893. lastmatch += linelen;
  894. }
  895. }
  896. else
  897. (*write1)(lastmatch, (int)(thismatch - lastmatch), 0);
  898. }
  899. int
  900. grepvbuffer(
  901. char *startbuf,
  902. char *endbuf,
  903. char *name
  904. )
  905. {
  906. char *cp; // Buffer pointer
  907. char *lastmatch; // Pointer to line after last match
  908. cp = startbuf; // Initialize to start of buffer
  909. lastmatch = cp;
  910. while((cp = (*find)((unsigned char *)cp, endbuf)) != NULL) {
  911. --cp; // Back up to previous character
  912. // Take care of '\n' as an artificial newline before line 1.
  913. if ((flags & BEGLINE) && (bStdIn || bLargeFile || cp >= BaseByteAddress) && *cp != '\n') {
  914. // If begin line conditions not met
  915. cp += strncspn(cp, "\n", (int)(endbuf - cp)) + 1;
  916. // Skip line
  917. continue; // Keep looking
  918. }
  919. cp -= preveol(cp) - 1; // Point at start of line
  920. if (cp > lastmatch) { // If we have lines without matches
  921. status = 0; // Lines without matches found
  922. if (flags & NAMEONLY) return(1);
  923. // Skip rest of file if NAMEONLY
  924. showv(name, startbuf, lastmatch, cp);
  925. // Show from last match to this
  926. }
  927. cp += strncspn(cp, "\n", (int)(endbuf - cp)) + 1;
  928. // Skip over line with match
  929. lastmatch = cp; // New "last" match
  930. ++lineno; // Increment line count
  931. }
  932. if (endbuf > lastmatch) { // If we have lines without matches
  933. status = 0; // Lines without matches found
  934. if (flags & NAMEONLY)
  935. return(1); // Skip rest of file if NAMEONLY
  936. showv(name, startbuf, lastmatch, endbuf);
  937. // Show buffer tail
  938. }
  939. return(0); // Keep searching file
  940. }
  941. void
  942. qgrep(
  943. int (*grep)( char *, char *, char * ),
  944. char *name,
  945. HANDLE fd
  946. )
  947. {
  948. register int cb; // Byte count
  949. char *cp; // Buffer pointer
  950. char *endbuf; // End of buffer
  951. int taillen; // Length of buffer tail
  952. int bufi; // Buffer index
  953. HANDLE MapHandle; // File mapping handle
  954. BOOL grep_result;
  955. cbfile = 0L; // File empty so far
  956. lineno = 1; // File starts on line 1
  957. taillen = 0; // No buffer tail yet
  958. bufi = 0; // Initialize buffer index
  959. cp = bufptr[0]; // Initialize to start of buffer
  960. bStdIn = (fd == GetStdHandle(STD_INPUT_HANDLE));
  961. // If fd is not std-input, use file mapping object method.
  962. if (!bStdIn) {
  963. DWORD cbread_high;
  964. if ((((cbread = (CBIO)GetFileSize(fd, &cbread_high)) == -1) && (GetLastError() != NO_ERROR)) ||
  965. (cbread == 0 && cbread_high == 0)) {
  966. return; // skip the file
  967. }
  968. if (cbread_high) {
  969. bLargeFile = TRUE; // too large to map and even if it succeed in mapping like under ia64, it
  970. // will probably fail in pointer arithmetics
  971. } else {
  972. MapHandle = CreateFileMapping(fd,
  973. NULL,
  974. PAGE_READONLY,
  975. 0L,
  976. 0L,
  977. NULL);
  978. if (MapHandle == NULL) {
  979. printmessage(stderr, MSG_FINDSTR_CANNOT_CREATE_FILE_MAPPING, program);
  980. return;
  981. }
  982. BaseByteAddress = (char *) MapViewOfFile(MapHandle,
  983. FILE_MAP_READ,
  984. 0L,
  985. 0L,
  986. 0);
  987. CloseHandle(MapHandle);
  988. if (BaseByteAddress == NULL) {
  989. bLargeFile = TRUE; // use alternate method
  990. } else {
  991. cp = bufptr[0] = BaseByteAddress;
  992. arrc = TRUE;
  993. }
  994. }
  995. }
  996. if (bStdIn || bLargeFile) {
  997. // Reset buffer pointers since they might have been changed.
  998. cp = bufptr[0] = filbuf + 4;
  999. arrc = ReadFile(fd, (PVOID)cp, FILBUFLEN, &cbread, NULL);
  1000. }
  1001. if (flags & PRINTABLE_ONLY) {
  1002. unsigned char *s;
  1003. unsigned long n;
  1004. s = (unsigned char *)cp;
  1005. n = cbread;
  1006. while (--n) {
  1007. if (*s < ' ') {
  1008. // If not backspace, tab, CR, LF, FF or Ctrl-Z then not a printable character.
  1009. if (strchr("\b\t\v\r\n\f\032", *s) == NULL) {
  1010. goto skipfile;
  1011. }
  1012. }
  1013. s += 1;
  1014. }
  1015. }
  1016. // Note: if FILEMAP && !bStdIn, 'while' is executed once(taillen is 0).
  1017. while((cb = finishread()) + taillen > 0) {
  1018. // While search incomplete
  1019. if (bStdIn || bLargeFile) {
  1020. if (cb == -1) { // If buffer tail is all that's left
  1021. *cp++ = '\r'; // Add end of line sequence
  1022. *cp++ = '\n';
  1023. endbuf = cp; // Note end of buffer
  1024. taillen = 0; // Set tail length to zero
  1025. } else { // Else start next read
  1026. taillen = preveol(cp + cb - 1); // Find length of partial line
  1027. endbuf = cp + cb - taillen; // Get pointer to end of buffer
  1028. cp = bufptr[bufi ^ 1]; // Pointer to other buffer
  1029. memmove(cp, endbuf, taillen); // Copy tail to head of other buffer
  1030. cp += taillen; // Skip over tail
  1031. if (taillen > (FILBUFLEN/2)) {
  1032. if (taillen >= FILBUFLEN) {
  1033. char tmp[15];
  1034. cbfile += taillen;
  1035. taillen = 0;
  1036. cp = bufptr[bufi^1];
  1037. startread(fd, cp, FILBUFLEN);
  1038. _ultoa((unsigned long)lineno, tmp, 10);
  1039. printmessage(stderr, MSG_FINDSTR_LINE_TOO_LONG, program, tmp);
  1040. } else
  1041. startread(fd, cp, (FILBUFLEN - taillen));
  1042. } else
  1043. startread(fd, cp, (FILBUFLEN - taillen) & (~0 << LG2SECLEN));
  1044. // Start next read
  1045. }
  1046. } else {
  1047. endbuf = cp + cb - taillen; // Get pointer to end of buffer
  1048. // Cause 'while' to terminate(since no next read is needed.)
  1049. cbread = 0;
  1050. arrc = TRUE;
  1051. }
  1052. __try {
  1053. grep_result = (*grep)(bufptr[bufi], endbuf, name);
  1054. } __except( GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR ) {
  1055. printmessage(stderr, MSG_FINDSTR_READ_ERROR, program, name);
  1056. break;
  1057. }
  1058. if (grep_result) { // If rest of file can be skipped
  1059. (*write1)(name, strlen(name), 0);
  1060. // Write file name
  1061. (*write1)("\r\n", 2, 0); // Write newline sequence
  1062. if (!bStdIn && !bLargeFile) {
  1063. if (BaseByteAddress != NULL)
  1064. UnmapViewOfFile(BaseByteAddress);
  1065. }
  1066. return; // Skip rest of file
  1067. }
  1068. cbfile += (long)(endbuf - bufptr[bufi]);
  1069. // Increment count of bytes in file
  1070. bufi ^= 1; // Switch buffers
  1071. }
  1072. skipfile:
  1073. if (!bStdIn && !bLargeFile) {
  1074. if (BaseByteAddress != NULL)
  1075. UnmapViewOfFile(BaseByteAddress);
  1076. }
  1077. }
  1078. char *
  1079. rmpath(
  1080. char *name
  1081. )
  1082. {
  1083. char *cp; // Char pointer
  1084. if (name[0] != '\0' && name[1] == ':')
  1085. name += 2; // Skip drive spec if any
  1086. cp = name; // Point to start
  1087. while(*name != '\0') { // While not at end
  1088. ++name; // Skip to next character
  1089. if (name[-1] == '/' || name[-1] == '\\') cp = name;
  1090. // Point past path separator
  1091. }
  1092. return(cp); // Return pointer to name
  1093. }
  1094. void
  1095. prepend_path(
  1096. char* file_name,
  1097. char* path
  1098. )
  1099. {
  1100. int path_len;
  1101. char* last;
  1102. // First figure out how much of the path to take.
  1103. // Check for the last occurance of '\' if there is one.
  1104. #ifdef FE_SB
  1105. // DBCS tailbytes can contain '\' character. Use MBCS function.
  1106. last = _mbsrchr(path, '\\');
  1107. #else
  1108. last = strrchr(path, '\\');
  1109. #endif
  1110. if (last) {
  1111. path_len = (int)(last - path) + 1;
  1112. } else if (path[1] == ':') {
  1113. path_len = 2;
  1114. } else {
  1115. path_len = 0;
  1116. }
  1117. memmove(file_name + path_len, file_name, strlen(file_name) + 1);
  1118. memmove(file_name, path, path_len);
  1119. }
  1120. void
  1121. ConvertAppToOem(
  1122. unsigned argc,
  1123. char* argv[]
  1124. )
  1125. /*++
  1126. Routine Description:
  1127. Converts the command line from ANSI to OEM, and force the app
  1128. to use OEM APIs
  1129. Arguments:
  1130. argc - Standard C argument count.
  1131. argv - Standard C argument strings.
  1132. Return Value:
  1133. None.
  1134. --*/
  1135. {
  1136. unsigned i;
  1137. for( i=0; i<argc; i++ ) {
  1138. CharToOem( argv[i], argv[i] );
  1139. }
  1140. SetFileApisToOEM();
  1141. }
  1142. int __cdecl
  1143. main(
  1144. int argc,
  1145. char **argv
  1146. )
  1147. {
  1148. char *cp;
  1149. char *cpaddstrings[MAX_SLASH_C_OPTION];
  1150. int add_string_count = 0;
  1151. char *dirlist = NULL;
  1152. HANDLE fd;
  1153. FILE *fi;
  1154. int fsubdirs; // Search subdirectories
  1155. int i;
  1156. int j;
  1157. char *inpfile = NULL;
  1158. char *strfile = NULL;
  1159. unsigned long tstart; // Start time
  1160. char filnam[MAX_PATH+1];
  1161. WIN32_FIND_DATA find_data;
  1162. HANDLE find_handle;
  1163. #ifdef FE_SB
  1164. LANGID LangId;
  1165. #endif
  1166. char *locale;
  1167. BOOLEAN option_L_specified = FALSE;
  1168. BOOLEAN option_R_specified = FALSE;
  1169. ConvertAppToOem( argc, argv );
  1170. tstart = clock(); // Get start time
  1171. GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
  1172. // Default color: just add intensity
  1173. wAttrib = csbi.wAttributes | FOREGROUND_INTENSITY;
  1174. memset(cpaddstrings, 0, sizeof(cpaddstrings));
  1175. #ifdef FE_SB
  1176. //
  1177. // Set TEB's language ID to correspond to the console output code page. This
  1178. // will ensure the correct language message is displayed when FormatMessage is
  1179. // called.
  1180. //
  1181. switch (GetConsoleOutputCP()) {
  1182. case 932:
  1183. LangId = MAKELANGID( LANG_JAPANESE, SUBLANG_DEFAULT );
  1184. break;
  1185. case 949:
  1186. LangId = MAKELANGID( LANG_KOREAN, SUBLANG_KOREAN );
  1187. break;
  1188. case 936:
  1189. LangId = MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED );
  1190. break;
  1191. case 950:
  1192. LangId = MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL );
  1193. break;
  1194. default:
  1195. LangId = PRIMARYLANGID(LANGIDFROMLCID( GetUserDefaultLCID() ));
  1196. if (LangId == LANG_JAPANESE ||
  1197. LangId == LANG_KOREAN ||
  1198. LangId == LANG_CHINESE ) {
  1199. LangId = MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US );
  1200. }
  1201. else {
  1202. LangId = MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT );
  1203. }
  1204. IsDBCSCodePage = FALSE;
  1205. break;
  1206. }
  1207. SetThreadLocale( MAKELCID(LangId, SORT_DEFAULT) );
  1208. if ((locale = setlocale(LC_ALL, ".OCP")) == NULL) {
  1209. UINT Codepage;
  1210. if (Codepage = GetConsoleOutputCP()) {
  1211. char achCodepage[10];
  1212. wsprintfA(achCodepage, ".%3.4d", Codepage);
  1213. if ((locale = setlocale(LC_ALL, achCodepage)) == NULL) {
  1214. error(MSG_FINDSTR_UNABLE_TO_SET_LOCALE);
  1215. }
  1216. } else
  1217. error(MSG_FINDSTR_UNABLE_TO_SET_LOCALE);
  1218. }
  1219. #endif
  1220. asyncio = pmode = 1; // Do asynchronous I/O
  1221. // program = rmpath(argv[0]); // Set program name
  1222. program ="FINDSTR";
  1223. memset(td1, 1, TRTABLEN); // Set up TD1 for startup
  1224. flags = 0;
  1225. _setmode(_fileno(stdout), O_BINARY); // No linefeed translation on output
  1226. _setmode(_fileno(stderr), O_BINARY); // No linefeed translation on output
  1227. fsubdirs = 0;
  1228. for(i = 1; i < argc && (argv[i][0] == '/' || argv[i][0] == '-'); ++i)
  1229. {
  1230. for(cp = &argv[i][1]; *cp != '\0'; ++cp)
  1231. {
  1232. switch(*cp)
  1233. {
  1234. case '?':
  1235. printmessage(stdout, MSG_FINDSTR_USAGE, NULL); // Verbose usage message
  1236. exit(0);
  1237. case 'b':
  1238. case 'B':
  1239. flags |= BEGLINE;
  1240. break;
  1241. case 'e':
  1242. case 'E':
  1243. flags |= ENDLINE;
  1244. break;
  1245. case 'i':
  1246. case 'I':
  1247. casesen = 0; // case-insensitive search
  1248. break;
  1249. case 'l':
  1250. case 'L':
  1251. addstr = addstring; // Treat strings literally
  1252. option_L_specified = TRUE;
  1253. break;
  1254. case 'm':
  1255. case 'M':
  1256. flags |= NAMEONLY;
  1257. break;
  1258. case 'n':
  1259. case 'N':
  1260. flags |= LINENOS;
  1261. break;
  1262. case 'o':
  1263. case 'O':
  1264. // Check whether this is an /o or /offline switch
  1265. if (0 == _stricmp(cp, "OFFLINE")) {
  1266. flags |= OFFLINE_FILES;
  1267. cp += (lstrlen( "OFFLINE" ) - 1);
  1268. } else if (0 == _stricmp(cp, "OFF")) {
  1269. flags |= OFFLINE_FILES;
  1270. cp += (lstrlen( "OFF" ) - 1);
  1271. } else {
  1272. flags |= SEEKOFF;
  1273. }
  1274. break;
  1275. case 'p':
  1276. case 'P':
  1277. flags |= PRINTABLE_ONLY;
  1278. break;
  1279. case 'r':
  1280. case 'R':
  1281. addstr = addexpr; // Add expression to list
  1282. option_R_specified = TRUE;
  1283. break;
  1284. case 's':
  1285. case 'S':
  1286. fsubdirs = 1;
  1287. break;
  1288. case 'v':
  1289. case 'V':
  1290. grep = grepvbuffer;
  1291. break;
  1292. case 'x':
  1293. case 'X':
  1294. flags |= BEGLINE | ENDLINE;
  1295. break;
  1296. #if DBG
  1297. case 'd':
  1298. // This is kinda cheezy, but I didn't want to change the
  1299. // debug flag as it's been here a while and I couldn't come
  1300. // up with a different flag for the dirlist, so...
  1301. if (*(cp + 1) == ':')
  1302. {
  1303. *cp-- = 'D';
  1304. break;
  1305. }
  1306. flags |= DEBUG;
  1307. break;
  1308. case 't':
  1309. flags |= TIMER;
  1310. break;
  1311. #endif
  1312. default:
  1313. {
  1314. int cch;
  1315. char chSwitch;
  1316. char tmp[3];
  1317. chSwitch = *cp;
  1318. if (*(cp + 1) == ':')
  1319. {
  1320. if (!*(cp + 2))
  1321. {
  1322. tmp[0]=chSwitch;
  1323. tmp[1]='\0';
  1324. printmessage(stderr, MSG_FINDSTR_ARGUMENT_MISSING, program, tmp);
  1325. exit(2);
  1326. }
  1327. cp += 2; // Point to string
  1328. cch = lstrlen(cp);
  1329. switch(chSwitch)
  1330. {
  1331. case 'd':
  1332. case 'D':
  1333. dirlist = cp;
  1334. cp += cch - 1;
  1335. continue;
  1336. case 'c':
  1337. case 'C':
  1338. // Add it after we've gone through all the flags
  1339. // don't add it now as things may change with
  1340. // later flags
  1341. addstr = addstring; // Treat strings literally
  1342. if (add_string_count >= MAX_SLASH_C_OPTION) {
  1343. error(MSG_FINDSTR_TOO_MANY_SLASH_C_OPTION);
  1344. }
  1345. cpaddstrings[add_string_count++] = cp;
  1346. cp += cch - 1;
  1347. continue;
  1348. case 'g':
  1349. case 'G': // Patterns in file
  1350. case 'f':
  1351. case 'F': // Names of files to search in file
  1352. if (chSwitch == 'f' || chSwitch == 'F')
  1353. inpfile = cp;
  1354. else
  1355. strfile = cp;
  1356. cp += cch - 1;
  1357. continue;
  1358. case 'a':
  1359. case 'A':
  1360. wAttrib = 0;
  1361. for(; *cp && isxdigit(*cp); ++cp) {
  1362. int digit = (int) (*cp <= TEXT('9'))
  1363. ? (int)*cp - (int)'0'
  1364. : (int)tolower(*cp)-(int)'W';
  1365. wAttrib = (wAttrib << 4) + digit;
  1366. }
  1367. cp--;
  1368. continue;
  1369. default:
  1370. cp += cch - 1;
  1371. // break out and spit out the switch ignored msg
  1372. break;
  1373. }
  1374. }
  1375. tmp[0]='/';
  1376. tmp[1]=chSwitch;
  1377. tmp[2]='\0';
  1378. printmessage(stderr, MSG_FINDSTR_SWITCH_IGNORED, program, tmp);
  1379. break;
  1380. }
  1381. }
  1382. }
  1383. } // for( i=1; )
  1384. if (option_L_specified && option_R_specified)
  1385. error(MSG_FINDSTR_CONFLICTING_OPTIONS_LR);
  1386. else if (option_L_specified)
  1387. addstr = addstring;
  1388. else if (option_R_specified)
  1389. addstr = addexpr;
  1390. // Explicit string (no separators). Add string "as is"
  1391. if (add_string_count) {
  1392. for (j=0; j<add_string_count && cpaddstrings[j]; j++)
  1393. addstrings( cpaddstrings[j], cpaddstrings[j] + lstrlen(cpaddstrings[j]), "" );
  1394. }
  1395. if (i == argc && strcnt == 0 && strfile == NULL)
  1396. error(MSG_FINDSTR_BAD_COMMAND_LINE);
  1397. bufptr[0][-1] = bufptr[1][-1] = '\n'; // Mark beginnings with newline
  1398. // Note: 4-Dec-90 w-barry Since there currently exists no method to query a
  1399. // handle with the Win32 api (no equivalent to
  1400. // DosQueryHType() ), the following piece of code
  1401. // replaces the commented section.
  1402. if (_isatty(_fileno(stdout))) { // If stdout is a device
  1403. write1 = write1nobuf; // Use unbuffered output
  1404. flush1 = flush1nobuf;
  1405. }
  1406. // /*
  1407. // * Check type of handle for std. out.
  1408. // */
  1409. // if (DosQueryHType(fileno(stdout),(PPARM) &j,(PPARM) &fd) != NO_ERROR)
  1410. // {
  1411. // error("Standard output bad handle");
  1412. // }
  1413. // // Die if error
  1414. // if (j != 0 && (fd & ISCOT)) // If handle is console output
  1415. //#else
  1416. // filbuf[3] = '\n'; // Mark beginning with newline
  1417. // if (isatty(fileno(stdout))) // If stdout is a device
  1418. //#endif
  1419. // {
  1420. // write1 = write1nobuf; // Use unbuffered output
  1421. // flush1 = flush1nobuf;
  1422. // }
  1423. if (strfile != NULL) { // If strings from file
  1424. if ((strcmp(strfile, "/") != 0) && (strcmp(strfile, "-") != 0)) {
  1425. // If strings not from std. input
  1426. if ( ( fd = CreateFile( strfile,
  1427. GENERIC_READ,
  1428. 0,
  1429. NULL,
  1430. OPEN_EXISTING,
  1431. 0,
  1432. NULL ) ) == (HANDLE)-1 )
  1433. { // If open fails
  1434. printmessage(stderr, MSG_FINDSTR_CANNOT_READ_STRINGS, program, strfile);
  1435. exit(2); // Die
  1436. }
  1437. }else {
  1438. fd = GetStdHandle( STD_INPUT_HANDLE ); // Else use std. input
  1439. }
  1440. qgrep( addstrings, "\r\n", fd );// Do the work
  1441. if ( fd != GetStdHandle( STD_INPUT_HANDLE ) ) {
  1442. CloseHandle( fd ); // Close strings file
  1443. }
  1444. } else if (strcnt == 0) { // Else if strings on command line
  1445. cp = argv[i++]; // Set pointer to strings
  1446. addstrings(cp, cp + strlen(cp), " \t");
  1447. // Add strings to list
  1448. }
  1449. if (strcnt == 0)
  1450. error(MSG_FINDSTR_NO_SEARCH_STRINGS); // Die if no strings
  1451. if (addstr != addexpr) { // If not using expressions
  1452. memset(td1, cchmin + 1, TRTABLEN);// Initialize table
  1453. find = findlist; // Assume finding many
  1454. if ((j = enumstrings()) != strcnt) {
  1455. char t1[15], t2[15];
  1456. _itoa(j, t1, 10);
  1457. _itoa(strcnt, t2, 10);
  1458. printmessage(stderr, MSG_FINDSTR_STRING_COUNT_ERROR, t1, t2);
  1459. }
  1460. // Enumerate strings and verify count
  1461. #if DBG
  1462. if (flags & DEBUG) { // If debugging output wanted
  1463. fprintf(stderr, "%u bytes wasted in heap\n", waste);
  1464. // Print storage waste
  1465. assert(chmin <= chmax); // Must have some entries
  1466. fprintf(stderr, "chmin = %u, chmax = %u, cchmin = %u\n", chmin, chmax, cchmin);
  1467. // Print range info
  1468. for (j = (int)chmin; j <= (int)chmax; ++j) {
  1469. // Loop to print TD1 table
  1470. if ( td1[j] <= (char)cchmin ) { // If not maximum shift
  1471. if (isascii(j) && isprint(j))
  1472. fprintf(stderr, "'%c'=%2u ", j, td1[j]); // Show literally if printable
  1473. else
  1474. fprintf(stderr, "\\%02x=%2u ", j, td1[j]); // Else show hex value
  1475. }
  1476. }
  1477. fprintf(stderr, "\n");
  1478. }
  1479. #endif
  1480. if (strcnt == 1 && casesen)
  1481. find = findone; // Find one case-sensitive string
  1482. } else if (find == NULL) {
  1483. find = findexpr; // Else find expressions
  1484. }
  1485. if (inpfile != NULL) { // If file list from file
  1486. flags |= SHOWNAME; // Always show name of file
  1487. if ((strcmp(inpfile, "/") != 0) && (strcmp(inpfile, "-") != 0)) {
  1488. if ((fi = fopen(inpfile, "r")) == NULL) {
  1489. // If open fails
  1490. printmessage(stderr, MSG_FINDSTR_CANNOT_READ_FILE_LIST, program, inpfile);
  1491. exit(2); // Error exit
  1492. }
  1493. } else
  1494. fi = stdin; // Else read file list from stdin
  1495. while(fgets(filnam, MAX_PATH+1, fi) != NULL) {
  1496. // While there are names
  1497. filnam[strcspn(filnam, "\r\n")] = '\0'; // Null-terminate the name
  1498. if ((fd = openfile(filnam)) == (HANDLE)-1) {
  1499. continue; // Skip file if it cannot be opened
  1500. }
  1501. qgrep(grep, filnam, fd); // Do the work
  1502. CloseHandle( fd );
  1503. }
  1504. if (fi != stdin)
  1505. fclose(fi); // Close the list file
  1506. } else if (i == argc) {
  1507. flags &= ~(NAMEONLY | SHOWNAME);
  1508. qgrep( grep, NULL, GetStdHandle( STD_INPUT_HANDLE ) );
  1509. }
  1510. if (argc > i + 1 || fsubdirs || has_wild_cards(argv[i]))
  1511. flags |= SHOWNAME;
  1512. if (dirlist && *dirlist) {
  1513. char *dir;
  1514. char *dirend = (char *)-1;
  1515. char *original_current_directory = NULL;
  1516. DWORD size;
  1517. size = GetCurrentDirectory(0, NULL);
  1518. if (size) {
  1519. original_current_directory = (PCHAR)malloc(size);
  1520. if (original_current_directory == NULL) {
  1521. printmessage(stderr, MSG_FINDSTR_OUT_OF_MEMORY, program);
  1522. exit(2);
  1523. }
  1524. size = GetCurrentDirectory(size, original_current_directory);
  1525. }
  1526. if (!size) {
  1527. free(original_current_directory);
  1528. printmessage(stderr, MSG_FINDSTR_UNABLE_TO_GET_CURRENT_DIRECTORY, program);
  1529. exit(2);
  1530. }
  1531. for(dir = dirlist; dirend; dir = dirend + 1) {
  1532. if (dirend = strchr(dir, ';'))
  1533. *dirend = 0;
  1534. if (*dir) {
  1535. (*write1)(" ", 2, wAttrib); // Indent a couple of spaces
  1536. (*write1)(dir, lstrlen(dir), wAttrib); // Show name
  1537. (*write1)(":\r\n", 3, wAttrib); // Write newline sequence
  1538. if (!SetCurrentDirectory(original_current_directory)) {
  1539. free(original_current_directory);
  1540. printmessage(stderr, MSG_FINDSTR_CANNOT_OPEN_FILE, program,
  1541. original_current_directory);
  1542. exit(2);
  1543. }
  1544. if (!SetCurrentDirectory(dir)) {
  1545. printmessage(stderr, MSG_FINDSTR_CANNOT_OPEN_FILE, program, dir);
  1546. } else {
  1547. while (filematch(filnam, argv + i, argc - i, fsubdirs) >= 0) {
  1548. #ifdef FE_SB
  1549. // _mbslwr((unsigned char *)filnam);
  1550. #else
  1551. // _strlwr(filnam);
  1552. #endif
  1553. if ((fd = openfile(filnam)) != (HANDLE)-1) {
  1554. qgrep(grep, filnam, fd);
  1555. CloseHandle( fd );
  1556. }
  1557. }
  1558. }
  1559. }
  1560. }
  1561. free(original_current_directory);
  1562. }
  1563. else if (fsubdirs && argc > i) { // If directory search wanted
  1564. while (filematch(filnam, argv + i, argc - i, fsubdirs) >= 0) {
  1565. #ifdef FE_SB
  1566. // _mbslwr((unsigned char *)filnam);
  1567. #else
  1568. // _strlwr(filnam);
  1569. #endif
  1570. if ((fd = openfile(filnam)) == (HANDLE)-1) {
  1571. continue;
  1572. }
  1573. qgrep(grep, filnam, fd);
  1574. CloseHandle( fd );
  1575. }
  1576. } else { // Else search files specified
  1577. for(; i < argc; ++i) {
  1578. #ifdef FE_SB
  1579. // _mbslwr((unsigned char *) argv[i]);
  1580. #else
  1581. // _strlwr(argv[i]);
  1582. #endif
  1583. find_handle = FindFirstFile(argv[i], &find_data);
  1584. if (find_handle == INVALID_HANDLE_VALUE) {
  1585. printmessage(stderr, MSG_FINDSTR_CANNOT_OPEN_FILE, program, argv[i]);
  1586. continue;
  1587. }
  1588. do {
  1589. #ifdef FE_SB
  1590. // _mbslwr((unsigned char *)find_data.cFileName);
  1591. #else
  1592. // _strlwr(find_data.cFileName);
  1593. #endif
  1594. prepend_path(find_data.cFileName, argv[i]);
  1595. fd = openfile(find_data.cFileName);
  1596. if (fd != INVALID_HANDLE_VALUE) {
  1597. qgrep(grep, find_data.cFileName, fd);
  1598. CloseHandle( fd );
  1599. }
  1600. } while (FindNextFile(find_handle, &find_data));
  1601. }
  1602. }
  1603. (*flush1)();
  1604. #if DBG
  1605. if ( flags & TIMER ) { // If timing wanted
  1606. unsigned long tend;
  1607. tend = clock();
  1608. tstart = tend - tstart; // Get time in milliseconds
  1609. fprintf(stderr, "%lu.%03lu seconds\n", ( tstart / CLK_TCK ), ( tstart % CLK_TCK ) );
  1610. // Print total elapsed time
  1611. }
  1612. #endif
  1613. // Print warning in case that offline files were skipped
  1614. if (fOfflineSkipped) {
  1615. printmessage(stderr, MSG_FINDSTR_OFFLINE_FILE_SKIPPED, program);
  1616. }
  1617. return( status );
  1618. } // main
  1619. char * findsub( unsigned char *, char * );
  1620. char * findsubi( unsigned char *, char * );
  1621. char * (*flworker[])(unsigned char *, char *) = { // Table of workers
  1622. findsubi,
  1623. findsub
  1624. };
  1625. char *
  1626. strnupr(
  1627. char *pch,
  1628. int cch
  1629. )
  1630. {
  1631. char c[2];
  1632. #ifdef FE_SB
  1633. int max = cch;
  1634. c[1] = 0;
  1635. for ( cch = 0; cch < max; cch++ ) {
  1636. #else
  1637. c[1] = 0;
  1638. while (cch-- > 0) { // Convert string to upper case
  1639. #endif
  1640. if (isalpha((unsigned char)pch[cch])) {
  1641. c[0] = pch[cch];
  1642. pch[cch] = (_strupr(c))[0];
  1643. }
  1644. #ifdef FE_SB
  1645. else if (IsDBCSCodePage && IsDBCSLeadByte(pch[cch]))
  1646. cch++;
  1647. #endif
  1648. }
  1649. return(pch);
  1650. }
  1651. /*
  1652. * This is an implementation of the QuickSearch algorith described
  1653. * by Daniel M. Sunday in the August 1990 issue of CACM. The TD1
  1654. * table is computed before this routine is called.
  1655. */
  1656. char *
  1657. findone(
  1658. unsigned char *buffer,
  1659. char *bufend
  1660. )
  1661. {
  1662. #ifdef FE_SB // Starting position of string for checking 2nd bytes of DBCS characters.
  1663. unsigned char *bufferhead = buffer;
  1664. #endif
  1665. if ((bufend -= targetlen - 1) <= (char *) buffer)
  1666. return((char *) 0); // Fail if buffer too small
  1667. while (buffer < (unsigned char *) bufend) { // While space remains
  1668. int cch; // Character count
  1669. register char *pch1; // Char pointer
  1670. register char *pch2; // Char pointer
  1671. pch1 = target; // Point at pattern
  1672. pch2 = (char *) buffer; // Point at buffer
  1673. #ifdef FE_SB
  1674. // If buffer points to the 2nd byte of a DBCS character,
  1675. // skip to next compare position.
  1676. if ( !IsTailByte( bufferhead, (int)(buffer - bufferhead) ) ) {
  1677. #endif
  1678. for (cch = targetlen; cch > 0; --cch) {
  1679. // Loop to try match
  1680. if (*pch1++ != *pch2++)
  1681. break; // Exit loop on mismatch
  1682. }
  1683. if (cch == 0)
  1684. return((char *)buffer); // Return pointer to match
  1685. #ifdef FE_SB
  1686. }
  1687. #endif
  1688. if (buffer + 1 < (unsigned char *) bufend) // Make sure buffer[targetlen] is valid.
  1689. buffer += ((unsigned char)td1[buffer[targetlen]]); // Skip ahead
  1690. else
  1691. break;
  1692. }
  1693. return((char *) 0); // No match
  1694. }
  1695. int
  1696. preveol(
  1697. char *s
  1698. )
  1699. {
  1700. register char *cp; // Char pointer
  1701. cp = s + 1; // Initialize pointer
  1702. if (!bStdIn && !bLargeFile) {
  1703. while((--cp >= BaseByteAddress) && (*cp != '\n'))
  1704. ; // Find previous end-of-line
  1705. } else {
  1706. while(*--cp != '\n') ; // Find previous end-of-line
  1707. }
  1708. return (int)(s - cp); // Return distance to match
  1709. }
  1710. int
  1711. countlines(
  1712. char *start,
  1713. char *finish
  1714. )
  1715. {
  1716. register int count; // Line count
  1717. for(count = 0; start < finish; ) {
  1718. // Loop to count lines
  1719. if (*start++ == '\n')
  1720. ++count; // Increment count if linefeed found
  1721. }
  1722. return(count); // Return count
  1723. }
  1724. char *
  1725. findlist(
  1726. unsigned char *buffer,
  1727. char *bufend
  1728. )
  1729. {
  1730. char *match; // Pointer to matching string
  1731. // Avoid writting to bufend. bufend[-1] is something(such as '\n') that is not
  1732. // part of search and will cause the search to stop.
  1733. match = (*flworker[casesen])(buffer, bufend); // Call worker
  1734. return(match); // Return matching string
  1735. }
  1736. char *
  1737. findsub(
  1738. unsigned char *buffer,
  1739. char *bufend
  1740. )
  1741. {
  1742. register char *cp; // Char pointer
  1743. STRINGNODE *s; // String node pointer
  1744. int i; // Index
  1745. #ifdef FE_SB // Head of buffer for checking if a certain offset is the 2nd byte of a DBCS character.
  1746. unsigned char *bufhead = buffer;
  1747. #endif
  1748. char *real_bufend = bufend;
  1749. if (cchmin != (unsigned)-1 &&
  1750. cchmin != 0 &&
  1751. (bufend -= cchmin - 1) < (char *) buffer)
  1752. return((char *) 0); // Compute effective buffer length
  1753. while(buffer < (unsigned char *) bufend) { // Loop to find match
  1754. #ifdef FE_SB
  1755. // Search cannot start at the second byte of a DBCS character,
  1756. // so check for it and skip it if it is a second byte.
  1757. if ((i = (UCHAR)transtab[*buffer]) != 0 &&
  1758. !IsTailByte( bufhead, (int)(buffer-bufhead) ) ) {
  1759. #else
  1760. if ((i = (UCHAR)transtab[*buffer]) != 0) {
  1761. #endif
  1762. // If valid first character
  1763. if ((s = stringlist[i]) == 0) {
  1764. return((char *)buffer); // Check for 1-byte match
  1765. }
  1766. for(cp = (char *) buffer + 1; (real_bufend - cp) >= s->s_must; ) { // Loop to search list
  1767. if ((i = _strncoll(cp, s_text(s), s->s_must)) == 0) {
  1768. // If portions match
  1769. cp += s->s_must; // Skip matching portion
  1770. if ((s = s->s_suf) == 0)
  1771. return((char *)buffer); // Return match if end of list
  1772. continue; // Else continue
  1773. }
  1774. if (i < 0 || (s = s->s_alt) == 0) {
  1775. break; // Break if not in this list
  1776. }
  1777. }
  1778. }
  1779. if (buffer + 1 < (unsigned char *) bufend) // Make sure buffer[cchmin] is valid.
  1780. if (cchmin == (unsigned)-1)
  1781. buffer++;
  1782. else
  1783. buffer += ((unsigned char)td1[buffer[cchmin]]); // Shift as much as possible
  1784. else
  1785. break;
  1786. }
  1787. return((char *) 0); // No match
  1788. }
  1789. char *
  1790. findsubi(
  1791. unsigned char *buffer,
  1792. char *bufend
  1793. )
  1794. {
  1795. register char *cp; // Char pointer
  1796. STRINGNODE *s; // String node pointer
  1797. int i; // Index
  1798. #ifdef FE_SB
  1799. // Keep head of buffer for checking if a certain offset is the 2nd byte of
  1800. // a DBCS character.
  1801. unsigned char *bufhead = buffer;
  1802. #endif
  1803. if (cchmin != (unsigned)-1 &&
  1804. cchmin != 0 &&
  1805. (bufend -= cchmin - 1) < (char *) buffer)
  1806. return((char *) 0); // Compute effective buffer length
  1807. while(buffer < (unsigned char *) bufend) { // Loop to find match
  1808. #ifdef FE_SB
  1809. // Search cannot start at the second byte of a DBCS character, so check for it
  1810. // and skip it if it is a second byte.
  1811. if ((i = (UCHAR)transtab[*buffer]) != 0 &&
  1812. !IsTailByte( bufhead, (int)(buffer-bufhead) ) ) {
  1813. // If valid first character
  1814. BOOL TailByte; // Flag to check if 1st char is leadbyte.
  1815. #else
  1816. if ((i = (UCHAR)transtab[*buffer]) != 0) { // If valid first character
  1817. #endif
  1818. if ((s = stringlist[i]) == 0)
  1819. return((char *) buffer); // Check for 1-byte match
  1820. #ifdef FE_SB
  1821. // Same leadbytes with tailbytes such as 0x41 and 0x61 will become the same
  1822. // character, so become aware of it and use the multibyte function.
  1823. //
  1824. // Check if buffer+1 is a tailbyte character.
  1825. //
  1826. TailByte = IsTailByte(buffer, 1);
  1827. for(cp = (char *) buffer + 1; ; ) { // Loop to search list
  1828. if ((i = _mbsnicmp((unsigned char *)cp, (unsigned char *) s_text(s), s->s_must, &TailByte)) == 0) {
  1829. #else
  1830. for(cp = (char *) buffer + 1; ; ) { // Loop to search list
  1831. if ((i = memicmp(cp, s_text(s), s->s_must)) == 0) {
  1832. #endif
  1833. // If portions match
  1834. cp += s->s_must; // Skip matching portion
  1835. if ((s = s->s_suf) == 0)
  1836. return((char *) buffer); // Return match if end of list
  1837. continue; // And continue
  1838. }
  1839. if (i < 0 || (s = s->s_alt) == 0)
  1840. break; // Break if not in this list
  1841. }
  1842. }
  1843. if (buffer + 1 < (unsigned char *) bufend) // Make sure buffer[cchmin] is valid.
  1844. if (cchmin == (unsigned)-1)
  1845. buffer++;
  1846. else
  1847. buffer += ((unsigned char)td1[buffer[cchmin]]); // Shift as much as possible
  1848. else
  1849. break;
  1850. }
  1851. return((char *) 0); // No match
  1852. }
  1853. int
  1854. strnspn(
  1855. char *s,
  1856. char *t,
  1857. int n
  1858. )
  1859. /*
  1860. Description:
  1861. Finds the position of the first character in s of length n that is not
  1862. in the character set t.
  1863. Argument:
  1864. s - string to search from.
  1865. t - character set to search for
  1866. n - length of s
  1867. Returns:
  1868. Returns the offset of the first character in s that is not in t
  1869. */
  1870. {
  1871. register char *s1; // String pointer
  1872. register char *t1; // String pointer
  1873. for(s1 = s; n-- != 0; ++s1) { // While not at end of s
  1874. for(t1 = t; *t1 != '\0'; ++t1) { // While not at end of t
  1875. if (*s1 == *t1)
  1876. break; // Break if match found
  1877. }
  1878. if (*t1 == '\0')
  1879. break; // Break if no match found
  1880. }
  1881. return (int)(s1 - s); // Return length
  1882. }
  1883. int
  1884. strncspn(
  1885. char *s,
  1886. char *t,
  1887. int n
  1888. )
  1889. /*
  1890. Description:
  1891. Finds the position of the first occurence of characters in t in string
  1892. s of length n.
  1893. Argument:
  1894. s - string to search from.
  1895. t - character set to search for
  1896. n - length of s
  1897. Returns:
  1898. Returns first offset position in s that consists of characters in t
  1899. Returns length of s if not found.
  1900. */
  1901. {
  1902. register char *s1; // String pointer
  1903. register char *t1; // String pointer
  1904. for(s1 = s; n-- != 0; ++s1) { // While not at end of s
  1905. for(t1 = t; *t1 != '\0'; ++t1) { // While not at end of t
  1906. if (*s1 == *t1)
  1907. return (int)(s1 - s); // Return if match found
  1908. }
  1909. }
  1910. return (int)(s1 - s); // Return length
  1911. }
  1912. void
  1913. matchstrings(
  1914. char *s1,
  1915. char *s2,
  1916. int len,
  1917. int *nmatched,
  1918. int *leg
  1919. )
  1920. {
  1921. register char *cp; // Char pointer
  1922. register int (__cdecl *cmp)(const char*, const char*, size_t); // Comparison function pointer
  1923. cmp = casesen ? _strncoll: _strnicoll; // Set pointer
  1924. if ((*leg = (*cmp)(s1, s2, len)) != 0) { // If strings don't match
  1925. for(cp = s1; (*cmp)(cp, s2++, 1) == 0; ++cp)
  1926. ;
  1927. // Find mismatch
  1928. *nmatched = (int)(cp - s1); // Return number matched
  1929. }
  1930. else *nmatched = len; // Else all matched
  1931. }
  1932. void
  1933. printmessage (
  1934. FILE* fp,
  1935. DWORD messageID,
  1936. ...
  1937. )
  1938. {
  1939. char messagebuffer[DISPLAYBUFFER_SIZE];
  1940. WCHAR widemessagebuffer[DISPLAYBUFFER_SIZE];
  1941. ULONG len;
  1942. NTSTATUS status;
  1943. va_list ap;
  1944. va_start(ap, messageID);
  1945. if (len = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE,
  1946. NULL,
  1947. messageID,
  1948. 0,
  1949. messagebuffer,
  1950. DISPLAYBUFFER_SIZE,
  1951. &ap)) {
  1952. // the messagebuffer should be null terminated
  1953. status = RtlMultiByteToUnicodeN(widemessagebuffer,
  1954. DISPLAYBUFFER_SIZE*sizeof(WCHAR),
  1955. &len,
  1956. messagebuffer,
  1957. len);
  1958. // the widemessagebuffer is not null terminated but len tells us how long
  1959. if (NT_SUCCESS(status)) {
  1960. status = RtlUnicodeToOemN(messagebuffer, DISPLAYBUFFER_SIZE-1, &len, widemessagebuffer, len);
  1961. // the messagebuffer is not null terminated but len tells us how long
  1962. if (NT_SUCCESS(status)) {
  1963. messagebuffer[len] = 0;
  1964. fprintf(fp, "%s", messagebuffer);
  1965. } else {
  1966. DbgPrint("Failure to convert Unicode to Oem: %d\n", GetLastError());
  1967. }
  1968. } else {
  1969. DbgPrint("Failure to convert MultiByte to Unicode: %d\n", GetLastError());
  1970. }
  1971. } else {
  1972. DbgPrint("FormatMessage failed: %d\n", GetLastError());
  1973. }
  1974. va_end(ap);
  1975. }
  1976. #ifdef FE_SB
  1977. int
  1978. IsTailByte(
  1979. unsigned const char *text,
  1980. const int offset
  1981. )
  1982. /*
  1983. Description:
  1984. This routine checks to see if the byte at the offset location is a
  1985. tail byte of a DBCS character. The offset is calculated such that the
  1986. first location has a value of 0.
  1987. Argument:
  1988. text - Points to a MBCS text string.
  1989. offset - zero base offset to check character is a tailbyte of a DBCS
  1990. character.
  1991. Returns:
  1992. TRUE - offset position is a tailbyte character.
  1993. FALSE - otherwise.
  1994. Modifications:
  1995. v-junm: 05/06/93 - Original.
  1996. */
  1997. {
  1998. int i = offset;
  1999. if ( !IsDBCSCodePage )
  2000. return( FALSE );
  2001. for ( ; i; i-- )
  2002. if ( !IsDBCSLeadByte ( text[i-1] ) )
  2003. break;
  2004. return( ( offset - i ) % 2 );
  2005. }
  2006. char *
  2007. _mbsrchr(
  2008. const char *string,
  2009. int c
  2010. )
  2011. /*
  2012. Description:
  2013. This function is a DBCS enabled version of the STRRCHR function
  2014. included in the MS C/C++ library. What DBCS enabled means is that
  2015. the SBCS character 'c' is found in a MBCS string 'string'. 'c' is
  2016. a SBCS character that cannot be contained in the tailbyte of a DBCS
  2017. character.
  2018. Argument:
  2019. string - Points to a MBCS text string.
  2020. offset - Character to find in string.
  2021. Returns:
  2022. Returns a pointer to the last occurance of c in string, or a NULL
  2023. pointer if c is not found.
  2024. Modifications:
  2025. v-junm: 05/06/93 - Original.
  2026. */
  2027. {
  2028. register int i = strlen( string );
  2029. for (; i >= 0; i-- ) {
  2030. if ( ( *(string + i) == (char)c ) && !IsTailByte( (unsigned char *) string, i ) )
  2031. return( (char*)(string + i) );
  2032. }
  2033. return ( NULL );
  2034. }
  2035. unsigned char *
  2036. _mbslwr(
  2037. unsigned char *s
  2038. )
  2039. /*
  2040. Description:
  2041. This function is a DBCS aware version of the strlwr function
  2042. included in the MS C/C++ library. SBCS alphabets contained in
  2043. the tailbyte of a DBCS character is not affected in the conversion.
  2044. Argument:
  2045. s - String to converted to lower case.
  2046. Returns:
  2047. Returns a string that was converted to lower case.
  2048. Modifications:
  2049. v-junm: 05/06/93 - Original.
  2050. */
  2051. {
  2052. //
  2053. // If NonJP code page, use original routine.
  2054. //
  2055. if ( !IsDBCSCodePage )
  2056. return( (unsigned char *) _strlwr( (char *) s ) );
  2057. //
  2058. // While not end of string convert to lower case.
  2059. //
  2060. for( ; *s; s++ ) {
  2061. //
  2062. // if Leadbyte and next character is not NULL
  2063. // skip tailbyte
  2064. // else if uppercase character
  2065. // convert it to lowercase
  2066. //
  2067. if ( IsDBCSLeadByte( *s ) && *(s+1) )
  2068. s++;
  2069. else if ( *s >= 0x41 && *s <= 0x5a )
  2070. *s = *s + 0x20;
  2071. }
  2072. return( s );
  2073. }
  2074. int
  2075. _mbsnicmp(
  2076. const unsigned char *s1,
  2077. const unsigned char *s2,
  2078. int n,
  2079. BOOL *TailByte
  2080. )
  2081. /*
  2082. Description:
  2083. This is similar to a DBCS aware version of the memicmp function
  2084. contained in the MS C/C++ library. The only difference is that
  2085. an additional parameter is passed which indicates if the first
  2086. character is a tailbyte of a DBCS character.
  2087. Argument:
  2088. s1 - string 1 to compare.
  2089. s2 - string 2 to compare.
  2090. n - maximum number of bytes to compare.
  2091. TailByte - flag to indicate first character in s1 and s2 is a tailbyte
  2092. of a DBCS character.
  2093. Returns:
  2094. RetVal < 0 - s1 < s2
  2095. RetVal = 0 - s1 == s2
  2096. RetVal > 0 - s1 > s2
  2097. Modifications:
  2098. v-junm: 05/06/93 - Original.
  2099. */
  2100. {
  2101. BOOL tail = *TailByte;
  2102. int i;
  2103. *TailByte = FALSE;
  2104. for( ; n; n--, s1++, s2++ ) {
  2105. if ( *s1 == *s2 ) {
  2106. if ( tail == FALSE && IsDBCSLeadByte( *s1 ) )
  2107. tail = TRUE;
  2108. else
  2109. tail = FALSE;
  2110. continue;
  2111. }
  2112. else if ( !tail ) {
  2113. i = _strnicoll((char *)s1, (char *)s2, 1);
  2114. if (i == 0)
  2115. continue;
  2116. return i;
  2117. }
  2118. return( *s1 - *s2 );
  2119. }
  2120. *TailByte = tail;
  2121. return( 0 );
  2122. }
  2123. #endif