DIRECTORY
ARAccess USING [AppendChar],
Ascii USING [CR, LF, NUL, SP, TAB],
CmFile USING [ErrorCode, Handle, noMatch, TableDesc],
Event: TYPE USING [fileSystem],
EventTypes: TYPE USING [newSearchPath],
Heap: TYPE USING [Create, Flush, systemZone],
IO USING [Close, EndOfStream, GetChar, GetIndex, SetIndex, STREAM],
MFile USING
[dontRelease, Error, GetProperties, Handle, maxNameLength, ReadOnly, Release],
MStream USING [Create, Error, GetFile, Handle, PleaseReleaseProc, ReadOnly],
PFS USING [Close, Error, Open, OpenFile, PathFromRope, StreamFromOpenFile],
Rope USING [Compare, Concat, Equal, Fetch, IsEmpty, Length, ROPE, Substr],
Stream USING [EndOfStream, GetChar, GetPosition, Handle, SetPosition],
String USING [
AppendChar, AppendString, Compare, CopyToNewString, EquivalentString,
EquivalentSubString, StringBoundsFault, SubStringDescriptor],
StringLookUp USING [InTable, noMatch, TableDesc],
Supervisor USING [
AddDependency, AgentProcedure, CreateSubsystem, SubsystemHandle],
System USING [switches],
Token
USING [
Filtered, FilterProcType, GetCharProcType, Handle, Line,
nonQuote, Object, QuoteProcType--, WhiteSpace--];
Volume USING [GetLabelString, systemID];
Syntax of a Cm file is:
CmFile ::= Section | CmFile Section
Section ::= SectionName SectionBody
SectionName ::= beginSectionName NameBody endSectionName
NameBody ::= NameBody ~endSectionName | ~endSectionName
SectionBody ::= SectionBody SectionLine | SectionLine |
SectionLine ::= Name nameBreak MiscLine | MiscLine
Name ::= Name ~nameBreak | ~nameBreak
MiscLines ::= MiscLines MiscLine | MiscLine |
Any line that begins with two commentChar's is ignored, as is any
SectionLine that is only a MiscLine.
beginSectionName: CHARACTER = '[;
commentChar: CHARACTER = '-;
endSectionName: CHARACTER = '];
nameBreak: CHARACTER = ':;
separateSectionName: CHARACTER = ':;
From StringLookUpsA
emptyKey: CARD = CmFile.noMatch - 1;
ambiguous: CARD = emptyKey - 1;
UpperCase:
PROCEDURE[c:
CHAR]
RETURNS[
CHAR] ~{
IF c <= 'z AND c >= 'a THEN RETURN[c - 'a + 'A];
RETURN[c];
};
InitialInternal:
PUBLIC
PROCEDURE [
key, entry: Rope.ROPE, maxLength: CARDINAL, caseFold: BOOLEAN ¬ TRUE]
RETURNS [matchLength: CARDINAL] =
Input restrictions: key # NIL AND entry # NIL AND maxLength > 0
AND key.length >= maxLength AND entry.length >= maxLength
{
matchLength ¬ 0;
IF caseFold
THEN DO
IF UpperCase[key.Fetch[matchLength]] # UpperCase[entry.Fetch[matchLength]] THEN EXIT;
IF (matchLength ¬ matchLength + 1) = maxLength THEN EXIT;
ENDLOOP
ELSE DO
IF key.Fetch[matchLength] # entry.Fetch[matchLength] THEN EXIT;
IF (matchLength ¬ matchLength + 1) = maxLength THEN EXIT;
ENDLOOP;
};
InTable:
PUBLIC
PROCEDURE [key: Rope.
ROPE, table: CmFile.TableDesc, caseFold:
BOOLEAN ¬
TRUE,
noAbbreviation: BOOLEAN ¬ FALSE] RETURNS [index: CARDINAL] =
If table has duplicate entries, only the first one's index might be returned
{
keyLength: INT;
IF Rope.IsEmpty[key] THEN {index ¬ emptyKey; GOTO Return};
index ¬ CmFile.noMatch;
keyLength ¬ key.Length[];
FOR i:
CARDINAL
IN [0..table.len)
DO
entry: Rope.ROPE = table[i];
lengthsAreSame: BOOLEAN;
SELECT entry.Length[]
FROM
< keyLength => LOOP; -- empty entry takes this arm --
keyLength => lengthsAreSame ¬ TRUE;
ENDCASE => IF noAbbreviation THEN LOOP ELSE lengthsAreSame ¬ FALSE;
{
leng: INT ¬ InitialInternal[key, entry, keyLength, caseFold];
SELECT leng
FROM
< keyLength => NULL;
keyLength => {
IF lengthsAreSame THEN {index ¬ i; EXIT};
index ¬
(IF index = CmFile.noMatch THEN i ELSE ambiguous);
};
ENDCASE;
};
ENDLOOP;
EXITS Return => NULL;
};
Simple Interface Procedures
WhiteSpace: PUBLIC Token.FilterProcType = { RETURN[WhiteSpaceInline[c]]; };
WhiteSpaceInline:
PROCEDURE [c:
CHAR]
RETURNS [
--isWhiteSpace:--
BOOLEAN] =
INLINE {
RETURN[SELECT c FROM Ascii.SP, Ascii.TAB, Ascii.CR, Ascii.LF => TRUE, ENDCASE => FALSE]; };
FindSection:
PUBLIC
PROCEDURE [h: CmFile.Handle, title: Rope.
ROPE]
RETURNS [opened: BOOLEAN] =
BEGIN
localLine: Rope.ROPE ¬ NIL; --[lineSize];
repH: Handle = Validate[h];
genericIndex: LONG CARDINAL ¬ noSection;
qualifiedTitle: Rope.ROPE;
Volume.GetLabelString[Volume.systemID, localLine];
localLine ¬ ARAccess.AppendChar[localLine, separateSectionName];
String.AppendChar[localLine, separateSectionName];
localLine ¬ localLine.Concat[title];
String.AppendString[localLine, title];
qualifiedTitle ¬ localLine;
qualifiedTitle ← String.CopyToNewString[localLine, Heap.systemZone];
First try to locate both generic and specific sections
IF IsUserDotCm[repH]
THEN {
str: Rope.ROPE;
doneOne: BOOLEAN ¬ FALSE;
temp: HintHandle;
SELECT Rope.Compare[qualifiedTitle, title]
FROM
less => str ¬ qualifiedTitle;
greater => str ¬ title;
ENDCASE;
CheckHints[];
FOR l: HintHandle ¬ sectionHints, temp
UNTIL l =
NIL
DO
SELECT Rope.Compare[str, l.name]
FROM
equal => {
IF str = title THEN genericIndex ¬ l.position
ELSE repH.specificIndex ¬ l.position;
IF doneOne THEN EXIT;
temp ¬ l.next};
greater => {temp ¬ l.next; LOOP};
ENDCASE => temp ¬ l; -- didn't find first title, so advance to second title
IF doneOne THEN EXIT;
doneOne ¬ TRUE;
IF str = qualifiedTitle THEN str ¬ title ELSE str ¬ qualifiedTitle;
ENDLOOP;
DoneReading[]
}
ELSE {
IO.SetIndex[repH.sh, 0];
DO
ENABLE UNWIND => NULL; --Heap.systemZone.FREE[@qualifiedTitle];
ReadLine[h, localLine];
IF h.break = Ascii.NUL THEN EXIT;
IF localLine.Length # 0
AND localLine.Fetch[0] = beginSectionName
THEN
BEGIN
IF TitleMatch[localLine, qualifiedTitle]
THEN
BEGIN
repH.specificIndex ¬ IO.GetIndex[repH.sh];
IF genericIndex # noSection THEN EXIT;
END;
IF TitleMatch[localLine, title]
THEN
BEGIN
genericIndex ¬ IO.GetIndex[repH.sh];
IF repH.specificIndex # noSection THEN EXIT;
END;
END;
ENDLOOP};
Heap.systemZone.FREE[@qualifiedTitle];
Process generic section, then specific section
IF opened ¬ (genericIndex # noSection)
THEN
IO.SetIndex[repH.sh, genericIndex]
ELSE opened ¬ ~TrySpecific[h];
END;