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.

1601 lines
58 KiB

  1. /*static char *SCCSID = "@(#)qgrep.c 13.11 90/08/14";*/
  2. /*
  3. * QGrep
  4. *
  5. * Modification History:
  6. *
  7. * Aug 1990 PeteS Created.
  8. * 1990 DaveGi Ported to Cruiser
  9. * 31-Oct-1990 W-Barry Removed the #ifdef M_I386 'cause this
  10. * code will never see 16bit again.
  11. */
  12. #include <stdio.h>
  13. #include <time.h>
  14. #include <stdlib.h>
  15. #include <string.h>
  16. #include <fcntl.h>
  17. #include <io.h>
  18. #include <windows.h>
  19. #include <ctype.h>
  20. #include <assert.h>
  21. /*
  22. * Miscellaneous constants and macros
  23. */
  24. #define FILBUFLEN (SECTORLEN*16) /* File buffer length */
  25. #define FILNAMLEN 80 /* File name length */
  26. #define ISCOT 0x0002 /* Handle is console output */
  27. #define LG2SECLEN 10 /* Log base two of sector length */
  28. #define LNOLEN 12 /* Maximum line number length */
  29. #define MAXSTRLEN 128 /* Maximum search string length */
  30. #define OUTBUFLEN (SECTORLEN*2) /* Output buffer length */
  31. #define PATHLEN 128 /* Path buffer length */
  32. #define SECTORLEN (1 << LG2SECLEN)/* Sector length */
  33. #define STKLEN 512 /* Stack length in bytes */
  34. #define TRTABLEN 256 /* Translation table length */
  35. #define s_text(x) (((char *)(x)) - ((x)->s_must))
  36. /* Text field access macro */
  37. #define EOS ('\r') /* End of string */
  38. /*
  39. * Bit flag definitions
  40. */
  41. #define SHOWNAME 0x01 /* Print filename */
  42. #define NAMEONLY 0x02 /* Print filename only */
  43. #define LINENOS 0x04 /* Print line numbers */
  44. #define BEGLINE 0x08 /* Match at beginning of line */
  45. #define ENDLINE 0x10 /* Match at end of line */
  46. #define DEBUG 0x20 /* Print debugging output */
  47. #define TIMER 0x40 /* Time execution */
  48. #define SEEKOFF 0x80 /* Print seek offsets */
  49. #define ZLINENOS 0x100 /* Print MEP style line numbers */
  50. #define DOQUOTES 0x200 /* Handle quoted strings in -f search file */
  51. /*
  52. * Type definitions
  53. */
  54. typedef struct stringnode {
  55. struct stringnode *s_alt; /* List of alternates */
  56. struct stringnode *s_suf; /* List of suffixes */
  57. size_t s_must; /* Length of portion that must match */
  58. }
  59. STRINGNODE; /* String node */
  60. typedef ULONG CBIO; /* I/O byte count */
  61. typedef ULONG PARM; /* Generic parameter */
  62. typedef CBIO *PCBIO; /* Pointer to I/O byte count */
  63. typedef PARM *PPARM; /* Pointer to generic parameter */
  64. /*
  65. * Global data
  66. */
  67. char filbuf[FILBUFLEN*2L + 12];
  68. char outbuf[OUTBUFLEN*2];
  69. char td1[TRTABLEN] = { 0};
  70. UINT_PTR cchmin = (UINT_PTR)-1; /* Minimum string length */
  71. UINT_PTR chmax = 0; /* Maximum character */
  72. UINT_PTR chmin = (UINT_PTR)-1; /* Minimum character */
  73. char transtab[TRTABLEN] = { 0};
  74. STRINGNODE *stringlist[TRTABLEN/2];
  75. int casesen = 1; /* Assume case-sensitivity */
  76. long cbfile; /* Number of bytes in file */
  77. static int clists = 1; /* One is first available index */
  78. int filbuflen = FILBUFLEN;
  79. /* File buffer length */
  80. int flags; /* Flags */
  81. unsigned lineno; /* Current line number */
  82. char *program; /* Program name */
  83. int status = 1; /* Assume failure */
  84. int strcnt = 0; /* String count */
  85. char target[MAXSTRLEN];
  86. /* Last string added */
  87. size_t targetlen; /* Length of last string added */
  88. unsigned waste; /* Wasted storage in heap */
  89. int arrc; /* I/O return code for DOSREAD */
  90. char asyncio; /* Asynchronous I/O flag */
  91. int awrc = TRUE; /* I/O return code for DOSWRITE */
  92. char *bufptr[] = { filbuf + 4, filbuf + FILBUFLEN + 8};
  93. CBIO cbread; /* Bytes read by DOSREAD */
  94. CBIO cbwrite; /* Bytes written by DOSWRITE */
  95. char *obuf[] = { outbuf, outbuf + OUTBUFLEN};
  96. int ocnt[] = { OUTBUFLEN, OUTBUFLEN};
  97. int oi = 0; /* Output buffer index */
  98. char *optr[] = { outbuf, outbuf + OUTBUFLEN};
  99. char pmode; /* Protected mode flag */
  100. char *t2buf; /* Async read buffer */
  101. int t2buflen; /* Async read buffer length */
  102. HANDLE t2fd; /* Async read file */
  103. char *t3buf; /* Async write buffer */
  104. int t3buflen; /* Async write buffer length */
  105. HANDLE t3fd; /* Async write file */
  106. HANDLE readdone; /* Async read done semaphore */
  107. HANDLE readpending; /* Async read pending semaphore */
  108. HANDLE writedone; /* Async write done semaphore */
  109. HANDLE writepending; /* Async write pending semaphore */
  110. /*
  111. * External functions and forward references
  112. */
  113. void addexpr( char *, int ); /* See QMATCH.C */
  114. void addstring( char *, int ); /* See below */
  115. int countlines( char *, char * );
  116. char *findexpr( unsigned char *, char *); /* See QMATCH.C */
  117. char *findlist( unsigned char *, char * );
  118. char *findone( unsigned char *buffer, char *bufend );
  119. void flush1buf( void ); /* See below */
  120. void flush1nobuf( void ); /* See below */
  121. int grepbuffer( char *, char *, char * ); /* See below */
  122. int isexpr( unsigned char *, int ); /* See QMATCH.C */
  123. void matchstrings( char *, char *, int, size_t *, int * );
  124. size_t preveol( char * );
  125. size_t strncspn( char *, char *, size_t );
  126. size_t strnspn( char *, char *, size_t );
  127. char *strnupr( char *pch, int cch );
  128. void write1buf( char *, size_t ); /* See below */
  129. void (*addstr)( char *, int ) = NULL;
  130. char *(*find)( unsigned char *, char * ) = NULL;
  131. void (*flush1)( void ) = flush1buf;
  132. int (*grep)( char *, char *, char * ) = grepbuffer;
  133. void (*write1)( char *, size_t ) = write1buf;
  134. #include "windows.h"
  135. void
  136. ConvertAppToOem(
  137. unsigned argc,
  138. char* argv[]
  139. )
  140. /*++
  141. Routine Description:
  142. Converts the command line from ANSI to OEM, and force the app
  143. to use OEM APIs
  144. Arguments:
  145. argc - Standard C argument count.
  146. argv - Standard C argument strings.
  147. Return Value:
  148. None.
  149. --*/
  150. {
  151. unsigned i;
  152. for ( i=0; i<argc; i++ ) {
  153. CharToOem( argv[i], argv[i] );
  154. }
  155. SetFileApisToOEM();
  156. }
  157. void
  158. error(
  159. char *mes
  160. )
  161. {
  162. fprintf(stderr,"%s: %s\n",program,mes);
  163. /* Print message */
  164. exit(2); /* Die */
  165. }
  166. char *
  167. alloc(
  168. size_t size
  169. )
  170. {
  171. char *cp; /* Char pointer */
  172. if ((cp = malloc(size)) == NULL) { /* If allocation fails */
  173. fprintf(stderr,"%s: Out of memory (%u)\n",program,waste);
  174. /* Write error message */
  175. exit(2); /* Die */
  176. }
  177. return(cp); /* Return pointer to buffer */
  178. }
  179. void
  180. freenode(
  181. STRINGNODE *x
  182. )
  183. {
  184. register STRINGNODE *y; /* Pointer to next node in list */
  185. while (x != NULL) { /* While not at end of list */
  186. if (x->s_suf != NULL)
  187. freenode(x->s_suf); /* Free suffix list if not end */
  188. else
  189. --strcnt; /* Else decrement string count */
  190. y = x; /* Save pointer */
  191. x = x->s_alt; /* Move down the list */
  192. free((char *)((INT_PTR) s_text(y) & ~(sizeof(DWORD_PTR)-1)));
  193. /* Free the node */
  194. }
  195. }
  196. STRINGNODE *
  197. newnode(
  198. char *s,
  199. size_t n
  200. )
  201. {
  202. register STRINGNODE *new; /* Pointer to new node */
  203. char *t; /* String pointer */
  204. size_t d; /* rounds to a dword boundary */
  205. d = n & (sizeof(DWORD_PTR)-1) ? sizeof(DWORD_PTR) - (n & (sizeof(DWORD_PTR)-1)) : 0; /* offset to next dword past n */
  206. t = alloc(sizeof(STRINGNODE) + n + d);
  207. /* Allocate string node */
  208. t += d; /* END of string word-aligned */
  209. strncpy(t,s,n); /* Copy string text */
  210. new = (STRINGNODE *)(t + n); /* Set pointer to node */
  211. new->s_alt = NULL; /* No alternates yet */
  212. new->s_suf = NULL; /* No suffixes yet */
  213. new->s_must = n; /* Set string length */
  214. return(new); /* Return pointer to new node */
  215. }
  216. STRINGNODE *
  217. reallocnode(
  218. STRINGNODE *node,
  219. char *s,
  220. int n
  221. )
  222. {
  223. register char *cp; /* Char pointer */
  224. assert(n <= node->s_must); /* Node must not grow */
  225. waste += (unsigned)(node->s_must - n);
  226. /* Add in wasted space */
  227. assert(sizeof(char *) == sizeof(INT_PTR));
  228. /* Optimizer should eliminate this */
  229. cp = (char *)((INT_PTR) s_text(node) & ~(sizeof(DWORD_PTR)-1));
  230. /* Point to start of text */
  231. node->s_must = n; /* Set new length */
  232. if (n & (sizeof(DWORD_PTR)-1))
  233. cp += sizeof(DWORD_PTR) - (n & (sizeof(DWORD_PTR)-1)); /* Adjust non dword-aligned string */
  234. memmove(cp,s,n); /* Copy new text */
  235. cp += n; /* Skip over new text */
  236. memmove(cp,node,sizeof(STRINGNODE));/* Copy the node */
  237. return((STRINGNODE *) cp); /* Return pointer to moved node */
  238. }
  239. /*** maketd1 - add entry for TD1 shift table
  240. *
  241. * This function fills in the TD1 table for the given
  242. * search string. The idea is adapted from Daniel M.
  243. * Sunday's QuickSearch algorithm as described in an
  244. * article in the August 1990 issue of "Communications
  245. * of the ACM". As described, the algorithm is suitable
  246. * for single-string searches. The idea to extend it for
  247. * multiple search strings is mine and is described below.
  248. *
  249. * Think of searching for a match as shifting the search
  250. * pattern p of length n over the source text s until the
  251. * search pattern is aligned with matching text or until
  252. * the end of the source text is reached.
  253. *
  254. * At any point when we find a mismatch, we know
  255. * we will shift our pattern to the right in the
  256. * source text at least one position. Thus,
  257. * whenever we find a mismatch, we know the character
  258. * s[n] will figure in our next attempt to match.
  259. *
  260. * For some character c, TD1[c] is the 1-based index
  261. * from right to left of the first occurrence of c
  262. * in p. Put another way, it is the count of places
  263. * to shift p to the right on s so that the rightmost
  264. * c in p is aligned with s[n]. If p does not contain
  265. * c, then TD1[c] = n + 1, meaning we shift p to align
  266. * p[0] with s[n + 1] and try our next match there.
  267. *
  268. * Computing TD1 for a single string is easy:
  269. *
  270. * memset(TD1,n + 1,sizeof TD1);
  271. * for (i = 0; i < n; ++i) {
  272. * TD1[p[i]] = n - i;
  273. * }
  274. *
  275. * Generalizing this computation to a case where there
  276. * are multiple strings of differing lengths is trickier.
  277. * The key is to generate a TD1 that is as conservative
  278. * as necessary, meaning that no shift value can be larger
  279. * than one plus the length of the shortest string for
  280. * which you are looking. The other key is to realize
  281. * that you must treat each string as though it were only
  282. * as long as the shortest string. This is best illustrated
  283. * with an example. Consider the following two strings:
  284. *
  285. * DYNAMIC PROCEDURE
  286. * 7654321 927614321
  287. *
  288. * The numbers under each letter indicate the values of the
  289. * TD1 entries if we computed the array for each string
  290. * separately. Taking the union of these two sets, and taking
  291. * the smallest value where there are conflicts would yield
  292. * the following TD1:
  293. *
  294. * DYNAMICPODURE
  295. * 7654321974321
  296. *
  297. * Note that TD1['P'] equals 9; since n, the length of our
  298. * shortest string is 7, we know we should not have any
  299. * shift value larger than 8. If we clamp our shift values
  300. * to this value, then we get
  301. *
  302. * DYNAMICPODURE
  303. * 7654321874321
  304. *
  305. * Already, this looks fishy, but let's try it out on
  306. * s = "DYNAMPROCEDURE". We know we should match on
  307. * the trailing procedure, but watch:
  308. *
  309. * DYNAMPROCEDURE
  310. * ^^^^^^^|
  311. *
  312. * Since DYNAMPR doesn't match one of our search strings,
  313. * we look at TD1[s[n]] == TD1['O'] == 7. Applying this
  314. * shift, we get
  315. *
  316. * DYNAMPROCEDURE
  317. * ^^^^^^^
  318. *
  319. * As you can see, by shifting 7, we have gone too far, and
  320. * we miss our match. When computing TD1 for "PROCEDURE",
  321. * we must take only the first 7 characters, "PROCEDU".
  322. * Any trailing characters can be ignored (!) since they
  323. * have no effect on matching the first 7 characters of
  324. * the string. Our modified TD1 then becomes
  325. *
  326. * DYNAMICPODURE
  327. * 7654321752163
  328. *
  329. * When applied to s, we get TD1[s[n]] == TD1['O'] == 5,
  330. * leaving us with
  331. *
  332. * DYNAMPROCEDURE
  333. * ^^^^^^^
  334. * which is just where we need to be to match on "PROCEDURE".
  335. *
  336. * Going to this algorithm has speeded qgrep up on multi-string
  337. * searches from 20-30%. The all-C version with this algorithm
  338. * became as fast or faster than the C+ASM version of the old
  339. * algorithm. Thank you, Daniel Sunday, for your inspiration!
  340. *
  341. * Note: if we are case-insensitive, then we expect the input
  342. * string to be upper-cased on entry to this routine.
  343. *
  344. * Pete Stewart, August 14, 1990.
  345. */
  346. void
  347. maketd1(
  348. unsigned char *pch,
  349. UINT_PTR cch,
  350. UINT_PTR cchstart
  351. )
  352. {
  353. UINT_PTR ch; /* Character */
  354. UINT_PTR i; /* String index */
  355. if ((cch += cchstart) > cchmin)
  356. cch = cchmin; /* Use smaller count */
  357. for (i = cchstart; i < cch; ++i) { /* Examine each char left to right */
  358. ch = *pch++; /* Get the character */
  359. for (;;) { /* Loop to set up entries */
  360. if (ch < chmin)
  361. chmin = ch; /* Remember if smallest */
  362. if (ch > chmax)
  363. chmax = ch; /* Remember if largest */
  364. if (cchmin - i < (UINT_PTR) td1[ch])
  365. td1[ch] = (unsigned char)(cchmin - i);
  366. /* Set value if smaller than previous */
  367. if (casesen || !isascii((int)ch) || !isupper((int)ch))
  368. break; /* Exit loop if done */
  369. ch = tolower((int)ch); /* Force to lower case */
  370. }
  371. }
  372. }
  373. static int
  374. newstring(
  375. char *s,
  376. size_t n
  377. )
  378. {
  379. register STRINGNODE *cur; /* Current string */
  380. register STRINGNODE **pprev; /* Pointer to previous link */
  381. STRINGNODE *new; /* New string */
  382. size_t i; /* Index */
  383. size_t j; /* Count */
  384. int k; /* Count */
  385. if ( (UINT_PTR)n < cchmin)
  386. cchmin = n; /* Remember length of shortest string */
  387. if ((i = transtab[*s]) == 0) { /* If no existing list */
  388. /*
  389. * We have to start a new list
  390. */
  391. if ((i = clists++) >= TRTABLEN/2) error("Too many string lists");
  392. /* Die if too many string lists */
  393. stringlist[i] = NULL; /* Initialize */
  394. transtab[*s] = (char) i; /* Set pointer to new list */
  395. if (!casesen && isalpha(*s)) transtab[*s ^ '\040'] = (char) i;
  396. /* Set pointer for other case */
  397. } else if (stringlist[i] == NULL) return(0);
  398. /* Check for existing 1-byte string */
  399. if (--n == 0) { /* If 1-byte string */
  400. freenode(stringlist[i]); /* Free any existing stuff */
  401. stringlist[i] = NULL; /* No record here */
  402. ++strcnt; /* We have a new string */
  403. return(1); /* String added */
  404. }
  405. ++s; /* Skip first char */
  406. pprev = stringlist + i; /* Get pointer to link */
  407. cur = *pprev; /* Get pointer to node */
  408. while (cur != NULL) { /* Loop to traverse match tree */
  409. i = (n > cur->s_must)? cur->s_must: n;
  410. /* Find minimum of string lengths */
  411. matchstrings(s,s_text(cur),i,&j,&k);
  412. /* Compare the strings */
  413. if (j == 0) { /* If complete mismatch */
  414. if (k < 0) break; /* Break if insertion point found */
  415. pprev = &(cur->s_alt); /* Get pointer to alternate link */
  416. cur = *pprev; /* Follow the link */
  417. } else if (i == j) { /* Else if strings matched */
  418. if (i == n) { /* If new is prefix of current */
  419. cur = *pprev = reallocnode(cur,s_text(cur),n);
  420. /* Shorten text of node */
  421. if (cur->s_suf != NULL) { /* If there are suffixes */
  422. freenode(cur->s_suf);
  423. /* Suffixes no longer needed */
  424. cur->s_suf = NULL;
  425. ++strcnt; /* Account for this string */
  426. }
  427. return(1); /* String added */
  428. }
  429. pprev = &(cur->s_suf); /* Get pointer to suffix link */
  430. if ((cur = *pprev) == NULL) return(0);
  431. /* Done if current is prefix of new */
  432. s += i; /* Skip matched portion */
  433. n -= i;
  434. } else /* Else partial match */ {
  435. /*
  436. * We must split an existing node.
  437. * This is the trickiest case.
  438. */
  439. new = newnode(s_text(cur) + j,cur->s_must - j);
  440. /* Unmatched part of current string */
  441. cur = *pprev = reallocnode(cur,s_text(cur),j);
  442. /* Set length to matched portion */
  443. new->s_suf = cur->s_suf; /* Current string's suffixes */
  444. if (k < 0) { /* If new preceded current */
  445. cur->s_suf = newnode(s + j,n - j);
  446. /* FIrst suffix is new string */
  447. cur->s_suf->s_alt = new;/* Alternate is part of current */
  448. } else /* Else new followed current */ {
  449. new->s_alt = newnode(s + j,n - j);
  450. /* Unmatched new string is alternate */
  451. cur->s_suf = new; /* New suffix list */
  452. }
  453. ++strcnt; /* One more string */
  454. return(1); /* String added */
  455. }
  456. }
  457. *pprev = newnode(s,n); /* Set pointer to new node */
  458. (*pprev)->s_alt = cur; /* Attach alternates */
  459. ++strcnt; /* One more string */
  460. return(1); /* String added */
  461. }
  462. void
  463. addstring(
  464. char *s,
  465. int n
  466. )
  467. {
  468. int endline; /* Match-at-end-of-line flag */
  469. register char *pch; /* Char pointer */
  470. endline = flags & ENDLINE; /* Initialize flag */
  471. pch = target; /* Initialize pointer */
  472. while (n-- > 0) { /* While not at end of string */
  473. switch (*pch = *s++) { /* Switch on character */
  474. case '\\': /* Escape */
  475. if (n > 0 && !isalnum(*s)) { /* If next character "special" */
  476. --n; /* Decrement counter */
  477. *pch = *s++; /* Copy next character */
  478. }
  479. ++pch; /* Increment pointer */
  480. break;
  481. case '$': /* Special end character */
  482. if (n == 0) { /* If end of string */
  483. endline = ENDLINE; /* Set flag */
  484. break; /* Exit switch */
  485. }
  486. /* Drop through */
  487. default: /* All others */
  488. ++pch; /* Increment pointer */
  489. break;
  490. }
  491. }
  492. if (endline)
  493. *pch++ = EOS; /* Add end character if needed */
  494. targetlen = (int)(pch - target); /* Compute target string length */
  495. if (!casesen)
  496. strnupr(target, targetlen); /* Force to upper case if necessary */
  497. newstring(target, targetlen); /* Add string */
  498. }
  499. int
  500. addstrings(
  501. char *buffer,
  502. char *bufend,
  503. char *seplist
  504. )
  505. {
  506. size_t len; /* String length */
  507. while (buffer < bufend) { /* While buffer not empty */
  508. len = strnspn(buffer, seplist, (size_t)(bufend - buffer));
  509. /* Count leading separators */
  510. if ((buffer += len) >= bufend) {
  511. break; /* Skip leading separators */
  512. }
  513. len = strncspn(buffer,seplist,(size_t)(bufend - buffer));
  514. /* Get length of search string */
  515. if (addstr == NULL) {
  516. addstr = isexpr( buffer, len ) ? addexpr : addstring;
  517. /* Select search string type */
  518. }
  519. if ( addstr == addexpr || (flags & BEGLINE) ||
  520. findlist( buffer, buffer + len ) == NULL) { /* If no match within string */
  521. (*addstr)(buffer,len); /* Add string to list */
  522. }
  523. buffer += len; /* Skip the string */
  524. }
  525. return(0); /* Keep looking */
  526. }
  527. int
  528. enumlist(
  529. STRINGNODE *node,
  530. int cchprev
  531. )
  532. {
  533. int i; /* Counter */
  534. int strcnt; /* String count */
  535. strcnt = 0; /* Initialize */
  536. while (node != NULL) { /* While not at end of list */
  537. maketd1(s_text(node),node->s_must,cchprev);
  538. /* Make TD1 entries */
  539. if (flags & DEBUG) { /* If verbose output wanted */
  540. for (i = 0; i < cchprev; ++i)
  541. fputc(' ',stderr); /* Indent line */
  542. fwrite(s_text(node),sizeof(char),node->s_must,stderr);
  543. /* Write this portion */
  544. fprintf(stderr,"\n"); /* Newline */
  545. }
  546. strcnt += (node->s_suf != NULL)?
  547. enumlist(node->s_suf,cchprev + node->s_must): 1;
  548. /* Recurse to do suffixes */
  549. node = node->s_alt; /* Do next alternate in list */
  550. }
  551. return(strcnt? strcnt: 1); /* Return string count */
  552. }
  553. int
  554. enumstrings()
  555. {
  556. char ch; /* Character */
  557. int i; /* Index */
  558. int strcnt; /* String count */
  559. strcnt = 0; /* Initialize */
  560. for (i = 0; i < TRTABLEN; ++i) { /* Loop through translation table */
  561. if (casesen || !isascii(i) || !islower(i)) { /* If case sensitive or not lower */
  562. if (transtab[i] == 0)
  563. continue; /* Skip null entries */
  564. ch = (char) i; /* Get character */
  565. maketd1(&ch,1,0); /* Make TD1 entry */
  566. if (flags & DEBUG)
  567. fprintf(stderr,"%c\n",i);
  568. /* Print the first byte */
  569. strcnt += enumlist(stringlist[transtab[i]],1);
  570. /* Enumerate the list */
  571. }
  572. }
  573. return(strcnt); /* Return string count */
  574. }
  575. HANDLE
  576. openfile(
  577. char *name
  578. )
  579. {
  580. HANDLE fd; /* File descriptor */
  581. DWORD er;
  582. if ((fd = CreateFile( name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ) ) == (HANDLE)-1)
  583. /* If error opening file */
  584. {
  585. er = GetLastError();
  586. fprintf(stderr,"%s: Cannot open %s (error = %x)\n",program,name,er);
  587. /* Print error message */
  588. }
  589. return( fd ); /* Return file descriptor */
  590. }
  591. void
  592. thread2() /* Read thread */
  593. {
  594. for (;;) { /* Loop while there is work to do */
  595. if ((WaitForSingleObject(readpending, (DWORD)-1L) != NO_ERROR)
  596. || (ResetEvent(readpending) != TRUE)) {
  597. break; /* Exit loop if event error */
  598. }
  599. arrc = ReadFile(t2fd,(PVOID)t2buf,t2buflen, &cbread, NULL);
  600. /* Do the read */
  601. SetEvent( readdone ); /* Signal read completed */
  602. }
  603. error("Thread 2 semaphore error"); /* Print error message and die */
  604. }
  605. void
  606. thread3() /* Write thread */
  607. {
  608. for (;;) { /* Loop while there is work to do */
  609. if ((WaitForSingleObject(writepending,(DWORD)-1L) != NO_ERROR)
  610. || (ResetEvent(writepending) != TRUE)) {
  611. break; /* Exit loop if event error */
  612. }
  613. awrc = WriteFile(t3fd,(PVOID)t3buf,t3buflen, &cbwrite, NULL);
  614. /* Do the write */
  615. SetEvent( writedone ); /* Signal write completed */
  616. }
  617. error("Thread 3 semaphore error"); /* Print error message and die */
  618. }
  619. void
  620. startread(
  621. HANDLE fd,
  622. char *buffer,
  623. int buflen
  624. )
  625. {
  626. if ( asyncio ) { /* If asynchronous I/O */
  627. if ((WaitForSingleObject(readdone,(DWORD)-1L) != NO_ERROR)
  628. || (ResetEvent(readdone) != TRUE)) {
  629. error("read synch error"); /* Die if we fail to get semaphore */
  630. }
  631. t2fd = fd; /* Set parameters for read */
  632. t2buf = buffer;
  633. t2buflen = buflen;
  634. SetEvent( readpending ); /* Signal read to start */
  635. Sleep( 0L ); /* Yield the CPU */
  636. } else {
  637. arrc = ReadFile(fd,(PVOID)buffer,buflen, &cbread, NULL);
  638. }
  639. }
  640. int
  641. finishread()
  642. {
  643. if (asyncio) { /* If asynchronous I/O */
  644. if ( WaitForSingleObject( readdone, (DWORD)-1L ) != NO_ERROR ) {
  645. error("read wait error"); /* Die if wait fails */
  646. }
  647. }
  648. return(arrc ? cbread : -1); /* Return number of bytes read */
  649. }
  650. void
  651. startwrite(
  652. HANDLE fd,
  653. char *buffer,
  654. int buflen
  655. )
  656. {
  657. if (asyncio) { /* If asynchronous I/O */
  658. if ((WaitForSingleObject(writedone,(DWORD)-1L) != NO_ERROR)
  659. || (ResetEvent(writedone) != TRUE)) {
  660. error("write synch error"); /* Die if we fail to get semaphore */
  661. }
  662. t3fd = fd; /* Set parameters for write */
  663. t3buf = buffer;
  664. t3buflen = buflen;
  665. SetEvent( writepending ); /* Signal read completed */
  666. Sleep( 0L ); /* Yield the CPU */
  667. } else {
  668. awrc = WriteFile(fd,(PVOID)buffer,buflen, &cbwrite, NULL);
  669. }
  670. }
  671. int
  672. finishwrite()
  673. {
  674. if (asyncio) { /* If asynchronous I/O */
  675. if ( WaitForSingleObject( writedone, (DWORD)-1L ) != NO_ERROR ) {
  676. error("write wait error"); /* Die if wait fails */
  677. }
  678. }
  679. return(awrc ? cbwrite : -1); /* Return number of bytes written */
  680. }
  681. void
  682. write1nobuf(
  683. char *buffer,
  684. size_t buflen
  685. )
  686. {
  687. CBIO cb; /* Count of bytes written */
  688. if (!WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),(PVOID)buffer,buflen, &cb, NULL)
  689. || (cb != (CBIO)buflen))
  690. {
  691. error("Write error"); /* Die if write fails */
  692. }
  693. }
  694. void
  695. write1buf(
  696. char *buffer,
  697. size_t buflen
  698. )
  699. {
  700. register size_t cb; /* Byte count */
  701. while (buflen > 0) { /* While bytes remain */
  702. if (!awrc) { /* If previous write failed */
  703. fprintf(stderr,"%s: Write error %d\n",program,GetLastError());
  704. /* Print error message */
  705. exit(2); /* Die */
  706. }
  707. if ((cb = ocnt[oi]) == 0) { /* If buffer full */
  708. startwrite( GetStdHandle( STD_OUTPUT_HANDLE ), obuf[oi], OUTBUFLEN );
  709. /* Write the buffer */
  710. ocnt[oi] = OUTBUFLEN; /* Reset count and pointer */
  711. optr[oi] = obuf[oi];
  712. oi ^= 1; /* Switch buffers */
  713. cb = ocnt[oi]; /* Get space remaining */
  714. }
  715. if (cb > buflen)
  716. cb = buflen; /* Get minimum */
  717. memmove(optr[oi],buffer,cb); /* Copy bytes to buffer */
  718. ocnt[oi] -= cb; /* Update buffer length and pointers */
  719. optr[oi] += cb;
  720. buflen -= cb;
  721. buffer += cb;
  722. }
  723. }
  724. void
  725. flush1nobuf(void)
  726. {
  727. }
  728. void
  729. flush1buf(void)
  730. {
  731. register int cb; /* Byte count */
  732. if ((cb = OUTBUFLEN - ocnt[oi]) > 0) { /* If buffer not empty */
  733. startwrite( GetStdHandle( STD_OUTPUT_HANDLE ), obuf[oi], cb ); /* Start write */
  734. if (finishwrite() != cb) { /* If write failed */
  735. fprintf(stderr,"%s: Write error %d\n",program,GetLastError());
  736. /* Print error message */
  737. exit(2); /* Die */
  738. }
  739. }
  740. }
  741. int
  742. grepnull(
  743. char *cp,
  744. char *endbuf,
  745. char *name
  746. )
  747. {
  748. /* keep compiler happy */
  749. cp = cp;
  750. endbuf = endbuf;
  751. name = name;
  752. return(0); /* Do nothing */
  753. }
  754. int
  755. grepbuffer(
  756. char *startbuf,
  757. char *endbuf,
  758. char *name
  759. )
  760. {
  761. char *cp; /* Buffer pointer */
  762. char *lastmatch; /* Last matching line */
  763. int linelen; /* Line length */
  764. int namlen = 0; /* Length of name */
  765. char lnobuf[LNOLEN]; /* Line number buffer */
  766. char nambuf[PATHLEN];/* Name buffer */
  767. cp = startbuf; /* Initialize to start of buffer */
  768. lastmatch = cp; /* No previous match yet */
  769. while ((cp = (*find)(cp,endbuf)) != NULL) { /* While matches are found */
  770. --cp; /* Back up to previous character */
  771. if ((flags & BEGLINE) && *cp != '\n') { /* If begin line conditions not met */
  772. cp += strncspn(cp,"\n", (size_t)(endbuf - cp)) + 1;
  773. /* Skip line */
  774. continue; /* Keep looking */
  775. }
  776. status = 0; /* Match found */
  777. if (flags & NAMEONLY) return(1); /* Return if filename only wanted */
  778. cp -= preveol(cp) - 1; /* Point at start of line */
  779. if (flags & SHOWNAME) { /* If name wanted */
  780. if (namlen == 0) { /* If name not formatted yet */
  781. if (flags & ZLINENOS)
  782. namlen = sprintf(nambuf,"%s",name);
  783. else
  784. namlen = sprintf(nambuf,"%s:",name);
  785. /* Format name if not done already */
  786. }
  787. (*write1)(nambuf, namlen); /* Show name */
  788. }
  789. if (flags & LINENOS) { /* If line number wanted */
  790. lineno += countlines(lastmatch,cp);
  791. /* Count lines since last match */
  792. if (flags & ZLINENOS)
  793. (*write1)(lnobuf,sprintf(lnobuf,"(%u) : ",lineno));
  794. else
  795. (*write1)(lnobuf,sprintf(lnobuf,"%u:",lineno));
  796. /* Print line number */
  797. lastmatch = cp; /* New last match */
  798. }
  799. if (flags & SEEKOFF) { /* If seek offset wanted */
  800. (*write1)(lnobuf,sprintf(lnobuf,"%lu:",
  801. cbfile + (long)(cp - startbuf)));
  802. /* Print seek offset */
  803. }
  804. linelen = strncspn(cp,"\n",(size_t)(endbuf - cp)) + 1;
  805. /* Calculate line length */
  806. (*write1)(cp,linelen); /* Print the line */
  807. cp += linelen; /* Skip the line */
  808. }
  809. if (flags & LINENOS)
  810. lineno += countlines(lastmatch,endbuf);
  811. /* Count remaining lines in buffer */
  812. return(0); /* Keep searching */
  813. }
  814. void
  815. showv(
  816. char *name,
  817. char *startbuf,
  818. char *lastmatch,
  819. char *thismatch
  820. )
  821. {
  822. register int linelen;
  823. int namlen = 0; /* Length of name */
  824. char lnobuf[LNOLEN]; /* Line number buffer */
  825. char nambuf[PATHLEN];/* Name buffer */
  826. if (flags & (SHOWNAME | LINENOS | SEEKOFF)) {
  827. while (lastmatch < thismatch) {
  828. if (flags & SHOWNAME) { /* If name wanted */
  829. if (namlen == 0) { /* If name not formatted yet */
  830. if (flags & ZLINENOS)
  831. namlen = sprintf(nambuf,"%s",name);
  832. else
  833. namlen = sprintf(nambuf,"%s:",name);
  834. /* Format name if not done already */
  835. }
  836. (*write1)(nambuf,namlen);
  837. /* Write the name */
  838. }
  839. if (flags & LINENOS) { /* If line numbers wanted */
  840. if (flags & ZLINENOS)
  841. (*write1)(lnobuf,sprintf(lnobuf,"(%u) : ",lineno++));
  842. else
  843. (*write1)(lnobuf,sprintf(lnobuf,"%u:",lineno++));
  844. /* Print the line number */
  845. }
  846. if (flags & SEEKOFF) { /* If seek offsets wanted */
  847. (*write1)(lnobuf,sprintf(lnobuf,"%lu:",
  848. cbfile + (long)(lastmatch - startbuf)));
  849. /* Print the line number */
  850. }
  851. linelen = strncspn(lastmatch,"\n",(size_t)(thismatch - lastmatch)) + 1;
  852. (*write1)(lastmatch,linelen);
  853. lastmatch += linelen;
  854. }
  855. } else (*write1)(lastmatch, (size_t)(thismatch - lastmatch));
  856. }
  857. int
  858. grepvbuffer(
  859. char *startbuf,
  860. char *endbuf,
  861. char *name
  862. )
  863. {
  864. register char *cp; /* Buffer pointer */
  865. register char *lastmatch; /* Pointer to line after last match */
  866. cp = startbuf; /* Initialize to start of buffer */
  867. lastmatch = cp;
  868. while ((cp = (*find)(cp,endbuf)) != NULL) {
  869. --cp; /* Back up to previous character */
  870. if ((flags & BEGLINE) && *cp != '\n') { /* If begin line conditions not met */
  871. cp += strncspn(cp,"\n", (size_t)(endbuf - cp)) + 1;
  872. /* Skip line */
  873. continue; /* Keep looking */
  874. }
  875. cp -= preveol(cp) - 1; /* Point at start of line */
  876. if (cp > lastmatch) { /* If we have lines without matches */
  877. status = 0; /* Lines without matches found */
  878. if (flags & NAMEONLY) return(1);
  879. /* Skip rest of file if NAMEONLY */
  880. showv(name,startbuf,lastmatch,cp);
  881. /* Show from last match to this */
  882. }
  883. cp += strncspn(cp,"\n",(size_t)(endbuf - cp)) + 1;
  884. /* Skip over line with match */
  885. lastmatch = cp; /* New "last" match */
  886. ++lineno; /* Increment line count */
  887. }
  888. if (endbuf > lastmatch) { /* If we have lines without matches */
  889. status = 0; /* Lines without matches found */
  890. if (flags & NAMEONLY) return(1); /* Skip rest of file if NAMEONLY */
  891. showv(name,startbuf,lastmatch,endbuf);
  892. /* Show buffer tail */
  893. }
  894. return(0); /* Keep searching file */
  895. }
  896. void
  897. qgrep(
  898. int (*grep)( char *, char *, char * ),
  899. char *name,
  900. HANDLE fd
  901. )
  902. {
  903. int cb; /* Byte count */
  904. register char *cp; /* Buffer pointer */
  905. char *endbuf; /* End of buffer */
  906. int taillen; /* Length of buffer tail */
  907. int bufi; /* Buffer index */
  908. cbfile = 0L; /* File empty so far */
  909. lineno = 1; /* File starts on line 1 */
  910. taillen = 0; /* No buffer tail yet */
  911. bufi = 0; /* Initialize buffer index */
  912. cp = bufptr[0]; /* Initialize to start of buffer */
  913. finishread(); /* Make sure no I/O activity */
  914. arrc = ReadFile(fd,(PVOID)cp,filbuflen, &cbread, NULL);
  915. while ((cb = finishread()) + taillen > 0) { /* While search incomplete */
  916. if (cb == 0) { /* If buffer tail is all that's left */
  917. taillen = 0; /* Set tail length to zero */
  918. *cp++ = '\r'; /* Add end of line sequence */
  919. *cp++ = '\n';
  920. endbuf = cp; /* Note end of buffer */
  921. } else /* Else start next read */ {
  922. taillen = preveol(cp + cb - 1);
  923. /* Find length of partial line */
  924. endbuf = cp + cb - taillen; /* Get pointer to end of buffer */
  925. cp = bufptr[bufi ^ 1]; /* Pointer to other buffer */
  926. memmove(cp,endbuf,taillen); /* Copy tail to head of other buffer */
  927. cp += taillen; /* Skip over tail */
  928. startread(fd,cp,(filbuflen - taillen) & (~0 << LG2SECLEN));
  929. /* Start next read */
  930. }
  931. if ((*grep)(bufptr[bufi],endbuf,name)) { /* If rest of file can be skipped */
  932. (*write1)(name,strlen(name));
  933. /* Write file name */
  934. (*write1)("\r\n",2); /* Write newline sequence */
  935. return; /* Skip rest of file */
  936. }
  937. cbfile += (long)(endbuf - bufptr[bufi]);
  938. /* Increment count of bytes in file */
  939. bufi ^= 1; /* Switch buffers */
  940. }
  941. }
  942. void
  943. usage(
  944. char *errmes
  945. )
  946. {
  947. static const char szUsage[] =
  948. {
  949. "-? - print this message\n"
  950. "-B - match pattern if at beginning of line\n"
  951. "-E - match pattern if at end of line\n"
  952. "-L - treat search strings literally (fgrep)\n"
  953. "-O - print seek offset before each matching line\n"
  954. "-X - treat search strings as regular expressions (grep)\n"
  955. "-l - print only file name if file contains match\n"
  956. "-n - print line number before each matching line\n"
  957. "-z - print matching lines in MSC error message format\n"
  958. "-v - print only lines not containing a match\n"
  959. "-x - print lines that match exactly (-BE)\n"
  960. "-y - treat upper and lower case as equivalent\n"
  961. "-e - treat next argument literally as a search string\n"
  962. "-f - read search strings from file named by next argument (- = stdin)\n"
  963. "-i - read file list from file named by next argument (- = stdin)\n"
  964. "White space separates search strings unless the argument is prefixed\n"
  965. "with -e, e.g., 'qgrep \"all out\" x.y' means find either \"all\" or\n"
  966. "\"out\" in x.y, while 'qgrep -e \"all out\" x.y' means find \"all out\".\n"
  967. };
  968. if (errmes != NULL)
  969. fprintf(stderr,"%s: %s\n", program, errmes);
  970. /* Print error message */
  971. fprintf(stderr,"usage: %s [-?BELOXlnzvxy][-e string][-f file][-i file][strings][files]\n", program);
  972. /* Print command line format */
  973. if (errmes == NULL) { /* If verbose message wanted */
  974. fputs(szUsage, stderr);
  975. }
  976. exit(2); /* Error exit */
  977. }
  978. char *
  979. rmpath(
  980. char *name
  981. )
  982. {
  983. char *cp; /* Char pointer */
  984. if (name[0] != '\0' && name[1] == ':') name += 2;
  985. /* Skip drive spec if any */
  986. cp = name; /* Point to start */
  987. while (*name != '\0') { /* While not at end */
  988. ++name; /* Skip to next character */
  989. if (name[-1] == '/' || name[-1] == '\\') cp = name;
  990. /* Point past path separator */
  991. }
  992. return(cp); /* Return pointer to name */
  993. }
  994. int
  995. __cdecl
  996. main(
  997. int argc,
  998. char **argv
  999. )
  1000. {
  1001. char *cp;
  1002. HANDLE fd;
  1003. DWORD dwTmp;
  1004. FILE *fi;
  1005. int i;
  1006. int j;
  1007. char *inpfile = NULL;
  1008. char *strfile = NULL;
  1009. unsigned long tstart, tend; /* Start time */
  1010. char filnam[FILNAMLEN];
  1011. tstart = clock(); /* Get start time */
  1012. ConvertAppToOem( argc, argv );
  1013. asyncio = pmode = 1; /* Do asynchronous I/O */
  1014. program = rmpath(argv[0]); /* Set program name */
  1015. memset(td1,1,TRTABLEN); /* Set up TD1 for startup */
  1016. flags = 0;
  1017. for (i = 1; i < argc && strchr("/-",argv[i][0]) != NULL; ++i) {
  1018. if (argv[i][1] == '\0' ||
  1019. strchr("?BELNOSXdlntvxyz",argv[i][1]) == NULL) break;
  1020. /* Break if unrecognized switch */
  1021. for (cp = &argv[i][1]; *cp != '\0'; ++cp) {
  1022. switch (*cp) {
  1023. case '?':
  1024. usage(NULL); /* Verbose usage message */
  1025. case 'B':
  1026. flags |= BEGLINE;
  1027. break;
  1028. case 'E':
  1029. flags |= ENDLINE;
  1030. break;
  1031. case 'L':
  1032. addstr = addstring; /* Treat strings literally */
  1033. break;
  1034. case 'N':
  1035. grep = grepnull;
  1036. break;
  1037. case 'O':
  1038. flags |= SEEKOFF;
  1039. break;
  1040. case 'S':
  1041. asyncio = 0; /* Force synchronous I/O */
  1042. break;
  1043. case 'X':
  1044. addstr = addexpr; /* Add expression to list */
  1045. break;
  1046. case 'd':
  1047. flags |= DEBUG;
  1048. break;
  1049. case 'l':
  1050. flags |= NAMEONLY;
  1051. break;
  1052. case 'n':
  1053. flags |= LINENOS;
  1054. break;
  1055. case 'z':
  1056. flags |= ZLINENOS | LINENOS;
  1057. break;
  1058. case 't':
  1059. flags |= TIMER;
  1060. break;
  1061. case 'v':
  1062. grep = grepvbuffer;
  1063. break;
  1064. case 'x':
  1065. flags |= BEGLINE | ENDLINE;
  1066. break;
  1067. case 'y':
  1068. casesen = 0;
  1069. break;
  1070. default:
  1071. fprintf(stderr,"%s: -%c ignored\n",program,*cp);
  1072. break;
  1073. }
  1074. }
  1075. }
  1076. for (; i < argc && argv[i][0] == '-'; ++i) {
  1077. if (argv[i][2] == '\0') { /* No multi-character switches */
  1078. switch (argv[i][1]) {
  1079. case 'e': /* Explicit string (no separators) */
  1080. if (++i == argc) usage("Argument missing after -e");
  1081. /* Argument missing after -e */
  1082. cp = argv[i]; /* Point to string */
  1083. addstrings( cp, cp + strlen(cp), "" );
  1084. /* Add string "as is" */
  1085. continue;
  1086. case 'F':
  1087. flags |= DOQUOTES; /* Handle quoted patterns in file */
  1088. case 'f': /* Patterns in file */
  1089. case 'i': /* Names of files to search in file */
  1090. if (i == argc - 1) usage("Argument missing after switch");
  1091. /* Argument missing */
  1092. if (argv[i++][1] == 'i') inpfile = argv[i];
  1093. else strfile = argv[i];
  1094. continue;
  1095. }
  1096. }
  1097. fprintf(stderr,"%s: %s ignored\n",program,argv[i]);
  1098. }
  1099. if (i == argc && strcnt == 0 && strfile == NULL) usage("Bad command line");
  1100. /* Usage message if arg error */
  1101. if (asyncio) { /* Initialize semaphores and threads */
  1102. if ( !( readdone = CreateEvent( NULL, TRUE, TRUE,NULL ) ) ||
  1103. !( readpending = CreateEvent( NULL, TRUE, FALSE,NULL ) ) ||
  1104. !( writedone = CreateEvent( NULL, TRUE, TRUE,NULL ) ) ||
  1105. !( writepending = CreateEvent( NULL, TRUE, FALSE,NULL ) ) ) {
  1106. error("Semaphore creation error");
  1107. }
  1108. if ((CreateThread( NULL,
  1109. STKLEN,
  1110. (LPTHREAD_START_ROUTINE)thread2,
  1111. NULL,
  1112. 0,
  1113. (LPDWORD)&dwTmp)
  1114. == NULL)
  1115. ||
  1116. (CreateThread( NULL,
  1117. STKLEN,
  1118. (LPTHREAD_START_ROUTINE)thread3,
  1119. NULL,
  1120. 0,
  1121. (LPDWORD)&dwTmp)
  1122. == NULL)) {
  1123. error("Thread creation error");
  1124. }
  1125. /* Die if thread creation fails */
  1126. }
  1127. _setmode(_fileno(stdout),O_BINARY); /* No linefeed translation on output */
  1128. bufptr[0][-1] = bufptr[1][-1] = '\n';
  1129. /* Mark beginnings with newline */
  1130. if (_isatty(_fileno(stdout))) { /* If stdout is a device */
  1131. write1 = write1nobuf; /* Use unbuffered output */
  1132. flush1 = flush1nobuf;
  1133. }
  1134. if (strfile != NULL) { /* If strings from file */
  1135. if (strcmp(strfile,"-") != 0) { /* If strings not from std. input */
  1136. if ( ( fd = CreateFile( strfile, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL ) ) == (HANDLE)-1 ) { /* If open fails */
  1137. fprintf(stderr,"%s: Cannot read strings from %s\n",
  1138. program,strfile); /* Print message */
  1139. exit(2); /* Die */
  1140. }
  1141. } else {
  1142. fd = GetStdHandle( STD_INPUT_HANDLE ); /* Else use std. input */
  1143. }
  1144. qgrep( addstrings, "\r\n", fd );/* Do the work */
  1145. if ( fd != GetStdHandle( STD_INPUT_HANDLE ) ) {
  1146. CloseHandle( fd ); /* Close strings file */
  1147. }
  1148. }
  1149. else if (strcnt == 0) { /* Else if strings on command line */
  1150. cp = argv[i++]; /* Set pointer to strings */
  1151. addstrings(cp,cp + strlen(cp)," \t");
  1152. /* Add strings to list */
  1153. }
  1154. if (strcnt == 0) error("No search strings");
  1155. /* Die if no strings */
  1156. if (addstr != addexpr) { /* If not using expressions */
  1157. memset(td1,(int)(cchmin + 1),TRTABLEN);/* Initialize table */
  1158. find = findlist; /* Assume finding many */
  1159. if ((j = enumstrings()) != strcnt)
  1160. fprintf(stderr,"String count error (%d != %d)\n",j,strcnt);
  1161. /* Enumerate strings and verify count */
  1162. if (flags & DEBUG) { /* If debugging output wanted */
  1163. fprintf(stderr,"%u bytes wasted in heap\n",waste);
  1164. /* Print storage waste */
  1165. assert(chmin <= chmax); /* Must have some entries */
  1166. fprintf(stderr,
  1167. "chmin = %u, chmax = %u, cchmin = %u\n",chmin,chmax,cchmin);
  1168. /* Print range info */
  1169. for (j = (int)chmin; j <= (int)chmax; ++j) { /* Loop to print TD1 table */
  1170. if ( td1[j] <= (char)cchmin ) { /* If not maximum shift */
  1171. if (isascii(j) && isprint(j))
  1172. fprintf(stderr,"'%c'=%2u ",j,td1[j]);
  1173. /* Show literally if printable */
  1174. else fprintf(stderr,"\\%02x=%2u ",j,td1[j]);
  1175. /* Else show hex value */
  1176. }
  1177. }
  1178. fprintf(stderr,"\n");
  1179. }
  1180. if (strcnt == 1 && casesen)
  1181. find = findone; /* Find one case-sensitive string */
  1182. } else if (find == NULL) {
  1183. find = findexpr; /* Else find expressions */
  1184. }
  1185. if (inpfile != NULL) { /* If file list from file */
  1186. flags |= SHOWNAME; /* Always show name of file */
  1187. if (strcmp(inpfile,"-") != 0) { /* If name is not "-" */
  1188. if ((fi = fopen(inpfile,"r")) == NULL) { /* If open fails */
  1189. fprintf(stderr,"%s: Cannot read file list from %s\n",
  1190. program,inpfile); /* Error message */
  1191. exit(2); /* Error exit */
  1192. }
  1193. } else fi = stdin; /* Else read file list from stdin */
  1194. while (fgets(filnam,FILNAMLEN,fi) != NULL) { /* While there are names */
  1195. filnam[strcspn(filnam,"\r\n")] = '\0';
  1196. /* Null-terminate the name */
  1197. if ((fd = openfile(filnam)) == (HANDLE)-1) {
  1198. continue; /* Skip file if it cannot be opened */
  1199. }
  1200. qgrep(grep,filnam,fd); /* Do the work */
  1201. CloseHandle( fd );
  1202. }
  1203. if (fi != stdin) fclose(fi); /* Close the list file */
  1204. } else if (i == argc) {
  1205. flags &= ~(NAMEONLY | SHOWNAME);
  1206. qgrep( grep, NULL, GetStdHandle( STD_INPUT_HANDLE ) );
  1207. }
  1208. if (argc > i + 1) flags |= SHOWNAME;
  1209. for (; i < argc; ++i) {
  1210. _strlwr(argv[i]);
  1211. if ((fd = openfile(argv[i])) == (HANDLE)-1) {
  1212. continue;
  1213. }
  1214. qgrep(grep,argv[i],fd);
  1215. CloseHandle( fd );
  1216. }
  1217. (*flush1)();
  1218. if ( flags & TIMER ) { /* If timing wanted */
  1219. tend = clock();
  1220. tstart = tend - tstart; /* Get time in milliseconds */
  1221. fprintf(stderr,"%lu.%03lu seconds\n", ( tstart / CLK_TCK ), ( tstart % CLK_TCK ) );
  1222. /* Print total elapsed time */
  1223. }
  1224. return( status );
  1225. }
  1226. char *findsub(); /* Findlist() worker */
  1227. char *findsubi(); /* Findlist() worker */
  1228. char *(*flworker[])() =
  1229. { /* Table of workers */
  1230. findsubi,
  1231. findsub
  1232. };
  1233. char *
  1234. strnupr(
  1235. char *pch,
  1236. int cch
  1237. )
  1238. {
  1239. while (cch-- > 0) { /* Convert string to upper case */
  1240. if (isascii(pch[cch]))
  1241. pch[cch] = (char)toupper(pch[cch]);
  1242. }
  1243. return(pch);
  1244. }
  1245. /*
  1246. * This is an implementation of the QuickSearch algorith described
  1247. * by Daniel M. Sunday in the August 1990 issue of CACM. The TD1
  1248. * table is computed before this routine is called.
  1249. */
  1250. char *
  1251. findone(
  1252. unsigned char *buffer,
  1253. char *bufend
  1254. )
  1255. {
  1256. if ((bufend -= targetlen - 1) <= buffer)
  1257. return((char *) 0); /* Fail if buffer too small */
  1258. while (buffer < bufend) { /* While space remains */
  1259. int cch; /* Character count */
  1260. register char *pch1; /* Char pointer */
  1261. register char *pch2; /* Char pointer */
  1262. pch1 = target; /* Point at pattern */
  1263. pch2 = buffer; /* Point at buffer */
  1264. for (cch = targetlen; cch > 0; --cch) {
  1265. /* Loop to try match */
  1266. if (*pch1++ != *pch2++)
  1267. break; /* Exit loop on mismatch */
  1268. }
  1269. if (cch == 0)
  1270. return(buffer); /* Return pointer to match */
  1271. buffer += td1[buffer[targetlen]];
  1272. /* Skip ahead */
  1273. }
  1274. return((char *) 0); /* No match */
  1275. }
  1276. size_t
  1277. preveol(
  1278. char *s
  1279. )
  1280. {
  1281. register char *cp; /* Char pointer */
  1282. cp = s + 1; /* Initialize pointer */
  1283. while (*--cp != '\n') ; /* Find previous end-of-line */
  1284. return((size_t)(s - cp)); /* Return distance to match */
  1285. }
  1286. int
  1287. countlines(
  1288. char *start,
  1289. char *finish
  1290. )
  1291. {
  1292. register int count; /* Line count */
  1293. for (count = 0; start < finish; ) { /* Loop to count lines */
  1294. if (*start++ == '\n') ++count; /* Increment count if linefeed found */
  1295. }
  1296. return(count); /* Return count */
  1297. }
  1298. char *
  1299. findlist(
  1300. unsigned char *buffer,
  1301. char *bufend
  1302. )
  1303. {
  1304. char *match; /* Pointer to matching string */
  1305. char endbyte; /* First byte past end */
  1306. endbyte = *bufend; /* Save byte */
  1307. *bufend = '\177'; /* Mark end of buffer */
  1308. match = (*flworker[casesen])(buffer,bufend);
  1309. /* Call worker */
  1310. *bufend = endbyte; /* Restore end of buffer */
  1311. return(match); /* Return matching string */
  1312. }
  1313. char *
  1314. findsub(
  1315. unsigned char *buffer,
  1316. char *bufend
  1317. )
  1318. {
  1319. register char *cp; /* Char pointer */
  1320. STRINGNODE *s; /* String node pointer */
  1321. int i; /* Index */
  1322. if ((bufend -= cchmin - 1) < buffer)
  1323. return((char *) 0); /* Compute effective buffer length */
  1324. while (buffer < bufend) { /* Loop to find match */
  1325. if ((i = transtab[*buffer]) != 0) { /* If valid first character */
  1326. if ((s = stringlist[i]) == 0)
  1327. return(buffer); /* Check for 1-byte match */
  1328. for (cp = buffer + 1; ; ) { /* Loop to search list */
  1329. if ((i = memcmp(cp,s_text(s),s->s_must)) == 0) { /* If portions match */
  1330. cp += s->s_must; /* Skip matching portion */
  1331. if ((s = s->s_suf) == 0)
  1332. return(buffer); /* Return match if end of list */
  1333. continue; /* Else continue */
  1334. }
  1335. if (i < 0 || (s = s->s_alt) == 0)
  1336. break; /* Break if not in this list */
  1337. }
  1338. }
  1339. buffer += td1[buffer[cchmin]]; /* Shift as much as possible */
  1340. }
  1341. return((char *) 0); /* No match */
  1342. }
  1343. char *
  1344. findsubi(
  1345. unsigned char *buffer,
  1346. char *bufend
  1347. )
  1348. {
  1349. register char *cp; /* Char pointer */
  1350. STRINGNODE *s; /* String node pointer */
  1351. int i; /* Index */
  1352. if ((bufend -= cchmin - 1) < buffer)
  1353. return((char *) 0); /* Compute effective buffer length */
  1354. while (buffer < bufend) { /* Loop to find match */
  1355. if ((i = transtab[*buffer]) != 0) { /* If valid first character */
  1356. if ((s = stringlist[i]) == 0)
  1357. return(buffer); /* Check for 1-byte match */
  1358. for (cp = buffer + 1; ; ) { /* Loop to search list */
  1359. if ((i = _memicmp(cp,s_text(s),s->s_must)) == 0) { /* If portions match */
  1360. cp += s->s_must; /* Skip matching portion */
  1361. if ((s = s->s_suf) == 0)
  1362. return(buffer); /* Return match if end of list */
  1363. continue; /* And continue */
  1364. }
  1365. if (i < 0 || (s = s->s_alt) == 0)
  1366. break; /* Break if not in this list */
  1367. }
  1368. }
  1369. buffer += td1[buffer[cchmin]]; /* Shift as much as possible */
  1370. }
  1371. return((char *) 0); /* No match */
  1372. }
  1373. size_t
  1374. strnspn(
  1375. char *s,
  1376. char *t,
  1377. size_t n
  1378. )
  1379. {
  1380. char *s1; /* String pointer */
  1381. char *t1; /* String pointer */
  1382. for (s1 = s; n-- != 0; ++s1) { /* While not at end of s */
  1383. for (t1 = t; *t1 != '\0'; ++t1) { /* While not at end of t */
  1384. if (*s1 == *t1) break; /* Break if match found */
  1385. }
  1386. if (*t1 == '\0') break; /* Break if no match found */
  1387. }
  1388. return((size_t)(s1 - s)); /* Return length */
  1389. }
  1390. size_t
  1391. strncspn(
  1392. char *s,
  1393. char *t,
  1394. size_t n
  1395. )
  1396. {
  1397. char *s1; /* String pointer */
  1398. char *t1; /* String pointer */
  1399. for (s1 = s; n-- != 0; ++s1) { /* While not at end of s */
  1400. for (t1 = t; *t1 != '\0'; ++t1) { /* While not at end of t */
  1401. if (*s1 == *t1)
  1402. return((size_t)(s1 - s));
  1403. /* Return if match found */
  1404. }
  1405. }
  1406. return((size_t)(s1 - s)); /* Return length */
  1407. }
  1408. void
  1409. matchstrings(
  1410. char *s1,
  1411. char *s2,
  1412. int len,
  1413. size_t *nmatched,
  1414. int *leg
  1415. )
  1416. {
  1417. register char *cp; /* Char pointer */
  1418. register int (__cdecl *cmp)(const char*,const char*, size_t); /* Comparison function pointer */
  1419. cmp = casesen? strncmp: _strnicmp;
  1420. /* Set pointer */
  1421. if ((*leg = (*cmp)(s1,s2,len)) != 0) { /* If strings don't match */
  1422. for (cp = s1; (*cmp)(cp,s2++,1) == 0; ++cp)
  1423. ;
  1424. /* Find mismatch */
  1425. *nmatched = (size_t)(cp - s1); /* Return number matched */
  1426. } else
  1427. *nmatched = len; /* Else all matched */
  1428. }