;
; msgs.s
;
; Message file handling (MDW)
;
;  1994-1998 Straylight
;

;----- Licensing note -------------------------------------------------------
;
; This file is part of Straylight's Sapphire library.
;
; Sapphire 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 2, or (at your option)
; any later version.
;
; Sapphire 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 Sapphire.  If not, write to the Free Software Foundation,
; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

;----- Standard header ------------------------------------------------------

		GET	libs:header
		GET	libs:swis

;----- External dependencies ------------------------------------------------

		GET	sapphire:alloc
		GET	sapphire:sapphire
		GET	sapphire:string
		GET	sapphire:res
		GET	sapphire:resources

;----- Main code ------------------------------------------------------------

		AREA	|Sapphire$$Code|,CODE,READONLY

; --- msgs_load ---
;
; On entry:	R0 == pointer to filename
;
; On exit:	May return an error
;
; Use:		Reads in the given messages file.

		EXPORT	msgs_load
msgs_load	ROUT

		STMFD	R13!,{R0-R5,R12,R14}	;Stack some registers
		WSPACE	msgs__wSpace		;Find my workspace

		; --- How big is the file? ---

		MOV	R1,R0			;Point to the filename
		MOV	R0,#17			;File info please
		SWI	XOS_File		;Get the info
		BVS	%99msgs_load		;Make the error if not OK
		TST	R0,#1			;Is it a file?
		BEQ	%80msgs_load		;No -- ignore it then

		; --- Allocate a heap block for it ---

		ADD	R3,R4,#1+8		;Get file size + 1 +size word
		MOV	R0,R3			;Get the size
		BL	alloc			;Try to allocate the block
		BLCS	alloc_error		;If failed, find error
		BCS	%99msgs_load		;And handle it
		MOV	R2,R0			;Get the pointer safely

		; --- Remember block position ---

		LDR	R14,msgs__list		;Find old list head
		STR	R14,[R2,#0]		;Save that here
		STR	R2,msgs__list		;Store this block away
		ADD	R3,R2,R3		;Get pointer to end of block
		STR	R3,[R2,#4]		;And save it
		ADD	R2,R2,#8		;Point to file space
		STMFD	R13!,{R2,R3}		;Save these registers

		; --- Load the messages file into buffer ---

		MOV	R0,#16			;Load file into memory
		MOV	R3,#0			;Load into my buffer
		SWI	XOS_File		;Load the file now
		BVS	%98msgs_load		;If error, then free block

		; --- Scan through the messages, and NULL terminate them ---

		LDMFD	R13!,{R2,R3}		;Get the start/end of mesgs
		SUBS	R3,R3,R2		;Get the length
		MOV	R1,#0			;The NULL byte
10msgs_load	LDRB	R0,[R2]			;Get a character
		CMP	R0,#' '			;Is it a control character
		STRLTB	R1,[R2]			;Yes, NULL terminate it
		SUBS	R3,R3,#1		;Decrement counter
		ADDGT	R2,R2,#1		;Increase index
		BGT	%10msgs_load		;And keep looping
		STRB	R1,[R2]			;Put NULL at end

		; --- Return to the user ---

80msgs_load	LDMFD	R13!,{R0-R5,R12,R14}	;Get registers
		BICS	PC,R14,#V_flag		;And return without error

		; --- Free memory on error ---

98msgs_load	MOV	R5,R10			;Keep hold of error pointer
		LDMFD	R13!,{R2,R3}		;Restore old registers
		LDR	R14,[R2,#0]		;Load next link
		STR	R14,msgs__list		;Store back as list head
		MOV	R0,R2			;Free memory from heap
		BL	free			;Free the block
		MOV	R0,R5			;Restore the error pointer

		; --- Report the error ---

99msgs_load	ADD	R2,R0,#4		;Point to error message
		ADR	R0,msgs__loadError	;Point to error skeleton
		BL	msgs_error		;Create the error message
		ADD	R13,R13,#4		;Don't restore R0
		LDMFD	R13!,{R1-R5,R12,R14}	;Restore other registers
		ORRS	PC,R14,#V_flag		;And return the error

msgs__loadError	DCD	1
		DCB	"msgsMLE:Error loading messages file: %0",0

		LTORG

; --- msgs_build ---
;
; On entry:	R0 == pointer to a message string
;		R1 == pointer to output buffer
;
; On exit:	R0 == pointer to buffer (R1 on entry)
;		R1 == pointer to terminating null
;
; Use:		Builds a message string, by substituting message references
;		by their values.  Each reference of the form `$tag' (or
;		optionally `$(tag)', to avoid having to have a trailing)
;		space is replaced by the actual message.  A literal `$' sign
;		may be represented as `$$'.

		EXPORT	msgs_build
msgs_build	ROUT

		STMFD	R13!,{R1-R4,R14}	;Save some registers
		MOV	R2,R0			;Remember this pointer

05msgs_build	LDRB	R14,[R2],#1		;Load the next character
		CMP	R14,#'$'		;Is it a message reference?
		BEQ	%f00			;Yes -- handle it then
10msgs_build	CMP	R14,#&20		;Is it the end of the line?
		MOVCC	R14,#0			;Yes -- store a zero then
		STRB	R14,[R1],#1		;Store in output buffer
		BCS	%05msgs_build		;And keep on going
		SUB	R1,R1,#1		;Point back to the null
		LDMFD	R13!,{R0,R2-R4,PC}^	;And return to caller

		; --- Handle a dollar sign ---

00		LDRB	R14,[R2],#1		;Load the next character
		CMP	R14,#'$'		;Is this a literal dollar?
		CMPNE	R14,#&1F		;Or maybe a control character
		BLS	%10msgs_build		;Yes -- then ignore this
		CMP	R14,#'('		;Is the reference bracketted?
		MOVEQ	R3,#')'			;Yes -- remember this char
		MOVNE	R3,#' '			;Otherwise, use a space
		LDREQB	R14,[R2],#1		;Load next char if `(`
		MOV	R4,R1			;Remember this output place

00		CMP	R14,R3			;Is this the end char?
		CMPNE	R14,#&1F		;Or a control character?
		STRHIB	R14,[R4],#1		;Write the character out
		LDRHIB	R14,[R2],#1		;Load the next one maybe
		BHI	%b00			;And keep going until done

		MOV	R0,#0			;A nice zero word
		STRB	R0,[R4],#1		;Store the word away nicely
		CMP	R14,#' '		;Is this a space character?
		SUBLS	R2,R2,#1		;Yes -- move back one space

		MOV	R0,R1			;Point to the tag start
		BL	msgs_lookup		;Translate the message
00		LDRB	R14,[R0],#1		;Load a byte from here
		CMP	R14,#&20		;Finished here yet?
		STRCSB	R14,[R1],#1		;No -- store the byte
		BCS	%b00			;And keep on going
		B	%05msgs_build		;Go back to the main loop

		LTORG

; --- msgs_error ---
;
; On entry:	R0 == pointer to an error skeleton string:
;		        R0+0 == error number
;		        R0+4 == message tag-and-default (null-terminated)
;		R2-R11 == filler strings (not message tags)
;
; On exit:	R0 == pointer to translated error message (in error buffer)
;		R1 == pointer to null terminator of message
;
; Use:		Performs string sustitution on an error message (as done by
;		str_subst), but translating the error string.

		EXPORT	msgs_error
msgs_error	ROUT

		STMFD	R13!,{R0,R14}		;Save some registers

		; --- First, translate the message ---

		ADD	R0,R0,#4		;Point to the message tag
		BL	msgs_lookup		;Translate the tag

		; --- Perform the substitution ---

		SUB	R0,R0,#4		;Create a dummy error number
		BL	str_error		;So the real work

		; --- Now copy the error number over ---

		LDR	R14,[R13],#4		;Get the initial error ptr
		LDR	R14,[R14,#0]		;Get the user's error number
		STR	R14,[R0,#0]		;Store in substituted error
		LDMFD	R13!,{PC}^		;Return to caller

		LTORG

; --- msgs_lookup ---
;
; On entry:	R0 == message tag (and default message)
;
; On exit:	R0 == pointer to located message
;
; Use:		Returns the real message from its tag. If the tag does not
;		exist, then the default message is used. If that is not
;		supplied, then the tag name itself is returned (ie. R0
;		is preserved).

		EXPORT	msgs_lookup
msgs_lookup	ROUT

		STMFD	R13!,{R1-R6,R12,R14}	;Stack some registers
		WSPACE	msgs__wSpace		;Load pointer

		LDR	R14,msgs__flags		;Load my flags word
		TST	R14,#msFlag__inited	;Are we initialised?
		BEQ	%60msgs_lookup		;No -- handle specially

		LDR	R6,msgs__list		;Find message block list

		; --- Loop through the message lists ---

10msgs_lookup	CMP	R6,#-1			;Is this the very end?
		BEQ	%90msgs_lookup		;Yes -- return default then
		MOVS	R4,R6			;Reached the end yet?
		ADREQ	R14,msgs__global	;Otherwise find shared msgs
		LDMEQIA	R14,{R4,R5}		;Load those out please
		MOVEQ	R6,#-1			;And stop next time
		LDMNEIA	R4!,{R1,R5}		;Load data from block
		MOVNE	R6,R1			;Set up the link

		; --- Try matching the next message ---

20msgs_lookup	MOV	R1,R0			;Remember start of tag
		CMP	R4,R5			;Finished yet?
		BCS	%10msgs_lookup		;Yes -- move to next block
		LDRB	R2,[R1],#1		;Load next byte from tag
		LDRB	R3,[R4],#1		;And one from the message

		; --- Handle a comment ---

		CMP	R3,#';'			;Is it a semicolon?
		CMPNE	R3,#'#'			;Or a hash?
		CMPNE	R3,#'|'			;Or a vertical bar?
		BEQ	%50msgs_lookup		;Yes -- skip on to next msg

		; --- Main comparison loop ---

00		CMP	R2,#0			;End of tag?
		CMPNE	R2,#':'			;Could have a default
		CMPNE	R3,#'*'			;Or is tag wildcarded?
		BEQ	%f00			;Yes -- investigate then
		CMP	R3,#0			;End of message?
		CMPNE	R3,#'/'			;End of alternative?
		BEQ	%20msgs_lookup		;Yes -- retry from here
		CMPNE	R3,#':'			;End of message tags?
		BEQ	%50msgs_lookup		;Move to next message then
		CMP	R2,R3			;Do the two match at all?
		CMPNE	R3,#'?'			;Allow a wildcard here
		BNE	%30msgs_lookup		;No -- move to next message
		LDRB	R2,[R1],#1		;Load next byte from tag
		LDRB	R3,[R4],#1		;And one from the message
		B	%b00			;And keep on looping

		; --- Investigate a possible match ---

00		CMP	R3,#':'			;End of message's tag?
		CMPNE	R3,#'/'			;End of this alternative?
		CMPNE	R3,#'*'			;Or just a wildcard?
		BNE	%30msgs_lookup		;No -- try next message

00		CMP	R3,#0			;End of message?
		SUBEQ	R4,R4,#1		;Yes -- backtrack one then
		CMPNE	R3,#':'			;Found the message text yet?
		LDRNEB	R3,[R4],#1		;No -- skip on then
		BNE	%b00			;Loop till we find it
		MOV	R0,R4			;Point to the message
		LDMFD	R13!,{R1-R6,R12,PC}^	;Return to caller OK

		; --- Skip on to next alternative ---

30msgs_lookup	CMP	R3,#0			;End of message?
		CMPNE	R3,#'/'			;Or of the alternative?
		LDRNEB	R3,[R4],#1		;No -- move on then
		BNE	%b30			;And loop round

		B	%20msgs_lookup		;Try the next message now

		; --- Skip on to next message ---

50msgs_lookup	CMP	R3,#0			;End of message?
		LDRNEB	R3,[R4],#1		;No -- move on then
		BNE	%b50			;And loop round

		B	%20msgs_lookup		;Try the next message now

		; --- Not yet initialised ---
		;
		; This is a bit of an odd circumstance, but people can have
		; errors at all sorts of times.  We should try to translate
		; the message using the [Sapphire.Resources] DLL, although
		; not be overly upset if this isn't possible -- either the
		; default message (English) will be used, or maybe

60msgs_lookup	MOV	R6,R0			;Look after this for a while
		MOV	R0,#rsType_message	;Request a message resource
		BL	resources_find		;Find the resource please
		MOVCC	R0,R6			;Failed -- refind tag pointer
		BCC	%90msgs_lookup		;And return tag as
		MOV	R4,R0			;Point to message block base
		MOV	R5,R1			;Point to the block limit
		MOV	R0,R6			;Point to the tag again
		MOV	R6,#-1			;Stop after scanning please
		B	%20msgs_lookup		;Now search default messages

		; --- No luck at all ---

90msgs_lookup	MOV	R1,R0			;Look after tag pointer
00		LDRB	R14,[R1],#1		;Load next byte
		CMP	R14,#':'		;Is it a tag separator?
		MOVEQ	R0,R1			;Yes -- return this string
		CMPNE	R14,#0			;Or is it the end?
		BNE	%b00			;No -- keep on going then

		LDMFD	R13!,{R1-R6,R12,PC}^	;Return to caller with that

		LTORG

; --- msgs_init ---
;
; On entry:	--
;
; On exit:	--
;
; Use:		Initialises the message system.

		EXPORT	msgs_init
msgs_init	ROUT

		STMFD	R13!,{R0-R5,R12,R14}	;Stack some registers
		WSPACE	msgs__wSpace		;Get workspace (silly billy)

		; --- Return if we are already initialised ---

		LDR	R0,msgs__flags		;Are we initialised
		TST	R0,#msFlag__inited	;Test the bit
		LDMNEFD	R13!,{R0-R5,R12,PC}^	;Yes -- return

		ORR	R0,R0,#msFlag__inited	;Set the initialised bit
		STR	R0,msgs__flags		;Store the flags back

		; --- Initialise properly ---

		LDR	R0,[R13]		;Get the app name
		BL	res_init

		; --- Set up fallback position ---

		MOV	R0,#rsType_message	;Find message resource
		BL	resources_find		;Find the resource please
		MOVCC	R0,#0			;Not there -- remember this
		MOVCC	R1,#0			;We get base and limit
		ADR	R14,msgs__global	;Find the global block
		STMIA	R14,{R0,R1}		;Store them away nicely

		; --- Load messages file ---

		MOV	R14,#0			;Clear the messages list
		STR	R14,msgs__list		;Store that over now

		SUB	R13,R13,#16		;Make some space available
		MOV	R0,R13			;Point at this buffer
		BL	res_country		;Read the country name
		BL	%10msgs_init		;Try to load that file
		ADRCC	R0,msgs__ukName		;Failed -- try UK in case
		BLCC	%10msgs_init		;Try that quickly too
		ADRCC	R0,msgs__fileName	;Failed -- use normal name
		BLCC	%10msgs_init		;Try that quickly too
		ADD	R13,R13,#16		;Restore my buffer now
		LDMFD	R13!,{R0-R5,R12,PC}^

		; --- Look up a name, and load if it is a file ---

10msgs_init	STMFD	R13!,{R14}		;Save a register
		MOV	R1,R11			;Point to a buffer
		BL	res_find		;Try to find the file
		MOV	R1,R0			;Point to the name
		MOV	R0,#17			;Read file information
		SWI	XOS_File		;Get the info on it
		MOVVS	R0,#0			;If failed, say not there
		CMN	R0,#0			;Clear V flag and others
		MOVS	R0,R0,LSR #1		;Is it a file?
		MOV	R0,R1			;Point to the name again
		BLCS	msgs_load		;If able, load the file
		SWIVS	OS_GenerateError	;If failed, be unhappy
		LDMFD	R13!,{PC}		;And return C state

msgs__fileName	DCB 	"Messages",0
msgs__ukName	DCB	"UK",0

		LTORG

msgs__wSpace	DCD	0

;----- Workspace ------------------------------------------------------------

		^	0,R12
msgs__wStart	#	0

msgs__flags	#	4			;Flags word
msgs__list	#	4			;Linked list of messages
msgs__global	#	8			;Shared resource messages

msgs__wSize	EQU	{VAR}-msgs__wStart

msFlag__inited	EQU	(1<<0)			;Has been initialised

		AREA	|Sapphire$$LibData|,CODE,READONLY

		DCD	msgs__wSize		;Workspace size
		DCD	msgs__wSpace		;Workspace pointer
		DCD	256			;Scratchpad size
		DCD	msgs_init		;Initialisation

;----- That's all folks -----------------------------------------------------

		END


