-- UserProfileImpl.mesa; Edited by McGregor on 18-Feb-82 11:23:34

DIRECTORY
  Convert USING [Int, Parse, Value],
  IOStream USING [Close, CreateFileStream, EndOf, GetChar, Handle, SetIndex],
  Rope USING [Compare, FromString, Ref, Size],
  UserProfile;

UserProfileImpl: MONITOR

IMPORTS Convert, IOStream, Rope
EXPORTS UserProfile =

BEGIN

name: Rope.Ref = "User.profile";
openCount: CARDINAL ← 0;
profile: IOStream.Handle;
missing: BOOLEAN ← FALSE;

Boolean: PUBLIC PROC [key: Rope.Ref, default: BOOLEAN ← FALSE]
	RETURNS [value: BOOLEAN] = BEGIN
	val: Rope.Ref;
	Open;
	IF missing THEN RETURN[default];
	val ← GetLineValue[key];
	Close;
	IF val=NIL THEN RETURN[default];
	IF Rope.Compare[val, "TRUE", FALSE]=0 THEN RETURN[TRUE];
	IF Rope.Compare[val, "FALSE", FALSE]=0 THEN RETURN[FALSE];
	RETURN[default];
	END;

Number: PUBLIC PROC [key: Rope.Ref, default: LONG INTEGER ← 0]
	RETURNS [value: LONG INTEGER] = BEGIN
	-- we'd like to use Convert.IntFromRope here, but the resultant
	-- fatal compiler errors in pass 5 wasted one afternoon already...
	IntFromRope: PROC [r: Rope.Ref] RETURNS [i: Convert.Int] = BEGIN
		v: Convert.Value ← Convert.Parse[[rope[r]]].value;
		RETURN[NARROW[v, Convert.Value[signed]].signed];
		END;
	val: Rope.Ref;
	Open;
	IF missing THEN RETURN[default];
	val ← GetLineValue[key];
	Close;
	IF val=NIL THEN RETURN[default];
	value ← IntFromRope[val ! ANY => {value ← default; CONTINUE}]
	END;

String: PUBLIC PROC [key: Rope.Ref, default: Rope.Ref ← NIL]
	RETURNS [value: Rope.Ref] = BEGIN
	Open;
	IF missing THEN RETURN[default];
	value ← GetLineValue[key];
	Close;
	IF value=NIL OR Rope.Size[value]=0 THEN RETURN[default];
	END;

Open: PUBLIC ENTRY PROC = BEGIN
	openCount ← openCount+1;
	IF openCount#1 OR missing THEN RETURN;	-- already open
	profile ← IOStream.CreateFileStream[fileName: name,
		accessOptions: read, createOptions: oldOnly
		! ANY => {MissingProfile[]; CONTINUE}];
	IF missing THEN RETURN;
	lineKey ← NEW[TEXT[50]];
	lineValue ← NEW[TEXT[250]];
	END;

Close: PUBLIC ENTRY PROC = BEGIN
	IF openCount=0 THEN ERROR;	-- Closed once too many
	openCount ← openCount-1;
	IF openCount#0 OR missing THEN RETURN;	-- already open
	IOStream.Close[profile];
	profile ← NIL;
	lineKey ← lineValue ← NIL;
	END;

MissingProfile: INTERNAL PROC = BEGIN
	-- maybe should notify the user someday
	missing ← TRUE;
	END;

lineKey, lineValue: REF TEXT;

GetLineValue: ENTRY PROC [key: Rope.Ref] RETURNS [value: Rope.Ref ← NIL] = BEGIN

	ReadALine: PROC RETURNS [eof: BOOLEAN] = BEGIN
		char: CHARACTER;
		lineKey.length ← lineValue.length ← 0;
		-- read key
		UNTIL (eof ← IOStream.EndOf[profile]) OR
		    (char ← IOStream.GetChar[profile]) = ': DO
			IF char=15C THEN {lineKey.length ← 0; LOOP};
			lineKey[lineKey.length] ← char;
			lineKey.length ← lineKey.length+1;
			ENDLOOP;
		-- skip spaces
		UNTIL (eof ← IOStream.EndOf[profile]) OR
		    (char ← IOStream.GetChar[profile]) # '  OR
		    char = 15C DO ENDLOOP;
		-- read value
		UNTIL (eof ← IOStream.EndOf[profile]) OR
		    char = 15C DO
		    	lineValue[lineValue.length] ← char;
			lineValue.length ← lineValue.length+1;
			char ← IOStream.GetChar[profile];
			ENDLOOP;
		END;

	IOStream.SetIndex[profile, 0];
	UNTIL ReadALine[] DO
		IF Rope.Compare[key, LOOPHOLE[lineKey], FALSE]=0 THEN
			RETURN[Rope.FromString[lineValue]];
		ENDLOOP;
	END;

END.