-- LineSorter.mesa
-- Line sorting program to run in Laurel --

-- Mike Schroeder, March 1, 1982 4:38 PM --
-- Taft, May 9, 1983 4:21 PM --

DIRECTORY
AltoFile,
BTreeDefs,
csD: FROM "CoreStreamDefs",
DiskIODefs,
Inline,
IODefs,
String,
VMDefs;

LineSorter: PROGRAM
IMPORTS AltoFile, BTreeDefs, csD, DiskIODefs,
Inline, IODefs,
String, VMDefs =

BEGIN

OPEN IODefs,
String;

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.EndOfStream => 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 MOD 2 # 0 THEN AppendChar[s, SP];
UNTIL c = CR DO c ← csD.Read[inputStr]; ENDLOOP;
END; --enable--
n ←
Inline.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.EndOfStream => CONTINUE;
c ← csD.Read[inputStr];
UNTIL c = ControlA OR c = CR DO
c ← csD.Read[inputStr];
ENDLOOP;
IF c # CR
THEN BEGIN
c ← csD.Read[inputStr];
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 MOD 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 ←
Inline.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];
Inline.COPY[from:@start,
to:BASE[k]+keyWords,
nwords:SIZE[LONG CARDINAL]];
Inline.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;
Inline.COPY[from:BASE[k]+LENGTH[k]
- SIZE[LONG CARDINAL] - SIZE[CARDINAL],
to:@first,
nwords:SIZE[LONG CARDINAL]];
Inline.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 --
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, byte, read
! VMDefs.Error, VMDefs.CantOpen, DiskIODefs.DiskError => {
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, byte, overwrite
! VMDefs.Error, VMDefs.CantOpen, DiskIODefs.DiskError, AltoFile.DiskFull => {
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
! VMDefs.Error, DiskIODefs.DiskError => {
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
! VMDefs.Error, DiskIODefs.DiskError, AltoFile.DiskFull => {
WriteChar[CR]; WriteLine["File error."L]; GOTO cleanup}];
GOTO cleanup;
EXITS
cleanup => BEGIN
IF inputStr # NIL THEN csD.Close[inputStr
!
VMDefs.Error, DiskIODefs.DiskError => CONTINUE];
IF outputStr # NIL THEN csD.Close[outputStr
!
VMDefs.Error, DiskIODefs.DiskError, AltoFile.DiskFull => CONTINUE];
VMDefs.AbandonFile[LOOPHOLE[BTreeDefs.ReleaseBTree[tree]]];
END;
END;
WriteLine["Done"L];
END;

Run[];

END.