/*

	findf

	A very fast case-sensitive grep-style search program using
	async i/o and the Boyer-Moore search algorithm.

	Usage - same as 'c:search' for the commands implemented.

	Michael Zucchi 1994

 */


MODULE 'dos/dos', 'dos/dosasl',
	'locale',
	'tools/async'

DEF skip[256]:ARRAY OF CHAR,	-> skip array
    line[1024]:ARRAY OF CHAR,	-> input buffer
    buffer[1024]:ARRAY OF CHAR,	-> for case-sensitive searches
    m,				-> length of string
    string:PTR TO CHAR,		-> string to match
    uppertable[256]:ARRAY OF CHAR,-> lower->upper conversion table
    quiet, nonum, quick, ncase	-> settings


PROC main()
DEF args:PTR TO LONG, rdargs, p:PTR TO LONG, an:PTR TO anchorpath,go,
	fib:PTR TO fileinfoblock, loc, i

args:=[0,0,0,0,0,0,0];

IF rdargs:=ReadArgs('FROM/M,SEARCH/A,ALL/S,NONUM/S,QUIET/S,QUICK/S,CASE/S', args, 0)
  string:=args[1];
  nonum:=args[3];
  quiet:=args[4];
  quick:=args[5];
  ncase:=args[6]=0;

  IF ncase
    IF localebase:=OpenLibrary('locale.library', 0)
      IF loc:=OpenLocale(0);
        FOR i:=0 TO 255 DO uppertable[i]:=ConvToUpper(loc, i);
        CloseLocale(loc);
      ELSE
        FOR i:=0 TO 255 DO uppertable[i]:=IF i>="a" AND i<="z" THEN i-32 ELSE i
      ENDIF
    ELSE
      FOR i:=0 TO 255 DO uppertable[i]:=IF i>="a" AND i<="z" THEN i-32 ELSE i
    ENDIF
    CloseLibrary(localebase);
	MOVE.L	uppertable,A0
	MOVE.L	string,A1
	MOVEQ	#0,D0
mn_up:	MOVE.B	(A1),D0
	MOVE.B	0(A0,D0.W),(A1)+
	BNE.S	mn_up
  ENDIF

  initskip();

  IF an:=New(SIZEOF anchorpath + 256)
    an.strlen:=255
    an.breakbits:=SIGBREAKF_CTRL_C;
    fib:=an.info
    p:=args[0];
    go:=0
    WHILE p[] AND (go=0)
      go:=MatchFirst(p[], an);
      WHILE go=0
        IF (fib.direntrytype) <0
	  go:=scanfile(an+SIZEOF anchorpath);
	ELSE
          IF args[2]
            IF (an.flags AND APF_DIRCHANGED)=0 THEN an.flags:=an.flags OR APF_DODIR
          ENDIF
        ENDIF
        IF go=0 THEN go:=MatchNext(an);
      ENDWHILE
      p++
    ENDWHILE
    IF quick THEN WriteF('\e[M');
    Dispose(an);
  ENDIF
  FreeArgs(rdargs);
ELSE
  WriteF('FindF, Michael Zucchi 1994.\nERROR: bad args\n'+
  	' FROM/M    files/patterns to search\n'+
  	' SEARCH/A  text to search for\n'+
  	' ALL/S     recursively scan subdirectories\n'+
  	' NONUM/S   do not output line numbers\n'+
  	' QUIET/S   do not output matched lines\n'+
  	' QUICK/S   faster file-scan display\n'+
  	' CASE/S    do a case-sensitive search\n');
ENDIF
ENDPROC


/*
  search a single file, by filename, line at a time
 */

PROC scanfile(name)
DEF number, file, found=0,go=0

IF quick
  WriteF('\s\e[K\b', name);
ELSE
  IF quiet=0 THEN WriteF('\s\n', name);
ENDIF

number:=1;
IF (file:=as_Open(name, MODE_OLDFILE, 3, 10240))
  WHILE as_FGetS(file, line, 1023) AND (go=0)
    IF testmatch()
      IF quiet
        IF found=0 THEN WriteF('\s\n', name);
        JUMP close
      ELSEIF nonum
->        IF quick AND found=0 THEN WriteF('\n');
        WriteF('\s', line);
      ELSE
->        IF quick AND found=0 THEN WriteF('\n');
        WriteF('\d[6] \s', number, line);
      ENDIF
      found:=1
    ENDIF
    number++
    go:=CheckSignal(SIGBREAKF_CTRL_C)
  ENDWHILE
close:
  as_Close(file);
ENDIF

ENDPROC go



/*
  Initialise the skip table for a given search string
 */

PROC initskip()
DEF i

m:=StrLen(string);

-> initialise skip table for all characters
FOR i:=0 TO 255
  skip[i]:=m+1;
ENDFOR

-> set skip table for characters in the string
FOR i:=0 TO m-1
  skip[string[i]]:=m-i;
ENDFOR

ENDPROC

/*
  Looks for a match in a given line
 */

PROC testmatch()

IF m<1 THEN RETURN

	MOVEM.L	A4-A5,-(A7)

	MOVE.L	m,D7
	SUBQ.L	#1,D7

	TST.L	ncase
	BEQ.S	tm_case

	-> non case-sensitive

	MOVE.L	line,A3
	MOVE.L	buffer,A0
	LEA	1(A0,D7.L),A2
	MOVE.L	uppertable,A1
	MOVEQ	#0,D0
tp_up:	MOVE.B	(A3)+,D0
	MOVE.B	0(A1,D0.W),(A0)+
	BNE.S	tp_up
	BRA.S	tp_match

	-> case sensitive
tm_case:
	MOVE.L	line,A0
	LEA	1(A0,D7.L),A2	-> a[i]
tm_scan:
	TST.B	(A0)+		-> <hack> find end of string
	BNE.S	tm_scan

tp_match:
	MOVE.L	string,A1
	MOVE.L	A1,A5
	LEA	1(A1,D7.L),A1	-> p[j]

	MOVE.L	skip,A4

	MOVE.L	D7,D3
	ADDQ.L	#2,D3
	MOVEQ	#0,D1
tm_loop0:
	MOVE.L	D7,D2
	MOVE.L	A1,A3
tm_loop:
	MOVE.B	-(A2),D1
	CMP.B	-(A3),D1
	BEQ.S	tm_matchchar

	MOVE.L	A5,D0
	SUB.L	A3,D0		-> -j
	ADD.L	D3,D0		-> M-j+1

	MOVE.B	0(A4,D1.W),D1	-> skip[character]
	CMP.L	D0,D1
	BLT.S	tm_add0

	ADD.L	D1,A2

tm_ok:	CMPA.L	A0,A2
	BLT.S	tm_loop0

	MOVEQ	#0,D0
	BRA.S	tm_exit

tm_add0:
	ADD.L	D0,A2
	BRA.S	tm_ok

tm_matchchar:
	DBF	D2,tm_loop

	MOVEQ	#-1,D0
tm_exit:
	MOVEM.L	(A7)+,A4-A5

ENDPROC	D0


