#
#   File:    fscan-d.prc
#   Author:  Tom Yuyitung
#   Version: 1.29 03/11/27 12:56:26     
#
#   Copyright (c) 1993-1997 Halcyon Inc.
#
#   File Scanning Utility Procedures
#

#
# scanfile - call fscan get file stats and scan file for patterns
#
proc scanfile {} {
    
    #
    # if first time, initialize filename, scanmode, patterns, and reset data
    #
    if { [ ilookup -d "" fscan filename ] == "" } {
	set filename [ getModuleParam filename "" ]
	if { [ llength $filename ] == 0 } {
	    set filename "-"
	}
        define fscan filename $filename
	clearFscanData

	#
	# ensure that scanmode is one of incremental, full, tail, and ptail 
	# default to incremental
	#
        set scanmode [ getModuleParam scanmode "incremental" ]

	switch -regexp $scanmode \
	    {^[Ii]} {
	   	set scanmode "incremental"
	    } \
	    {^[Ff]} {
	   	set scanmode "full"
	    } \
	    {^[Tt]} {
	   	set scanmode "tail"
	    } \
	    {^[\Pp]} {
		set scanmode "ptail"

		#
		# retrieve saved fscan data
		#
		set pInode [ getFscanData inode ]
		set pFilename [ getFscanData filename ]
		if { $pInode != "" && $filename == $pFilename } {
		    define internal inode $pInode
		    define internal startline [ expr [ getFscanData endline ] + 1 ]
		    define internal persist 1
		} 
	    } \
	    default {
	   	set scanmode "incremental"
	    }

        define fscan scanmode $scanmode 
	getPatternData
    }

    #
    # call fscan to get file stats and scan file for patterns
    #
    set tmpValue  [ fscan ]


    set filename [ilookup -d "" fscan filename ]
    set tmpfilename \"$filename\"
    regsub $filename $tmpValue $tmpfilename rtValue

    #
    # save data
    #
    saveFscanData

    return $rtValue
}

proc cleanAcks { index data } {
    set name [ getRowName $index ]
    set reraise [ getRuleParm EDIT rCompare_match $name reraise ]
    if { ( [ _getMatches $name ] > 0 ) && ( $reraise == 1 ) } {
	#
        # clear any acks and recompute the alarm state
        #
        if { [ getAckByName $name ] != "" } {
            closeEvent rCompare $name ""
            clearAckByName $name
            clearAlarmState $name
            refreshStatus
        }
    }
    return $data
}

proc _getMatches { name } {
    return [ slookup -d "0" nmatches $name ]
}

#
# this is an obsolete proc (scr 4668)
#
#proc incrMatches { index increment } {
    #
    # get row name and value
    #
#   set name [ getRowName $index ]

    #
    # before agent is fully come up, the getRowName may return index
    #  we need to check prevdata:$index if the prevdata:$name does not
    #  exist
#   if { [ catch { ilookup prevdata $name } result ] } {
#       set value [ ilookup -d "0" prevdata $index ]
#       define prevdata $name $value
#       undefine prevdata $index
#   } else {
#       set value $result
#   }

    #
    # if pattern is changed, set reset flag to cleanup any acks ...
    #
#   set reset [ ilookup -d "0" reset $name ]
#   undefine reset $name

#   set scanmode [ getModuleParam scanmode "incremental" ]

    #
    # in full scan mode, matches = total matches
    #
#   if { [ regexp {^[Ff]} $scanmode ] } {
        #
        # if match changed, clean any old acks ...
        #
#       if { $value != [ set value [ slookup -d "0" pmatches $name ] ] } {
#           set reset 1
#       }
#   } elseif { $increment > 0 } {
	#
        # find new matches, clean any old acks ...
	#
#       incr value $increment
#       set reset 1
#   }

#   if { $reset } {
	#
	# clear any acks and recompute the alarm state
	#
#       if { [ getAckByName $name ] != "" } {
#           closeEvent rCompare $name ""
#           clearAckByName $name 
#           clearAlarmState $name
#    	    refreshStatus
#       }
#  }
#   define prevdata $name $value
#   return $value
#}

proc fileTimeFilter { index time } {
    if { $time > 0 } {
	return [ clock format $time -format {%b %d %Y %T} ]
    } else {
	return "n/a"
    }
}

proc resetMatches { name } {
    if { [ catch { getRowIndex $name } index ] } {
	ddl print warning "resetMatches: no such row name $name\n"
	return [ list "Pattern $name not found" ]
    }

    #
    # reset the matches value to 0
    #
    define data $index 0
    define prevdata $name 0

    #
    # get the total number of matches
    #
    set total [ toe_send [ locate total ] getValue $index ]

    #
    # sync the reset data to make it persistent
    #
    toe_send [ locate fscanstats ] syncResetData $name $total

    refreshValueAndTrap

    return [ list "Matches reset to 0" ]  
}


proc removePattern { index name } {
    #
    # clear any alarm status and attributes (limits, status command, and acks) 
    # associated with this row
    #
    set tableObject [ locate scanTable.scanEntry ]
    if { $tableObject != "" } {
        toe_send $tableObject cleanupRow $name CLEAR_PARMS
    } else {
        ddl print warning "could not find scanEntry object while removing entry $name in module [ getModuleParam moduleSpec ] \n"
    }
    #
    # clear pattern and associated data
    #
    undefine rowstatus $name 
    undefine patterns $name 
    undefine pdescs $name 
    undefine pmatches $name 
    undefine nmatches $name
    undefine pstates $name 
    undefine reset $name 

    #
    # cleanup prevdata from match node
    #
    toe_send [ locate scanTable.scanEntry.matches ] undefine prevdata $name

    return
}

proc activatePattern { index name } {

    if { [ lookup -d "" pstates $name ] != 1 } {
	define rowstatus $name 1
	define pstates $name 1
	
	#
	# for full scan module, force fscan to rescan the file
	#
	set scanmode [ getModuleParam scanmode "" ]
	if { [ regexp {^[Ff]} $scanmode ] } {
	    define fscan mtime 0
	} 
    }
}

proc deactivatePattern { index name } {

    if { [ lookup -d "" pstates $name ] != 0 } {
	define rowstatus $name 2
	define pstates $name 0
	#define pmatches $name 0
    }
}

proc togglePattern { name } {

    if { [ lookup -d "" pstates $name ] == 1 } {
	define pstates $name 0
    } else {
	define pstates $name 1
    }
    refreshValueAndTrap
    syncPatternList
}


proc syncPatternList {} {

    set patternList ""

    sliceforeach key value patterns {
	set state [ lookup -d "1" pstates $key ]
	set desc [ lookup -d "1" pdescs $key ]
	set rowstatus [ lookup -d "1" rowstatus $key ]
	lappend patternList [ list $key $rowstatus $state $desc $value ] 
    }

    setAttribute patternList $patternList
}


proc syncResetData { name total } {

    set resetTotals ""

    #
    # get the current inode
    #
    set inode [ ilookup -d "0" fscan inode ]  

    #
    # check whether file currently exists
    #
    if { $inode == 0 } {
	#
	# file does not exist, clear the inode and reset data
	#
	undefine value inode 
	undefine reset

    } else {
    	#
    	# file exists, get the inode associated with previous reset data
    	#
    	set reset_inode [ getAttribute inode "" ]  

	#
	# if the inodes are different 
	#
	if { $inode != $reset_inode } {
	    #
	    # clear the old reset data
	    #
	    undefine reset

	    #
	    # set the inode if there is new reset data
	    #
	    if { $total != 0 } {
		define value inode $inode
     		define reset $name $total
	    }

	} else {
	    #
	    # inodes are the same, combine with existing reset data
	    #
	    if { $total != 0 } {
		define reset $name $total
	    } else {
		undefine reset $name
	    }
	}

	#
	# if there is reset data, compile the reset data list
	#
	if { [ entries reset ] != "" } {
	    sliceforeach key value reset {
	    	lappend resetTotals [ list $key $value ] 
	    }

	} else {
	    #
	    # no reset data, clear the inode if it is set 
	    #
	    if { $reset_inode != "" } {
	    	undefine value inode
	    }
	}
    }
    setAttribute resetTotals $resetTotals
}

proc getPatternData {} {
    #
    # get pattern info
    #
    set patternList [ getAttribute patternList "" ]

    undefine pstates
    undefine pdescs
    undefine patterns
    undefine rowstatus

    if { $patternList != "" } {

        set i 1
        set rowstatusid [ locate scanTable.scanEntry.rowstatus ] 

	foreach entry $patternList {
	    lextract $entry 0 name 1 rowstatus 2 state 3 desc 4 pattern

	    define pstates $name $state 
	    define rowstatus $name $rowstatus 
	    define pdescs $name $desc 
	    define patterns $name $pattern 

            #
            # set the rowstatus
            #
            toe_send $rowstatusid setRowStatus $i $name $rowstatus 
	    incr i
	}
    }

    #
    # if the reset inode is set, get the reset data
    #
    if { [ getAttribute inode "" ] != "" } {

	set resetTotals [ getAttribute resetTotals "" ]

	undefine reset
	if { $resetTotals != "" } {
	    foreach entry $resetTotals {
		lextract $entry 0 name 1 total
		define reset $name $total 
	    }
	}
    }
}

proc nameToKey { name } {
    regsub -all {\.} $name {/} key
    regsub -all { } $key {_} key
    return $key
}

proc keyToName { key } {
    regsub -all {/} $key {.} name
    regsub -all {_} $name { } name
    return $name
}

proc setFilename { filename } {

    if { $filename != [ ilookup -d "" fscan filename ] } {
	define fscan filename $filename
	refreshValueAndTrap
    }
    return $filename
}


proc setPattern { attrib name value } {

    set key [ nameToKey $name ] 
   
    #
    # set newpattern to trigger fscan if new pattern is added
    # 
    if { $attrib == "patterns" && \
      ![ string match $value [ lookup -d "" patterns $key ] ] } {
	define newpattern $key true
    }
    define $attrib $key $value 
    return $value 
}

proc addPattern { index name rowstatus } {

ddl print info "addPattern: $index $name $rowstatus <[ getValues ]>\n"

    set key [ nameToKey $name ] 

    define newpattern $key true

    if { $rowstatus == 1 } {
        define pstates $key "1" 
    } else {
        define pstates $key "0" 
    }
    define rowstatus $key $rowstatus 
}

proc waitrow {} {
     return [ checkWaitRow [ getParameter index ] ]
}

proc saveFscanData {} {
    #
    # save fscan data only if the scan mode is ptail
    #
    if { [ lookup -d "" fscan scanmode ] != "ptail" } {
	return
    }

    #
    # do not save fscan data if the file is a clog file
    #
    if { [ isClogFile ] } {
	return
    }
   
    #
    # do not save fscan data if not exist
    #
    set inode [ lookup -d "-1" fscan inode ]
    if { $inode == "-1" } {
	return
    }

    set filename [ lookup -d "" fscan filename ]

    if { [ catch { lookup fscan endline } end_line ] } {
	return
    }

    if { [ catch { lookup internal timeStamp } timeStamp ] } {
            set timeStamp [ clock seconds ]
    }

    setAttribute fscan "$timeStamp,$filename,$inode,$end_line"
}

proc getFscanData { variable } {
    #
    # retrieve the saved fscan data
    #
    set retrievedValue  [ split [ getAttribute fscan "" ] , ]

    #
    # if no data hase been saved before, return blank string
    #
    if { [ llength $retrievedValue ] != "4" } {
	return ""
    }

    switch $variable {
	"timeStamp" {
	    set rtvalue [ lindex $retrievedValue 0 ]
	}
	"filename" {
	    set rtValue [ lindex $retrievedValue 1 ]
	}
	"inode" {
	    set rtValue [ lindex $retrievedValue 2 ]
	}
	"endline" {
	    set rtValue [ lindex $retrievedValue 3 ]
	}
	default {
	    set rtValue ""
	}
    }

    return $rtValue
}

proc isClogFile {} {
    switch [ lookup -d "" internal isClogFile ] {
	"true" {
	    return 1
        }
	"false" {
	    return 0
        }
	default {
	    #
	    # use "clog createtime ?lofid" to test the file type 
	    #
	    set lofid [ lookup -d "null" fscan lofid ]
	    if { $lofid == "null" } {
		#
		# file not open
		#
	        return 0
	    } elseif { [ catch { clog createtime $lofid } result ] } {
		#
		# invalid lofid
		#
	        return 0
	    } elseif { $result == "0" } {
		#
		# not a clog file
		#
	        define internal isClogFile "false"
	        return 0
            } else {
	        define internal isClogFile "true"
	        return 1
            }
	}
    }
}

proc clearFscanData {} {
    #
    # remove all internal persist fscan data
    #
    undefine internal persist
    undefine internal startline
    undefine internal inode
    undefine isClogFile
}

