|
|
// FINDSTR (used to be QGREP), June 1992
//
// 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.
// June 1992 t-petes Added recursive file search in subdirs.
// Used file mapping instead of multi-thread.
// Disabled internal switches.
// Internatioanlized display messages.
// Made switches case-insensitive.
// 05/08/93 v-junm Added Japanese search support.
// 06/03/93 v-junm Added Bilingual Message support>
/* About FILEMAP support:
* The file mapping object is used to speed up string searches. The new * file mapping method is coded as #ifdef-#else-#endif to show the * changes needed to be made. The old code(non-filemapping) has a read * buffer like this: * * filbuf[] = {.....................................} * ^ ^ * BegPtr EndPtr * * This means there are some spare space before BegPtr and after EndPtr * for the search algorithm to work its way. The old code also * occasionally modifies filbuf[](like filbuf[i] = '\n';). * * The new code(filemapping) must avoid doing all of the above because * there are no spare space before BegPtr or after EndPtr when mapping * view of the file which is opened as read-only. */
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#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>
#include <locale.h>
#include <stdarg.h>
#include "fsmsg.h"
#define FILBUFLEN (SECTORLEN*2)
#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 (MAX_PATH+2) // 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
#define CURRENT_DIRECTORY_MAX_LENGTH 512
#define MAX_SLASH_C_OPTION 100
// 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 PRINTABLE_ONLY 0x100 // Skip files with non-printable characters
#define OFFLINE_FILES 0x200 // Do not skip offline files
#define DISPLAYBUFFER_SIZE 4096
// Type definitions
typedef struct stringnode { struct stringnode *s_alt; // List of alternates
struct stringnode *s_suf; // List of suffixes
int 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 *BaseByteAddress = NULL; // File mapping base address
BOOL bStdIn = FALSE; // Std-input file flag
BOOL bLargeFile = FALSE; // Dealing with non-memory mapped file
#ifdef FE_SB
BOOL IsDBCSCodePage = TRUE; #endif
char filbuf[FILBUFLEN*2L + 12]; char outbuf[OUTBUFLEN*2]; char td1[TRTABLEN] = { 0 }; unsigned cchmin = (unsigned)-1; // Minimum string length
unsigned chmax = 0; // Maximum character
unsigned chmin = (unsigned)-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 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
int 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
WORD wAttrib = 0; // filename color
CONSOLE_SCREEN_BUFFER_INFO csbi = {0}; // Our default screen info
CRITICAL_SECTION critSection; BOOLEAN fExiting = FALSE;
BOOLEAN fOfflineSkipped = FALSE; // Whether offline files were skipped
// External functions and forward references
void printmessage(FILE *fp, DWORD messagegID, ...); // Message display function for internationalization
int filematch(char *pszfile, char **ppszpat, int cpat, int fsubdirs);
#ifdef FE_SB
// Function to check if a certain location in a string is the second byte
// of a DBCS character.
int IsTailByte( unsigned const char *, const int ); int _mbsnicmp( const unsigned char *, const unsigned char *, int, BOOL * ); unsigned char *_mbslwr( unsigned char * ); char *_mbsrchr( const char *, int ); #endif
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, int *, int * ); int preveol( char * ); int strncspn( char *, char *, int ); int strnspn( char *, char *, int ); char *strnupr( char *pch, int cch ); void write1buf( char *, int, WORD ); // 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 *, int, WORD ) = write1buf; void write1nobuf( char *, int, WORD );
int has_wild_cards( char* p ) { if (!p) return 0;
for (; *p; p++) { if (*p == '?' || *p == '*') { return 1; } }
return 0; }
void error( DWORD messageID ) { printmessage(stderr, messageID, program); // Print message
exit(2); // Die
}
char * alloc( unsigned size ) { char *cp; // Char pointer
if ((cp = (char *) malloc(size)) == NULL) { // If allocation fails
printmessage(stderr, MSG_FINDSTR_OUT_OF_MEMORY, program); // 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(void *) - 1))); // Free the node
} }
STRINGNODE * newnode( char *s, int n ) { register STRINGNODE *newNode; // Pointer to new node
char *t; // String pointer
int d; // rounds to a dword boundary
d = n & (sizeof(void *) - 1) ? sizeof(void *) - (n & (sizeof(void *) - 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
newNode = (STRINGNODE *)(t + n); // Set pointer to node
newNode->s_alt = NULL; // No alternates yet
newNode->s_suf = NULL; // No suffixes yet
newNode->s_must = n; // Set string length
return(newNode); // 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)); // Optimizer should eliminate this
cp = (char *)((INT_PTR) s_text(node) & ~(sizeof(void *) - 1)); // Point to start of text
node->s_must = n; // Set new length
if (n & (sizeof(void *) - 1)) cp += sizeof(void *) - (n & (sizeof(void *) - 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, unsigned cch, unsigned cchstart ) { unsigned ch, ch1; // Character
unsigned i; // String index
unsigned char s[2];
s[1] = 0; 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 < (unsigned) td1[ch]) td1[ch] = (unsigned char)(cchmin - i); // Set value if smaller than previous
if (casesen || !isalpha(ch) || islower(ch)) break; // Exit loop if done
ch1 = ch; s[0] = (char)ch; ch = (unsigned char)(_strlwr((char*)s))[0]; // Force to lower case
if (ch1 == s[0]) // Lower case is the same to previous.
break; // Exit loop if done
} } }
static int newstring( unsigned char *s, int n ) { register STRINGNODE *cur; // Current string
register STRINGNODE **pprev; // Pointer to previous link
STRINGNODE *newNode; // New string
int i; // Index
int j; // Count
int k; // Count
unsigned char c[2];
c[1] = 0;
if ( (unsigned)n < cchmin) cchmin = n; // Remember length of shortest string
if ((i = (UCHAR)transtab[*s]) == 0) { // If no existing list
// We have to start a new list
if ((i = clists++) >= TRTABLEN/2) error(MSG_FINDSTR_TOO_MANY_STRING_LISTS); //"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)) { c[0] = *s; if ((unsigned char)(_strlwr((char*)c))[0] != *s || (unsigned char)(_strupr((char*)c))[0] != *s) transtab[c[0]] = (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((char *)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.
newNode = 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
newNode->s_suf = cur->s_suf; // Current string's suffixes
if (k < 0) { // If new preceded current
cur->s_suf = newnode((char *)s + j, n - j); // FIrst suffix is new string
cur->s_suf->s_alt = newNode;// Alternate is part of current
} else { // Else new followed current
newNode->s_alt = newnode((char *)(s + j), n - j); // Unmatched new string is alternate
cur->s_suf = newNode; // New suffix list
} ++strcnt; // One more string
return(1); // String added
} } *pprev = newnode((char *)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;
default: // All others
if (IsDBCSLeadByte(*pch)) { --n; ++pch; // Increment pointer
*pch = *s++; } ++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((unsigned char *)target, targetlen); // Add string
}
int addstrings( char *buffer, char *bufend, char *seplist ) { int len; // String length
char tmpbuf[MAXSTRLEN+2];
while(buffer < bufend) { // While buffer not empty
len = strnspn(buffer, seplist, (int)(bufend - buffer)); // Count leading separators
if ((buffer += len) >= bufend) { break; // Skip leading separators
} len = strncspn(buffer, seplist, (int)(bufend - buffer)); // Get length of search string
//
// We need to verify the length of the string before we call
// isexpr since the size of the buffer used is BUFLEN = 256
//
if (len >= MAXSTRLEN) error(MSG_FINDSTR_SEARCH_STRING_TOO_LONG);
if (addstr == NULL) { addstr = isexpr( (unsigned char *) buffer, len ) ? addexpr : addstring; // Select search string type
}
memcpy(tmpbuf, buffer, len); tmpbuf[len] = '\n'; tmpbuf[len+1] = 0;
if ( addstr == addexpr || (flags & BEGLINE) || findlist((unsigned char *)tmpbuf, tmpbuf + len + 1) == 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 strcnt; // String count
strcnt = 0; // Initialize
while(node != NULL) { // While not at end of list
maketd1((unsigned char *)s_text(node), node->s_must, cchprev); // Make TD1 entries
#if DBG
if (flags & DEBUG) { // If verbose output wanted
int i; // Counter
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
} #endif
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() { unsigned char ch; // Character
unsigned i; // Index
int strcnt; // String count
strcnt = 0; // Initialize
for(i = 0; i < TRTABLEN; ++i) { // Loop through translation table
if (casesen || !isalpha(i) || !islower(i)) { // If case sensitive or not lower
if (transtab[i] == 0) continue; // Skip null entries
ch = (char) i; // Get character
maketd1((unsigned char *)&ch, 1, 0); // Make TD1 entry
#if DBG
if (flags & DEBUG) fprintf(stderr, "%c\n", i); // Print the first byte
#endif
strcnt += enumlist(stringlist[transtab[i]], 1); // Enumerate the list
} } return (strcnt); // Return string count
}
HANDLE openfile( char *name ) { HANDLE fd; DWORD attr;
attr = GetFileAttributes(name);
if (attr != (DWORD) -1 && (attr & FILE_ATTRIBUTE_DIRECTORY)) return (HANDLE)-1;
// Skip offline files unless instructed otherwise
if (attr != (DWORD) -1 && (attr & FILE_ATTRIBUTE_OFFLINE) && !(flags & OFFLINE_FILES)) { fOfflineSkipped = TRUE; return (HANDLE)-1; }
if ((fd = CreateFile(name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_NO_RECALL, NULL)) == (HANDLE)-1) {
printmessage(stderr, MSG_FINDSTR_CANNOT_OPEN_FILE, program, name); } return( fd ); // Return file descriptor
}
void startread( HANDLE fd, char *buffer, int buflen ) { if (bStdIn || bLargeFile) { arrc = ReadFile(fd,(PVOID)buffer, buflen, &cbread, NULL); } }
int finishread() { return(arrc ? cbread : -1); // Return number of bytes read
}
void startwrite( HANDLE fd, char *buffer, int buflen) { awrc = WriteFile(fd,(PVOID)buffer, buflen, &cbwrite, NULL); return; }
int finishwrite() { return(awrc ? cbwrite : -1); // Return number of bytes written
}
BOOL CtrlHandler(DWORD CtrlType) { // We'll handle Ctrl-C events
switch(CtrlType) { case CTRL_C_EVENT: case CTRL_BREAK_EVENT: if (csbi.wAttributes) { EnterCriticalSection(&critSection); fExiting = TRUE; SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), csbi.wAttributes); LeaveCriticalSection(&critSection); } break; }
// Deal with all other events as normal
return (FALSE); }
void write1nobuf( char *buffer, int buflen, WORD wAttributes ) { int nT; CBIO cb; // Count of bytes written
BOOL fCR; BOOL fLF; char buf[STKLEN]; char *szT; static HANDLE hConOut = INVALID_HANDLE_VALUE; int remaining_length;
// Get the console screen buffer info if we haven't yet.
if (hConOut == INVALID_HANDLE_VALUE) { hConOut = GetStdHandle(STD_OUTPUT_HANDLE); InitializeCriticalSection(&critSection); SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE); }
if (wAttributes) { EnterCriticalSection(&critSection); // if not exiting, highlight the output
if (!fExiting) SetConsoleTextAttribute(hConOut, wAttributes); LeaveCriticalSection(&critSection); if (fExiting) ExitProcess(2); }
remaining_length = buflen;
while (remaining_length) {
buflen = (int)min(sizeof(buf) / sizeof(buf[0]), remaining_length); szT = buf;
if (IsDBCSCodePage) { memcpy(buf, buffer, buflen); } else { for(nT = 0; nT < buflen; nT++) { *(szT++) = ((isprint((unsigned char)buffer[nT]) || isspace((unsigned char)buffer[nT])) ? buffer[nT] : '.'); } }
if (!WriteFile(hConOut, (PVOID)buf, buflen, &cb, NULL) || (cb != (CBIO)(buflen))) { SetConsoleTextAttribute(hConOut, csbi.wAttributes); error(MSG_FINDSTR_WRITE_ERROR); // Die if write fails
} remaining_length -= buflen; buffer += buflen; }
if (wAttributes) SetConsoleTextAttribute(hConOut, csbi.wAttributes); }
void write1buf( char *buffer, int buflen, WORD wAttributes ) { register int cb; // Byte count
while(buflen > 0) { // While bytes remain
if (!awrc) { // If previous write failed
printmessage(stderr, MSG_FINDSTR_WRITE_ERROR, program); // 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
printmessage(stderr, MSG_FINDSTR_WRITE_ERROR, program); // Print error message
exit(2); // Die
} } }
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)((unsigned char *)cp, endbuf)) != NULL) { // While matches are found
--cp; // Back up to previous character
// Take care of '\n' as an artificial newline before line 1.
if ((flags & BEGLINE) && (bStdIn || bLargeFile || cp >= BaseByteAddress) && *cp != '\n' ) { // If begin line conditions not met
cp += strncspn(cp, "\n", (int)(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
namlen = sprintf(nambuf, "%s:", name); // Format name if not done already
} (*write1)(nambuf, namlen, wAttrib); // Show name
} if (flags & LINENOS) { // If line number wanted
lineno += countlines(lastmatch, cp); // Count lines since last match
(*write1)(lnobuf, sprintf(lnobuf, "%u:", lineno), wAttrib); // Print line number
lastmatch = cp; // New last match
} if (flags & SEEKOFF) { // If seek offset wanted
(*write1)(lnobuf, sprintf(lnobuf, "%lu:", cbfile + (long)(cp - startbuf)), wAttrib); // Print seek offset
} linelen = strncspn(cp, "\n", (int)(endbuf - cp)) + 1; // Calculate line length
if (linelen > endbuf - cp) { linelen = (int)(endbuf - cp); } (*write1)(cp, linelen, 0); // Print the line
cp += linelen; // Skip the line
} 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
namlen = sprintf(nambuf, "%s:", name); // Format name if not done already
} (*write1)(nambuf, namlen, wAttrib); // Write the name
} if (flags & LINENOS) // If line numbers wanted
{ (*write1)(lnobuf, sprintf(lnobuf, "%u:", lineno++), wAttrib); // Print the line number
} if (flags & SEEKOFF) { // If seek offsets wanted
(*write1)(lnobuf, sprintf(lnobuf, "%lu:", cbfile + (long)(lastmatch - startbuf)), wAttrib); // Print the line number
} linelen = strncspn(lastmatch, "\n", (int)(thismatch - lastmatch)); // If there's room for the '\n' then pull it in. Otherwise
// the buffer doesn't have a '\n' within the range here.
if (linelen < thismatch - lastmatch) { linelen++; } (*write1)(lastmatch, linelen, 0); lastmatch += linelen; } } else (*write1)(lastmatch, (int)(thismatch - lastmatch), 0); }
int grepvbuffer( char *startbuf, char *endbuf, char *name ) { char *cp; // Buffer pointer
char *lastmatch; // Pointer to line after last match
cp = startbuf; // Initialize to start of buffer
lastmatch = cp; while((cp = (*find)((unsigned char *)cp, endbuf)) != NULL) { --cp; // Back up to previous character
// Take care of '\n' as an artificial newline before line 1.
if ((flags & BEGLINE) && (bStdIn || bLargeFile || cp >= BaseByteAddress) && *cp != '\n') { // If begin line conditions not met
cp += strncspn(cp, "\n", (int)(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", (int)(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 ) { register int cb; // Byte count
char *cp; // Buffer pointer
char *endbuf; // End of buffer
int taillen; // Length of buffer tail
int bufi; // Buffer index
HANDLE MapHandle; // File mapping handle
BOOL grep_result;
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
bStdIn = (fd == GetStdHandle(STD_INPUT_HANDLE));
// If fd is not std-input, use file mapping object method.
if (!bStdIn) { DWORD cbread_high;
if ((((cbread = (CBIO)GetFileSize(fd, &cbread_high)) == -1) && (GetLastError() != NO_ERROR)) || (cbread == 0 && cbread_high == 0)) { return; // skip the file
}
if (cbread_high) { bLargeFile = TRUE; // too large to map and even if it succeed in mapping like under ia64, it
// will probably fail in pointer arithmetics
} else { MapHandle = CreateFileMapping(fd, NULL, PAGE_READONLY, 0L, 0L, NULL); if (MapHandle == NULL) { printmessage(stderr, MSG_FINDSTR_CANNOT_CREATE_FILE_MAPPING, program); return; }
BaseByteAddress = (char *) MapViewOfFile(MapHandle, FILE_MAP_READ, 0L, 0L, 0); CloseHandle(MapHandle); if (BaseByteAddress == NULL) { bLargeFile = TRUE; // use alternate method
} else { cp = bufptr[0] = BaseByteAddress; arrc = TRUE; } }
}
if (bStdIn || bLargeFile) { // Reset buffer pointers since they might have been changed.
cp = bufptr[0] = filbuf + 4;
arrc = ReadFile(fd, (PVOID)cp, FILBUFLEN, &cbread, NULL); }
if (flags & PRINTABLE_ONLY) { unsigned char *s; unsigned long n;
s = (unsigned char *)cp; n = cbread; while (--n) { if (*s < ' ') {
// If not backspace, tab, CR, LF, FF or Ctrl-Z then not a printable character.
if (strchr("\b\t\v\r\n\f\032", *s) == NULL) { goto skipfile; } }
s += 1; } }
// Note: if FILEMAP && !bStdIn, 'while' is executed once(taillen is 0).
while((cb = finishread()) + taillen > 0) { // While search incomplete
if (bStdIn || bLargeFile) { if (cb == -1) { // If buffer tail is all that's left
*cp++ = '\r'; // Add end of line sequence
*cp++ = '\n'; endbuf = cp; // Note end of buffer
taillen = 0; // Set tail length to zero
} 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
if (taillen > (FILBUFLEN/2)) { if (taillen >= FILBUFLEN) {
char tmp[15];
cbfile += taillen; taillen = 0; cp = bufptr[bufi^1]; startread(fd, cp, FILBUFLEN); _ultoa((unsigned long)lineno, tmp, 10); printmessage(stderr, MSG_FINDSTR_LINE_TOO_LONG, program, tmp); } else startread(fd, cp, (FILBUFLEN - taillen)); } else startread(fd, cp, (FILBUFLEN - taillen) & (~0 << LG2SECLEN)); // Start next read
} } else { endbuf = cp + cb - taillen; // Get pointer to end of buffer
// Cause 'while' to terminate(since no next read is needed.)
cbread = 0; arrc = TRUE; }
__try { grep_result = (*grep)(bufptr[bufi], endbuf, name); } __except( GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR ) { printmessage(stderr, MSG_FINDSTR_READ_ERROR, program, name); break; }
if (grep_result) { // If rest of file can be skipped
(*write1)(name, strlen(name), 0); // Write file name
(*write1)("\r\n", 2, 0); // Write newline sequence
if (!bStdIn && !bLargeFile) { if (BaseByteAddress != NULL) UnmapViewOfFile(BaseByteAddress); }
return; // Skip rest of file
}
cbfile += (long)(endbuf - bufptr[bufi]); // Increment count of bytes in file
bufi ^= 1; // Switch buffers
}
skipfile: if (!bStdIn && !bLargeFile) { if (BaseByteAddress != NULL) UnmapViewOfFile(BaseByteAddress); } }
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
}
void prepend_path( char* file_name, char* path ) { int path_len; char* last;
// First figure out how much of the path to take.
// Check for the last occurance of '\' if there is one.
#ifdef FE_SB
// DBCS tailbytes can contain '\' character. Use MBCS function.
last = _mbsrchr(path, '\\'); #else
last = strrchr(path, '\\'); #endif
if (last) { path_len = (int)(last - path) + 1; } else if (path[1] == ':') { path_len = 2; } else { path_len = 0; }
memmove(file_name + path_len, file_name, strlen(file_name) + 1); memmove(file_name, path, path_len); }
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(); }
int __cdecl main( int argc, char **argv ) { char *cp; char *cpaddstrings[MAX_SLASH_C_OPTION]; int add_string_count = 0; char *dirlist = NULL;
HANDLE fd;
FILE *fi; int fsubdirs; // Search subdirectories
int i; int j; char *inpfile = NULL; char *strfile = NULL; unsigned long tstart; // Start time
char filnam[MAX_PATH+1]; WIN32_FIND_DATA find_data; HANDLE find_handle; #ifdef FE_SB
LANGID LangId; #endif
char *locale; BOOLEAN option_L_specified = FALSE; BOOLEAN option_R_specified = FALSE;
ConvertAppToOem( argc, argv ); tstart = clock(); // Get start time
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi); // Default color: just add intensity
wAttrib = csbi.wAttributes | FOREGROUND_INTENSITY;
memset(cpaddstrings, 0, sizeof(cpaddstrings));
#ifdef FE_SB
//
// Set TEB's language ID to correspond to the console output code page. This
// will ensure the correct language message is displayed when FormatMessage is
// called.
//
switch (GetConsoleOutputCP()) { case 932: LangId = MAKELANGID( LANG_JAPANESE, SUBLANG_DEFAULT ); break; case 949: LangId = MAKELANGID( LANG_KOREAN, SUBLANG_KOREAN ); break; case 936: LangId = MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED ); break; case 950: LangId = MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL ); break; default: LangId = PRIMARYLANGID(LANGIDFROMLCID( GetUserDefaultLCID() )); if (LangId == LANG_JAPANESE || LangId == LANG_KOREAN || LangId == LANG_CHINESE ) { LangId = MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US ); } else { LangId = MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ); } IsDBCSCodePage = FALSE; break; }
SetThreadLocale( MAKELCID(LangId, SORT_DEFAULT) );
if ((locale = setlocale(LC_ALL, ".OCP")) == NULL) {
UINT Codepage;
if (Codepage = GetConsoleOutputCP()) {
char achCodepage[10];
wsprintfA(achCodepage, ".%3.4d", Codepage); if ((locale = setlocale(LC_ALL, achCodepage)) == NULL) { error(MSG_FINDSTR_UNABLE_TO_SET_LOCALE); } } else error(MSG_FINDSTR_UNABLE_TO_SET_LOCALE); } #endif
asyncio = pmode = 1; // Do asynchronous I/O
// program = rmpath(argv[0]); // Set program name
program ="FINDSTR";
memset(td1, 1, TRTABLEN); // Set up TD1 for startup
flags = 0;
_setmode(_fileno(stdout), O_BINARY); // No linefeed translation on output
_setmode(_fileno(stderr), O_BINARY); // No linefeed translation on output
fsubdirs = 0;
for(i = 1; i < argc && (argv[i][0] == '/' || argv[i][0] == '-'); ++i) { for(cp = &argv[i][1]; *cp != '\0'; ++cp) { switch(*cp) { case '?': printmessage(stdout, MSG_FINDSTR_USAGE, NULL); // Verbose usage message
exit(0);
case 'b': case 'B': flags |= BEGLINE; break;
case 'e': case 'E': flags |= ENDLINE; break;
case 'i': case 'I': casesen = 0; // case-insensitive search
break;
case 'l': case 'L': addstr = addstring; // Treat strings literally
option_L_specified = TRUE; break;
case 'm': case 'M': flags |= NAMEONLY; break;
case 'n': case 'N': flags |= LINENOS; break;
case 'o': case 'O': // Check whether this is an /o or /offline switch
if (0 == _stricmp(cp, "OFFLINE")) { flags |= OFFLINE_FILES; cp += (lstrlen( "OFFLINE" ) - 1); } else if (0 == _stricmp(cp, "OFF")) { flags |= OFFLINE_FILES; cp += (lstrlen( "OFF" ) - 1); } else { flags |= SEEKOFF; } break;
case 'p': case 'P': flags |= PRINTABLE_ONLY; break;
case 'r': case 'R': addstr = addexpr; // Add expression to list
option_R_specified = TRUE; break;
case 's': case 'S': fsubdirs = 1; break;
case 'v': case 'V': grep = grepvbuffer; break;
case 'x': case 'X': flags |= BEGLINE | ENDLINE; break;
#if DBG
case 'd': // This is kinda cheezy, but I didn't want to change the
// debug flag as it's been here a while and I couldn't come
// up with a different flag for the dirlist, so...
if (*(cp + 1) == ':') { *cp-- = 'D'; break; } flags |= DEBUG; break;
case 't': flags |= TIMER; break; #endif
default: { int cch; char chSwitch; char tmp[3];
chSwitch = *cp;
if (*(cp + 1) == ':') { if (!*(cp + 2)) { tmp[0]=chSwitch; tmp[1]='\0'; printmessage(stderr, MSG_FINDSTR_ARGUMENT_MISSING, program, tmp); exit(2); }
cp += 2; // Point to string
cch = lstrlen(cp); switch(chSwitch) { case 'd': case 'D': dirlist = cp; cp += cch - 1; continue;
case 'c': case 'C': // Add it after we've gone through all the flags
// don't add it now as things may change with
// later flags
addstr = addstring; // Treat strings literally
if (add_string_count >= MAX_SLASH_C_OPTION) { error(MSG_FINDSTR_TOO_MANY_SLASH_C_OPTION); } cpaddstrings[add_string_count++] = cp; cp += cch - 1; continue;
case 'g': case 'G': // Patterns in file
case 'f': case 'F': // Names of files to search in file
if (chSwitch == 'f' || chSwitch == 'F') inpfile = cp; else strfile = cp; cp += cch - 1; continue;
case 'a': case 'A': wAttrib = 0; for(; *cp && isxdigit(*cp); ++cp) {
int digit = (int) (*cp <= TEXT('9')) ? (int)*cp - (int)'0' : (int)tolower(*cp)-(int)'W';
wAttrib = (wAttrib << 4) + digit; } cp--; continue;
default: cp += cch - 1; // break out and spit out the switch ignored msg
break; } }
tmp[0]='/'; tmp[1]=chSwitch; tmp[2]='\0'; printmessage(stderr, MSG_FINDSTR_SWITCH_IGNORED, program, tmp); break; } } } } // for( i=1; )
if (option_L_specified && option_R_specified) error(MSG_FINDSTR_CONFLICTING_OPTIONS_LR); else if (option_L_specified) addstr = addstring; else if (option_R_specified) addstr = addexpr;
// Explicit string (no separators). Add string "as is"
if (add_string_count) { for (j=0; j<add_string_count && cpaddstrings[j]; j++) addstrings( cpaddstrings[j], cpaddstrings[j] + lstrlen(cpaddstrings[j]), "" ); }
if (i == argc && strcnt == 0 && strfile == NULL) error(MSG_FINDSTR_BAD_COMMAND_LINE);
bufptr[0][-1] = bufptr[1][-1] = '\n'; // Mark beginnings with newline
// Note: 4-Dec-90 w-barry Since there currently exists no method to query a
// handle with the Win32 api (no equivalent to
// DosQueryHType() ), the following piece of code
// replaces the commented section.
if (_isatty(_fileno(stdout))) { // If stdout is a device
write1 = write1nobuf; // Use unbuffered output
flush1 = flush1nobuf; }
// /*
// * Check type of handle for std. out.
// */
// if (DosQueryHType(fileno(stdout),(PPARM) &j,(PPARM) &fd) != NO_ERROR)
// {
// error("Standard output bad handle");
// }
// // Die if error
// if (j != 0 && (fd & ISCOT)) // If handle is console output
//#else
// filbuf[3] = '\n'; // Mark beginning with newline
// if (isatty(fileno(stdout))) // If stdout is a device
//#endif
// {
// write1 = write1nobuf; // Use unbuffered output
// flush1 = flush1nobuf;
// }
if (strfile != NULL) { // If strings from file
if ((strcmp(strfile, "/") != 0) && (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
printmessage(stderr, MSG_FINDSTR_CANNOT_READ_STRINGS, program, strfile); 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(MSG_FINDSTR_NO_SEARCH_STRINGS); // Die if no strings
if (addstr != addexpr) { // If not using expressions
memset(td1, cchmin + 1, TRTABLEN);// Initialize table
find = findlist; // Assume finding many
if ((j = enumstrings()) != strcnt) {
char t1[15], t2[15];
_itoa(j, t1, 10); _itoa(strcnt, t2, 10); printmessage(stderr, MSG_FINDSTR_STRING_COUNT_ERROR, t1, t2); }
// Enumerate strings and verify count
#if DBG
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"); } #endif
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) && (strcmp(inpfile, "-") != 0)) { if ((fi = fopen(inpfile, "r")) == NULL) { // If open fails
printmessage(stderr, MSG_FINDSTR_CANNOT_READ_FILE_LIST, program, inpfile); exit(2); // Error exit
} } else fi = stdin; // Else read file list from stdin
while(fgets(filnam, MAX_PATH+1, 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 || fsubdirs || has_wild_cards(argv[i])) flags |= SHOWNAME;
if (dirlist && *dirlist) { char *dir; char *dirend = (char *)-1; char *original_current_directory = NULL; DWORD size;
size = GetCurrentDirectory(0, NULL); if (size) { original_current_directory = (PCHAR)malloc(size); if (original_current_directory == NULL) { printmessage(stderr, MSG_FINDSTR_OUT_OF_MEMORY, program); exit(2); } size = GetCurrentDirectory(size, original_current_directory); }
if (!size) { free(original_current_directory); printmessage(stderr, MSG_FINDSTR_UNABLE_TO_GET_CURRENT_DIRECTORY, program); exit(2); }
for(dir = dirlist; dirend; dir = dirend + 1) {
if (dirend = strchr(dir, ';')) *dirend = 0;
if (*dir) { (*write1)(" ", 2, wAttrib); // Indent a couple of spaces
(*write1)(dir, lstrlen(dir), wAttrib); // Show name
(*write1)(":\r\n", 3, wAttrib); // Write newline sequence
if (!SetCurrentDirectory(original_current_directory)) { free(original_current_directory); printmessage(stderr, MSG_FINDSTR_CANNOT_OPEN_FILE, program, original_current_directory); exit(2); } if (!SetCurrentDirectory(dir)) { printmessage(stderr, MSG_FINDSTR_CANNOT_OPEN_FILE, program, dir); } else { while (filematch(filnam, argv + i, argc - i, fsubdirs) >= 0) { #ifdef FE_SB
// _mbslwr((unsigned char *)filnam);
#else
// _strlwr(filnam);
#endif
if ((fd = openfile(filnam)) != (HANDLE)-1) { qgrep(grep, filnam, fd); CloseHandle( fd ); } } } } } free(original_current_directory); } else if (fsubdirs && argc > i) { // If directory search wanted
while (filematch(filnam, argv + i, argc - i, fsubdirs) >= 0) { #ifdef FE_SB
// _mbslwr((unsigned char *)filnam);
#else
// _strlwr(filnam);
#endif
if ((fd = openfile(filnam)) == (HANDLE)-1) { continue; }
qgrep(grep, filnam, fd); CloseHandle( fd ); } } else { // Else search files specified
for(; i < argc; ++i) { #ifdef FE_SB
// _mbslwr((unsigned char *) argv[i]);
#else
// _strlwr(argv[i]);
#endif
find_handle = FindFirstFile(argv[i], &find_data); if (find_handle == INVALID_HANDLE_VALUE) { printmessage(stderr, MSG_FINDSTR_CANNOT_OPEN_FILE, program, argv[i]); continue; }
do {
#ifdef FE_SB
// _mbslwr((unsigned char *)find_data.cFileName);
#else
// _strlwr(find_data.cFileName);
#endif
prepend_path(find_data.cFileName, argv[i]); fd = openfile(find_data.cFileName);
if (fd != INVALID_HANDLE_VALUE) { qgrep(grep, find_data.cFileName, fd); CloseHandle( fd ); } } while (FindNextFile(find_handle, &find_data)); } }
(*flush1)();
#if DBG
if ( flags & TIMER ) { // If timing wanted
unsigned long tend;
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
} #endif
// Print warning in case that offline files were skipped
if (fOfflineSkipped) { printmessage(stderr, MSG_FINDSTR_OFFLINE_FILE_SKIPPED, program); }
return( status ); } // main
char * findsub( unsigned char *, char * ); char * findsubi( unsigned char *, char * );
char * (*flworker[])(unsigned char *, char *) = { // Table of workers
findsubi, findsub };
char * strnupr( char *pch, int cch ) { char c[2];
#ifdef FE_SB
int max = cch; c[1] = 0; for ( cch = 0; cch < max; cch++ ) { #else
c[1] = 0; while (cch-- > 0) { // Convert string to upper case
#endif
if (isalpha((unsigned char)pch[cch])) { c[0] = pch[cch]; pch[cch] = (_strupr(c))[0]; } #ifdef FE_SB
else if (IsDBCSCodePage && IsDBCSLeadByte(pch[cch])) cch++; #endif
} 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 ) { #ifdef FE_SB // Starting position of string for checking 2nd bytes of DBCS characters.
unsigned char *bufferhead = buffer; #endif
if ((bufend -= targetlen - 1) <= (char *) buffer) return((char *) 0); // Fail if buffer too small
while (buffer < (unsigned char *) bufend) { // While space remains
int cch; // Character count
register char *pch1; // Char pointer
register char *pch2; // Char pointer
pch1 = target; // Point at pattern
pch2 = (char *) buffer; // Point at buffer
#ifdef FE_SB
// If buffer points to the 2nd byte of a DBCS character,
// skip to next compare position.
if ( !IsTailByte( bufferhead, (int)(buffer - bufferhead) ) ) { #endif
for (cch = targetlen; cch > 0; --cch) { // Loop to try match
if (*pch1++ != *pch2++) break; // Exit loop on mismatch
} if (cch == 0) return((char *)buffer); // Return pointer to match
#ifdef FE_SB
} #endif
if (buffer + 1 < (unsigned char *) bufend) // Make sure buffer[targetlen] is valid.
buffer += ((unsigned char)td1[buffer[targetlen]]); // Skip ahead
else break; } return((char *) 0); // No match
}
int preveol( char *s ) { register char *cp; // Char pointer
cp = s + 1; // Initialize pointer
if (!bStdIn && !bLargeFile) { while((--cp >= BaseByteAddress) && (*cp != '\n')) ; // Find previous end-of-line
} else { while(*--cp != '\n') ; // Find previous end-of-line
}
return (int)(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
// Avoid writting to bufend. bufend[-1] is something(such as '\n') that is not
// part of search and will cause the search to stop.
match = (*flworker[casesen])(buffer, bufend); // Call worker
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
#ifdef FE_SB // Head of buffer for checking if a certain offset is the 2nd byte of a DBCS character.
unsigned char *bufhead = buffer; #endif
char *real_bufend = bufend;
if (cchmin != (unsigned)-1 && cchmin != 0 && (bufend -= cchmin - 1) < (char *) buffer) return((char *) 0); // Compute effective buffer length
while(buffer < (unsigned char *) bufend) { // Loop to find match
#ifdef FE_SB
// Search cannot start at the second byte of a DBCS character,
// so check for it and skip it if it is a second byte.
if ((i = (UCHAR)transtab[*buffer]) != 0 && !IsTailByte( bufhead, (int)(buffer-bufhead) ) ) { #else
if ((i = (UCHAR)transtab[*buffer]) != 0) { #endif
// If valid first character
if ((s = stringlist[i]) == 0) { return((char *)buffer); // Check for 1-byte match
}
for(cp = (char *) buffer + 1; (real_bufend - cp) >= s->s_must; ) { // Loop to search list
if ((i = _strncoll(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((char *)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
} } }
if (buffer + 1 < (unsigned char *) bufend) // Make sure buffer[cchmin] is valid.
if (cchmin == (unsigned)-1) buffer++; else buffer += ((unsigned char)td1[buffer[cchmin]]); // Shift as much as possible
else break; } 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
#ifdef FE_SB
// Keep head of buffer for checking if a certain offset is the 2nd byte of
// a DBCS character.
unsigned char *bufhead = buffer; #endif
if (cchmin != (unsigned)-1 && cchmin != 0 && (bufend -= cchmin - 1) < (char *) buffer) return((char *) 0); // Compute effective buffer length
while(buffer < (unsigned char *) bufend) { // Loop to find match
#ifdef FE_SB
// Search cannot start at the second byte of a DBCS character, so check for it
// and skip it if it is a second byte.
if ((i = (UCHAR)transtab[*buffer]) != 0 && !IsTailByte( bufhead, (int)(buffer-bufhead) ) ) { // If valid first character
BOOL TailByte; // Flag to check if 1st char is leadbyte.
#else
if ((i = (UCHAR)transtab[*buffer]) != 0) { // If valid first character
#endif
if ((s = stringlist[i]) == 0) return((char *) buffer); // Check for 1-byte match
#ifdef FE_SB
// Same leadbytes with tailbytes such as 0x41 and 0x61 will become the same
// character, so become aware of it and use the multibyte function.
//
// Check if buffer+1 is a tailbyte character.
//
TailByte = IsTailByte(buffer, 1);
for(cp = (char *) buffer + 1; ; ) { // Loop to search list
if ((i = _mbsnicmp((unsigned char *)cp, (unsigned char *) s_text(s), s->s_must, &TailByte)) == 0) { #else
for(cp = (char *) buffer + 1; ; ) { // Loop to search list
if ((i = memicmp(cp, s_text(s), s->s_must)) == 0) { #endif
// If portions match
cp += s->s_must; // Skip matching portion
if ((s = s->s_suf) == 0) return((char *) 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
} }
if (buffer + 1 < (unsigned char *) bufend) // Make sure buffer[cchmin] is valid.
if (cchmin == (unsigned)-1) buffer++; else buffer += ((unsigned char)td1[buffer[cchmin]]); // Shift as much as possible
else break; } return((char *) 0); // No match
}
int strnspn( char *s, char *t, int n ) /*
Description:
Finds the position of the first character in s of length n that is not in the character set t.
Argument:
s - string to search from. t - character set to search for n - length of s
Returns:
Returns the offset of the first character in s that is not in t */ { register char *s1; // String pointer
register 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 (int)(s1 - s); // Return length
}
int strncspn( char *s, char *t, int n ) /*
Description:
Finds the position of the first occurence of characters in t in string s of length n.
Argument:
s - string to search from. t - character set to search for n - length of s
Returns:
Returns first offset position in s that consists of characters in t Returns length of s if not found. */ { register char *s1; // String pointer
register 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 (int)(s1 - s); // Return if match found
} } return (int)(s1 - s); // Return length
}
void matchstrings( char *s1, char *s2, int len, int *nmatched, int *leg ) { register char *cp; // Char pointer
register int (__cdecl *cmp)(const char*, const char*, size_t); // Comparison function pointer
cmp = casesen ? _strncoll: _strnicoll; // 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 = (int)(cp - s1); // Return number matched
} else *nmatched = len; // Else all matched
}
void printmessage ( FILE* fp, DWORD messageID, ... ) { char messagebuffer[DISPLAYBUFFER_SIZE]; WCHAR widemessagebuffer[DISPLAYBUFFER_SIZE]; ULONG len; NTSTATUS status;
va_list ap;
va_start(ap, messageID);
if (len = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE, NULL, messageID, 0, messagebuffer, DISPLAYBUFFER_SIZE, &ap)) { // the messagebuffer should be null terminated
status = RtlMultiByteToUnicodeN(widemessagebuffer, DISPLAYBUFFER_SIZE*sizeof(WCHAR), &len, messagebuffer, len); // the widemessagebuffer is not null terminated but len tells us how long
if (NT_SUCCESS(status)) { status = RtlUnicodeToOemN(messagebuffer, DISPLAYBUFFER_SIZE-1, &len, widemessagebuffer, len); // the messagebuffer is not null terminated but len tells us how long
if (NT_SUCCESS(status)) { messagebuffer[len] = 0; fprintf(fp, "%s", messagebuffer); } else { DbgPrint("Failure to convert Unicode to Oem: %d\n", GetLastError()); } } else { DbgPrint("Failure to convert MultiByte to Unicode: %d\n", GetLastError()); } } else { DbgPrint("FormatMessage failed: %d\n", GetLastError()); }
va_end(ap); }
#ifdef FE_SB
int IsTailByte( unsigned const char *text, const int offset )
/*
Description:
This routine checks to see if the byte at the offset location is a tail byte of a DBCS character. The offset is calculated such that the first location has a value of 0.
Argument:
text - Points to a MBCS text string.
offset - zero base offset to check character is a tailbyte of a DBCS character.
Returns:
TRUE - offset position is a tailbyte character.
FALSE - otherwise.
Modifications:
v-junm: 05/06/93 - Original. */
{ int i = offset;
if ( !IsDBCSCodePage ) return( FALSE );
for ( ; i; i-- ) if ( !IsDBCSLeadByte ( text[i-1] ) ) break;
return( ( offset - i ) % 2 ); }
char * _mbsrchr( const char *string, int c )
/*
Description:
This function is a DBCS enabled version of the STRRCHR function included in the MS C/C++ library. What DBCS enabled means is that the SBCS character 'c' is found in a MBCS string 'string'. 'c' is a SBCS character that cannot be contained in the tailbyte of a DBCS character.
Argument:
string - Points to a MBCS text string.
offset - Character to find in string.
Returns:
Returns a pointer to the last occurance of c in string, or a NULL pointer if c is not found.
Modifications:
v-junm: 05/06/93 - Original. */
{ register int i = strlen( string );
for (; i >= 0; i-- ) { if ( ( *(string + i) == (char)c ) && !IsTailByte( (unsigned char *) string, i ) ) return( (char*)(string + i) ); } return ( NULL ); }
unsigned char * _mbslwr( unsigned char *s )
/*
Description:
This function is a DBCS aware version of the strlwr function included in the MS C/C++ library. SBCS alphabets contained in the tailbyte of a DBCS character is not affected in the conversion.
Argument:
s - String to converted to lower case.
Returns:
Returns a string that was converted to lower case.
Modifications:
v-junm: 05/06/93 - Original. */
{ //
// If NonJP code page, use original routine.
//
if ( !IsDBCSCodePage ) return( (unsigned char *) _strlwr( (char *) s ) );
//
// While not end of string convert to lower case.
//
for( ; *s; s++ ) {
//
// if Leadbyte and next character is not NULL
// skip tailbyte
// else if uppercase character
// convert it to lowercase
//
if ( IsDBCSLeadByte( *s ) && *(s+1) ) s++; else if ( *s >= 0x41 && *s <= 0x5a ) *s = *s + 0x20; } return( s ); }
int _mbsnicmp( const unsigned char *s1, const unsigned char *s2, int n, BOOL *TailByte )
/*
Description:
This is similar to a DBCS aware version of the memicmp function contained in the MS C/C++ library. The only difference is that an additional parameter is passed which indicates if the first character is a tailbyte of a DBCS character.
Argument:
s1 - string 1 to compare.
s2 - string 2 to compare.
n - maximum number of bytes to compare.
TailByte - flag to indicate first character in s1 and s2 is a tailbyte of a DBCS character.
Returns:
RetVal < 0 - s1 < s2
RetVal = 0 - s1 == s2
RetVal > 0 - s1 > s2
Modifications:
v-junm: 05/06/93 - Original. */
{ BOOL tail = *TailByte; int i;
*TailByte = FALSE;
for( ; n; n--, s1++, s2++ ) {
if ( *s1 == *s2 ) {
if ( tail == FALSE && IsDBCSLeadByte( *s1 ) ) tail = TRUE; else tail = FALSE;
continue;
} else if ( !tail ) {
i = _strnicoll((char *)s1, (char *)s2, 1); if (i == 0) continue; return i; }
return( *s1 - *s2 ); }
*TailByte = tail;
return( 0 ); }
#endif
|