-- Line sorting program to run in Laurel --

-- [Juniper]<Grapevine>User>LineSort>LineSorter.mesa

-- Mike Schroeder, March 1, 1982 4:38 PM --

DIRECTORY BTreeDefs, crD: FROM "CoreDefs", csD: FROM "CoreStreamDefs", InlineDefs, IODefs, ovD: FROM "OverviewDefs", StringDefs, VMDefs;

LineSorter: PROGRAM
IMPORTS BTreeDefs, csD, InlineDefs, IODefs, StringDefs, VMDefs =

BEGIN

OPEN IODefs, StringDefs;

MyLowerCase: PROCEDURE[c: CHARACTER] RETURNS[CHARACTER] = INLINE
BEGIN
RETURN[IF c IN [’A..’Z] THEN c - ’A + ’a ELSE c];
END; --MyLowerCase--

IsFirstGE: BTreeDefs.TestKeys --[a, b: DESC] RETURNS[BOOLEAN] -- =
BEGIN
aC: POINTER TO PACKED ARRAY OF CHARACTER = LOOPHOLE[BASE[a]];
bC: POINTER TO PACKED ARRAY OF CHARACTER = LOOPHOLE[BASE[b]];
FOR i:CARDINAL IN [0..2*MIN[LENGTH[a],LENGTH[b]]) DO
IF MyLowerCase[aC[i]] < MyLowerCase[bC[i]] THEN RETURN[FALSE];
IF MyLowerCase[aC[i]] > MyLowerCase[bC[i]] THEN RETURN[TRUE];
ENDLOOP;
RETURN[LENGTH[a] >= LENGTH[b]];
END; -- of IsFirstGE --

AreTheyE: BTreeDefs.TestKeys --[a, b: DESC] RETURNS[BOOLEAN] -- =
BEGIN
aC: POINTER TO PACKED ARRAY OF CHARACTER = LOOPHOLE[BASE[a]];
bC: POINTER TO PACKED ARRAY OF CHARACTER = LOOPHOLE[BASE[b]];
IF LENGTH[a] = LENGTH[b] THEN
FOR i:CARDINAL IN [0..2*LENGTH[a]) DO
IF MyLowerCase[aC[i]] # MyLowerCase[bC[i]] THEN EXIT;
REPEAT FINISHED => RETURN[TRUE];
ENDLOOP;
RETURN[FALSE];
END; -- of AreTheyE --

Run: PROCEDURE =
BEGIN

tree: BTreeDefs.BTreeHandle;
buffer: STRING = [62];
inputStr, outputStr: csD.StreamHandle ← NIL;
NextLine: PROCEDURE [s: STRING] RETURNS [ f: LONG CARDINAL, n: CARDINAL];

FWNextLine: PROCEDURE [s: STRING]
RETURNS [ f: LONG CARDINAL, n: CARDINAL] =
BEGIN
c: CHARACTER;
s.length ← 0;
f ← csD.GetPosition[inputStr];
BEGIN ENABLE csD.Error =>
IF reason = ovD.endOfStream THEN CONTINUE;
c ← csD.Read[inputStr];
UNTIL c # SP AND c # TAB DO c ← csD.Read[inputStr]; ENDLOOP;
UNTIL c = SP OR c = TAB OR c = CR DO
AppendChar[s, c ! StringBoundsFault => CONTINUE];
c ← csD.Read[inputStr];
ENDLOOP;
IF s.length/2 # 0 THEN AppendChar[s, SP];
UNTIL c = CR DO c ← csD.Read[inputStr]; ENDLOOP;
END; --enable--
n ← InlineDefs.LowHalf[ csD.GetPosition[inputStr] - f ];
END; --FWNextLine--

MarkerNextLine: PROCEDURE [s: STRING]
RETURNS [ f: LONG CARDINAL, n: CARDINAL] =
BEGIN
c: CHARACTER;
s.length ← 0;
f ← csD.GetPosition[inputStr];
BEGIN ENABLE csD.Error =>
IF reason = ovD.endOfStream THEN CONTINUE;
c ← csD.Read[inputStr];
UNTIL c = ControlA OR c = CR DO
c ← csD.Read[inputStr];
ENDLOOP;
IF c # CR
THEN BEGIN
UNTIL c # SP AND c # TAB DO
c ← csD.Read[inputStr];
ENDLOOP;
UNTIL c = ControlB OR c = CR DO
AppendChar[s, c ! StringBoundsFault => CONTINUE];
c ← csD.Read[inputStr];
ENDLOOP;
IF s.length/2 # 0 THEN AppendChar[s, SP];
UNTIL c = CR DO c ← csD.Read[inputStr]; ENDLOOP;
END
ELSE
IF csD.GetPosition[inputStr] - f > 1
THEN{ csD.SetPosition[inputStr, f]; [] ← FWNextLine[s]};
END; --enable--
n ← InlineDefs.LowHalf[ csD.GetPosition[inputStr] - f ];
END; --MarkerNextLine--

PutEntry: PROCEDURE [key: STRING, start: LONG CARDINAL, count: CARDINAL] =
BEGIN
keyWords: CARDINAL = (key.length+1)/2;
k: DESCRIPTOR FOR ARRAY OF WORD =
DESCRIPTOR [@(key.text), (keyWords+SIZE[LONG CARDINAL]+SIZE[CARDINAL])];
v: DESCRIPTOR FOR ARRAY OF WORD =
DESCRIPTOR[NIL, 0];
InlineDefs.COPY[from:@start,
to:BASE[k]+keyWords,
nwords:SIZE[LONG CARDINAL]];
InlineDefs.COPY[from:@count,
to:BASE[k]+keyWords+SIZE[LONG CARDINAL],
nwords:SIZE[CARDINAL]];
BTreeDefs.Insert[tree, k, v];
END; --PutEntry--

PrintingWork: BTreeDefs.Call
--PROCEDURE[k, v: DESCRIPTOR] RETURNS[more, dirty: BOOLEAN]-- =
BEGIN
first: LONG CARDINAL;
number: CARDINAL;
more ← TRUE; dirty ← FALSE;
IF LENGTH[k] = 0 THEN RETURN;
InlineDefs.COPY[from:BASE[k]+LENGTH[k]
- SIZE[LONG CARDINAL] - SIZE[CARDINAL],
to:@first,
nwords:SIZE[LONG CARDINAL]];
InlineDefs.COPY[from:BASE[k]+LENGTH[k] - SIZE[CARDINAL],
to:@number,
nwords:SIZE[CARDINAL]];
csD.SetPosition[inputStr, first];
csD.StreamCopy[inputStr, outputStr, LONG[number]];
END; --PrintingWork--

BEGIN --for EXITS --
VMDefs.InitializeVM [min:4, max:40];
tree ← BTreeDefs.CreateAndInitializeBTree[
fileH:LOOPHOLE[VMDefs.OpenFile[name: "DLMap.btree$", options: oldOrNew]],
initializeFile:TRUE,
useDefaultOrderingRoutines:FALSE,
isFirstGreaterOrEqual:IsFirstGE,
areTheyEqual:AreTheyE];
DO
WriteChar[CR];
WriteString["Sort by f(irst word) or m(arkers)? "L];
SELECT LowerCase[ReadChar[]] FROM
’f => NextLine ← FWNextLine;
’m => NextLine ← MarkerNextLine;
ENDCASE => LOOP;
EXIT;
ENDLOOP;
DO
WriteChar[CR];
WriteString["Type input file name: "L];
buffer.length ← 0;
AppendString[buffer, "temp$$"L];
ReadID[buffer ! Rubout => LOOP];
IF buffer.length = 0 THEN GOTO cleanup;
inputStr ← csD.OpenFromName[buffer,
crD.DMSUser[NIL, NIL, NIL], byte, read, 4
! csD.Error => {WriteString["Can’t open input file."L]; LOOP}];
EXIT;
ENDLOOP;
DO
WriteChar[CR];
WriteString["Type output file name: "L];
buffer.length ← 0;
AppendString[buffer, "temp$"L];
ReadID[buffer ! Rubout => LOOP];
IF buffer.length = 0 THEN GOTO cleanup;
outputStr ← csD.OpenFromName[buffer,
crD.DMSUser[NIL, NIL, NIL], byte, overwrite, 1
! csD.Error => {WriteString["Can’t open output file."L]; LOOP}];
EXIT;
ENDLOOP;
WriteChar[CR];
WriteLine["Reading input file."L];
DO
start: LONG CARDINAL;
length: CARDINAL;
[start, length] ← NextLine[buffer
! csD.Error => {WriteChar[CR]; WriteLine["File error."L]; GOTO cleanup}];
IF length=0 THEN EXIT;
IF buffer.length # 0 THEN PutEntry[buffer, start, length];
ENDLOOP;
WriteLine["Writing output file."L];
BTreeDefs.EnumerateFrom[tree, DESCRIPTOR[NIL, 0], PrintingWork
! csD.Error => {WriteChar[CR]; WriteLine["File error."L]; GOTO cleanup}];
GOTO cleanup;
EXITS
cleanup => BEGIN
IF inputStr # NIL THEN csD.Close[inputStr ! csD.Error => CONTINUE];
IF outputStr # NIL THEN csD.Close[outputStr ! csD.Error => CONTINUE];
VMDefs.AbandonFile[LOOPHOLE[BTreeDefs.ReleaseBTree[tree]]];
VMDefs.FinalizeVM[];
END;
END;
WriteLine["Done"L];
END;

Run[];

END.