1602 lines
58 KiB
C
1602 lines
58 KiB
C
/*static char *SCCSID = "@(#)qgrep.c 13.11 90/08/14";*/
|
|
/*
|
|
* QGrep
|
|
*
|
|
* Modification History:
|
|
*
|
|
* Aug 1990 PeteS Created.
|
|
* 1990 DaveGi Ported to Cruiser
|
|
* 31-Oct-1990 W-Barry Removed the #ifdef M_I386 'cause this
|
|
* code will never see 16bit again.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <time.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <io.h>
|
|
#include <windows.h>
|
|
#include <ctype.h>
|
|
#include <assert.h>
|
|
|
|
/*
|
|
* Miscellaneous constants and macros
|
|
*/
|
|
|
|
#define FILBUFLEN (SECTORLEN*16) /* File buffer length */
|
|
#define FILNAMLEN 80 /* File name length */
|
|
#define ISCOT 0x0002 /* Handle is console output */
|
|
#define LG2SECLEN 10 /* Log base two of sector length */
|
|
#define LNOLEN 12 /* Maximum line number length */
|
|
#define MAXSTRLEN 128 /* Maximum search string length */
|
|
#define OUTBUFLEN (SECTORLEN*2) /* Output buffer length */
|
|
#define PATHLEN 128 /* Path buffer length */
|
|
#define SECTORLEN (1 << LG2SECLEN)/* Sector length */
|
|
#define STKLEN 512 /* Stack length in bytes */
|
|
#define TRTABLEN 256 /* Translation table length */
|
|
#define s_text(x) (((char *)(x)) - ((x)->s_must))
|
|
/* Text field access macro */
|
|
#define EOS ('\r') /* End of string */
|
|
|
|
|
|
/*
|
|
* Bit flag definitions
|
|
*/
|
|
|
|
#define SHOWNAME 0x01 /* Print filename */
|
|
#define NAMEONLY 0x02 /* Print filename only */
|
|
#define LINENOS 0x04 /* Print line numbers */
|
|
#define BEGLINE 0x08 /* Match at beginning of line */
|
|
#define ENDLINE 0x10 /* Match at end of line */
|
|
#define DEBUG 0x20 /* Print debugging output */
|
|
#define TIMER 0x40 /* Time execution */
|
|
#define SEEKOFF 0x80 /* Print seek offsets */
|
|
#define ZLINENOS 0x100 /* Print MEP style line numbers */
|
|
#define DOQUOTES 0x200 /* Handle quoted strings in -f search file */
|
|
|
|
|
|
/*
|
|
* Type definitions
|
|
*/
|
|
|
|
typedef struct stringnode {
|
|
struct stringnode *s_alt; /* List of alternates */
|
|
struct stringnode *s_suf; /* List of suffixes */
|
|
size_t s_must; /* Length of portion that must match */
|
|
}
|
|
STRINGNODE; /* String node */
|
|
|
|
typedef ULONG CBIO; /* I/O byte count */
|
|
typedef ULONG PARM; /* Generic parameter */
|
|
|
|
typedef CBIO *PCBIO; /* Pointer to I/O byte count */
|
|
typedef PARM *PPARM; /* Pointer to generic parameter */
|
|
|
|
|
|
/*
|
|
* Global data
|
|
*/
|
|
|
|
char filbuf[FILBUFLEN*2L + 12];
|
|
char outbuf[OUTBUFLEN*2];
|
|
char td1[TRTABLEN] = { 0};
|
|
UINT_PTR cchmin = (UINT_PTR)-1; /* Minimum string length */
|
|
UINT_PTR chmax = 0; /* Maximum character */
|
|
UINT_PTR chmin = (UINT_PTR)-1; /* Minimum character */
|
|
char transtab[TRTABLEN] = { 0};
|
|
STRINGNODE *stringlist[TRTABLEN/2];
|
|
int casesen = 1; /* Assume case-sensitivity */
|
|
long cbfile; /* Number of bytes in file */
|
|
static int clists = 1; /* One is first available index */
|
|
int filbuflen = FILBUFLEN;
|
|
/* File buffer length */
|
|
int flags; /* Flags */
|
|
unsigned lineno; /* Current line number */
|
|
char *program; /* Program name */
|
|
int status = 1; /* Assume failure */
|
|
int strcnt = 0; /* String count */
|
|
char target[MAXSTRLEN];
|
|
/* Last string added */
|
|
size_t targetlen; /* Length of last string added */
|
|
unsigned waste; /* Wasted storage in heap */
|
|
int arrc; /* I/O return code for DOSREAD */
|
|
char asyncio; /* Asynchronous I/O flag */
|
|
int awrc = TRUE; /* I/O return code for DOSWRITE */
|
|
char *bufptr[] = { filbuf + 4, filbuf + FILBUFLEN + 8};
|
|
CBIO cbread; /* Bytes read by DOSREAD */
|
|
CBIO cbwrite; /* Bytes written by DOSWRITE */
|
|
char *obuf[] = { outbuf, outbuf + OUTBUFLEN};
|
|
int ocnt[] = { OUTBUFLEN, OUTBUFLEN};
|
|
int oi = 0; /* Output buffer index */
|
|
char *optr[] = { outbuf, outbuf + OUTBUFLEN};
|
|
char pmode; /* Protected mode flag */
|
|
char *t2buf; /* Async read buffer */
|
|
int t2buflen; /* Async read buffer length */
|
|
HANDLE t2fd; /* Async read file */
|
|
char *t3buf; /* Async write buffer */
|
|
int t3buflen; /* Async write buffer length */
|
|
HANDLE t3fd; /* Async write file */
|
|
|
|
HANDLE readdone; /* Async read done semaphore */
|
|
HANDLE readpending; /* Async read pending semaphore */
|
|
HANDLE writedone; /* Async write done semaphore */
|
|
HANDLE writepending; /* Async write pending semaphore */
|
|
|
|
/*
|
|
* External functions and forward references
|
|
*/
|
|
|
|
void addexpr( char *, int ); /* See QMATCH.C */
|
|
void addstring( char *, int ); /* See below */
|
|
int countlines( char *, char * );
|
|
char *findexpr( unsigned char *, char *); /* See QMATCH.C */
|
|
char *findlist( unsigned char *, char * );
|
|
char *findone( unsigned char *buffer, char *bufend );
|
|
void flush1buf( void ); /* See below */
|
|
void flush1nobuf( void ); /* See below */
|
|
int grepbuffer( char *, char *, char * ); /* See below */
|
|
int isexpr( unsigned char *, int ); /* See QMATCH.C */
|
|
void matchstrings( char *, char *, int, size_t *, int * );
|
|
size_t preveol( char * );
|
|
size_t strncspn( char *, char *, size_t );
|
|
size_t strnspn( char *, char *, size_t );
|
|
char *strnupr( char *pch, int cch );
|
|
void write1buf( char *, size_t ); /* See below */
|
|
void (*addstr)( char *, int ) = NULL;
|
|
char *(*find)( unsigned char *, char * ) = NULL;
|
|
void (*flush1)( void ) = flush1buf;
|
|
int (*grep)( char *, char *, char * ) = grepbuffer;
|
|
void (*write1)( char *, size_t ) = write1buf;
|
|
|
|
|
|
#include "windows.h"
|
|
|
|
|
|
void
|
|
ConvertAppToOem(
|
|
unsigned argc,
|
|
char* argv[]
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Converts the command line from ANSI to OEM, and force the app
|
|
to use OEM APIs
|
|
|
|
Arguments:
|
|
|
|
argc - Standard C argument count.
|
|
|
|
argv - Standard C argument strings.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
unsigned i;
|
|
|
|
for ( i=0; i<argc; i++ ) {
|
|
CharToOem( argv[i], argv[i] );
|
|
}
|
|
SetFileApisToOEM();
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
error(
|
|
char *mes
|
|
)
|
|
{
|
|
fprintf(stderr,"%s: %s\n",program,mes);
|
|
/* Print message */
|
|
exit(2); /* Die */
|
|
}
|
|
|
|
|
|
char *
|
|
alloc(
|
|
size_t size
|
|
)
|
|
{
|
|
char *cp; /* Char pointer */
|
|
|
|
if ((cp = malloc(size)) == NULL) { /* If allocation fails */
|
|
fprintf(stderr,"%s: Out of memory (%u)\n",program,waste);
|
|
/* Write error message */
|
|
exit(2); /* Die */
|
|
}
|
|
return(cp); /* Return pointer to buffer */
|
|
}
|
|
|
|
|
|
void
|
|
freenode(
|
|
STRINGNODE *x
|
|
)
|
|
{
|
|
register STRINGNODE *y; /* Pointer to next node in list */
|
|
|
|
while (x != NULL) { /* While not at end of list */
|
|
if (x->s_suf != NULL)
|
|
freenode(x->s_suf); /* Free suffix list if not end */
|
|
else
|
|
--strcnt; /* Else decrement string count */
|
|
y = x; /* Save pointer */
|
|
x = x->s_alt; /* Move down the list */
|
|
free((char *)((INT_PTR) s_text(y) & ~(sizeof(DWORD_PTR)-1)));
|
|
/* Free the node */
|
|
}
|
|
}
|
|
|
|
|
|
STRINGNODE *
|
|
newnode(
|
|
char *s,
|
|
size_t n
|
|
)
|
|
{
|
|
register STRINGNODE *new; /* Pointer to new node */
|
|
char *t; /* String pointer */
|
|
size_t d; /* rounds to a dword boundary */
|
|
|
|
d = n & (sizeof(DWORD_PTR)-1) ? sizeof(DWORD_PTR) - (n & (sizeof(DWORD_PTR)-1)) : 0; /* offset to next dword past n */
|
|
t = alloc(sizeof(STRINGNODE) + n + d);
|
|
/* Allocate string node */
|
|
t += d; /* END of string word-aligned */
|
|
strncpy(t,s,n); /* Copy string text */
|
|
new = (STRINGNODE *)(t + n); /* Set pointer to node */
|
|
new->s_alt = NULL; /* No alternates yet */
|
|
new->s_suf = NULL; /* No suffixes yet */
|
|
new->s_must = n; /* Set string length */
|
|
return(new); /* Return pointer to new node */
|
|
}
|
|
|
|
|
|
STRINGNODE *
|
|
reallocnode(
|
|
STRINGNODE *node,
|
|
char *s,
|
|
int n
|
|
)
|
|
{
|
|
register char *cp; /* Char pointer */
|
|
|
|
assert(n <= node->s_must); /* Node must not grow */
|
|
waste += (unsigned)(node->s_must - n);
|
|
/* Add in wasted space */
|
|
assert(sizeof(char *) == sizeof(INT_PTR));
|
|
/* Optimizer should eliminate this */
|
|
cp = (char *)((INT_PTR) s_text(node) & ~(sizeof(DWORD_PTR)-1));
|
|
/* Point to start of text */
|
|
node->s_must = n; /* Set new length */
|
|
if (n & (sizeof(DWORD_PTR)-1))
|
|
cp += sizeof(DWORD_PTR) - (n & (sizeof(DWORD_PTR)-1)); /* Adjust non dword-aligned string */
|
|
memmove(cp,s,n); /* Copy new text */
|
|
cp += n; /* Skip over new text */
|
|
memmove(cp,node,sizeof(STRINGNODE));/* Copy the node */
|
|
return((STRINGNODE *) cp); /* Return pointer to moved node */
|
|
}
|
|
|
|
|
|
/*** maketd1 - add entry for TD1 shift table
|
|
*
|
|
* This function fills in the TD1 table for the given
|
|
* search string. The idea is adapted from Daniel M.
|
|
* Sunday's QuickSearch algorithm as described in an
|
|
* article in the August 1990 issue of "Communications
|
|
* of the ACM". As described, the algorithm is suitable
|
|
* for single-string searches. The idea to extend it for
|
|
* multiple search strings is mine and is described below.
|
|
*
|
|
* Think of searching for a match as shifting the search
|
|
* pattern p of length n over the source text s until the
|
|
* search pattern is aligned with matching text or until
|
|
* the end of the source text is reached.
|
|
*
|
|
* At any point when we find a mismatch, we know
|
|
* we will shift our pattern to the right in the
|
|
* source text at least one position. Thus,
|
|
* whenever we find a mismatch, we know the character
|
|
* s[n] will figure in our next attempt to match.
|
|
*
|
|
* For some character c, TD1[c] is the 1-based index
|
|
* from right to left of the first occurrence of c
|
|
* in p. Put another way, it is the count of places
|
|
* to shift p to the right on s so that the rightmost
|
|
* c in p is aligned with s[n]. If p does not contain
|
|
* c, then TD1[c] = n + 1, meaning we shift p to align
|
|
* p[0] with s[n + 1] and try our next match there.
|
|
*
|
|
* Computing TD1 for a single string is easy:
|
|
*
|
|
* memset(TD1,n + 1,sizeof TD1);
|
|
* for (i = 0; i < n; ++i) {
|
|
* TD1[p[i]] = n - i;
|
|
* }
|
|
*
|
|
* Generalizing this computation to a case where there
|
|
* are multiple strings of differing lengths is trickier.
|
|
* The key is to generate a TD1 that is as conservative
|
|
* as necessary, meaning that no shift value can be larger
|
|
* than one plus the length of the shortest string for
|
|
* which you are looking. The other key is to realize
|
|
* that you must treat each string as though it were only
|
|
* as long as the shortest string. This is best illustrated
|
|
* with an example. Consider the following two strings:
|
|
*
|
|
* DYNAMIC PROCEDURE
|
|
* 7654321 927614321
|
|
*
|
|
* The numbers under each letter indicate the values of the
|
|
* TD1 entries if we computed the array for each string
|
|
* separately. Taking the union of these two sets, and taking
|
|
* the smallest value where there are conflicts would yield
|
|
* the following TD1:
|
|
*
|
|
* DYNAMICPODURE
|
|
* 7654321974321
|
|
*
|
|
* Note that TD1['P'] equals 9; since n, the length of our
|
|
* shortest string is 7, we know we should not have any
|
|
* shift value larger than 8. If we clamp our shift values
|
|
* to this value, then we get
|
|
*
|
|
* DYNAMICPODURE
|
|
* 7654321874321
|
|
*
|
|
* Already, this looks fishy, but let's try it out on
|
|
* s = "DYNAMPROCEDURE". We know we should match on
|
|
* the trailing procedure, but watch:
|
|
*
|
|
* DYNAMPROCEDURE
|
|
* ^^^^^^^|
|
|
*
|
|
* Since DYNAMPR doesn't match one of our search strings,
|
|
* we look at TD1[s[n]] == TD1['O'] == 7. Applying this
|
|
* shift, we get
|
|
*
|
|
* DYNAMPROCEDURE
|
|
* ^^^^^^^
|
|
*
|
|
* As you can see, by shifting 7, we have gone too far, and
|
|
* we miss our match. When computing TD1 for "PROCEDURE",
|
|
* we must take only the first 7 characters, "PROCEDU".
|
|
* Any trailing characters can be ignored (!) since they
|
|
* have no effect on matching the first 7 characters of
|
|
* the string. Our modified TD1 then becomes
|
|
*
|
|
* DYNAMICPODURE
|
|
* 7654321752163
|
|
*
|
|
* When applied to s, we get TD1[s[n]] == TD1['O'] == 5,
|
|
* leaving us with
|
|
*
|
|
* DYNAMPROCEDURE
|
|
* ^^^^^^^
|
|
* which is just where we need to be to match on "PROCEDURE".
|
|
*
|
|
* Going to this algorithm has speeded qgrep up on multi-string
|
|
* searches from 20-30%. The all-C version with this algorithm
|
|
* became as fast or faster than the C+ASM version of the old
|
|
* algorithm. Thank you, Daniel Sunday, for your inspiration!
|
|
*
|
|
* Note: if we are case-insensitive, then we expect the input
|
|
* string to be upper-cased on entry to this routine.
|
|
*
|
|
* Pete Stewart, August 14, 1990.
|
|
*/
|
|
|
|
void
|
|
maketd1(
|
|
unsigned char *pch,
|
|
UINT_PTR cch,
|
|
UINT_PTR cchstart
|
|
)
|
|
{
|
|
UINT_PTR ch; /* Character */
|
|
UINT_PTR i; /* String index */
|
|
|
|
if ((cch += cchstart) > cchmin)
|
|
cch = cchmin; /* Use smaller count */
|
|
for (i = cchstart; i < cch; ++i) { /* Examine each char left to right */
|
|
ch = *pch++; /* Get the character */
|
|
for (;;) { /* Loop to set up entries */
|
|
if (ch < chmin)
|
|
chmin = ch; /* Remember if smallest */
|
|
if (ch > chmax)
|
|
chmax = ch; /* Remember if largest */
|
|
if (cchmin - i < (UINT_PTR) td1[ch])
|
|
td1[ch] = (unsigned char)(cchmin - i);
|
|
/* Set value if smaller than previous */
|
|
if (casesen || !isascii((int)ch) || !isupper((int)ch))
|
|
break; /* Exit loop if done */
|
|
ch = tolower((int)ch); /* Force to lower case */
|
|
}
|
|
}
|
|
}
|
|
|
|
static int
|
|
newstring(
|
|
char *s,
|
|
size_t n
|
|
)
|
|
{
|
|
register STRINGNODE *cur; /* Current string */
|
|
register STRINGNODE **pprev; /* Pointer to previous link */
|
|
STRINGNODE *new; /* New string */
|
|
size_t i; /* Index */
|
|
size_t j; /* Count */
|
|
int k; /* Count */
|
|
|
|
if ( (UINT_PTR)n < cchmin)
|
|
cchmin = n; /* Remember length of shortest string */
|
|
if ((i = transtab[*s]) == 0) { /* If no existing list */
|
|
/*
|
|
* We have to start a new list
|
|
*/
|
|
if ((i = clists++) >= TRTABLEN/2) error("Too many string lists");
|
|
/* Die if too many string lists */
|
|
stringlist[i] = NULL; /* Initialize */
|
|
transtab[*s] = (char) i; /* Set pointer to new list */
|
|
if (!casesen && isalpha(*s)) transtab[*s ^ '\040'] = (char) i;
|
|
/* Set pointer for other case */
|
|
} else if (stringlist[i] == NULL) return(0);
|
|
/* Check for existing 1-byte string */
|
|
if (--n == 0) { /* If 1-byte string */
|
|
freenode(stringlist[i]); /* Free any existing stuff */
|
|
stringlist[i] = NULL; /* No record here */
|
|
++strcnt; /* We have a new string */
|
|
return(1); /* String added */
|
|
}
|
|
++s; /* Skip first char */
|
|
pprev = stringlist + i; /* Get pointer to link */
|
|
cur = *pprev; /* Get pointer to node */
|
|
while (cur != NULL) { /* Loop to traverse match tree */
|
|
i = (n > cur->s_must)? cur->s_must: n;
|
|
/* Find minimum of string lengths */
|
|
matchstrings(s,s_text(cur),i,&j,&k);
|
|
/* Compare the strings */
|
|
if (j == 0) { /* If complete mismatch */
|
|
if (k < 0) break; /* Break if insertion point found */
|
|
pprev = &(cur->s_alt); /* Get pointer to alternate link */
|
|
cur = *pprev; /* Follow the link */
|
|
} else if (i == j) { /* Else if strings matched */
|
|
if (i == n) { /* If new is prefix of current */
|
|
cur = *pprev = reallocnode(cur,s_text(cur),n);
|
|
/* Shorten text of node */
|
|
if (cur->s_suf != NULL) { /* If there are suffixes */
|
|
freenode(cur->s_suf);
|
|
/* Suffixes no longer needed */
|
|
cur->s_suf = NULL;
|
|
++strcnt; /* Account for this string */
|
|
}
|
|
return(1); /* String added */
|
|
}
|
|
pprev = &(cur->s_suf); /* Get pointer to suffix link */
|
|
if ((cur = *pprev) == NULL) return(0);
|
|
/* Done if current is prefix of new */
|
|
s += i; /* Skip matched portion */
|
|
n -= i;
|
|
} else /* Else partial match */ {
|
|
/*
|
|
* We must split an existing node.
|
|
* This is the trickiest case.
|
|
*/
|
|
new = newnode(s_text(cur) + j,cur->s_must - j);
|
|
/* Unmatched part of current string */
|
|
cur = *pprev = reallocnode(cur,s_text(cur),j);
|
|
/* Set length to matched portion */
|
|
new->s_suf = cur->s_suf; /* Current string's suffixes */
|
|
if (k < 0) { /* If new preceded current */
|
|
cur->s_suf = newnode(s + j,n - j);
|
|
/* FIrst suffix is new string */
|
|
cur->s_suf->s_alt = new;/* Alternate is part of current */
|
|
} else /* Else new followed current */ {
|
|
new->s_alt = newnode(s + j,n - j);
|
|
/* Unmatched new string is alternate */
|
|
cur->s_suf = new; /* New suffix list */
|
|
}
|
|
++strcnt; /* One more string */
|
|
return(1); /* String added */
|
|
}
|
|
}
|
|
*pprev = newnode(s,n); /* Set pointer to new node */
|
|
(*pprev)->s_alt = cur; /* Attach alternates */
|
|
++strcnt; /* One more string */
|
|
return(1); /* String added */
|
|
}
|
|
|
|
|
|
void
|
|
addstring(
|
|
char *s,
|
|
int n
|
|
)
|
|
{
|
|
int endline; /* Match-at-end-of-line flag */
|
|
register char *pch; /* Char pointer */
|
|
|
|
endline = flags & ENDLINE; /* Initialize flag */
|
|
pch = target; /* Initialize pointer */
|
|
while (n-- > 0) { /* While not at end of string */
|
|
switch (*pch = *s++) { /* Switch on character */
|
|
case '\\': /* Escape */
|
|
if (n > 0 && !isalnum(*s)) { /* If next character "special" */
|
|
--n; /* Decrement counter */
|
|
*pch = *s++; /* Copy next character */
|
|
}
|
|
++pch; /* Increment pointer */
|
|
break;
|
|
|
|
case '$': /* Special end character */
|
|
if (n == 0) { /* If end of string */
|
|
endline = ENDLINE; /* Set flag */
|
|
break; /* Exit switch */
|
|
}
|
|
/* Drop through */
|
|
|
|
default: /* All others */
|
|
++pch; /* Increment pointer */
|
|
break;
|
|
}
|
|
}
|
|
if (endline)
|
|
*pch++ = EOS; /* Add end character if needed */
|
|
targetlen = (int)(pch - target); /* Compute target string length */
|
|
if (!casesen)
|
|
strnupr(target, targetlen); /* Force to upper case if necessary */
|
|
newstring(target, targetlen); /* Add string */
|
|
}
|
|
|
|
|
|
int
|
|
addstrings(
|
|
char *buffer,
|
|
char *bufend,
|
|
char *seplist
|
|
)
|
|
{
|
|
size_t len; /* String length */
|
|
|
|
while (buffer < bufend) { /* While buffer not empty */
|
|
len = strnspn(buffer, seplist, (size_t)(bufend - buffer));
|
|
/* Count leading separators */
|
|
if ((buffer += len) >= bufend) {
|
|
break; /* Skip leading separators */
|
|
}
|
|
len = strncspn(buffer,seplist,(size_t)(bufend - buffer));
|
|
/* Get length of search string */
|
|
if (addstr == NULL) {
|
|
addstr = isexpr( buffer, len ) ? addexpr : addstring;
|
|
/* Select search string type */
|
|
}
|
|
if ( addstr == addexpr || (flags & BEGLINE) ||
|
|
findlist( buffer, buffer + len ) == NULL) { /* If no match within string */
|
|
(*addstr)(buffer,len); /* Add string to list */
|
|
}
|
|
buffer += len; /* Skip the string */
|
|
}
|
|
return(0); /* Keep looking */
|
|
}
|
|
|
|
|
|
int
|
|
enumlist(
|
|
STRINGNODE *node,
|
|
int cchprev
|
|
)
|
|
{
|
|
int i; /* Counter */
|
|
int strcnt; /* String count */
|
|
|
|
strcnt = 0; /* Initialize */
|
|
while (node != NULL) { /* While not at end of list */
|
|
maketd1(s_text(node),node->s_must,cchprev);
|
|
/* Make TD1 entries */
|
|
if (flags & DEBUG) { /* If verbose output wanted */
|
|
for (i = 0; i < cchprev; ++i)
|
|
fputc(' ',stderr); /* Indent line */
|
|
fwrite(s_text(node),sizeof(char),node->s_must,stderr);
|
|
/* Write this portion */
|
|
fprintf(stderr,"\n"); /* Newline */
|
|
}
|
|
strcnt += (node->s_suf != NULL)?
|
|
enumlist(node->s_suf,cchprev + node->s_must): 1;
|
|
/* Recurse to do suffixes */
|
|
node = node->s_alt; /* Do next alternate in list */
|
|
}
|
|
return(strcnt? strcnt: 1); /* Return string count */
|
|
}
|
|
|
|
|
|
int
|
|
enumstrings()
|
|
{
|
|
char ch; /* Character */
|
|
int i; /* Index */
|
|
int strcnt; /* String count */
|
|
|
|
strcnt = 0; /* Initialize */
|
|
for (i = 0; i < TRTABLEN; ++i) { /* Loop through translation table */
|
|
if (casesen || !isascii(i) || !islower(i)) { /* If case sensitive or not lower */
|
|
if (transtab[i] == 0)
|
|
continue; /* Skip null entries */
|
|
ch = (char) i; /* Get character */
|
|
maketd1(&ch,1,0); /* Make TD1 entry */
|
|
if (flags & DEBUG)
|
|
fprintf(stderr,"%c\n",i);
|
|
/* Print the first byte */
|
|
strcnt += enumlist(stringlist[transtab[i]],1);
|
|
/* Enumerate the list */
|
|
}
|
|
}
|
|
return(strcnt); /* Return string count */
|
|
}
|
|
|
|
HANDLE
|
|
openfile(
|
|
char *name
|
|
)
|
|
{
|
|
HANDLE fd; /* File descriptor */
|
|
DWORD er;
|
|
|
|
if ((fd = CreateFile( name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ) ) == (HANDLE)-1)
|
|
/* If error opening file */
|
|
{
|
|
er = GetLastError();
|
|
fprintf(stderr,"%s: Cannot open %s (error = %x)\n",program,name,er);
|
|
/* Print error message */
|
|
}
|
|
return( fd ); /* Return file descriptor */
|
|
}
|
|
|
|
void
|
|
thread2() /* Read thread */
|
|
{
|
|
for (;;) { /* Loop while there is work to do */
|
|
if ((WaitForSingleObject(readpending, (DWORD)-1L) != NO_ERROR)
|
|
|| (ResetEvent(readpending) != TRUE)) {
|
|
break; /* Exit loop if event error */
|
|
}
|
|
arrc = ReadFile(t2fd,(PVOID)t2buf,t2buflen, &cbread, NULL);
|
|
/* Do the read */
|
|
SetEvent( readdone ); /* Signal read completed */
|
|
}
|
|
error("Thread 2 semaphore error"); /* Print error message and die */
|
|
}
|
|
|
|
|
|
void
|
|
thread3() /* Write thread */
|
|
{
|
|
for (;;) { /* Loop while there is work to do */
|
|
if ((WaitForSingleObject(writepending,(DWORD)-1L) != NO_ERROR)
|
|
|| (ResetEvent(writepending) != TRUE)) {
|
|
break; /* Exit loop if event error */
|
|
}
|
|
awrc = WriteFile(t3fd,(PVOID)t3buf,t3buflen, &cbwrite, NULL);
|
|
/* Do the write */
|
|
SetEvent( writedone ); /* Signal write completed */
|
|
}
|
|
error("Thread 3 semaphore error"); /* Print error message and die */
|
|
}
|
|
|
|
|
|
void
|
|
startread(
|
|
HANDLE fd,
|
|
char *buffer,
|
|
int buflen
|
|
)
|
|
{
|
|
if ( asyncio ) { /* If asynchronous I/O */
|
|
if ((WaitForSingleObject(readdone,(DWORD)-1L) != NO_ERROR)
|
|
|| (ResetEvent(readdone) != TRUE)) {
|
|
error("read synch error"); /* Die if we fail to get semaphore */
|
|
}
|
|
t2fd = fd; /* Set parameters for read */
|
|
t2buf = buffer;
|
|
t2buflen = buflen;
|
|
SetEvent( readpending ); /* Signal read to start */
|
|
Sleep( 0L ); /* Yield the CPU */
|
|
} else {
|
|
arrc = ReadFile(fd,(PVOID)buffer,buflen, &cbread, NULL);
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
finishread()
|
|
{
|
|
if (asyncio) { /* If asynchronous I/O */
|
|
if ( WaitForSingleObject( readdone, (DWORD)-1L ) != NO_ERROR ) {
|
|
error("read wait error"); /* Die if wait fails */
|
|
}
|
|
}
|
|
return(arrc ? cbread : -1); /* Return number of bytes read */
|
|
}
|
|
|
|
|
|
void
|
|
startwrite(
|
|
HANDLE fd,
|
|
char *buffer,
|
|
int buflen
|
|
)
|
|
{
|
|
if (asyncio) { /* If asynchronous I/O */
|
|
if ((WaitForSingleObject(writedone,(DWORD)-1L) != NO_ERROR)
|
|
|| (ResetEvent(writedone) != TRUE)) {
|
|
error("write synch error"); /* Die if we fail to get semaphore */
|
|
}
|
|
t3fd = fd; /* Set parameters for write */
|
|
t3buf = buffer;
|
|
t3buflen = buflen;
|
|
SetEvent( writepending ); /* Signal read completed */
|
|
Sleep( 0L ); /* Yield the CPU */
|
|
} else {
|
|
awrc = WriteFile(fd,(PVOID)buffer,buflen, &cbwrite, NULL);
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
finishwrite()
|
|
{
|
|
if (asyncio) { /* If asynchronous I/O */
|
|
if ( WaitForSingleObject( writedone, (DWORD)-1L ) != NO_ERROR ) {
|
|
error("write wait error"); /* Die if wait fails */
|
|
}
|
|
}
|
|
return(awrc ? cbwrite : -1); /* Return number of bytes written */
|
|
}
|
|
|
|
|
|
void
|
|
write1nobuf(
|
|
char *buffer,
|
|
size_t buflen
|
|
)
|
|
{
|
|
CBIO cb; /* Count of bytes written */
|
|
|
|
if (!WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),(PVOID)buffer,buflen, &cb, NULL)
|
|
|| (cb != (CBIO)buflen))
|
|
{
|
|
error("Write error"); /* Die if write fails */
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
write1buf(
|
|
char *buffer,
|
|
size_t buflen
|
|
)
|
|
{
|
|
register size_t cb; /* Byte count */
|
|
|
|
while (buflen > 0) { /* While bytes remain */
|
|
if (!awrc) { /* If previous write failed */
|
|
fprintf(stderr,"%s: Write error %d\n",program,GetLastError());
|
|
/* Print error message */
|
|
exit(2); /* Die */
|
|
}
|
|
if ((cb = ocnt[oi]) == 0) { /* If buffer full */
|
|
startwrite( GetStdHandle( STD_OUTPUT_HANDLE ), obuf[oi], OUTBUFLEN );
|
|
/* Write the buffer */
|
|
ocnt[oi] = OUTBUFLEN; /* Reset count and pointer */
|
|
optr[oi] = obuf[oi];
|
|
oi ^= 1; /* Switch buffers */
|
|
cb = ocnt[oi]; /* Get space remaining */
|
|
}
|
|
if (cb > buflen)
|
|
cb = buflen; /* Get minimum */
|
|
memmove(optr[oi],buffer,cb); /* Copy bytes to buffer */
|
|
ocnt[oi] -= cb; /* Update buffer length and pointers */
|
|
optr[oi] += cb;
|
|
buflen -= cb;
|
|
buffer += cb;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
flush1nobuf(void)
|
|
{
|
|
}
|
|
|
|
|
|
void
|
|
flush1buf(void)
|
|
{
|
|
register int cb; /* Byte count */
|
|
|
|
if ((cb = OUTBUFLEN - ocnt[oi]) > 0) { /* If buffer not empty */
|
|
startwrite( GetStdHandle( STD_OUTPUT_HANDLE ), obuf[oi], cb ); /* Start write */
|
|
if (finishwrite() != cb) { /* If write failed */
|
|
fprintf(stderr,"%s: Write error %d\n",program,GetLastError());
|
|
/* Print error message */
|
|
exit(2); /* Die */
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
grepnull(
|
|
char *cp,
|
|
char *endbuf,
|
|
char *name
|
|
)
|
|
{
|
|
/* keep compiler happy */
|
|
cp = cp;
|
|
endbuf = endbuf;
|
|
name = name;
|
|
return(0); /* Do nothing */
|
|
}
|
|
|
|
|
|
int
|
|
grepbuffer(
|
|
char *startbuf,
|
|
char *endbuf,
|
|
char *name
|
|
)
|
|
{
|
|
char *cp; /* Buffer pointer */
|
|
char *lastmatch; /* Last matching line */
|
|
int linelen; /* Line length */
|
|
int namlen = 0; /* Length of name */
|
|
char lnobuf[LNOLEN]; /* Line number buffer */
|
|
char nambuf[PATHLEN];/* Name buffer */
|
|
|
|
cp = startbuf; /* Initialize to start of buffer */
|
|
lastmatch = cp; /* No previous match yet */
|
|
while ((cp = (*find)(cp,endbuf)) != NULL) { /* While matches are found */
|
|
--cp; /* Back up to previous character */
|
|
if ((flags & BEGLINE) && *cp != '\n') { /* If begin line conditions not met */
|
|
cp += strncspn(cp,"\n", (size_t)(endbuf - cp)) + 1;
|
|
/* Skip line */
|
|
continue; /* Keep looking */
|
|
}
|
|
status = 0; /* Match found */
|
|
if (flags & NAMEONLY) return(1); /* Return if filename only wanted */
|
|
cp -= preveol(cp) - 1; /* Point at start of line */
|
|
if (flags & SHOWNAME) { /* If name wanted */
|
|
if (namlen == 0) { /* If name not formatted yet */
|
|
if (flags & ZLINENOS)
|
|
namlen = sprintf(nambuf,"%s",name);
|
|
else
|
|
namlen = sprintf(nambuf,"%s:",name);
|
|
/* Format name if not done already */
|
|
}
|
|
(*write1)(nambuf, namlen); /* Show name */
|
|
}
|
|
if (flags & LINENOS) { /* If line number wanted */
|
|
lineno += countlines(lastmatch,cp);
|
|
/* Count lines since last match */
|
|
if (flags & ZLINENOS)
|
|
(*write1)(lnobuf,sprintf(lnobuf,"(%u) : ",lineno));
|
|
else
|
|
(*write1)(lnobuf,sprintf(lnobuf,"%u:",lineno));
|
|
/* Print line number */
|
|
lastmatch = cp; /* New last match */
|
|
}
|
|
if (flags & SEEKOFF) { /* If seek offset wanted */
|
|
(*write1)(lnobuf,sprintf(lnobuf,"%lu:",
|
|
cbfile + (long)(cp - startbuf)));
|
|
/* Print seek offset */
|
|
}
|
|
linelen = strncspn(cp,"\n",(size_t)(endbuf - cp)) + 1;
|
|
/* Calculate line length */
|
|
(*write1)(cp,linelen); /* Print the line */
|
|
cp += linelen; /* Skip the line */
|
|
}
|
|
if (flags & LINENOS)
|
|
lineno += countlines(lastmatch,endbuf);
|
|
/* Count remaining lines in buffer */
|
|
return(0); /* Keep searching */
|
|
}
|
|
|
|
|
|
void
|
|
showv(
|
|
char *name,
|
|
char *startbuf,
|
|
char *lastmatch,
|
|
char *thismatch
|
|
)
|
|
{
|
|
register int linelen;
|
|
int namlen = 0; /* Length of name */
|
|
char lnobuf[LNOLEN]; /* Line number buffer */
|
|
char nambuf[PATHLEN];/* Name buffer */
|
|
|
|
if (flags & (SHOWNAME | LINENOS | SEEKOFF)) {
|
|
while (lastmatch < thismatch) {
|
|
if (flags & SHOWNAME) { /* If name wanted */
|
|
if (namlen == 0) { /* If name not formatted yet */
|
|
if (flags & ZLINENOS)
|
|
namlen = sprintf(nambuf,"%s",name);
|
|
else
|
|
namlen = sprintf(nambuf,"%s:",name);
|
|
/* Format name if not done already */
|
|
}
|
|
(*write1)(nambuf,namlen);
|
|
/* Write the name */
|
|
}
|
|
if (flags & LINENOS) { /* If line numbers wanted */
|
|
if (flags & ZLINENOS)
|
|
(*write1)(lnobuf,sprintf(lnobuf,"(%u) : ",lineno++));
|
|
else
|
|
(*write1)(lnobuf,sprintf(lnobuf,"%u:",lineno++));
|
|
/* Print the line number */
|
|
}
|
|
if (flags & SEEKOFF) { /* If seek offsets wanted */
|
|
(*write1)(lnobuf,sprintf(lnobuf,"%lu:",
|
|
cbfile + (long)(lastmatch - startbuf)));
|
|
/* Print the line number */
|
|
}
|
|
linelen = strncspn(lastmatch,"\n",(size_t)(thismatch - lastmatch)) + 1;
|
|
(*write1)(lastmatch,linelen);
|
|
lastmatch += linelen;
|
|
}
|
|
} else (*write1)(lastmatch, (size_t)(thismatch - lastmatch));
|
|
}
|
|
|
|
|
|
int
|
|
grepvbuffer(
|
|
char *startbuf,
|
|
char *endbuf,
|
|
char *name
|
|
)
|
|
{
|
|
register char *cp; /* Buffer pointer */
|
|
register char *lastmatch; /* Pointer to line after last match */
|
|
|
|
cp = startbuf; /* Initialize to start of buffer */
|
|
lastmatch = cp;
|
|
while ((cp = (*find)(cp,endbuf)) != NULL) {
|
|
--cp; /* Back up to previous character */
|
|
if ((flags & BEGLINE) && *cp != '\n') { /* If begin line conditions not met */
|
|
cp += strncspn(cp,"\n", (size_t)(endbuf - cp)) + 1;
|
|
/* Skip line */
|
|
continue; /* Keep looking */
|
|
}
|
|
cp -= preveol(cp) - 1; /* Point at start of line */
|
|
if (cp > lastmatch) { /* If we have lines without matches */
|
|
status = 0; /* Lines without matches found */
|
|
if (flags & NAMEONLY) return(1);
|
|
/* Skip rest of file if NAMEONLY */
|
|
showv(name,startbuf,lastmatch,cp);
|
|
/* Show from last match to this */
|
|
}
|
|
cp += strncspn(cp,"\n",(size_t)(endbuf - cp)) + 1;
|
|
/* Skip over line with match */
|
|
lastmatch = cp; /* New "last" match */
|
|
++lineno; /* Increment line count */
|
|
}
|
|
if (endbuf > lastmatch) { /* If we have lines without matches */
|
|
status = 0; /* Lines without matches found */
|
|
if (flags & NAMEONLY) return(1); /* Skip rest of file if NAMEONLY */
|
|
showv(name,startbuf,lastmatch,endbuf);
|
|
/* Show buffer tail */
|
|
}
|
|
return(0); /* Keep searching file */
|
|
}
|
|
|
|
void
|
|
qgrep(
|
|
int (*grep)( char *, char *, char * ),
|
|
char *name,
|
|
HANDLE fd
|
|
)
|
|
{
|
|
int cb; /* Byte count */
|
|
register char *cp; /* Buffer pointer */
|
|
char *endbuf; /* End of buffer */
|
|
int taillen; /* Length of buffer tail */
|
|
int bufi; /* Buffer index */
|
|
|
|
cbfile = 0L; /* File empty so far */
|
|
lineno = 1; /* File starts on line 1 */
|
|
taillen = 0; /* No buffer tail yet */
|
|
bufi = 0; /* Initialize buffer index */
|
|
cp = bufptr[0]; /* Initialize to start of buffer */
|
|
finishread(); /* Make sure no I/O activity */
|
|
arrc = ReadFile(fd,(PVOID)cp,filbuflen, &cbread, NULL);
|
|
while ((cb = finishread()) + taillen > 0) { /* While search incomplete */
|
|
if (cb == 0) { /* If buffer tail is all that's left */
|
|
taillen = 0; /* Set tail length to zero */
|
|
*cp++ = '\r'; /* Add end of line sequence */
|
|
*cp++ = '\n';
|
|
endbuf = cp; /* Note end of buffer */
|
|
} else /* Else start next read */ {
|
|
taillen = preveol(cp + cb - 1);
|
|
/* Find length of partial line */
|
|
endbuf = cp + cb - taillen; /* Get pointer to end of buffer */
|
|
cp = bufptr[bufi ^ 1]; /* Pointer to other buffer */
|
|
memmove(cp,endbuf,taillen); /* Copy tail to head of other buffer */
|
|
cp += taillen; /* Skip over tail */
|
|
startread(fd,cp,(filbuflen - taillen) & (~0 << LG2SECLEN));
|
|
/* Start next read */
|
|
}
|
|
if ((*grep)(bufptr[bufi],endbuf,name)) { /* If rest of file can be skipped */
|
|
(*write1)(name,strlen(name));
|
|
/* Write file name */
|
|
(*write1)("\r\n",2); /* Write newline sequence */
|
|
return; /* Skip rest of file */
|
|
}
|
|
cbfile += (long)(endbuf - bufptr[bufi]);
|
|
/* Increment count of bytes in file */
|
|
bufi ^= 1; /* Switch buffers */
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
usage(
|
|
char *errmes
|
|
)
|
|
{
|
|
static const char szUsage[] =
|
|
{
|
|
"-? - print this message\n"
|
|
"-B - match pattern if at beginning of line\n"
|
|
"-E - match pattern if at end of line\n"
|
|
"-L - treat search strings literally (fgrep)\n"
|
|
"-O - print seek offset before each matching line\n"
|
|
"-X - treat search strings as regular expressions (grep)\n"
|
|
"-l - print only file name if file contains match\n"
|
|
"-n - print line number before each matching line\n"
|
|
"-z - print matching lines in MSC error message format\n"
|
|
"-v - print only lines not containing a match\n"
|
|
"-x - print lines that match exactly (-BE)\n"
|
|
"-y - treat upper and lower case as equivalent\n"
|
|
"-e - treat next argument literally as a search string\n"
|
|
"-f - read search strings from file named by next argument (- = stdin)\n"
|
|
"-i - read file list from file named by next argument (- = stdin)\n"
|
|
"White space separates search strings unless the argument is prefixed\n"
|
|
"with -e, e.g., 'qgrep \"all out\" x.y' means find either \"all\" or\n"
|
|
"\"out\" in x.y, while 'qgrep -e \"all out\" x.y' means find \"all out\".\n"
|
|
};
|
|
|
|
if (errmes != NULL)
|
|
fprintf(stderr,"%s: %s\n", program, errmes);
|
|
/* Print error message */
|
|
fprintf(stderr,"usage: %s [-?BELOXlnzvxy][-e string][-f file][-i file][strings][files]\n", program);
|
|
/* Print command line format */
|
|
if (errmes == NULL) { /* If verbose message wanted */
|
|
fputs(szUsage, stderr);
|
|
}
|
|
exit(2); /* Error exit */
|
|
}
|
|
|
|
char *
|
|
rmpath(
|
|
char *name
|
|
)
|
|
{
|
|
char *cp; /* Char pointer */
|
|
|
|
if (name[0] != '\0' && name[1] == ':') name += 2;
|
|
/* Skip drive spec if any */
|
|
cp = name; /* Point to start */
|
|
while (*name != '\0') { /* While not at end */
|
|
++name; /* Skip to next character */
|
|
if (name[-1] == '/' || name[-1] == '\\') cp = name;
|
|
/* Point past path separator */
|
|
}
|
|
return(cp); /* Return pointer to name */
|
|
}
|
|
|
|
|
|
int
|
|
__cdecl
|
|
main(
|
|
int argc,
|
|
char **argv
|
|
)
|
|
{
|
|
char *cp;
|
|
|
|
HANDLE fd;
|
|
DWORD dwTmp;
|
|
|
|
FILE *fi;
|
|
int i;
|
|
int j;
|
|
char *inpfile = NULL;
|
|
char *strfile = NULL;
|
|
unsigned long tstart, tend; /* Start time */
|
|
char filnam[FILNAMLEN];
|
|
|
|
tstart = clock(); /* Get start time */
|
|
|
|
ConvertAppToOem( argc, argv );
|
|
asyncio = pmode = 1; /* Do asynchronous I/O */
|
|
|
|
program = rmpath(argv[0]); /* Set program name */
|
|
memset(td1,1,TRTABLEN); /* Set up TD1 for startup */
|
|
flags = 0;
|
|
for (i = 1; i < argc && strchr("/-",argv[i][0]) != NULL; ++i) {
|
|
if (argv[i][1] == '\0' ||
|
|
strchr("?BELNOSXdlntvxyz",argv[i][1]) == NULL) break;
|
|
/* Break if unrecognized switch */
|
|
for (cp = &argv[i][1]; *cp != '\0'; ++cp) {
|
|
switch (*cp) {
|
|
case '?':
|
|
usage(NULL); /* Verbose usage message */
|
|
|
|
case 'B':
|
|
flags |= BEGLINE;
|
|
break;
|
|
|
|
case 'E':
|
|
flags |= ENDLINE;
|
|
break;
|
|
|
|
case 'L':
|
|
addstr = addstring; /* Treat strings literally */
|
|
break;
|
|
|
|
case 'N':
|
|
grep = grepnull;
|
|
break;
|
|
|
|
case 'O':
|
|
flags |= SEEKOFF;
|
|
break;
|
|
|
|
case 'S':
|
|
asyncio = 0; /* Force synchronous I/O */
|
|
break;
|
|
|
|
case 'X':
|
|
addstr = addexpr; /* Add expression to list */
|
|
break;
|
|
|
|
case 'd':
|
|
flags |= DEBUG;
|
|
break;
|
|
|
|
case 'l':
|
|
flags |= NAMEONLY;
|
|
break;
|
|
|
|
case 'n':
|
|
flags |= LINENOS;
|
|
break;
|
|
|
|
case 'z':
|
|
flags |= ZLINENOS | LINENOS;
|
|
break;
|
|
|
|
case 't':
|
|
flags |= TIMER;
|
|
break;
|
|
|
|
case 'v':
|
|
grep = grepvbuffer;
|
|
break;
|
|
|
|
case 'x':
|
|
flags |= BEGLINE | ENDLINE;
|
|
break;
|
|
|
|
case 'y':
|
|
casesen = 0;
|
|
break;
|
|
|
|
default:
|
|
fprintf(stderr,"%s: -%c ignored\n",program,*cp);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
for (; i < argc && argv[i][0] == '-'; ++i) {
|
|
if (argv[i][2] == '\0') { /* No multi-character switches */
|
|
switch (argv[i][1]) {
|
|
case 'e': /* Explicit string (no separators) */
|
|
if (++i == argc) usage("Argument missing after -e");
|
|
/* Argument missing after -e */
|
|
cp = argv[i]; /* Point to string */
|
|
addstrings( cp, cp + strlen(cp), "" );
|
|
/* Add string "as is" */
|
|
continue;
|
|
|
|
case 'F':
|
|
flags |= DOQUOTES; /* Handle quoted patterns in file */
|
|
case 'f': /* Patterns in file */
|
|
case 'i': /* Names of files to search in file */
|
|
if (i == argc - 1) usage("Argument missing after switch");
|
|
/* Argument missing */
|
|
if (argv[i++][1] == 'i') inpfile = argv[i];
|
|
else strfile = argv[i];
|
|
continue;
|
|
}
|
|
}
|
|
fprintf(stderr,"%s: %s ignored\n",program,argv[i]);
|
|
}
|
|
if (i == argc && strcnt == 0 && strfile == NULL) usage("Bad command line");
|
|
/* Usage message if arg error */
|
|
|
|
if (asyncio) { /* Initialize semaphores and threads */
|
|
if ( !( readdone = CreateEvent( NULL, TRUE, TRUE,NULL ) ) ||
|
|
!( readpending = CreateEvent( NULL, TRUE, FALSE,NULL ) ) ||
|
|
!( writedone = CreateEvent( NULL, TRUE, TRUE,NULL ) ) ||
|
|
!( writepending = CreateEvent( NULL, TRUE, FALSE,NULL ) ) ) {
|
|
error("Semaphore creation error");
|
|
}
|
|
if ((CreateThread( NULL,
|
|
STKLEN,
|
|
(LPTHREAD_START_ROUTINE)thread2,
|
|
NULL,
|
|
0,
|
|
(LPDWORD)&dwTmp)
|
|
== NULL)
|
|
||
|
|
(CreateThread( NULL,
|
|
STKLEN,
|
|
(LPTHREAD_START_ROUTINE)thread3,
|
|
NULL,
|
|
0,
|
|
(LPDWORD)&dwTmp)
|
|
== NULL)) {
|
|
error("Thread creation error");
|
|
}
|
|
/* Die if thread creation fails */
|
|
}
|
|
_setmode(_fileno(stdout),O_BINARY); /* No linefeed translation on output */
|
|
|
|
bufptr[0][-1] = bufptr[1][-1] = '\n';
|
|
/* Mark beginnings with newline */
|
|
|
|
if (_isatty(_fileno(stdout))) { /* If stdout is a device */
|
|
write1 = write1nobuf; /* Use unbuffered output */
|
|
flush1 = flush1nobuf;
|
|
}
|
|
|
|
if (strfile != NULL) { /* If strings from file */
|
|
if (strcmp(strfile,"-") != 0) { /* If strings not from std. input */
|
|
if ( ( fd = CreateFile( strfile, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL ) ) == (HANDLE)-1 ) { /* If open fails */
|
|
fprintf(stderr,"%s: Cannot read strings from %s\n",
|
|
program,strfile); /* Print message */
|
|
exit(2); /* Die */
|
|
}
|
|
} else {
|
|
fd = GetStdHandle( STD_INPUT_HANDLE ); /* Else use std. input */
|
|
}
|
|
qgrep( addstrings, "\r\n", fd );/* Do the work */
|
|
if ( fd != GetStdHandle( STD_INPUT_HANDLE ) ) {
|
|
CloseHandle( fd ); /* Close strings file */
|
|
}
|
|
}
|
|
else if (strcnt == 0) { /* Else if strings on command line */
|
|
cp = argv[i++]; /* Set pointer to strings */
|
|
addstrings(cp,cp + strlen(cp)," \t");
|
|
/* Add strings to list */
|
|
}
|
|
if (strcnt == 0) error("No search strings");
|
|
/* Die if no strings */
|
|
if (addstr != addexpr) { /* If not using expressions */
|
|
memset(td1,(int)(cchmin + 1),TRTABLEN);/* Initialize table */
|
|
find = findlist; /* Assume finding many */
|
|
if ((j = enumstrings()) != strcnt)
|
|
fprintf(stderr,"String count error (%d != %d)\n",j,strcnt);
|
|
/* Enumerate strings and verify count */
|
|
if (flags & DEBUG) { /* If debugging output wanted */
|
|
fprintf(stderr,"%u bytes wasted in heap\n",waste);
|
|
/* Print storage waste */
|
|
assert(chmin <= chmax); /* Must have some entries */
|
|
fprintf(stderr,
|
|
"chmin = %u, chmax = %u, cchmin = %u\n",chmin,chmax,cchmin);
|
|
/* Print range info */
|
|
for (j = (int)chmin; j <= (int)chmax; ++j) { /* Loop to print TD1 table */
|
|
if ( td1[j] <= (char)cchmin ) { /* If not maximum shift */
|
|
if (isascii(j) && isprint(j))
|
|
fprintf(stderr,"'%c'=%2u ",j,td1[j]);
|
|
/* Show literally if printable */
|
|
else fprintf(stderr,"\\%02x=%2u ",j,td1[j]);
|
|
/* Else show hex value */
|
|
}
|
|
}
|
|
fprintf(stderr,"\n");
|
|
}
|
|
if (strcnt == 1 && casesen)
|
|
find = findone; /* Find one case-sensitive string */
|
|
} else if (find == NULL) {
|
|
find = findexpr; /* Else find expressions */
|
|
}
|
|
if (inpfile != NULL) { /* If file list from file */
|
|
flags |= SHOWNAME; /* Always show name of file */
|
|
if (strcmp(inpfile,"-") != 0) { /* If name is not "-" */
|
|
if ((fi = fopen(inpfile,"r")) == NULL) { /* If open fails */
|
|
fprintf(stderr,"%s: Cannot read file list from %s\n",
|
|
program,inpfile); /* Error message */
|
|
exit(2); /* Error exit */
|
|
}
|
|
} else fi = stdin; /* Else read file list from stdin */
|
|
while (fgets(filnam,FILNAMLEN,fi) != NULL) { /* While there are names */
|
|
filnam[strcspn(filnam,"\r\n")] = '\0';
|
|
/* Null-terminate the name */
|
|
if ((fd = openfile(filnam)) == (HANDLE)-1) {
|
|
continue; /* Skip file if it cannot be opened */
|
|
}
|
|
qgrep(grep,filnam,fd); /* Do the work */
|
|
CloseHandle( fd );
|
|
}
|
|
if (fi != stdin) fclose(fi); /* Close the list file */
|
|
} else if (i == argc) {
|
|
flags &= ~(NAMEONLY | SHOWNAME);
|
|
qgrep( grep, NULL, GetStdHandle( STD_INPUT_HANDLE ) );
|
|
}
|
|
if (argc > i + 1) flags |= SHOWNAME;
|
|
for (; i < argc; ++i) {
|
|
_strlwr(argv[i]);
|
|
if ((fd = openfile(argv[i])) == (HANDLE)-1) {
|
|
continue;
|
|
}
|
|
qgrep(grep,argv[i],fd);
|
|
CloseHandle( fd );
|
|
}
|
|
(*flush1)();
|
|
if ( flags & TIMER ) { /* If timing wanted */
|
|
tend = clock();
|
|
tstart = tend - tstart; /* Get time in milliseconds */
|
|
fprintf(stderr,"%lu.%03lu seconds\n", ( tstart / CLK_TCK ), ( tstart % CLK_TCK ) );
|
|
/* Print total elapsed time */
|
|
}
|
|
|
|
return( status );
|
|
}
|
|
|
|
|
|
char *findsub(); /* Findlist() worker */
|
|
char *findsubi(); /* Findlist() worker */
|
|
|
|
|
|
char *(*flworker[])() =
|
|
{ /* Table of workers */
|
|
findsubi,
|
|
findsub
|
|
};
|
|
|
|
|
|
char *
|
|
strnupr(
|
|
char *pch,
|
|
int cch
|
|
)
|
|
{
|
|
while (cch-- > 0) { /* Convert string to upper case */
|
|
if (isascii(pch[cch]))
|
|
pch[cch] = (char)toupper(pch[cch]);
|
|
}
|
|
return(pch);
|
|
}
|
|
|
|
|
|
/*
|
|
* This is an implementation of the QuickSearch algorith described
|
|
* by Daniel M. Sunday in the August 1990 issue of CACM. The TD1
|
|
* table is computed before this routine is called.
|
|
*/
|
|
|
|
char *
|
|
findone(
|
|
unsigned char *buffer,
|
|
char *bufend
|
|
)
|
|
{
|
|
if ((bufend -= targetlen - 1) <= buffer)
|
|
return((char *) 0); /* Fail if buffer too small */
|
|
while (buffer < bufend) { /* While space remains */
|
|
int cch; /* Character count */
|
|
register char *pch1; /* Char pointer */
|
|
register char *pch2; /* Char pointer */
|
|
|
|
pch1 = target; /* Point at pattern */
|
|
pch2 = buffer; /* Point at buffer */
|
|
for (cch = targetlen; cch > 0; --cch) {
|
|
/* Loop to try match */
|
|
if (*pch1++ != *pch2++)
|
|
break; /* Exit loop on mismatch */
|
|
}
|
|
if (cch == 0)
|
|
return(buffer); /* Return pointer to match */
|
|
buffer += td1[buffer[targetlen]];
|
|
/* Skip ahead */
|
|
}
|
|
return((char *) 0); /* No match */
|
|
}
|
|
|
|
|
|
size_t
|
|
preveol(
|
|
char *s
|
|
)
|
|
{
|
|
register char *cp; /* Char pointer */
|
|
|
|
cp = s + 1; /* Initialize pointer */
|
|
while (*--cp != '\n') ; /* Find previous end-of-line */
|
|
return((size_t)(s - cp)); /* Return distance to match */
|
|
}
|
|
|
|
|
|
int
|
|
countlines(
|
|
char *start,
|
|
char *finish
|
|
)
|
|
{
|
|
register int count; /* Line count */
|
|
|
|
for (count = 0; start < finish; ) { /* Loop to count lines */
|
|
if (*start++ == '\n') ++count; /* Increment count if linefeed found */
|
|
}
|
|
return(count); /* Return count */
|
|
}
|
|
|
|
|
|
char *
|
|
findlist(
|
|
unsigned char *buffer,
|
|
char *bufend
|
|
)
|
|
{
|
|
char *match; /* Pointer to matching string */
|
|
char endbyte; /* First byte past end */
|
|
|
|
endbyte = *bufend; /* Save byte */
|
|
*bufend = '\177'; /* Mark end of buffer */
|
|
match = (*flworker[casesen])(buffer,bufend);
|
|
/* Call worker */
|
|
*bufend = endbyte; /* Restore end of buffer */
|
|
return(match); /* Return matching string */
|
|
}
|
|
|
|
|
|
char *
|
|
findsub(
|
|
unsigned char *buffer,
|
|
char *bufend
|
|
)
|
|
{
|
|
register char *cp; /* Char pointer */
|
|
STRINGNODE *s; /* String node pointer */
|
|
int i; /* Index */
|
|
|
|
if ((bufend -= cchmin - 1) < buffer)
|
|
return((char *) 0); /* Compute effective buffer length */
|
|
|
|
while (buffer < bufend) { /* Loop to find match */
|
|
if ((i = transtab[*buffer]) != 0) { /* If valid first character */
|
|
if ((s = stringlist[i]) == 0)
|
|
return(buffer); /* Check for 1-byte match */
|
|
for (cp = buffer + 1; ; ) { /* Loop to search list */
|
|
if ((i = memcmp(cp,s_text(s),s->s_must)) == 0) { /* If portions match */
|
|
cp += s->s_must; /* Skip matching portion */
|
|
if ((s = s->s_suf) == 0)
|
|
return(buffer); /* Return match if end of list */
|
|
continue; /* Else continue */
|
|
}
|
|
if (i < 0 || (s = s->s_alt) == 0)
|
|
break; /* Break if not in this list */
|
|
}
|
|
}
|
|
buffer += td1[buffer[cchmin]]; /* Shift as much as possible */
|
|
}
|
|
return((char *) 0); /* No match */
|
|
}
|
|
|
|
|
|
char *
|
|
findsubi(
|
|
unsigned char *buffer,
|
|
char *bufend
|
|
)
|
|
{
|
|
register char *cp; /* Char pointer */
|
|
STRINGNODE *s; /* String node pointer */
|
|
int i; /* Index */
|
|
|
|
if ((bufend -= cchmin - 1) < buffer)
|
|
return((char *) 0); /* Compute effective buffer length */
|
|
while (buffer < bufend) { /* Loop to find match */
|
|
if ((i = transtab[*buffer]) != 0) { /* If valid first character */
|
|
if ((s = stringlist[i]) == 0)
|
|
return(buffer); /* Check for 1-byte match */
|
|
for (cp = buffer + 1; ; ) { /* Loop to search list */
|
|
if ((i = _memicmp(cp,s_text(s),s->s_must)) == 0) { /* If portions match */
|
|
cp += s->s_must; /* Skip matching portion */
|
|
if ((s = s->s_suf) == 0)
|
|
return(buffer); /* Return match if end of list */
|
|
continue; /* And continue */
|
|
}
|
|
if (i < 0 || (s = s->s_alt) == 0)
|
|
break; /* Break if not in this list */
|
|
}
|
|
}
|
|
buffer += td1[buffer[cchmin]]; /* Shift as much as possible */
|
|
}
|
|
return((char *) 0); /* No match */
|
|
}
|
|
|
|
|
|
size_t
|
|
strnspn(
|
|
char *s,
|
|
char *t,
|
|
size_t n
|
|
)
|
|
{
|
|
char *s1; /* String pointer */
|
|
char *t1; /* String pointer */
|
|
|
|
for (s1 = s; n-- != 0; ++s1) { /* While not at end of s */
|
|
for (t1 = t; *t1 != '\0'; ++t1) { /* While not at end of t */
|
|
if (*s1 == *t1) break; /* Break if match found */
|
|
}
|
|
if (*t1 == '\0') break; /* Break if no match found */
|
|
}
|
|
return((size_t)(s1 - s)); /* Return length */
|
|
}
|
|
|
|
|
|
size_t
|
|
strncspn(
|
|
char *s,
|
|
char *t,
|
|
size_t n
|
|
)
|
|
{
|
|
char *s1; /* String pointer */
|
|
char *t1; /* String pointer */
|
|
|
|
for (s1 = s; n-- != 0; ++s1) { /* While not at end of s */
|
|
for (t1 = t; *t1 != '\0'; ++t1) { /* While not at end of t */
|
|
if (*s1 == *t1)
|
|
return((size_t)(s1 - s));
|
|
/* Return if match found */
|
|
}
|
|
}
|
|
return((size_t)(s1 - s)); /* Return length */
|
|
}
|
|
|
|
|
|
void
|
|
matchstrings(
|
|
char *s1,
|
|
char *s2,
|
|
int len,
|
|
size_t *nmatched,
|
|
int *leg
|
|
)
|
|
{
|
|
register char *cp; /* Char pointer */
|
|
register int (__cdecl *cmp)(const char*,const char*, size_t); /* Comparison function pointer */
|
|
|
|
cmp = casesen? strncmp: _strnicmp;
|
|
/* Set pointer */
|
|
if ((*leg = (*cmp)(s1,s2,len)) != 0) { /* If strings don't match */
|
|
for (cp = s1; (*cmp)(cp,s2++,1) == 0; ++cp)
|
|
;
|
|
/* Find mismatch */
|
|
*nmatched = (size_t)(cp - s1); /* Return number matched */
|
|
} else
|
|
*nmatched = len; /* Else all matched */
|
|
}
|