C+
C NAME:
C	iSetSymbol
C PURPOSE:
C	Portable way of setting 'symbol' values
C CALLING SEQUENCE:
	function iSetSymbol(cName,cValue,iTable)
C INPUTS:
C	cName		character*(*)		name of symbol
C	cValue		character*(*)		value to be assigned to symbol
C	iTable		integer			not used (maintained for backward
C						compatibility with VMS version)
C OUTPUTS:
C	iSetSymbol	integer			0: failed to set symbol
C						1: succesful setting symbol
C CALLS:
C	itrim, LogModFile
C SEE ALSO:
C	iGetSymbol, iDeleteSymbol, iSetLogical, iGetLogical, iDeleteLogical
C MODIFICATION HISTORY:
C	???-????, Paul Hick (UCSD/CASS; pphick@ucsd.edu)
C-
	    character	cName *(*)
	    character	cValue*(*)
	    integer	iTable

	iSetSymbol = iTable
	iSetSymbol = itrim(cName)
	if (iSetSymbol .gt. 0) iSetSymbol = LogModFile(1,'SYM',cName,cValue)

	return
	end
C+
C NAME:
C	iGetSymbol
C PURPOSE:
C	Portable way of retrieving 'symbol' values
C CALLING SEQUENCE:
	function iGetSymbol(cName,cValue)
C INPUTS:
C	cName		character*(*)		name of symbol
C OUTPUTS:
C	iGetSymbol	integer			0: failed to get value of symbol
C						1: succesful getting value of symbol
C	cValue		character*(*)		value of symbol
C CALLS:
C	itrim, LogModFile
C SEE ALSO:
C	iSetSymbol, iDeleteSymbol, iSetLogical, iGetLogical, iDeleteLogical
C MODIFICATION HISTORY:
C	???-????, Paul Hick (UCSD/CASS; pphick@ucsd.edu)
C-
	    character	cName *(*)
	    character	cValue*(*)

	iGetSymbol = itrim(cName)
	if (iGetSymbol .gt. 0) iGetSymbol = LogModFile(2,'SYM',cName,cValue)
	if (iGetSymbol .eq. 0) then
	    cValue = ' '
	else
	    iGetSymbol = itrim(cValue)
	end if

	return
	end
C+
C NAME:
C	iDeleteSymbol
C PURPOSE:
C	Portable way of deleting 'symbol' values
C CALLING SEQUENCE:
	function iDeleteSymbol(cName,iTable)
C INPUTS:
C	cName		character*(*)		name of symbol
C	iTable		integer			not used (maintained for backward
C						compatibility with VMS version)
C OUTPUTS:
C	iDeleteSymbol	integer			0: failed to delete symbol
C						1: succesful deleting symbol
C CALLS:
C	itrim, LogModFile
C SEE ALSO:
C	iSetSymbol, iGetSymbol, iSetLogical, iGetLogical, iDeleteLogical
C MODIFICATION HISTORY:
C	???-????, Paul Hick (UCSD/CASS; pphick@ucsd.edu)
C-
	    character	cName*(*)
	    integer	iTable

	iDeleteSymbol = iTable
	iDeleteSymbol = itrim(cName)
	if (iDeleteSymbol .gt. 0) iDeleteSymbol = LogModFile(3,'SYM',cName,' ')

	return
	end
C+
C NAME:
C	iSetLogical
C PURPOSE:
C	Portable way of setting 'logical' values
C CALLING SEQUENCE:
	function iSetLogical(cName,cValue,cTable)
C INPUTS:
C	cName		character*(*)		name of logical
C	cValue		character*(*)		value to be assigned to logical
C	cTable		character*(*)		not used (maintained for backward
C						compatibility with VMS version)
C OUTPUTS:
C	iSetLogical	integer			0: failed to set logical
C						1: succesful setting logical
C CALLS:
C	itrim, LogModFile
C SEE ALSO:
C	iSetSymbol, iGetSymbol, iDeleteSymbol, iGetLogical, iDeleteLogical
C MODIFICATION HISTORY:
C	???-????, Paul Hick (UCSD/CASS; pphick@ucsd.edu)
C-
	    character	cName *(*)
	    character	cValue*(*)
	    character	cTable*(*)

	character cDum		! Dummy to avoid compiler warning

	cDum = cTable
	iSetLogical = itrim(cName)
	if (iSetLogical .gt. 0) iSetLogical = LogModFile(1,'LOG',cName,cValue)

	return
	end
C+
C NAME:
C	iGetLogical
C PURPOSE:
C	Portable way of retrieving 'logical' values
C CALLING SEQUENCE:
	function iGetLogical(cName,cValue)
C INPUTS:
C	cName		character*(*)		name of logical
C OUTPUTS:
C	iGetLogical	integer			0: failed to get value of logical
C						1: succesful getting value of logical
C	cValue		character*(*)		value of logical
C INCLUDE:
	include   	'dirspec.h'
C CALLS:
C	itrim, LogModFile
C SEE ALSO:
C	iSetSymbol, iGetSymbol, iDeleteSymbol, iSetLogical, iDeleteLogical
C MODIFICATION HISTORY:
C	???-????, Paul Hick (UCSD/CASS; pphick@ucsd.edu)
C-
	    character	cName *(*)
	    character	cValue*(*)

	iGetLogical = itrim(cName)
	if (iGetLogical .gt. 0) iGetLogical = LogModFile(2,'LOG',cName,cValue)
	if (iGetLogical .eq. 0) then
	    cValue = ' '
	else
	    iGetLogical = itrim(cValue)

	    ! KLUDGE: cTemp and cHome are directories and should be defined with
	    ! a trailing backslash (on NT) or slash (on Unix). If there is no
	    ! trailing (back)slash functions like MkTmpFile will get into trouble.

	    if (cName .eq. cHome .or. cName .eq. cEnvi//cHome .or.
     &		cName .eq. cTemp .or. cName .eq. cEnvi//cTemp) then
		if (cValue(iGetLogical:iGetLogical) .ne. cTrail) then
		    cValue(iGetLogical+1:) = cTrail
		    iGetLogical = iGetLogical+iTrail
		end if
	    end if

	end if

	return
	end
C+
C NAME:
C	iDeleteLogical
C PURPOSE:
C	Portable way of deleting 'logical' values
C CALLING SEQUENCE:
	function iDeleteLogical(cName)
C INPUTS:
C	cName		character*(*)		name of symbol
C OUTPUTS:
C	iDeleteLogical	integer			0: failed to delete logical
C						1: succesful deleting logical
C CALLS:
C	itrim, LogModFile
C SEE ALSO:
C	iSetSymbol, iGetSymbol, iDeleteSymbol, iSetLogical, iGetLogical
C MODIFICATION HISTORY:
C	???-????, Paul Hick (UCSD/CASS; pphick@ucsd.edu)
C-
	    character	cName*(*)

	iDeleteLogical = itrim(cName)
	if (iDeleteLogical .gt. 0) iDeleteLogical = LogModFile(3,'LOG',cName,' ')

	return
	end
C+
C NAME:
C	LogModFile
C CALLING SEQUENCE:
	function LogModFile(iAct,cType0,cNameIn,cDef0)
C INPUTS:
C	iAct		integer		1: set logical/symbol
C					2: get logical/symbol
C					3: delete logical/symbol
C	cType0		character*(*)	'LOG': manipulate logicals
C					'SYM': manipulate symbols
C	cNameIn		character*(*)	name of logical/symbol
C
C	cDef0		character*(*)	(iAct=1 only) value of logical/symbol
C OUTPUTS:
C	LogModFile	integer		0: failure
C					1: success
C	cDef0		character*(*)   (iAct=2 only) value of logical/symbol
C INCLUDE:
	include		'dirspec.h'
C CALLS:
C	itrim, bGetLun, iFreeLun, iOSRenameFile, iOSDeleteFile
C PROCEDURE:
C >	Should not be called directly (used internally only by functions for
C	manipulating logicals and symbols).
C >	LogModFile should stay as simple as possible. In particular, do not use calls
C	to href=Say= or href=bOpenFile=, since this would create recursion (since they
C	in turn call LogModFile through calls to iSetSymbol etc.)
C
C >	On VMS there are system calls to manipulate logicals and symbols. This setup
C	maintains a set of 'logicals' and 'symbols' in a plain ascii file in the user
C	home directory. This addresses one of the main portability issues we ran into
C	when moving programs to Windows, Unix and Linux.
C >	VMS logicals are functionally equivalent to environment variables in Windows
C	and Linux (typically they refer to directories).
C	VMS symbols are loosely equivalent to aliases in Linux.
C DEPENDENCY TREE:
C	itrim
C	LogModFile
C		itrim
C		bGetLun
C			iGetLun
C		iFreeLun
C		iOSRenameFile *(system call)
C		iOSDeleteFile *(system call)
C MODIFICATION HISTORY:
C	???-????, Paul Hick (UCSD/CASS)
C	SEP-2007, Paul Hick (UCSD/CASS; pphick@ucsd.edu)
C	    Now uses TimeOut to pause program for 1 second
C-
	    integer	iAct

	    character	cType0*3
	    character	cNameIn*(*)
	    character	cDef0*(*)

	character	cLogDir*120
	!character	cLogSysName*10	/'LOGSYS.TXT'/
	character	cLogFilName*10	/'LOGFIL.TXT'/
	character	cLogTmpName*10	/'LOGTMP.TXT'/
	character	cLogOldName*10	/'LOGOLD.TXT'/
	character	cId*10		/'LogModFile'/

	character	cType*3
	character	cName*80
	character	cName0*81
	!character	cLogSys*120
	character	cLogFil*120
	character	cLogTmp*120
	character	cLogOld*120

	parameter	(L=3+1+80+1+256)
	character	cRec*(L)
	logical		bGetLun

	!-------
	! Needed to list all logicals and symbols (used by mkenv.f)

	integer		iLastRec	/0/
	character	cNextName*11	/'#next_name#'/

	save		iLastRec, cNextName

	cName0 = cNameIn
	if (cType0 .eq. 'LOG' .and. cName0(:iEnvi) .ne. cEnvi .and.
     &		cName0 .ne. cNextName) cName0 = cEnvi//cName0

	LogModFile = 0				! Initialize to 'failure'
	iName = itrim(cName0)
	if (iName .eq. 0) return		! Missing input

	if (.not. bGetLun(iU0,cLogFilName)) then! Reserve logical unit for log file
	    I = iListAllLun()
	    if (I .eq. 0) write (*,*) 'All logical units in use. Somebody is not calling iFreeLun'
	    return
	end if

	!-------
	! cLogDir should be set to a directory which exists and to which the user has access.

	if (cOpSys .eq. OS__UNIX .or. cOpSys .eq. OS__LINUX) then
	    call getenv('HOME',cLogDir)
	    iLogDir = itrim(cLogDir)
	    cLogDir(iLogDir+1:) = cTrail
	    iLogDir = iLogDir+iTrail

	    !cLogDir = '~'//cTrail		! This works on Sun Unix, but not on Linux
	    !iLogDir = itrim(cLogDir)
	else if (cOpSys .eq. OS__DOS) then
	    cLogDir = 'C:'//cTrail
	    iLogDir =   2 + iTrail 
	else					! VMS
	    cLogDir = 'SYS$LOGIN:'
	    iLogDir = itrim(cLogDir)
	end if

	cLogFil = cLogDir(:iLogDir)//cLogFilName
	cLogTmp = cLogDir(:iLogDir)//cLogTmpName
	cLogOld = cLogDir(:iLogDir)//cLogOldName

	!-------
	! There is a small probability that an unusual program termination
	! results in a non-existing LOGFIL.TXT file, while LOGOLD.TXT and
	! LOGTMP.TXT files survive. So we check for one of these files before
	! creating a new (empty) LOGFIL.TXT.

	open (iU0,file=cLogFil,status='OLD',iostat=I)
	I1 = 0
	do while (I .ne. 0 .and. I1 .lt. 5)
	    I1 = I1+1
	    write (*,*) 'LogModFile; Error opening ',cLogFil(:itrim(cLogFil)),' error code:',I,' attempt',I1
	    call TimeOut(1.0,0.0)
	    open (iU0,file=cLogFil,status='OLD',iostat=I)
	end do

	if (I .ne. 0) then	 		! Try using old log file as log file
	    if (iOSRenameFile(cLogOld,cLogFil) .ne. 0)
     &		open (iU0,file=cLogFil,status='OLD',iostat=I)
	else
	     if (I1 .ne. 0) write (*,*) 'LogModFile; opened ',cLogFil(:itrim(cLogFil)),' at attempt',I1
	end if

	if (I .ne. 0) then			! Try using tmp log file as log file
	    if (iOSRenameFile(cLogTmp,cLogFil) .ne. 0)
     &		open (iU0,file=cLogFil,status='OLD',iostat=I)
	end if

	if (I .ne. 0) then			! Open failed
	    iU0 = iFreeLun(-iU0)		! Release logical unit
	    if (iAct .ne. 1) return		! Return if retrieving/deleting

	    if (.not. bGetLun(iU0,cLogFilName)) return! Reserve logical unit for new log file

	    open (iU0,file=cLogFil,status='NEW',iostat=I)
	    if (I .ne. 0) then			! Open failed
		iU0 = iFreeLun(-iU0)		! Release logical unit
	    	return
	    end if
	end if

	if (iAct .ne. 2) then			! Changing log file: setting or deleting
	    if (.not. bGetLun(iU1,cLogFilName)) then! Reserve logical unit for tmp log file
	        iU0 = iFreeLun(iU0)		! Release logical unit for old log file
	        return
	    end if

	    !-------
	    ! Normally the temporary log file is cleaned up and an open with
	    ! status='NEW' works, but an abnormal program termination might
	    ! leave the temporary file lying around. The open statement with
	    ! status='NEW' would fail and it would no longer be possible to
	    ! change logical and symbol definitions until the LOGTMP.TXT file
	    ! is manually deleted. Using status='UNKNOWN' will overwrite an
	    ! existing temporary log file.

	    open (iU1,file=cLogTmp,status='UNKNOWN',iostat=I)
	    if (I .ne. 0) then			! Open failed
	        iU0 = iFreeLun( iU0)		! Release logical units
	    	iU1 = iFreeLun(-iU1)
	    	return
	    end if
	end if

	iRec = 0				! Initialize record counter (needed when cName0 = cNextName)

	read (iU0,'(A)',iostat=I) cRec		! Read record from log file
	do while (I .eq. 0)			! While read succesful

	    iRec = iRec+1			! Increment record counter

	    I1 = index(cRec,'=')
	    I2 = I1+index(cRec(I1+1:),'=')
	    cType = cRec(    :I1-1)		! Type: logical or symbol

	    if (iAct .eq. 1) then		! Set definition
		if (cType .eq. cType0) then	! Types match
 		    cName = cRec(I1+1:I2-1)	! Logical/symbol name
 		    if (cName .eq. cName0) then	! Name already exists
			cRec(I2+1:) = cDef0	! Update definition
			LogModFile = 1		! Done (but don't return)
		    end if
		end if				! Write old or updated record to temp file
		write (iU1,'(A)') cRec(:itrim(cRec))

	    else if (iAct .eq. 2) then 		! Get definition
		if (cType .eq. cType0) then	! Types match
		    cName = cRec(I1+1:I2-1)	! Logical/symbol name
		    if ( cName0 .eq. cName .or.	! Name exists
     &			(cName0 .eq. cNextName .and. iRec .gt. iLastRec)) then ! List all logicals/symbols

			cDef0 = cRec(I2+1:)	! Get definition
			LogModFile = 1		! Done
			iU0 = iFreeLun(iU0)	! Close log file and release logical unit

			if (cName0 .eq. cNextName) then ! If listing all logicals/symbols
			    cNameIn = cName	! Store name of next logical/symbol
			    iLastRec = iRec	! Store record number
			end if
			return
 		    end if
		end if

	    else if (iAct .eq. 3) then		! Delete definition
 		cName = cRec(I1+1:I2-1)		! Logical/symbol name
 						! If type and name match ..
		if (cType .eq. cType0 .and. cName .eq. cName0) then
		    LogModFile = 1		! .. done (but don't return)
		else				! .. otherwise
		    write (iU1,'(A)') cRec(:itrim(cRec)) ! .. retain record in temp file
		end if

	    end if

	    read (iU0,'(A)',iostat=I) cRec	! Read next entry from log file
	end do

	!-------
	! Setting definition: if entry not present yet, add it to the temp file

	if (iAct .eq. 1 .and. LogModFile .eq. 0) then
	    write (iU1,'(5A)') cType0(:itrim(cType0)),'=',cName0(:itrim(cName0)),'=',cDef0(:itrim(cDef0))
	    LogModFile = 1
	end if

	iU0 = iFreeLun(iU0)			! Close old log file and release logical unit
	if (iAct .eq. 2) then			! Get definition (but not found: LogModFile=0; temp file was never created)

	    if (cName0 .eq. cNextName) then	! All logicals/symbols have been processed
		iLastRec = 0			! Reset record number back to zero
		cNameIn = ' '			! Clear logical/symbol name
	    end if

	    ! Would be nice to be able to search a system wide file here
	    ! but what system wide directory does always exist?

	    !open (iU0,file=cLogFil,status='OLD',iostat=I)

	    return			
	end if				

	iU1 = iFreeLun(iU1)			! Close temp file and release logical unit
						! Replace old log file by temp file
						! (the OSRenameFile is supposed to overwrite an existing file, if it exists)
	if (iOSRenameFile(cLogFil,cLogOld) .eq. 0) then ! Back up old log file
	    LogModFile = 0
	    return
	end if

	if (iOSRenameFile(cLogTmp,cLogFil) .eq. 0) then ! Make temp file new log file
	    LogModFile = 0
	    return
	end if

	if (iOSDeleteFile(cLogOld) .eq. 0) then ! Delete the backed up old log
	    LogModFile = 0
	    return
	end if

	return
	end
