| Template:
The command has the following template:
ROOT/A/M Where to start
searching from.
May be multiple root sources - with wild-cards
ex : FSearch ram: dh0:mydir#? all
List all files in all dirs in ram: and all
files in all dirs matching dh0:mydir#?
This option must be given.
All others are optional.
PAT/K
(string) File pattern to match
Only files meeting this pattern will be searched
ex : FSearch ram: pat=#?.info all
List all info files in ram: or it's sub-dirs
TXT/K
(string) Text to search for.
If this option is not specified FSearch will act
like "list" and will not search the files.
ex : FSearch ram: txt="my name" all
Note: By default the search is case-insensitive.
FROM/K (date)
Only search files FROM this date and up.
Dates must be written as dd-mm-yy.
ex : FSearch ram: from=07-08-96
TO/K
(date) Only search files UP to this date
MIN/N/K (number)
Only search files more than MIN Kb.
ex : FSearch ram: min=100 (look for files >= 100k)
MAX/N/K (number)
Only search files less than MAX Kb.
HEADER/K (string) Only
search files which have this header.
The Header specification may contain wild cards.
The first 100 bytes of the file will be loaded
and checked against this Header.
ex : FSearch ram: header=FORM????ILBM#? all
will list all iff pictures in ram:
ALL/S
Recursively scan sub-directories
CS=CASESENSITIVE/S
Make the search case-sensitive
V=VERBOSE/S Print Line number & text
of lines found.
The default is to just print the file name on the
first occurance of the TXT you're looking for.
With this option all the matches and their
context will be displayed. If the line length is
over 80 chars it will be clipped.
NOBIN/S Do not check
binary files. Up to 100 bytes of the
file will be loaded and file will be skipped if
this header contains any non-ascii characters.
HL=HIGHLIGHT/S Highlight the search results
INFO/S Print
file size/date/time next to it's name
No file paths are printed if this option is on.
NOPATH/S Print only the
file name - not the full path.
The Source:
/*---------------------------------------------------------------------------*/
/* Command : fsearch.c
*/
/*
fast, pure CLI command for listing/searching files.
*/
/*
*/
/* Author : Dimitri Keletsekis
*/
/*
Parts of the recursive dir scanning and argument parsing
*/
/*
used in here are based on the cat example in the SAS C pack,
*/
/*
but have been altered as needed.
*/
/*
The program is just one function with no global variables
*/
/*
so its pure and can be made resident. Also, its compiled with
*/
/*
the NOSTARTUP code option making the binary very small.
*/
/*
*/
/*
To compile (with SAS C v6.50+), place this file and the
*/
/*
SCOPTIONS file in a dir, open a shell, CD to that dir and
*/
/*
write "sc fsearch link"
*/
/*
*/
/* Date : Athens, 7 September 97
*/
/*
*/
/*---------------------------------------------------------------------------*/
#define __USE_SYSBASE
#include <exec/types.h>
#include <exec/execbase.h>
#include <exec/memory.h>
#include <dos/dosextens.h>
#include <dos/rdargs.h>
#include <string.h>
#include <proto/dos.h>
#include <proto/exec.h>
#define THISPROC ((struct Process *)(SysBase->ThisTask))
#define Result2(x) THISPROC->pr_Result2 = x
static const char VERSION[] = "\0$VER: FSearch 1.1";
// The program's options
#define TEMPLATE "ROOT/A/M,PAT/K,TXT/K,FROM/K,TO/K,MIN/N/K,MAX/N/K,HEADER/K,ALL/S,CS=CASESENSITIVE/S,V=VERBOSE/S,NOBIN/S,HL=HIGHLIGHT/S,NOPATH/S,INFO/S"
#define OPT_ROOT 0 //A/M, multiple,
wildcards - dir(s) to search
#define OPT_PAT 1 //K,
file pattern to search - ex - PAT=#?.h
#define OPT_TXT 2 //K,
text to search for - ex - TXT="some string"
#define OPT_FROM 3 //K,
search files over this date - FROM=25/3/96
#define OPT_TO 4 //K,
search files up to this date
#define OPT_MIN 5 //N/K,
search files of this minimum size (in Kb)
#define OPT_MAX 6 //N/K,
search files of maximum this size
#define OPT_HEADER 7 //K, search
files starting with this header
#define OPT_ALL 8 //S,
recurse sub-directories
#define OPT_CS 9 //=CASESENSITIVE/S,
search case sensitive
#define OPT_V 10 //=VERBOSE/S,
print text found
#define OPT_NOBIN 11 //S, do
not search binary files
#define OPT_HL 12 //S,
Highlight filename & linenumbers
#define OPT_NOPATH 13 //S, do not
print the file's full path
#define OPT_INFO 14 //S,
print size/date etc next to file name
#define OPT_COUNT 15 // the number of options
// ------- escape sequences for changing color of text output
#define SET_WHITE Write (outpt,"›32m", 4)
#define SET_BLUE Write (outpt,"›33m", 4)
#define SET_BLACK Write (outpt,"›31m", 4)
// ------------------------ Structure definition ---------------------
// Altered AnchorPath structure with enlarged ap_Buf for storing
// the full path & file name - set ap_Strlen to indicate it.
struct MyAnchorPath {
struct AChain
*ap_Base; /* pointer to first anchor
*/
#define ap_First ap_Base
struct AChain
*ap_Last; /* pointer to last anchor
*/
#define ap_Current ap_Last
LONG
ap_BreakBits; /* Bits we want to break on */
LONG
ap_FoundBreak; /* Bits we broke on. Also returns ERROR_BREAK */
BYTE
ap_Flags; /* New use for extra word.
*/
BYTE
ap_Reserved;
WORD
ap_Strlen; /* This is what ap_Length used
to be */
#define ap_Length ap_Flags /* Old
compatability for LONGWORD ap_Length */
struct FileInfoBlock
ap_Info;
UBYTE ap_Buf[150];
/* Buffer for path name, allocated by user */
};
// ------------------------- Program start ----------------------------
int cmd_fsearch(void)
{
struct ExecBase *SysBase = (*((struct ExecBase **) 4));
struct DosLibrary *DOSBase;
long temprc, rc, hdlength;
long buffsize, actual, line;
long opts[OPT_COUNT];
struct RDArgs *rdargs;
struct MyAnchorPath __aligned ua;
struct FileInfoBlock *fib;
BPTR oldlock, curlock, lk, fh=NULL, outpt;
char *fname;
char root[150], *rtpt; // buffer for root
char head[120];
// buffer to store header
char patbuff[512]; // buffer for
pattern matching
char hdbuff[512]; // buffer
for header matching
char *str, upstr[256]; // buffer for other case string
char linebuff[30]; // buffer for
line number convertion
char info[100];
// buffer for file/dir info
char outbuff[100]; // buffer for
possible split line output
char *p, *d, *u, *sl, *ssl;
char *el, *endbuff, *t;
char *curarg, **argptr, *buff=NULL;
char *pat, *hd;
struct DateTime __aligned dt; // for FORM/TO
struct DateTime __aligned dd; // for info
int c, strline, linelength;
BOOL flag, printfile;
// the default return code
rc = RETURN_FAIL;
// clear the locks we'll use so we know if we used them
oldlock = NULL;
curlock = NULL;
// setup the DateTime structure flag for dd-mm-yy
memset(&dt, 0, sizeof(struct DateTime));
dt.dat_Format = FORMAT_CDN;
// open dos lib or die..
if (!(DOSBase = (struct DosLibrary *)OpenLibrary("dos.library",
36L)))
{ Result2(ERROR_INVALID_RESIDENT_LIBRARY);
return (20);
}
// get our output (pointer to the console or redirected file)
outpt = Output();
// ------------ clear options array & parse command line
memset((char *)opts, 0, sizeof(opts));
rdargs = ReadArgs(TEMPLATE, opts, NULL);
// if there was a parser error - print slogan etc and skip to end
if (rdargs == NULL)
{ Write (outpt, "›32mby D.Keletsekis\n›33mdck@hol.gr›31m\n",
39);
PrintFault(IoErr(), NULL);
goto endprog;
}
// ------------ ok, now set up the CLI arguments
// point to first (of possibly multiple) source args,
// which are set up as an array of pointers to chars
argptr = (char **)opts[OPT_ROOT];
// setup ParsePattern for PAT and HEADER
if (pat = (char *)opts[OPT_PAT])
{ if ((ParsePatternNoCase (pat, patbuff, 500)) <
1)
{ Write (outpt, "Error parsing file
pattern\n", 27);
goto endprog;
}
}
if (hd = (char *)opts[OPT_HEADER])
{ if ((ParsePatternNoCase (hd, hdbuff, 500)) < 1)
{ Write (outpt, "Error parsing header\n",
27);
goto endprog;
}
}
// allow / an . as date separators
if (opts[OPT_FROM])
{ for (p = (char *)opts[OPT_FROM]; *p; ++p)
{ if ((*p=='/')||(*p=='.'))
*p = '-';
} }
if (opts[OPT_TO])
{ for (p = (char *)opts[OPT_TO]; *p; ++p)
{ if ((*p=='/')||(*p=='.'))
*p = '-';
} }
// setup comparisson & read buffer for searching
if (opts[OPT_TXT])
{
// get copy buffer - try 32k.. 8k
for (c = 32, flag = 0; c > 2 && !flag;)
{ buffsize = c * 1024;
if (buff = (char *)AllocVec(buffsize+16,
MEMF_ANY))
++flag;
else c-=5;
}
if (!buff)
{ Write (outpt, "No memory!\n",
11);
goto endprog;
}
str = (char *)opts[OPT_TXT];
strncpy (upstr, str, 250);
upstr[250] = 0;
// count new lines in search string (if any)
for (p = str, strline = 0; *p; ++p)
if (*p == 10)
++strline;
// if not case sensitive make upstr oposite case
to str
// we can not use isupper() for some reason
- because no startup code?
if (opts[OPT_CS] == NULL)
{ u = upstr;
while (*u)
{ if ((*u >= 'A')
&& (*u < 'a')) *u = ((*u) + ('a' - 'A'));
else
if ((*u >= 'a') && (*u <= 'z')) *u = ((*u) - ('a' - 'A'));
++u;
}
}
}
// -------------- LOOP FOR ALL "ROOT" ARGUMENTS ----------
while (curarg = *argptr++)
{
// clear the anchorpath structure to 0's
memset(&ua, 0, sizeof(struct MyAnchorPath));
// indicate - put full path in ap_Buf which is 150
bytes
ua.ap_Strlen = 150;
// set up what to print in case of error
strcpy(ua.ap_Buf, "FSearch failed :(\n");
// append #? to the root if all is not declared
if (opts[OPT_ALL])
rtpt = curarg;
else
{ strcpy (root, curarg);
p = &root[strlen(root)-1];
if ((*p=='/') || (*p==':'))
strcat
(root, "#?");
else if (*p!='?')
strcat
(root, "/#?");
rtpt = root;
}
// call the matcher.. (0=success)
MatchFirst(rtpt, (struct AnchorPath *)&ua);
temprc = IoErr();
// ---------------- LOOP FOR ALL MATCHES --------------------
while (temprc == 0)
{
// check for control-C this way
since we use nostartup
if (SetSignal(0, 0) & SIGBREAKF_CTRL_C)
{ temprc = ERROR_BREAK;
goto enderror;
}
// ------ if it's a directory and
the ALL option is on..
if (ua.ap_Info.fib_DirEntryType
> 0 && opts[OPT_ALL])
{
// if the backing-out-of-dir
flag is not set...
if (!(ua.ap_Flags
& APF_DIDDIR))
{
// tell matcher to enter this sub-dir.
ua.ap_Flags |= APF_DODIR;
}
// and clear the
flag
ua.ap_Flags &=
~APF_DIDDIR;
}
// ------ if dir changed - cd to
it
if (ua.ap_Flags & APF_DirChanged)
{
if (curlock)
UnLock (curlock);
curlock
= DupLock(ua.ap_Current->an_Lock);
lk = CurrentDir(curlock);
// if this
is the first dir change, store the lock
if (!oldlock)
oldlock = lk;
}
// -------------- IF IT'S A FILE
------- do your stuff...
if (ua.ap_Info.fib_DirEntryType <
0)
{
// point
to our FileInfoBlock & clear flags
fib = &ua.ap_Info;
flag = 0;
printfile
= 0;
// point
to file name
if (opts[OPT_NOPATH])
fname = fib->fib_FileName;
else
fname = ua.ap_Buf;
// ---------
check file pattern
if (opts[OPT_PAT])
{
if(!(MatchPatternNoCase(patbuff, fib->fib_FileName)))
goto skipfile;
}
// ----------
check file size (MIN/MAX)
if (opts[OPT_MIN])
{
if ((fib->fib_Size / 1024) < (*((LONG *)opts[OPT_MIN])))
goto skipfile;
}
if (opts[OPT_MAX])
{
if ((fib->fib_Size / 1024) > (*((LONG *)opts[OPT_MAX])))
goto skipfile;
}
// ---------
check dates
if (opts[OPT_FROM])
{
dt.dat_StrDate = (char *)opts[OPT_FROM];
if (StrToDate(&dt))
{ if ((CompareDates(&dt.dat_Stamp, &fib->fib_Date))
<= 0)
goto skipfile;
}
// error in date format..
else
{ Write (outpt, "FROM error\n", 11);
goto enderror;
}
}
if (opts[OPT_TO])
{
dt.dat_StrDate = (char *)opts[OPT_TO];
if (StrToDate(&dt))
{ if ((CompareDates(&dt.dat_Stamp, &fib->fib_Date))
>= 0)
goto skipfile;
}
else
{ Write (outpt, "TO error\n", 9);
goto enderror;
}
}
// ------------
HEADER or NOBIN options
if (opts[OPT_HEADER]
|| opts[OPT_NOBIN])
{
hdlength = 0;
// open & read file
if (fh = Open(fib->fib_FileName, MODE_OLDFILE))
{ hdlength = Read(fh, head, 100);
Close (fh);
}
if (hdlength == -1)
{ temprc = IoErr();
goto enderror;
}
else if (!hdlength)
goto skipfile;
// check the options
head[hdlength] = 0;
if (opts[OPT_HEADER])
{ // replace null bytes with full stops
for (c=0; c < hdlength; ++c)
if (!head[c]) head[c] = '.';
if(!(MatchPatternNoCase(hdbuff, head)))
goto skipfile;
}
if (opts[OPT_NOBIN])
{ p = head;
c = 0;
while (c < hdlength)
{ if ( ((*p < 32) || (*p > 127)) &&
(*p != '\n') && (*p != '\r') && (*p != '\t') )
goto skipfile;
++p; ++c;
}
}
}
// -------------
prepare file info buffer - uses outbuff&linebuff
if (opts[OPT_INFO])
{
memset (info, ' ', 80);
c = stcl_d (outbuff, fib->fib_Size);
strcpy (&info[12-c], outbuff);
strcat (info, " ");
dd.dat_Stamp = fib->fib_Date;
dd.dat_StrDate = outbuff;
dd.dat_StrTime = linebuff;
DateToStr (&dd);
strcat (info, outbuff);
strcat (info, " ");
strcat (info, linebuff);
}
// -------------
Search the file
if (opts[OPT_TXT])
{
// open the file
if ((fh = Open(fib->fib_FileName, MODE_OLDFILE)) == NULL)
{ temprc = IoErr();
goto enderror;
}
// reset the line number counter & split line stuff
line = 1;
linelength = 80;
outbuff[0] = 0;
// start reading - flag will indicate if we're finished
flag = 0;
while (!flag)
{
actual = Read (fh, buff, buffsize);
// read error
if (actual == -1)
{ temprc = IoErr();
Close (fh);
goto enderror;
}
// file is empty
else if (!actual)
{ Close (fh);
goto skipfile;
}
// file end
else if (actual < buffsize)
++flag;
p = buff; // our main working pointer
sl = p; // start of line pointer
endbuff = &buff[actual]; // ptr to end of buffer
// ----- start comparisson
while (p < endbuff)
{
// match first letter
if ((*p == *str) || (*p == *upstr))
{ t = p;
d = str;
u = upstr;
// match rest of txt string - read buffer if string is
// between buffers
while ((*d) && (*t) && ((*d == *t) || (*u == *t)))
{ ++t; ++d; ++u;
// read in more text if needed
if ((t >= endbuff) && !flag)
{ // copy part of previous buff
c = endbuff - sl;
if (c > 40)
{ sl = endbuff - 40;
c = 40;
}
strncpy (outbuff, sl, c);
outbuff[c] = 0;
// read in more stuff
if ((actual = Read (fh, buff, buffsize)) == -1)
{ temprc = IoErr();
Close (fh);
goto enderror;
}
// file end
else if (actual < buffsize)
++flag;
t = buff;
p = buff;
sl = buff;
endbuff = &buff[actual];
}
}
// ------ found match !
if (*d == 0)
{ --t;
p = t;
// if not already printed - print file name
if (!printfile)
{
if (opts[OPT_HL]) SET_WHITE;
// if info - print name & info in head buffer
if (opts[OPT_INFO])
{ memset(head, ' ', 110);
strcpy (head, fib->fib_FileName);
head[strlen(head)] = ' ';
strcpy (&head[35], info);
Write (outpt, head, strlen(head));
}
else
Write(outpt, fname, strlen(fname));
if (opts[OPT_HL]) SET_BLACK;
Write(outpt, "\n", 1);
printfile = 1;
}
// if verbose, print the line context
if (opts[OPT_V])
{
linebuff[0] = ' ';
stcl_d (&linebuff[1], line);
strcat (linebuff, ": ");
if (opts[OPT_HL]) SET_BLUE;
Write (outpt, linebuff, strlen(linebuff));
if (opts[OPT_HL]) SET_BLACK;
// go to line start
ssl = sl; ++ssl;
for (el=ssl, c=0; (*el) && (*el!=10) && (el < endbuff);
++el, ++c);
// adjust start of printing
if ((sl == buff) && (*sl != '\n'))
--ssl;
// if we had split line...
if (outbuff[0])
{
Write (outpt, outbuff, strlen(outbuff));
linelength -= (strlen(outbuff));
}
// or if line is over 80 chars
else if ((p - ssl) > 80)
ssl = p - 79;
if (c < linelength)
Write (outpt, ssl, c);
else
Write (outpt, ssl, linelength);
Write (outpt, "\n", 1);
line += strline; // add any lines the string had in it
p = --el; // go to end of shown
line
}
// if no verbose we're finished with this file
else
{ Close (fh);
goto skipfile;
}
linelength = 80;
outbuff[0] = 0;
}
else
if ((!*p) || (*p == 10)) { ++line; sl=p; }
}
else
if ((!*p) || (*p == 10)) { ++line; sl=p; }
++p;
} // end of compare loop
} // end of read loop
// close the file
Close (fh);
} // end
of dealing with file for opts[OPT_TXT]
// -------
if no txt option was declared, we list the file
else
{
if (opts[OPT_INFO])
{ memset(head, ' ', 110);
strcpy (head, fib->fib_FileName);
head[strlen(head)] = ' ';
strcpy (&head[35], info);
Write (outpt, head, strlen(head));
}
else
{ Write(outpt, fname, strlen(fname));
}
Write(outpt, "\n", 1);
}
} // end of IF IT'S A FILE
// point at which we goto if file
does not meet our requirement
skipfile:
// match the next file
MatchNext((struct AnchorPath *)&ua);
temprc = IoErr();
}
// ------------------- END OF MATCH LOOP ---------------------
// tell the matcher we've finished
MatchEnd((struct AnchorPath *)&ua);
}
// ------------- END OF LOOP FOR ALL "ROOT" ARGS -------------
// check if we had any errors
enderror:
if (temprc)
{
// if we ran out of files, we set error code
to OK - i.e. 0
if (temprc == ERROR_NO_MORE_ENTRIES)
rc = RETURN_OK;
else
{
// if the user hit control-C,
set WARN return code (5)
if (temprc == ERROR_BREAK)
rc = RETURN_WARN;
else
{ Write (outpt, "FSearch
Error", 13);
rc = RETURN_FAIL;
}
// there was a reportable
error - so print it
PrintFault(temprc, fname);
}
}
// ------------------------- end - clean up --------------------
endprog:
// change back to our original dir..
if (oldlock)
CurrentDir(oldlock);
// and unlock the current lock we have (if any)
if (curlock)
UnLock(curlock);
if (rdargs) FreeArgs(rdargs);
if (buff) FreeVec(buff);
CloseLibrary((struct Library *)DOSBase);
return(rc);
}
|