#!/bin/sh
# platform ... devuan dash
# GPL_3+
cat << 'EEE' > /dev/null
/* fns .... find string, grep & find parser bourne-shell script
 * Copyright (C) 2017-2019 Momi-g
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
EEE

# 2-clause BSD license
# https://sites.google.com/site/jdisnard/realpath
cat << 'EEE' > /dev/null
/* Copyright 2010 Jon Disnard. All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, are
 * permitted provided that the following conditions are met:
 * 
 *    1. Redistributions of source code must retain the above copyright notice, this list of
 *       conditions and the following disclaimer.
 * 
 *    2. Redistributions in binary form must reproduce the above copyright notice, this list
 *       of conditions and the following disclaimer in the documentation and/or other materials
 *       provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY Jon Disnard ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * The views and conclusions contained in the software and documentation are those of the
 * authors and should not be interpreted as representing official policies, either expressed
 * or implied, of Jon Disnard.
 */
EEE

# readlink -f portable & posix.  func_rlp "$file" ..or.. cat file + \000 | func_rlp	
func_rlp() {
# GPL_3+ license
# '(' ... for local scope
(
# normalize locale
bk_lcl=`set`
export LC_ALL=C
export LANG=C
pf1='( eval "$bk_lcl" ; cat - )'

rlp_input='cat -'	
if [ "$#" != '0' ] ; then
	rlp_input='for ii
do
	printf "%s\000" "$ii"
done'
fi

# ...awk for detect input err.
eval "$rlp_input" | od -An -to1 -w16 -v | awk -v bf="$0" '
BEGIN{err=1} 
$0 ~ /000/ {err=0}
{print $0}
END{
	if(NR > 0 && err == 1 ) {
		printf("%s: link search err. \134000 not found. sleep.\n", bf) > "/dev/stderr"
		for(;;){
			system("sleep 1000")
		}
	}
}' | sed -e 's/ 000/@/g' | tr -d '\n' | 
	tr ' ' '\134' | tr '@' '\n' | while read -r rlp_A
do
	# fname ... dir
	rlp_fin=`printf "$rlp_A"'@'`
	rlp_fin="${rlp_fin%?}"

	#	normalize name to have dir data	(aaa.txt -> ./aaa.txt etc)
	rlp_fin="${rlp_fin%/}"
	if [ "${rlp_fin#*/}" = "$rlp_fin" ] ; then
		rlp_fin="./$rlp_fin"
	fi
	
	rlp_ftail=''	#file name only
	# kick err files
	if ! [ -e "$rlp_fin" ] ; then
#		echo "$0: link search err. file not found. '$rlp_fname'" > /dev/stderr
		printf '//%s\000' "$rlp_fin"
		continue
	fi
	
	# split dir + name. -f ... cant get ln, pipe etc.
	if ! [ -d "$rlp_fin" ] ; then		# -d ! not dir == files
	#	split dir + filename. and resolve soft link
	# ls --show-control-chars
	#	表示不可能な文字をそのまま表示 (プログラムが  'ls'  で  なかった
	#	り、出力が端末以外の場合は、これがデフォルト動作になる)
	#	...つまり、端末に表示せず変数等に代入する時は正確なファイル名が取得できるはず。
	#	-Lオプションはサイズや日付データのみでファイル名は対象外

		rlp_fname="$rlp_fin"
		while :		# recheck for multiple link (a -> b -> c etc)
		do
		if ! [ -h "$rlp_fname" ] ; then		# -h ... exist & link
			break
		fi

# 2-clause BSD license
# https://sites.google.com/site/jdisnard/realpath
# use 'ls -l' to get link-dst infomation.
# '->' use as string spliter	... http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html
# need LANG=c ? safety?
#	rlp_LANGbk="$LANG"
#	LANG="c"
		rlp_base=`/bin/ls -l "$rlp_fname" ; printf @`	# $() removes end '\n' (posix)
		rlp_base="${rlp_base%??}"	# aaa\n@ -> aaa	... ls add \n (like echo)
		rlp_count=`printf '%s\n' "$rlp_fname" | tr -c '>-' ' ' |
			sed -e 's/->/@/g' | tr -d -c '@' | wc -c`	# check '->' string(filename, username etc)
		rlp_count=$((rlp_count+1))
		
		for ii in `seq 1 $rlp_count `	# ls -h... exist -> 
		do
			rlp_base="${rlp_base#*->}"
		done
		
# debug... ls -al
# /dev/stdin -> /proc/self/fd/0
# /proc/self/fd/0 -> pipe:[3082488]		<<< not file. inode system. needs kick
		test -e '/'"${rlp_base#*/}" || break	
		rlp_fname='/'"${rlp_base#*/}"	# remove head ' '	ls disp filename (linkfile)

		done	#while end
		
		# if files, rlp_ftail != '', 
		rlp_ftail="${rlp_fname##*/}"	# filename only
		rlp_fdir="${rlp_fname%/*}"	# dirname only
	else
		# fin is dir
		rlp_fdir="$rlp_fin"
	fi
	
	# normalized. check path reallink
	# pwd -P: get full realpath. posix. (2001-?)
	# echo @ ... command substitution deletes lineend '\n, \n\n ...'.
	# http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_03
	# pwd permission... dir exec may make some problem. run at subshell.
	
	(
	for ii in skip		# jump emulate
	do
		cd "$rlp_fdir"
		if [ "$?" = "1" ] ; then
			echo "$0: link search err. dir access failed. ($rlp_fdir)" | eval "$pf1" > /dev/stderr
			printf '//%s\000' "$rlp_fdir"
			break
		fi		
		
		rlp_fpath=`pwd -P; printf '@'`
		if [ "$rlp_fpath" = '@' ] && [ "$err" != "" ] ; then
			echo "$0: link search err. pwd -P failed. ($rlp_fdir)" | eval "$pf1" > /dev/stderr
			printf '//%s\000' "$rlp_fdir"
			break
		fi
	
		rlp_fpath="${rlp_fpath%??}"	# aaa\n@ -> aaa	... pwd add \n (like echo)
		rlp_fpath="${rlp_fpath%/}"	# if aaa/ -> aaa etc
		rlp_fname="$rlp_fpath"'/'"$rlp_ftail"	# dir has end '/' ... 'aaa/' etc
		printf '%s\000' "$rlp_fname"
	done
	)
done | od -An -to1 -w16 -v | tr -d '\n' |
sed -e 's# 000#@#g' | tr ' ' '\134' | tr '@' '\n' | awk -v name="$0" '
	$0 ~ /^.057.057/ {
		gsub(/^.057.057/, "", $0)
		cmd="printf \047" $0 "\012\047 >/dev/stderr"
		printf "%s: link check err. file not found: ", name > "/dev/stderr"
		system(cmd)
		printf "//"
		}
	{print $0}' | eval "$pf1"
)
}



# grepだと8ms, sed流しで20ms.ファイルはいっぱいでるからなるべく多めに絞りたい。
# ってことは最速のgrepがいいか。大量にhitするのは検索ワードが悪い=すぐにやり直すだろうから
# ファイルを高速に選別するほうが重要。gnuは-Cの存在が強いからそっち。

# gnuはwhileしても6ms, posixは350ms.
cmd_fulloct="func_rlp"
readlink -zf "$0" --version >/dev/null 2>&1
if [ "$?" = "0" ] ; then
cmd_fulloct=`cat << 'EEE'
xargs -0 -I aaa readlink -zf -- aaa |
od -An -to1 -w16 -v | 
sed -e 's/ 000/@/g' | tr -d '\n' | tr ' ' '\134' | 
tr '@' '\n'
EEE
`
fi


#---main
#	fpath=$(printf `func_rlp "$0"`'@')
#	fpath=${fpath%?}
#	fname=`basename "$fpath"`
#	fdir=${fpath%/*}'/'
#	cd "$fdir"


#optcheck--------

# change -+ -> -p	plus-minus
for ii
do
	if [ "$ii" = "--" ] ; then skip=1 ; fi
	if [ "$skip" != "1" ] && [ "${ii%%+*}" = "-" ]; then
		ii='-p'"${ii#-+}"
	fi
	shift
	set -- "$@" "$ii"
done

# ini=`cat << 'END'
# -h  0  bool
# -p -1  int '[ $opt_p -ge 0 ]'
# -r  0  int '[ $opt_r -ge -1 ]'
# -o  0  bool
# -B  0  bool
# -f "./" str
# -F  0  bool
# -P  0  bool
# END
# `
# buf=`ckopt "$ini"`
# eval "$buf"


## ---this code is generated by ckopt
#	---optsetting
#-h  0  bool
#-p -1  int '[ $opt_p -ge 0 ]'
#-r  0  int '[ $opt_r -ge -1 ]'
#-o  0  bool
#-B  0  bool
#-f "./" str
#-F  0  bool
#-P  0  bool

ckopt_func () { 
if [ "$1" = "-d" ] ; then
	eval "opt_${2#?}"'="$3"'		# opt_?="$3"
	return 0
fi

ckopt_opt="$2"
if [ "$1" = "-e" ] ; then
shift 4
while [ $# -gt 0 ]
do
if [ "$1" != "${1#[#]e}" ] || [ "$1" != "${1#[#]E}" ] ; then
	eval "${1#[#]?}"
	if [ "$?" != "0" ] ; then
		OPTARG="errmsg $ckopt_opt $1"
		return 1
	fi
fi
shift
done
return 0
fi

if [ "$1" = "-c" ] ; then
shift 3
printf '%s\n' "$1" | tr '[:upper:]' '[:lower:]' | awk '$1 ~ /int/ {exit 1}'
	if [ "$?" = "1" ] ; then
		shift
		set -- "dummy" 'printf "%d" "$OPTARG" >/dev/null 2>&1' "$@"
	fi
shift
while [ $# -gt 0 ]
do
	eval "$1"
	if [ "$?" != "0" ] ; then
		OPTARG="errmsg $ckopt_opt $1"
		return 1
	fi
shift
done
return 0
fi

echo "$0: bug. sleep" >/dev/stderr
while :
do
	sleep 1000
done
exit 1
}

# ---dflset

OPTIND=1	# fixed value. posix rule.
OPTERR=0
ckopt_buf=''
ckopt_opt=''

 ckopt_func -d -h 0 bool
 ckopt_func -d -p -1 int '[ $opt_p -ge 0 ]'
 ckopt_func -d -r 0 int '[ $opt_r -ge -1 ]'
 ckopt_func -d -o 0 bool
 ckopt_func -d -B 0 bool
 ckopt_func -d -f "./" str
 ckopt_func -d -F 0 bool
 ckopt_func -d -P 0 bool

#---getopts loop. break if all args read or detect '--'
while :
do
if [ 0 -eq "$#" ] || [ "${OPTARG%% *}" = "errmsg" ] ; then
	break
fi
check_opt='$'"$OPTIND"	#add code. debug. 
eval "check_opt=\"$check_opt\""
if [ "$check_opt" = "--" ] ; then
	shift $OPTIND ; break
fi
getopts ":hp:r:oBf:FP" ckopt_opt "$@"		# ":a:bc:f:" etc...
if [ "$?" = "1" ] ; then	# detect end/normal args. save general args.
	shift $((OPTIND - 1))
	if [ "$#" -eq "0" ] ; then
		break
	fi
	ckopt_buf="$ckopt_buf "`printf '%s' "$1" | sed -e "{        
	s#'#'\"'\"'#g
	s/^/'/g
	s/$/'/g
	}"`
	shift
	OPTIND=1
	continue
fi
# --- your setting

if [ "$ckopt_opt" = "h" ] ; then
	opt_h="1"
	ckopt_func -c -h  0  bool
	continue
fi
if [ "$ckopt_opt" = "p" ] ; then
	opt_p="$OPTARG"
	ckopt_func -c -p -1  int '[ $opt_p -ge 0 ]'
	continue
fi
if [ "$ckopt_opt" = "r" ] ; then
	opt_r="$OPTARG"
	ckopt_func -c -r  0  int  '[ $opt_r -ge -1 ]'
	continue
fi
if [ "$ckopt_opt" = "o" ] ; then
	opt_o="1"
	ckopt_func -c -o  0  bool
	continue
fi
if [ "$ckopt_opt" = "B" ] ; then
	opt_B="1"
	ckopt_func -c -B  0  bool
	continue
fi
if [ "$ckopt_opt" = "f" ] ; then
	opt_f="$OPTARG"
	ckopt_func -c -f "./" str
	continue
fi
if [ "$ckopt_opt" = "F" ] ; then
	opt_F="1"
	ckopt_func -c -F  0  bool
	continue
fi
if [ "$ckopt_opt" = "P" ] ; then
	opt_P="1"
	ckopt_func -c -P  0  bool
	continue
fi
# --- your setting end

# hit err. ckopt chars...
# err detect@silent mode
# $?=1 ... detect optend or '--'
# ':' ... detect option, but dont have subargs (OPTARG="factor").
# '?' ... detect unsupported option char (OPTARG="factor") or args end (OPTARG="blank").
# OPTARG ... "" is optend. "a/b/c..." is invalid option 
# OPTIND ... if err, OPTIND indicates next (new) arg pos. 

OPTARG="errmsg -$OPTARG invalid option / misses subargs"
done

# --- post process. set not optional args & clean & #eE last cmd.

for ii in 1	# dummyjump logic
do
	OPTIND=1
	# skip
	test "${OPTARG%% *}" != "errmsg" || break
	ckopt_buf="set -- $ckopt_buf"' "$@"'	# set -- 'cmd' .. "$@"
	eval "$ckopt_buf"
	# run #E cmd.  cmd + test $? ... xn
 ckopt_func -e -h 0 bool
 ckopt_func -e -p -1 int '[ $opt_p -ge 0 ]'
 ckopt_func -e -r 0 int '[ $opt_r -ge -1 ]'
 ckopt_func -e -o 0 bool
 ckopt_func -e -B 0 bool
 ckopt_func -e -f "./" str
 ckopt_func -e -F 0 bool
 ckopt_func -e -P 0 bool
done

if [ "${OPTARG%% *}" = "errmsg" ] ; then
	printf "$0: opterr. %s\n" "$OPTARG" >/dev/stderr ; while : ; do sleep 1000 ; done ; exit 1
	test 1 = 0
fi
## ---generate by ckopt end

#exit	direct option ... 20ms prsopt ...50ms

if [ $? -ne 0 ] ; then
	echo "$0: optErr($OPTARG). see -h. sleep 1000" >/dev/stderr
	while : ; do sleep 1000 ; done
	exit 1
fi


if [ "$opt_h" = "1" ] || [ "$#" = "0" ] ; then
cat << 'EEE'
HowTo (FiNdString from dir files. bourne-shell script)
opt: -h, -+2(range), -r(ecurse -1,0,1,2..), -o(ctal)
	-B(asic-regex BRE), -F(ixed, not regex), -f(ilename)
------
ex.) ~$ fns   abc str ing	(-F -+2 -r 0)	>> dfl option
>>	/foo/bar/aaa.txt	...find file from './', including words
	10- ... ing
	11: moo abc		...search range, 'abc' -+2 lines
	12- ... 'str'		

ex.) ~$ fns 'string'		... one word, almost equals to 'grep string ./*'
     ~$ fns 'string' -r -1	...recursive find (-1:non stop 0:pwd 1:pwd+next)
     
     ~$ cat a.txt | fns -B '\(b\|B\)ob.*'	... pipein equals (-f 'a.txt')
     ~$ fns 'abc' -o	...show octal filename. (a.txt -> \141\056\164\170\164)

ex.) ~$ fns -+1 -- abc 'str' '-foo is bar'
...input '--'(opt end) if you want to avoid parsor troubles.
EEE
exit 0
fi

# -f or stdin
sfile="$opt_f"
if [ -p /dev/stdin ] ; then
	sfile="/dev/stdin"
fi

# one word
if [ "$#" = "1" ] && [ "$opt_p" = "-1" ] ; then
	opt_p=0
fi
if [ "$opt_p" = "-1" ] ; then
	opt_p=2
fi


# install ck
buf=0
find -print0 --version >/dev/null 2>&1
buf=$((buf+$?))
grep -Z --version >/dev/null 2>&1
buf=$((buf+$?))
xargs -0 --version >/dev/null 2>&1
buf=$((buf+$?))

mposix=0
if [ "$buf" != 0 ] || [ "$opt_P" = "1" ] ; then
	echo "$0: gnu-find,grep,xargs not found or detect -P. run posix mode(slow)" >/dev/stderr
	mposix=1
fi



# change regex. if -F un^k\o > [u][n]\^[k][\][o]
# only '^' needs escape. 

if [ "$opt_B" = "0" ] ; then
	for i in "$@"
	do
		cmd="printf '%s\n' "'"$i"'" | sed -e '"'s#.#[&]#g;s#\[\^\]#\\^#g'"'"
		buf=`eval "$cmd"`
		#printf '%s\n' "$buf"
		shift
		set -- "$@" "$buf"
	done
fi
fstword="$1"


charcode="041"	# '!', ascii 33@decimal	0041+041 とか\0041 はposixで処理してくれると思う。
while :
do
	ws2=`printf '%b' '\'"$charcode"`
	ck=`printf '%s\n' "$@" | tr -dc "$ws2" `
	if [ "$ck" = "" ] ; then
		break
	fi
	if	[ $((charcode + 1)) -gt 254 ] ; then
		echo "regex separater err. search word hax too many kinds of char.(o041-o254). sleep" >/dev/stderr
		while : ; do sleep 1000 ; done
		exit 1
	fi
	# 進数のままで計算可能だけど出力は10進固定。8に戻す
	charcode=`printf '0%o' $((charcode + 1))`
done
ws1='\'"$ws2"	#>> \@ \# etc
# echo "@ $charcode $ws1 @"
# 66ms
# exit



# for stream 
dmy=""
fstread=""	# null comm is ignored(sed)
preread=""

for i in `seq 1 $opt_p`
do
dmy='\060\072\012'"$dmy"	#0: *n, head/tail water down.

#output (5/opt_p)lines loop. buffer read
fstread="$fstread"'N;N;'		#if opt_p=3 2*3. N;N;N;N;N;N
preread="$preread"'n;H;'		# skip headlines
done

#echo "$dmy" | b2rs
#exit 0	#0.084 >> 0.053

# avoid slow comm, 's/(.*)(.*)/\2\1/g'	etc
# edit output format
# 入力行を?行ずつ水増しして吐く。スプリッタで等分して正規化
cmd_split="sed -ne '
1 { $fstread }
p
i"'\'"
--:
N;D
'"

quch=`printf '\047\042\047\042\047'`	# ' -> '"'"'
# kick if center line doesnt have fstword. pre ... n;H *n
cmd_fst="
:label1
h; $preread
s/^[0123456789]\{1,\}.//1
$ws1$1$ws2 b label3

:label2
/^--:/ ! {n;b label2 }
d

:label3
/^--:/ ! {n;H;b label3 }
g;p"
cmd_fst="sed -ne '"`printf '%s\n' "$cmd_fst" | sed -e "s/'/$quch/g"`"'"

# fst word. make base output
# output block if center line is hit fstword
# p=-+1, fst=/b/
# ...
# --:
# 2:aaa
# 3:bbb
# 4:ccc
# --:
# 11:vvv
# 12:abc ...

# echo "$cmd_fst"  >/dev/stderr
# sleep 10
# --+行番号で加工済みの群をふるいにかける。

# second or later word.
# remove --: and ck
buf="$#"
buf=$((buf - 1))
cmd_filter='cat -'
for ii in `seq 1 "$buf" `
do
	set -- "$@" "$1"
	shift
	bufcmd="
:label1
h
:label2
/^--:/ d
s/^[0123456789]\{1,\}.//1
$ws1$1$ws2 ! { n;H; b label2 }

:label3
/^--:/ ! {n;H;b label3 }
g;p"
	bufcmd="sed -ne '"`printf '%s\n' "$bufcmd" | sed -e "s/'/$quch/g" `"'"
	cmd_filter=`printf '%s | %s' "$cmd_filter" "$bufcmd"`
done
# echo "$cmd_filter"  >/dev/stderr
# sleep 10

set -- "$@" "$1"
shift


#	echo "___$# $ws1"
#	printf '%s\n@   %s\n   @%s\n' "$cmd_stdin" "$cmd_fst" "$cmd_filter"
#	exit


# dispname '\111\222\333'
dispfname(){
 	if [ "$opt_o" = "0" ] ; then
		printf "'"
 		printf '%b//\n' "$1" | sed -e "s/[']/$quch/g" | sed -e "s#//#'#"
 	 else
 	 	printf '%s\n' "$1"
 	 fi
}


# output format		10:aaa	...	-> 10: aaa, remove spliter if one word
if [ "$opt_p" = "0" ] ; then	#one word
	outfmt="sed -e 's/^[0123456789-]\{1,\}:/& /g' | sed -e 's/^--: //g' | grep -v '^$'" 
else
	outfmt="sed -e 's/^[0123456789-]\{1,\}:/& /g' | sed -e 's/^--: /--:/g'"
fi




# ここら辺でgnuとそれ以外を分割ってところ。
# file検索方法分け

# 行付き本文を整えてwordを追加検索
# cat file | comm_stream_func "\123\123"
func_rawline_input (){
	eval "$cmd_split" | eval "$cmd_fst" |
	eval "$cmd_filter" | sed -e 's#\(^[0123456789]\{1,\}\).#\1:#1' | eval "$outfmt" |
# \(\)は後方利用で\1に必要。
# ヒットして初めてファイル名と結果を出力。ファイル名はfuncで変換
	( read -r A
	if [ "$A" != "" ] ; then
		dispfname "$1"
		printf "%s\n" "$A"
		cat -
		echo
	fi )
}


# single file
if [ "$sfile" != "./" ] ; then
	octfile=`func_rlp "$sfile"`
	cat "$sfile" | ( printf "$dmy" ; grep -n '' ; printf "$dmy" ) |
	func_rawline_input "$octfile"
	exit 0
fi


# multi file
if [ "$mposix" = "0" ] ; then
	buf=$((opt_r + 1))
	# 140ms eval	... pwdで単体80ms	深さ2とか5とか細かい指定がある場合。
	findcmd=`cat << 'EEE'
find -L ./ -maxdepth $buf -type f -print0 |
xargs -0 -I aaa grep -Zle "$fstword" -- aaa
EEE
`
	# pwdのみ。単体5ms程度
	if [ "$opt_r" = "0" ] ; then
		findcmd="grep -Zle "'"$fstword" -- * .* 2>/dev/null'
		# glob() ... shell pattern. * doesnt get hiding file (.config.txt etc)
		# grep is default BRE mode. -E uses ERE mode (posix)
	fi

	# 再帰。70ms eval	...単体10ms程度
	if [ "$opt_r" = "-1" ] ; then	# grepの高速検索
		findcmd="grep -Zlre "'"$fstword" --'
	fi

	# fix. one file
	if [ "$sfile" != "./" ] ; then	# grepの高速検索
		findcmd="grep -Zlre "'"$fstword" -- "$sfile"'
	fi

	# ここまで60ms 5ms程度の遅れだけど殆どさはない。
	
	#	file-filter. word kick
	zfilter='cat -'
	for ii in `seq 1 $#`
	do
		zfilter="$zfilter | xargs -0 grep -lZe \"\$$ii\" --"
	done
	#	cat - | xargs -0 grep -lZe "$1" -- | xargs -0 grep -lZe "$2" --


#	eval "$findcmd" | eval "$cmd_fulloct" ; exit
#	findcmdの時点で+10ms. 単体は*系(grob検索)の方が圧倒的に早い。1msと5msで4msも早い。
	eval "$findcmd" |
	eval "$zfilter" |
#	eval "$cmd_octal" |
	eval "$cmd_fulloct" |
	#	fulloctはreadlink_gnuの有無で切り替える。あれば早いgnu.
	# ここで105ms。 40ms遅れる。ファイルにフィルターをかけるか。>>絞っても変わらない
	# fnsも100ms程度だった。+20ms. rlpのせいではないみたい。
	# grep -lで絞っているのが効いているみたい。こっちも先に絞るか。
	# むしろ遅くなった。130ms. 分からん。rdoptの差だとしとこ。 >> 嘘。はやくなった
	#	>>> 発見。やっぱりreadlinkっぽい。全部処理すると40ms増える。
	#	octal化のみだとそれが省略可能。結局毎回xargsで読み出すからオーバーヘッド
	#	がどんどん増えるみたい。後回しにしてみる
	#	後回しでloopは逆転レベル。-10ms. fstwordが悪いのか？
	#	grepは大して遅くない。awkで弾いてもok. rawか。
	#	同じfuncを使ってるのに30msも差が出る。
	#	rawでも同じぐらい30ms遅れる。ってことはrawは悪くない。
	#	レジスタとか環境のせい？
	#	()から{}に変えてサブシェル以外にしても変化無し。
	#	whileの中身を処理にかけると途端に遅くなる。なぜ。
	#	while conならむしろ早いんだけど。
	#	whileに入る前にgrepfilterでファイルを絞れば早くなった。結局sedで5倍に増えてしまうから
	#	なるべく採用を絞った方が結果的に早くなる.
	#	結果。whileに入る前にファイルを厳選する。fulloctはその後。ストリームで流した方が
	#	ovhが少ない。
	
	while read -r octfile
	do
		file=`printf "$octfile"'@'`
		file=${file%@}
		( printf "$dmy" 
		grep -nC "$opt_p" -e "$fstword" -- "$file"
		printf "$dmy" ) | awk '
			$1 ~ /^[0123456789]/ {print $0}' | func_rawline_input "$octfile"
	done
fi


if [ "$mposix" = "1" ] ; then

	if [ "$opt_r" = "-1" ] ; then
		# 基本的にはgnuと一緒だけど-Z系が無いのでその辺を補完。
	
		# grepはlでファイルのみ。発見次第打ち切り。未発見でerrが返る。execを入れると劇的に遅くなる
		# 後工程に任せる。gnuでも4msが131msに膨れ上がる。でもこれ以外に検索方法は無い。
		# locate> posixではない。
		# print0が10msでprintf \000は441ms
		# posixでは\000ファイル出力ができないのでgrep絞りは後で。
		# grepは-Zが無いので使用不可
		
		findcmd="find -L ./ -type f -a -exec printf '%s\000' '{}' ';'"
	else
		ptn='*/*/*'		#>> pwdは/一つだけ。//はサブディレクトリ。///はサブサブ。
		for ii in `seq 1 $opt_r `
		do
			ptn="$ptn"'/*'
		done
		findcmd="find -L ./ -path '$ptn' -prune -o -type f -a -exec printf '%s\000' '{}' ';'"
	fi
	
	# fix. one file
	if [ "$sfile" != "./" ] ; then	# grepの高速検索
		findcmd='cat "$sfile"'
	fi

	grep_cnt=''
	for ii in `seq 1 $#`
	do
		grep_cnt="$grep_cnt grep -le \"\$$ii\" -- \"\$file\" ;"
	done
	grep_cnt="( $grep_cnt ) | wc -l"
	# ( grep -le "$1" -- "$file" ; grep -le "$2" -- "$file" ; grep -le "$3" -- "$file" ; ) | wc -l
	# マルチgrepの代用。一つのファイルに大して単語ごとに検索をかけてヒットを数える。
	# \000が使えずストリーム処理が出来ないので、せめてもの抵抗。少しでも絞る。
	# 3 wordなら3になるはず。小さければどれかの単語が入ってないのでスキップ。

#---main
	eval "$findcmd" |
	eval "$cmd_fulloct" |
#	od -An -to1 -w16 -v | 
#	sed -e 's/ 000/@/g' | tr -d '\n' | tr ' ' '\134' | 
#	tr '@' '\n' |
	while read -r octfile
	do
		file=`printf "$octfile"'@'`
		file=${file%@}
		buf=`eval "$grep_cnt"`
		if [ "$buf" != "$#" ] ; then
			continue
		fi

#		octfull=`printf "$file\000" | func_rlp`	# fullpath. 
#		ファイルを絞ってから検査したい。10ms程度早い。微妙か。誤差範囲。むしろovhで遅い。
		cat "$file" | ( printf "$dmy" ; grep -n '' ; printf "$dmy" ) |
		func_rawline_input "$octfile"
	done
fi
