#!/usr/bin/gawk -f 
# 20140825/allfd
BEGIN{
	program="fopfilter"
	version="0.0.7"
	showComments=0
	showNumbers=1
	onlyNumbers=0
	showDebug=0
	# ein Argument ist der Aufruf gawk, was nach den Argumenten noch übrig
	# bleibt, ist die Zahl der ausgewerteten Dateien
	files=ARGC-1 
	exactMatch=0
	matchcount=0
	errorCount=0
	exitCode=0
	onlyActive=0
	stoppedAt=0
	verbose=0
	#ARgumente auswerten
	readArguments()
}
#/^#.*/{print}
#/^[[:space:]]*[0-9]+.*[]].*/{print $1,$2,$3,$4,$5,$6,$7,$8}
#/^[[:space:]]*[0-9]+.*[^]].*/{
#	print $1,$2,$3,$4,$5,$6,"[S]",$7
#}
#wenn Zeile eine Kommentarzeile
$0 ~ /^[[:space:]]*#.*/ {handleComments()}
#
$0 ~ stopFilter && $0 !~ filter {handleStopfilter()}
$0 ~ filter {handleFilter()}
END {handleEnd()}

function readArguments() {
	for(i=1; i < ARGC; i++){
		debugMsg(sprintf("%s von %s ist %s",i,ARGC,ARGV[i]))
		if(ARGV[i] ~ /^((--mask)|(-s)|(--screen))$/) {
			#Maskennummer
			files=files-2
			ARGV[i]=""
			if(ARGV[++i] ~ /^[0-9]*$/){
				mask=ARGV[i]
				useFilter["mask"]=1
				ARGV[i]=""
			} else if (ARGV[i] !~ /^\*$/){
				wrongArgumentMessageAndReset("Keine Maskennummer in %s erkannt",i)
			}
		}else if (ARGV[i] ~ /^((--command)|(-c))$/) {
			#Kommando (zeigen, änder, neu)
			files=files-2
			ARGV[i]=""
			if(ARGV[++i] ~ /^.*$/){
				command=ARGV[i]
				useFilter["command"]=1
				ARGV[i]=""
			} else if (ARGV[i] !~ /^\*$/){
				ARGV[i]=""
				wrongArgumentMessageAndReset("Kommando %s nicht erkannt",i)
			}
		}else if (ARGV[i] ~ /^((--event)|(-e))$/) {
			#Event
			files=files-2
			ARGV[i]=""
			if(ARGV[++i] ~ /^(buttonvor|buttonnach|feldfuell|feldpruef|feldaus|maskein|maskpruef|workflow|maskaus|multisite|maskabbr|maskende|zeileloevor|zeileloenach|zeileeinvor|zeileeinnach|zeilewechsel|zeilemark|zeilebewnach|zeilebewvor)$/){
				event=ARGV[i]
				useFilter["event"]=1
				ARGV[i]=""
			} else if (ARGV[i] !~ /^\*$/){
				wrongArgumentMessageAndReset("Event %s nicht erkannt",i)
			}
		}else if (ARGV[i] ~ /^((--key)|(-k))$/) {
			#Taste
			files=files-2
			ARGV[i]=""
			if(ARGV[++i] ~ /^hole$/){
				key=ARGV[i]
				useFilter["key"]=1
				ARGV[i]=""
			} else if (ARGV[i] !~ /^\*$/){
				wrongArgumentMessageAndReset("Taste %s nicht erkannt",i)
			}
		}else if (ARGV[i] ~ /^((--field)|(-t))$/) {
			#Variable
			files=files-2
			ARGV[i]=""
			if(ARGV[++i] ~ /^([[:alpha:]][[:alnum:]]*)$/){
				field=ARGV[i]
				useFilter["field"]=1
			} else if (ARGV[i] !~ /^\*$/){
				wrongArgumentMessageAndReset("%s sieht nicht wie ein Variablenname aus.",i)
			}
			ARGV[i]=""
		}else if (ARGV[i] ~ /^(--part)|(-p)$/) {
			#Kopf oder Tabelle
			files=files-2
			ARGV[i]=""
			if(ARGV[++i] ~ /^[KT]$/){
				part=ARGV[i]
				useFilter["part"]=1
				ARGV[i]=""
			} else if (ARGV[i] !~ /^\*$/){
				wrongArgumentMessageAndReset("%s ist ein unbekannter Teil",i)
			}
		}else if (ARGV[i] ~ /^(--continue-status)$/) {
			#[C], [S] oder leer
			files=files-2
			ARGV[i]=""
			if(ARGV[++i] ~ /^[SC]$/){
				continuestatus=ARGV[i]
				useFilter["continuestatus"]=1
			} else if (ARGV[i] !~ /^\*$/){
				wrongArgumentMessageAndReset("%s ist ein unbekannter Fortsetzungsstatus",i)
			}
			ARGV[i]=""
		}else if (ARGV[i] ~ /^(--fop)$/) {
			#Programmname
			files=files-2
			ARGV[i]=""
			if(ARGV[++i] ~ /^.+$/){
				fop=ARGV[i]
				useFilter["fop"]=1
			} else if (ARGV[i] !~ /^\*$/){
				ARGV[i]=""
				wrongArgumentMessageAndReset("%s sieht nicht wie ein FOP-Name aus.",i)
			}
			ARGV[i]=""
		}else if (ARGV[i] ~ /^(--row)$/) {
			#ganze Zeile
			files=files-2
			ARGV[i]=""
			if(ARGV[++i] ~ /^.+$/){
				row=ARGV[i]
				useFilter["row"]=1
				ARGV[i]=""
			} else if (ARGV[i] !~ /^\*$/){
				wrongArgumentMessageAndReset("%s sieht nicht wie eine Filter-Zeile aus.",i)
			}
		}else if (ARGV[i] ~ /^--with-comments$/ ){
			files--
			showComments=1
			ARGV[i]=""
		}else if (ARGV[i] ~ /^--without-line-numbers$/ ){
			files--
			showNumbers=0
			ARGV[i]=""
		}else if (ARGV[i] ~ /^--only-line-numbers$/ ){
			files--
			onlyNumbers=1
			ARGV[i]=""
		}else if (ARGV[i] ~ /^--exact-match$/ ){
			files--
			exactMatch=1
			ARGV[i]=""
		}else if (ARGV[i] ~ /^((--verbose)|(-v))$/) {
			files--
			verbose=1
			ARGV[i]=""
		}else if (ARGV[i] ~ /^--debug$/ ){
			files--
			showDebug=1
			ARGV[i]=""
		}else if (ARGV[i] ~ /^--only-active$/ ){
			files--
			onlyActive=1
			ARGV[i]=""
		}else if (ARGV[i] ~ /^[-][-]?.+$/ ){
			wrongArgumentMessageAndReset("falsche Option %s",i)
		}
	}
	if(errorCount==0){
		filter=getFilter()
	}else{
		usageAndExit()
	}
}

function debugMsg(msg){
	if(showDebug){
		writeToStdErr(msg)
	}
}

function wrongArgumentMessageAndReset(msg, ix) {
	msg=sprintf(msg,ARGV[ix])
	ARGV[ix]=""
	writeToStdErr(msg)
	errorCount++
	exitCode=2
}

function errorMsg(msg){
	writeToStdErr(msg)
	errorCount++
	if(exitCode==0){
		exitCode=2
	}
	usageAndExit()
}

function writeToStdErr(msg) {
	sys=sprintf("echo \"%s\" >&2",msg) 
	system(sys)
}

function usageAndExit() {
	usage()
	exit exitCode
}

function usage(){
	msg=sprintf("\n\nAufruf:\n\n%s -- [OPTION]... [DATEI]...\n\nOptionen:\n\n",program)
	#-----------|--------------------------------------------------------------------------------|
	msg=sprintf("%s     --                                  awk- und Skript-Optionen trennen (erforderlich) \n",msg)
	msg=sprintf("%s     --row <fop.txt-Zeile>		Gesamte Zeile suchen statt \n",msg)
	msg=sprintf("%s     					Einzelfilter, kann nicht mit\n",msg)
	msg=sprintf("%s     					Einzelfilter-Optionen kombiniert\n",msg)
	msg=sprintf("%s     					werden.",msg)
	msg=sprintf("%s\n\n\nEinzelfilter-Optionen:\n\n",msg)
	msg=sprintf("%s -s, --mask, --screen <maskennummer>\n", msg )
	msg=sprintf("%s -c, --command <Dateikommando> \n", msg)
	msg=sprintf("%s -e, --event (maskein|maskaus|buttonach|...) \n", msg)
	msg=sprintf("%s -k, --key <Taste> \n", msg)
	msg=sprintf("%s -t, --field <Maskenfeld> \n", msg)
	msg=sprintf("%s -p, --part (K|T|*) 			Kopf- oder Tabellenteil\n", msg)
	msg=sprintf("%s     --continue-status (S|C)		Weitersuchen-Kennzeichen\n", msg)
	msg=sprintf("%s     --fop <FOP-Name> 			\n", msg)
	msg=sprintf("%s\n\n\nSonstige Optionen:\n\n",msg)
	#-----------|--------------------------------------------------------------------------------|
	msg=sprintf("%s     --exact-match			Die angegebenen Filter-Optionen müssen\n", msg )
	msg=sprintf("%s                  			genau erfüllt werden (Standard ist,\n", msg )
	msg=sprintf("%s     					dass z.B. bei --event maskein auch\n", msg )
	msg=sprintf("%s     					Zeilen gefunden werden, die in der\n", msg )
	msg=sprintf("%s     					betreffenden Spalte * enthalten).\n\n", msg )
	msg=sprintf("%s     --only-active			Nur Zeilen vor einem Stop werden\n", msg )
	msg=sprintf("%s 					angezeigt.\n\n", msg )
	msg=sprintf("%s     --verbose				Vermisste (und bei --only-active auch\n", msg )
	msg=sprintf("%s     					gestoppte Zeilen) werden mit \n", msg )
	msg=sprintf("%s                  			Kommentar an STDERR geschickt.\n\n", msg )
	msg=sprintf("%s     --with-comments			auch auskommentierte Zeilen werden\n", msg )
	msg=sprintf("%s                    			angezeigt, wenn sie erkannt werden.\n\n", msg )
	msg=sprintf("%s     --without-line-numbers		Zeilennummern werden nicht angezeigt.\n\n", msg )
	msg=sprintf("%s     --only-line-numbers		Es werden nur die Zeilennummern\n", msg )
	msg=sprintf("%s					angezeigt.\n\n", msg )
	msg=sprintf("%s     --debug				Einige Informationen werden an STDERR\n", msg )
	msg=sprintf("%s                  			geschickt.\n\n", msg )
	msg=sprintf("%s\n\n\nExitcodes:\n\n",msg)
	msg=sprintf("%s     0					Treffer, keine Fehler.\n", msg )
	msg=sprintf("%s     1					Keine Treffer, keine Fehler.\n", msg )
	msg=sprintf("%s     8					Bei --only-active: Gestoppte Zeilen,\n", msg )
	msg=sprintf("%s     					keine Fehler.", msg )
	writeToStdErr(msg)
}

function getFilter(){
	if(useFilter["row"]){
		checkRow()
	}else{
		if(onlyActive){
			errorMsg("--only-active erfordert --row")
		}
		if(verbose){
			errorMsg("--verbose erfordert --row")
		}
	}
	debugMsg(sprintf("Maske: %s\nCom: %s\nevent: %s\nkey: %s\nfield: %s\npart: %s\ncont: %s\nfop: %s\n",mask,command,event,key,field,part,continuestatus,fop))
	filter=""
	if(continuestatus && ! fop) {
		errorMsg("--continue-status erfordert --fop")
	}
	extendFilter(mask,"mask")
	extendFilter(command,"command")
	extendFilter(event,"event")
	extendFilter(key,"key")
	extendFilter(field,"field")
	extendFilter(part,"part")	
	extendFilter(continuestatus,"continuestatus")	
	extendFilter(fop,"fop")
	if(showComments==1){
		filter=sprintf("(^|^[#[:space:]]+[^*[:digit:]]*)%s",filter)	
	}else{
		filter=sprintf("^[[:space:]]*%s",filter)
	}
	return filter
}

function checkRow() {
	if(row ~ /^.*[#]+.*/){
		writeToStdErr("Filterzeilen dürfen keine Kommentare enthalten")
		exitCode=6
		exit exitCode
	}
	if(useFilter["mask"] || useFilter["command"] || useFilter["event"] || useFilter["key"] || useFilter["field"] || useFilter["part"] || useFilter["continuestatus"] || useFilter["fop"]){
		errorMsg("Die Option --row kann nicht zusammen mit Einzelfiltern verwendet werden.")
	}
	splitCount=split(row,filters," ")
	for (i=1;i<=splitCount;i++) {
		debugMsg(sprintf("%s. Filter: %s",i,filters[i]))
	}
	if(splitCount<7){
		writeToStdErr(sprintf("Die Filterzeile enthält nur %s Elemente",splitCount))
		exitCode=7
		exit exitCode
	}
	mask=setFilter(filters,1,"mask")
	command=setFilter(filters,2,"command")
	event=setFilter(filters,3,"event")
	key=setFilter(filters,4,"key")
	field=setFilter(filters,5,"field")
	part=setFilter(filters,6,"part")
	continuestatus=setFilter(filters,7,"continuestatus",splitCount)
	fop=setFilter(filters,8,"fop",splitCount)
}

function setFilter(filters,k,filtername,maxIndex){
	useFilter[filtername]=1
	if(k<7){
		return filters[k]
	}
	if(k == 7) {
		if( filters[k] ~ /.*\[C\]/){
			return "C"
		}else if ( filters[k] ~ /.*\[S\]/){
			return "S"
		}else if (maxIndex==7){
			return ""
		}
	}
	if(k == 8){
		if (k>maxIndex || ! continuestatus){
			k = 7
		}
		return filters[k]
	}
}

function extendFilter(varName,idxVarName) {
	debugMsg(sprintf("Filter %s wird ausgewertet",idxVarName))
	if(idxVarName=="continuestatus"){
		if(! continuestatus){
			continuestatus="."
			extendFilter(continuestatus,"continuestatus")
		}else if(varName=="C"){
			filter=sprintf("%s[[]C[]][[:space:]]+",filter)
		}else{
			debugMsg("im Nicht-C-Zweig")
			filter=sprintf("%s([[]%s[]][[:space:]]+)?",filter,varName)
		}
		return
	}else if(idxVarName=="fop"){
		if (useFilter[idxVarName]){
			filter=sprintf("%s[^/[:alnum:][]*%s",filter,varName)
		}else{
			filter=sprintf("%s[^[[:space:][:alnum:]]*",filter)
		}
		filter=sprintf("%s(([[:space:]]*$)|([[:space:]#]+.*))",filter)
		return
	}else if(useFilter[idxVarName]){
		if(exactMatch){
			wildcard=""
		}else{
			wildcard="*|"
		}
		filter=sprintf("%s(%s%s)",filter,wildcard,varName) 
		stopFilter=sprintf("%s(%s%s)",stopFilter,"*|",varName)
	}else{
			filter=sprintf("%s[^[:space:]]+",filter)
			stopFilter=sprintf("%s[^[:space:]]+",stopFilter)
	}
	filter=sprintf("%s[[:space:]]+",filter)
	if(idxVarName!="fop" && idxVarName!="continuestatus")
		stopFilter=sprintf("%s[[:space:]]+",stopFilter)
	if(idxVarName=="mask"){
		if(showComments){
			filter=sprintf("(^[[:space:]]*#[[:space:]]*|^)%s",filter)
			stopFilter=sprintf("(^[[:space:]]*#[[:space:]]*|^)%s",stopFilter)
		}else{
			filter=sprintf("^%s",filter)
			stopFilter=sprintf("^%s",stopFilter)
		}
	}
}

function handleComments() {
	if(showComments==1)	{
		nix=""
	}else{
		next
	}
}

function handleStopfilter() {
	if(stoppedAt==0 && onlyActive==1 && $7 !~ /^\[[cC]\]$/)
		stoppedAt=NR
}

function handleFilter() {
	file=""
	number=""
	if(showNumbers)
		number=sprintf("%s%s",NR,OFS)
	if(files>1)
		file=sprintf("%s%s",FILENAME,OFS)
	if(onlyActive==0 || stoppedAt==0){
		if (onlyNumbers) {
		    printf("%s%s\n",file,number)
		}else {
		    printf("%s%s%s\n",file,number,$0)
		}
		matchcount++
		handleStopfilter()
	}else{
		if(verbose){
			ausgabe=sprintf("%s <-- Zeile %s gestoppt in Zeile %s.",$0,NR,stoppedAt)
			writeToStdErr(ausgabe)
		}
		errorCount++
	}
}

function handleEnd() {
	# Achtung wird auch nach einem Fehlerexit ausgeführt
	if(showDebug){
		if(filter){
			debugMsg(filter)
		}
		for (i=0;i<ARGC;i++)
			debugMsg(ARGV[i])
		debugMsg(sprintf("Dateien: %d", files))
		debugMsg(sprintf("Argumente: %d", ARGC))
		debugMsg(sprintf("Treffer: %d", matchcount))
		debugMsg(sprintf("Fehler: %d", errorCount))
		for (i in useFilter){
			useFilterMsg=sprintf("%s|%s->%s|",useFilterMsg,i,useFilter[i])
		}
		debugMsg(sprintf("Filter: %s", useFilterMsg))
		debugMsg(sprintf("StopFilter: %s", stopFilter))
	}
	if(errorCount==0 && exitCode==0){
		if(matchcount==0){ 
			exitCode=1
			if(verbose){
				ausgabe=sprintf("%s --> nicht gefunden.",row)
				writeToStdErr(ausgabe)
			}
		}else{
			exitCode=0
		}
	}
	if(onlyActive==1 && stoppedAt!=0 && exitCode==0 && errorCount>0)
		exitCode=8
	exit exitCode
}
