File: AdobeToolsD.mesa - created by JCS. Last edit:
Copyright Ó 1992 by Xerox Corporation. All rights reserved.
JCS 4-Mar-86 14:08:06
RSF   17-Apr-86 18:14:39
RLC   14-May-87 13:54:20
Philip James, March 3, 1992 6:10 pm PST
Copyright (C) 1985, 1986 by Xerox Corporation. All rights reserved.
Sort
DIRECTORY
AdobeCommon USING [CatchErrors, Handle, PostMessage, PostNumber],
AdobeCommonInternal USING [InstanceData, InstanceDataHandle],
AdobeTool,
ARAccess USING [AppendChar],
Ascii USING [CR, LF, NUL],
Convert USING [CardFromRope, Error, RopeFromCard],
IO USING [Close, EndOf, EndOfStream, GetChar, PutChar, PutRope, SetIndex, STREAM],
PFS USING [Error, PathFromRope, StreamOpen],
Rope USING [Concat, Equal, Fetch, Length, ROPE],
TopoSort USING [ListSort, PartialComparison];
AdobeToolImplD: CEDAR MONITOR
IMPORTS
AdobeCommon, ARAccess, Convert, IO,PFS, Rope, TopoSort
EXPORTS AdobeTool =
BEGIN
globals for sort
sortPos: CARDINAL = 0;
showPos: CARDINAL = 1;
sortBkgdPos: CARDINAL = 2;
maxLenPos: CARDINAL = 3;
inputPos: CARDINAL = 4;
outputPos: CARDINAL = 5;
keyspecsPos: CARDINAL = 6;
nSortParams: CARDINAL = 7;
LT: INTEGER = -1;
EQ: INTEGER = 0;
GT: INTEGER = 1;
adobeBytesPerPage: LONG CARDINAL = 65536;
SortKeySpec: TYPE = REF SortKeySpecItem;
SortKeySpecItem: TYPE = RECORD [
posInSortKeyFile: CARDINAL,
ascending: BOOLEAN,
next: SortKeySpec];
SortStringBuffer: TYPE = RECORD [s: Rope.ROPE];
CreateSortSW: PUBLIC PROCEDURE [
w: Window.Handle, makeCmdSW: BOOLEANTRUE]
RETURNS [initialMsg: Rope.ROPENIL] =
BEGIN
data: AdobeCommon.Handle ← AdobeCommon.GetData[w];
instanceData: AdobeCommonInternal.InstanceDataHandle ←
data.instanceData;
IF makeCmdSW THEN {
instanceData ← AdobeCommonInternal.AllocateInstanceData[data];
instanceData.cmdSW ← Tool.MakeFormSW[
window: w, formProc: SortCmdSW, zone: data.heap]};
has no formSW
instanceData.formSW ← NIL;
AdobeCommon.DisableAdobeAborts[w];
AdobeCommonInternal.SetWindowName[
w,
IF data.knownSystems # NIL AND data.context # NIL
AND data.system # LAST[CARDINAL] THEN data.knownSystems[
data.system] ELSE NIL, AdobeCommonInternal.sortHeraldName];
END;
SortCmdSW: FormSW.ClientItemsProcType =
BEGIN OPEN FormSW;
windowData: AdobeCommon.Handle ← AdobeCommon.GetDataSW[sw];
instanceData: AdobeCommonInternal.InstanceDataHandle ←
windowData.instanceData;
WITH sortToolData: instanceData SELECT FROM
sort => {
items ← AllocateItemDescriptor[nSortParams, windowData.heap];
freeDesc ← TRUE;
items[sortPos] ← CommandItem[
tag: "Sort", place: [0, line0], proc: SortCommandProc,
z: windowData.heap];
items[showPos] ← CommandItem[
tag: "Show Sortkeys", proc: SortCommandProc,
z: windowData.heap];
items[sortBkgdPos] ← BooleanItem[
tag: "Use background", switch: @sortToolData.background,
z: windowData.heap];
items[maxLenPos] ← StringItem[
tag: "Max output file length",
string: @sortToolData.maxLen, z: windowData.heap,
inHeap: TRUE];
items[inputPos] ← StringItem[
tag: "Input File", place: [0, nextLine],
string: @sortToolData.sortInputFile, z: windowData.heap,
inHeap: TRUE];
items[outputPos] ← StringItem[
tag: "Output File", string: @sortToolData.sortOutputFile,
place: [225, sameLine], z: windowData.heap, inHeap: TRUE];
items[keyspecsPos] ← StringItem[
tag: "Key specs", string: @sortToolData.keySpecs,
place: [0, nextLine], z: windowData.heap, inHeap: TRUE]};
ENDCASE => ERROR; --shouldn't happen
END;
SortCommandProc: PUBLIC PROCEDURE [h: AdobeCommon.Handle] = {
windowData: AdobeCommon.Handle ¬ h; --AdobeCommon.GetDataSW[sw];
instanceData: AdobeCommonInternal.InstanceDataHandle ¬
NARROW[windowData.instanceData];
MsgSW.Clear[windowData.msgSW];
IF windowData.system = LAST[CARDINAL] --OR windowData.context = NIL--
OR windowData.knownSystems = NIL
OR windowData.knownSystems.next = 0 THEN {
AdobeCommon.PostMessage[windowData, TRUE, "No available systems"];
RETURN};
IF windowData.isBusy THEN
AdobeCommon.PostMessage[windowData, TRUE,
"Adobe system is Busy. Please try again later. "]
ELSE {
windowData.isBusy ¬ TRUE;
IF instanceData.background AND ~TajoMisc.toolDriverRunning THEN
Process.Detach[
windowData.processID ← FORK AdobeCommon.CatchErrors[
sw, item, index, windowData, ChooseSortCmd]]
ELSE {
windowData.processID ← Process.GetCurrent[];
AdobeCommon.CatchErrors[
--sw, item, index, --windowData, ChooseSortCmd]}--};
};
UpperCase: PROCEDURE [c: CHAR] RETURNS [CHAR] = {
IF c IN ['a..'z] THEN
RETURN [c - 'a + 'A]
ELSE RETURN[c];
};
ChooseSortCmd: PROCEDURE [windowData: AdobeCommon.Handle] =
FormSW.ProcType =
{
windowData: AdobeCommon.Handle ← AdobeCommon.GetDataSW[sw];
{ENABLE UNWIND => windowData.isBusy ¬ FALSE;
SELECT index FROM
--sortPos => --Sort[windowData];
showPos => ShowSortKeys[windowData];
ENDCASE => ERROR;
windowData.isBusy ¬ FALSE;
windowData.processID ← NIL;
AdobeCommon.PostMessage[windowData, TRUE, "Done. "];
};
};
New Data structures for use with a different sort interface
ReportData: TYPE = REF ReportDataRec;
ReportDataRec: TYPE = RECORD [
next: ReportData ¬ NIL,
data: Rope.ROPE ¬ NIL
];
Sort: PROCEDURE [data: AdobeCommon.Handle] = {
outputFileName: Rope.ROPE ¬ NIL;
sortKeySpecRef: REF SortKeySpec ¬ NEW[SortKeySpec ¬ NIL];
sortKeySpec: SortKeySpec ← NIL;
numberOfKeySpecsEntered: CARDINAL ¬ 0;
maxCharsAllStrings: CARDINAL ¬ 100; --will be reset later
headerLength, currentFileLength, maxFileLength: CARDINAL ¬ 0;
maxWordsThisString, maxWordsAllStrings: CARDINAL ¬ SIZE[
StringBody];
sortStringBuffer: Rope.ROPE ¬ NIL; --String.MakeString[Heap.systemZone, 100];
sortKeyStringBuffer: Rope.ROPE ¬ NIL; --String.MakeString[Heap.systemZone, 100];
tempstring: Rope.ROPE ¬ NIL; --String.MakeString[data.heap, 10];
reportData: ReportData ¬ NIL;
instanceData: AdobeCommonInternal.InstanceDataHandle ¬
NARROW[data.instanceData];
WITH instanceData SELECT FROM
sortData: REF AdobeCommonInternal.InstanceData.sort => {
ENABLE {
UNWIND => {
IF sortData.inputSH # NIL THEN
sortData.inputSH ¬ CleanUpStream[sortData.inputSH];
IF sortData.outputSH # NIL THEN
sortData.outputSH ¬ CleanUpStream[sortData.outputSH];
IF sortData.sortKeysSH # NIL THEN
sortData.sortKeysSH ¬ CleanUpStream[
sortData.sortKeysSH];
Heap.systemZone.FREE[@sortStringBuffer];
Heap.systemZone.FREE[@sortKeyStringBuffer];
data.heap.FREE[@tempstring];
data.heap.FREE[@outputFileName];
IF sortKeySpecRef­ # NIL THEN
FreeSortKeySpec[data, sortKeySpecRef­];
};
};
GetSortData: PROCEDURE RETURNS [repData: ReportData ¬ NIL] = {
rover: ReportData ¬ NIL;
nextLine: Rope.ROPE ¬ NIL;
nextLine ¬ SortKeysGetString[NIL];
WHILE nextLine # NIL DO
IF repData = NIL THEN {
repData ¬ NEW[ReportDataRec];
rover ¬ repData;
}
ELSE {
rover.next ¬ NEW[ReportDataRec];
rover ¬ rover.next;
};
rover.data ¬ nextLine;
nextLine ¬ SortKeysGetString[NIL];
ENDLOOP;
};
GetLink: PROCEDURE [in: REF ANY] RETURNS [REF ANY] = {
data: ReportData ¬ NARROW[in];
IF data = NIL THEN
RETURN[reportData]
ELSE
RETURN[data.next];
};
SetLink: PROCEDURE [from, to: REF ANY] = {
d1: ReportData ¬ NARROW[from];
d2: ReportData ¬ NARROW[to];
IF d1 # NIL THEN
d1.next ¬ d2
ELSE
reportData ¬ d2;
};
Compare: PROCEDURE [ref1, ref2: REF ANY] RETURNS [TopoSort.PartialComparison] = {
h1: ReportData ¬ NARROW[ref1];
h2: ReportData ¬ NARROW[ref2];
p1: Rope.ROPE ¬ h1.data;
p2: Rope.ROPE ¬ h2.data;
index, count: CARDINAL ¬ 0;
keySpecPtr: SortKeySpec ¬ sortKeySpecRef­;
DO
IF p1.Fetch[index] = Ascii.NUL AND p2.Fetch[index] = Ascii.NUL THEN {
index ¬ index + 1;
count ¬ count + 1;
keySpecPtr ¬ keySpecPtr.next;
IF count = numberOfKeySpecsEntered THEN RETURN[equal]} --0 means equal
ELSE {
IF p1.Fetch[index] = Ascii.NUL THEN {
IF keySpecPtr.ascending THEN RETURN[less]
ELSE RETURN[greater]}
ELSE
IF p2.Fetch[index] = Ascii.NUL THEN {
IF keySpecPtr.ascending THEN RETURN[greater]
ELSE RETURN[less]};
p1.Fetch[index] ← UpperCase[p1.Fetch[index]];
p2.Fetch[index] ← UpperCase[p2.Fetch[index]];
SELECT TRUE FROM
UpperCase[p1.Fetch[index]] < UpperCase[p2.Fetch[index]] =>
IF keySpecPtr.ascending THEN RETURN[less]
ELSE RETURN[greater];
UpperCase[p1.Fetch[index]] > UpperCase[p2.Fetch[index]] =>
IF keySpecPtr.ascending THEN RETURN[greater]
ELSE RETURN[less];
ENDCASE;
index ¬ index + 1};
ENDLOOP; --of DO
}; --of CompareStrings
SortKeysGetString: PROCEDURE [sortKeyString: Rope.ROPE] RETURNS [data: Rope.ROPE] = {
char: CHARACTER;
result: CARDINAL ¬ 0;
keySpec: SortKeySpec ¬ sortKeySpecRef­;
out: REF Rope.ROPE ¬ NEW[Rope.ROPE ¬ NIL];
--put a single line of the sortkeys file into a buffer called sortStringBuffer
sortStringBuffer ¬ NIL;
sortKeyString ¬ NIL;
IF ~sortData.sortKeysSH.EndOf[] THEN
char ¬ IO.GetChar[sortData.sortKeysSH];
WHILE ~sortData.sortKeysSH.EndOf[] AND char # Ascii.CR AND char # Ascii.LF DO
sortStringBuffer ¬ ARAccess.AppendChar[sortStringBuffer, char];
char ¬ IO.GetChar[sortData.sortKeysSH];
ENDLOOP;
this creates a new string with the fields in sorted order
IF sortStringBuffer.Length = 0 THEN RETURN[NIL];
UNTIL keySpec = NIL DO
pos, index: CARDINAL ¬ 0;
finds the position of the field in the sortstringbuffer
WHILE keySpec.posInSortKeyFile # pos DO
WHILE sortStringBuffer.Fetch[index] # Ascii.NUL DO
index ¬ index + 1; ENDLOOP;
pos ¬ pos + 1;
index ¬ index + 1;
ENDLOOP; --of while loop
WHILE sortStringBuffer.Fetch[index] # Ascii.NUL DO
sortKeyString ¬ ARAccess.AppendChar[
sortKeyString, sortStringBuffer.Fetch[index]];
index ¬ index + 1;
ENDLOOP; --of while loop;
sortKeyString ¬ ARAccess.AppendChar[sortKeyString, Ascii.NUL];
keySpec ¬ keySpec.next;
ENDLOOP; --of until loop
RETURN[sortKeyString];
}; --of SortKeysGetString
SortKeysPutString: PROCEDURE [s: Rope.ROPE, len: CARDINAL] = {
count, index: CARDINAL ¬ 0;
c: CHAR;
i, pagePos, bytePos, lenPos: LONG CARDINAL ¬ 0;
GetString: PROCEDURE RETURNS [LONG CARDINAL ¬ 0] = {
tempstring ¬ NIL;
tempstring.length ← 0;
WHILE s.Fetch[index] # Ascii.NUL DO
tempstring ¬ ARAccess.AppendChar[tempstring, s.Fetch[index]];
String.AppendChar[tempstring, s[index]];
index ¬ index + 1;
ENDLOOP;
index ¬ index + 1;
RETURN[Convert.CardFromRope[tempstring, 10]];
RETURN[String.StringToLongNumber[tempstring, 10]];
}; --of GetString
pass the beginning of s until the position values are reached
x is number or character
n is a number
z is the nul character
s looks like x..xzx..xz...x..xzn..nzn..nzn..n
UNTIL count = numberOfKeySpecsEntered DO
WHILE s.Fetch[index] # Ascii.NUL DO index ¬ index + 1; ENDLOOP;
count ¬ count + 1;
index ¬ index + 1;
ENDLOOP;
pagePos ¬ GetString[];
bytePos ¬ GetString[];
lenPos ¬ GetString[];
IF currentFileLength + lenPos > maxFileLength THEN {
sortData.outputSH ¬ CleanUpStream[sortData.outputSH];
currentFileLength ¬ GetNextFileAndPutHeader[
data, outputFileName, headerLength];
sortData.fileNumber ¬ sortData.fileNumber + 1};
IO.SetIndex[sortData.inputSH, (adobeBytesPerPage * pagePos + bytePos)];
Stream.SetPosition[
sortData.inputSH,
(adobeBytesPerPage * pagePos + bytePos)];
FOR i IN [1..lenPos] DO
c ¬ IO.GetChar[sortData.inputSH];
IF c = Ascii.LF THEN
c ¬ Ascii.CR;
IO.PutChar[sortData.outputSH, c];
ENDLOOP; --of for loop
currentFileLength ¬ currentFileLength + lenPos;
}; --of SortKeysPutString
CompareStrings: PROCEDURE [p1: Rope.ROPE, p2: Rope.ROPE]
RETURNS [INTEGER] =
BEGIN
index, count: CARDINAL ¬ 0;
keySpecPtr: SortKeySpec ¬ sortKeySpecRef­;
DO
IF p1.Fetch[index] = Ascii.NUL AND p2.Fetch[index] = Ascii.NUL THEN {
index ¬ index + 1;
count ¬ count + 1;
keySpecPtr ¬ keySpecPtr.next;
IF count = numberOfKeySpecsEntered THEN RETURN[EQ]} --0 means equal
ELSE {
IF p1.Fetch[index] = Ascii.NUL THEN {
IF keySpecPtr.ascending THEN RETURN[LT]
ELSE RETURN[GT]}
ELSE
IF p2.Fetch[index] = Ascii.NUL THEN {
IF keySpecPtr.ascending THEN RETURN[GT]
ELSE RETURN[LT]};
p1.Fetch[index] ← UpperCase[p1.Fetch[index]];
p2.Fetch[index] ← UpperCase[p2.Fetch[index]];
SELECT TRUE FROM
UpperCase[p1.Fetch[index]] < UpperCase[p2.Fetch[index]] =>
IF keySpecPtr.ascending THEN RETURN[LT]
ELSE RETURN[GT];
UpperCase[p1.Fetch[index]] > UpperCase[p2.Fetch[index]] =>
IF keySpecPtr.ascending THEN RETURN[GT]
ELSE RETURN[LT];
ENDCASE;
index ¬ index + 1};
ENDLOOP; --of DO
END; --of CompareStrings
CompareStrings: PROCEDURE [p1: Rope.ROPE, p2: Rope.ROPE]
RETURNS [INTEGER] =
BEGIN
index, count: CARDINAL ← 0;
keySpecPtr: SortKeySpec ← sortKeySpec;
DO
IF p1.Fetch[index] = Ascii.NUL AND p2.Fetch[index] = Ascii.NUL THEN {
index ← index + 1;
count ← count + 1;
keySpecPtr ← keySpecPtr.next;
IF count = numberOfKeySpecsEntered THEN RETURN[EQ]} --0 means equal
ELSE {
IF p1.Fetch[index] = Ascii.NUL THEN {
IF keySpecPtr.ascending THEN RETURN[LT]
ELSE RETURN[GT]}
ELSE
IF p2.Fetch[index] = Ascii.NUL THEN {
IF keySpecPtr.ascending THEN RETURN[GT]
ELSE RETURN[LT]};
p1.Fetch[index] ← UpperCase[p1.Fetch[index]];
p2.Fetch[index] ← UpperCase[p2.Fetch[index]];
SELECT TRUE FROM
p1.Fetch[index] < p2.Fetch[index] =>
IF keySpecPtr.ascending THEN RETURN[LT]
ELSE RETURN[GT];
p1.Fetch[index] > p2.Fetch[index] =>
IF keySpecPtr.ascending THEN RETURN[GT]
ELSE RETURN[LT];
ENDCASE;
index ← index + 1};
ENDLOOP; --of DO
END; --of CompareStrings
AdobeCommon.PostMessage[data, FALSE, "Sorting..."];
[currentFileLength, maxFileLength, headerLength,
numberOfKeySpecsEntered, outputFileName] ¬
InitializeSortVariables[data, sortKeySpecRef];
ReadFirstLine[sortData.sortKeysSH];
reportData ¬ GetSortData[];
WHILE maxWordsThisString # 0 DO
maxWordsThisString ← SortKeysGetString[sortKeyStringBuffer];
maxWordsAllStrings ← MAX[
maxWordsThisString, maxWordsAllStrings];
ENDLOOP;
maxCharsAllStrings ← 2 * maxWordsAllStrings;
sortStringBuffer ¬ NIL;
String.MakeString[Heap.systemZone, maxCharsAllStrings];
ReadFirstLine[sortData.sortKeysSH];
IF sortKeySpecRef­ = NIL THEN
CopyInputFileToOutputFile[
data, outputFileName, maxFileLength, tempstring,
headerLength]
no sorting necessary
ELSE {
TopoSort.ListSort[
alpha: NIL,
omega: NIL,
GetLink: GetLink,
SetLink: SetLink,
Compare: Compare];
maxCharsAllStrings ¬ 0;
WHILE reportData # NIL DO
SortKeysPutString[reportData.data, 0];
reportData ¬ reportData.next;
ENDLOOP;
};
GSort.Sort[
get: LOOPHOLE[SortKeysGetString],
put: LOOPHOLE[SortKeysPutString],
compare: LOOPHOLE[CompareStrings],
expectedItemSize: maxWordsAllStrings,
maxItemSize: maxWordsAllStrings, pagesInHeap: 127];
IF maxFileLength # LAST[LONG CARDINAL] THEN {
AdobeCommon.PostMessage[data, FALSE, "Output Files: "];
AdobeCommon.PostMessage[data, FALSE, outputFileName];
AdobeCommon.PostMessage[data, FALSE, "1.."];
AdobeCommon.PostMessage[data, FALSE, outputFileName];
AdobeCommon.PostNumber[data, TRUE, sortData.fileNumber - 1]};
free storage
Heap.systemZone.FREE[@sortStringBuffer];
Heap.systemZone.FREE[@sortKeyStringBuffer];
data.heap.FREE[@tempstring];
data.heap.FREE[@outputFileName];
IF sortKeySpecRef­ # NIL THEN FreeSortKeySpec[data, sortKeySpecRef­];
sortData.sortKeysSH ¬ CleanUpStream[sortData.sortKeysSH];
sortData.inputSH ¬ CleanUpStream[sortData.inputSH];
sortData.outputSH ¬ CleanUpStream[sortData.outputSH];
};
ENDCASE;
}; --of Sort
ShowSortKeys: PUBLIC PROC [handle: AdobeCommon.Handle] = {
data: AdobeCommon.Handle ¬ handle;
char: CHARACTER;
not sure about the size of string
sortKey: Rope.ROPE ¬ NIL; --String.MakeString[data.heap, 16];
instanceData: AdobeCommonInternal.InstanceDataHandle ¬
NARROW[data.instanceData];
WITH instanceData SELECT FROM
sortData: REF AdobeCommonInternal.InstanceData.sort => {
ENABLE {
UNWIND => {
IF sortData.sortKeysSH # NIL THEN
sortData.sortKeysSH ¬ CleanUpStream[
sortData.sortKeysSH];
String.FreeString[data.heap, sortKey]
}
};
sortData.sortInputFile ¬ data.getContents[data.editors[2]];
IF sortData.sortInputFile = NIL OR sortData.sortInputFile.Length = 0 THEN {
AdobeCommon.PostMessage[data, TRUE, "No input file specified."];
ERROR ABORTED
};
GetSortKeysFile[data];
IF sortData.sortKeysSH # NIL THEN {
AdobeCommon.PostMessage[data, FALSE, "Choose from these sort keys: "];
char ¬ IO.GetChar[sortData.sortKeysSH];
WHILE char # Ascii.CR AND char # Ascii.LF DO
IF char = Ascii.NUL THEN {
AdobeCommon.PostMessage[data, FALSE, sortKey];
--ClearString[sortKey];
sortKey ¬ NIL;
sortKey.length ← 0;
char ¬ IO.GetChar[sortData.sortKeysSH];
IF char # Ascii.CR AND char # Ascii.LF THEN {
AdobeCommon.PostMessage[data, FALSE, ", "];
sortKey ¬ ARAccess.AppendChar[sortKey, char]
String.AppendCharAndGrow[@sortKey, char, data.heap]
}
ELSE EXIT;
}
ELSE
sortKey ¬ ARAccess.AppendChar[sortKey, char];
String.AppendCharAndGrow[@sortKey, char, data.heap];
char ¬ IO.GetChar[sortData.sortKeysSH];
ENDLOOP;
AdobeCommon.PostMessage[data, TRUE, " "]
}
ELSE {
AdobeCommon.PostMessage[data, TRUE, "the sortkeys file does not exist"];
ERROR ABORTED
};
sortData.sortKeysSH ¬ CleanUpStream[sortData.sortKeysSH];
};
ENDCASE; --should not happen
String.FreeString[data.heap, sortKey];
}; --of ShowSortKeys
InitializeSortVariables: PROCEDURE [data: AdobeCommon.Handle,
ptrToSortKeySpec: REF SortKeySpec] RETURNS [currentFileLength: LONG CARDINAL,
maxFileLength, headerLength: LONG CARDINAL, numberOfKeySpecsEntered: CARDINAL, outputFileName: Rope.ROPE] = {
instanceData: AdobeCommonInternal.InstanceDataHandle ¬
NARROW[data.instanceData];
numberofSortKeys: CARDINAL ¬ 0;
currentFileLength ¬ 0;
WITH instanceData SELECT FROM
sortData: REF AdobeCommonInternal.InstanceData.sort => {
sortData.sortInputFile ¬ data.getContents[data.editors[2]];
IF sortData.sortInputFile = NIL OR sortData.sortInputFile.Length = 0 THEN {
AdobeCommon.PostMessage[data, TRUE, "ERROR--no input file specified."];
ERROR ABORTED
};
sortData.sortOutputFile ¬ data.getContents[data.editors[3]];
IF sortData.sortOutputFile = NIL OR sortData.sortOutputFile.Length = 0 THEN {
AdobeCommon.PostMessage[data, TRUE, "ERROR--no output file specified."];
ERROR ABORTED
};
sortData.keySpecs ¬ data.getContents[data.editors[4]];
IF sortData.keySpecs = NIL OR sortData.keySpecs.Length = 0 THEN {
AdobeCommon.PostMessage[data, TRUE, "ERROR--no key specs were specified."];
ERROR ABORTED
};
sortData.inputSH ¬ GetFile[data, sortData.sortInputFile, TRUE];
GetSortKeysFile[data];
outputFileName ¬ sortData.sortOutputFile;
outputFileName ← String.CopyToNewString[sortData.sortOutputFile, data.heap];
[numberofSortKeys, numberOfKeySpecsEntered] ¬ GetKeySpec[data, ptrToSortKeySpec];
headerLength ¬ GetHeaderLength[data, numberofSortKeys];
sortData.fileNumber ¬ 1;
sortData.maxLen ¬ data.getContents[data.editors[1]];
IF sortData.maxLen = NIL OR sortData.maxLen.Length = 0 THEN {
maxFileLength ¬ LAST[LONG CARDINAL];
sortData.outputSH ¬ GetFile[data, sortData.sortOutputFile, FALSE];
currentFileLength ¬ PutHeader[data, outputFileName, headerLength]}
ELSE {
maxFileLength ¬ Convert.CardFromRope[sortData.maxLen, 10 ! Convert.Error => {maxFileLength ¬ LAST[CARD]; CONTINUE}];
maxFileLength ← String.StringToLongNumber[sortData.maxLen, 10];
currentFileLength ¬ GetNextFileAndPutHeader[data, outputFileName, headerLength];
sortData.fileNumber ¬ sortData.fileNumber + 1
}
};
ENDCASE; --should not happen 
}; --of InitializeSortVariables
GetFile: PROCEDURE [data: AdobeCommon.Handle, fileName: Rope.ROPE,
read: BOOLEAN] RETURNS [fileHandle: IO.STREAM --MStream.Handle-- ¬ NIL] =
BEGIN
mfRD: MStream.ReleaseData ← [NIL, NIL];
fileHandle ¬
IF read THEN
PFS.StreamOpen[PFS.PathFromRope[fileName] !
PFS.Error => {
AdobeCommon.PostMessage[data, FALSE, fileName];
AdobeCommon.PostMessage[data, TRUE, " cannot be acquired."];
ERROR ABORTED;
}]
MStream.ReadOnly[fileName, mfRD !
MStream.Error => {
AdobeCommon.PostMessage[data, FALSE, fileName];
AdobeCommon.PostMessage[
data, TRUE, " cannot be acquired."];
ERROR ABORTED;
}]
ELSE
PFS.StreamOpen[PFS.PathFromRope[fileName], write !
PFS.Error => {
AdobeCommon.PostMessage[data, FALSE, fileName];
AdobeCommon.PostMessage[data, TRUE, " cannot be acquired."];
ERROR ABORTED;
}];
MStream.WriteOnly[fileName, mfRD, text !
MStream.Error => {
AdobeCommon.PostMessage[data, FALSE, fileName];
AdobeCommon.PostMessage[
data, TRUE, " cannot be acquired."];
ERROR ABORTED;
}];
END;
GetNextFileAndPutHeader: PROCEDURE [data: AdobeCommon.Handle, outputFileName: Rope.ROPE, headerLength: LONG CARDINAL] RETURNS [currentFileLength: LONG CARDINAL] = {
instanceData: AdobeCommonInternal.InstanceDataHandle ¬
NARROW[data.instanceData];
mfRD: MStream.ReleaseData ← [NIL, NIL];
newFileName: Rope.ROPE ¬ outputFileName;
String.CopyToNewString[s: outputFileName, z: Heap.systemZone, longer: 5];
WITH instanceData SELECT FROM
sortData: REF AdobeCommonInternal.InstanceData.sort => {
newFileName ¬ newFileName.Concat[Convert.RopeFromCard[sortData.fileNumber, 10, FALSE]];
String.AppendNumber[newFileName, sortData.fileNumber];
sortData.outputSH ¬ PFS.StreamOpen[PFS.PathFromRope[newFileName], write];
sortData.outputSH ← MStream.WriteOnly[
newFileName, mfRD, text];
currentFileLength ¬ 0;
currentFileLength ¬ PutHeader[data, newFileName, headerLength];
}
ENDCASE => ERROR; --shouldn't happen
Heap.systemZone.FREE[@newFileName];
}; --of GetNextFileAndPutHeader
ReadFirstLine: PROCEDURE [sortSH: IO.STREAM--MStream.Handle--] =
BEGIN
read first line of the sortSH file
stops at the Ascii.CR which is the current character
char: CHARACTER ¬ ' ;
IO.SetIndex[sortSH, 0];
Stream.SetPosition[sortSH, 0];
char ¬ IO.GetChar[sortSH];
WHILE char # Ascii.CR AND char # Ascii.LF DO
char ¬ IO.GetChar[sortSH];
ENDLOOP;
END; --of ReadFirstLine
GetKeySpec: PROCEDURE [data: AdobeCommon.Handle, ptrToSortKeySpec: REF SortKeySpec]
RETURNS [numberOfSortKeys, numberOfKeySpecsEntered: CARDINAL] =
BEGIN
index: INTEGER ¬ 0;
sortKeySpec, newSortKeySpec, endOfSortKeySpec: SortKeySpec ¬ NIL;
char: CHARACTER ¬ ' ;
keySpecAscending: BOOLEAN ¬ TRUE;
keySpecsString: Rope.ROPE ¬ NIL;
keySpecToken: Rope.ROPE ¬ NIL;
String.MakeString[data.heap, 20];
instanceData: AdobeCommonInternal.InstanceDataHandle ¬ NARROW[data.instanceData];
formItem: FormSW.ItemHandle;
WITH instanceData SELECT FROM
sortData: REF AdobeCommonInternal.InstanceData.sort => {
numberOfSortKeys ¬ numberOfKeySpecsEntered ¬ 0;
IF sortData.keySpecs = NIL OR sortData.keySpecs.Length = 0 THEN
RETURN;
formItem ← FormSW.FindItem[instanceData.cmdSW, keyspecsPos];
formItem.flags.invisible ← TRUE;
{
ENABLE
UNWIND => {
formItem.flags.invisible ← FALSE;
data.heap.FREE[@keySpecsString];
FormSW.DisplayItem[instanceData.cmdSW, keyspecsPos]
};
keySpecsString ¬ NIL;
String.MakeString[data.heap, sortData.keySpecs.length+10];
sortData.keySpecs.length ← 0;
WHILE index < sortData.keySpecs.Length DO
keySpecAscending ¬ TRUE; --default switch value;
WHILE index < sortData.keySpecs.Length
AND sortData.keySpecs.Fetch[index] # '/ DO
IF sortData.keySpecs.Fetch[index] = '' THEN {
index ¬ index + 1;
IF sortData.keySpecs.Fetch[index] # '/ THEN {
AdobeCommon.PostMessage[data, TRUE, "ERROR--invalid field name"];
ERROR ABORTED}};
keySpecToken ¬ ARAccess.AppendChar[keySpecToken, sortData.keySpecs.Fetch[index]];
keySpecsString ¬ ARAccess.AppendChar[keySpecToken, sortData.keySpecs.Fetch[index]];
String.AppendCharAndGrow[
@keySpecToken, sortData.keySpecs[index], data.heap];
String.AppendCharAndGrow[
@keySpecsString, sortData.keySpecs[index], data.heap];
index ¬ index + 1;
ENDLOOP;
keySpecsString ¬ ARAccess.AppendChar[keySpecsString, '/];
String.AppendCharAndGrow[@keySpecsString, '/, data.heap];
index ¬ index + 1;
IF index < sortData.keySpecs.Length THEN
SELECT sortData.keySpecs.Fetch[index] FROM
'a, 'A, 'u, 'U => {
keySpecAscending ¬ TRUE;
keySpecsString ¬
ARAccess.AppendChar[keySpecsString, sortData.keySpecs.Fetch[index]];
index ¬ index + 1;
};
'd, 'D => {
keySpecAscending ¬ FALSE;
keySpecsString ¬
ARAccess.AppendChar[keySpecsString, sortData.keySpecs.Fetch[index]];
index ¬ index + 1;
};
' => {
AdobeCommon.PostMessage[data, TRUE,
"no switch was entered--the default switch is ascending. "];
keySpecsString ¬ ARAccess.AppendChar[keySpecsString, 'a]
};
String.AppendCharAndGrow[
@keySpecsString, 'a, data.heap]
};
ENDCASE => {
AdobeCommon.PostMessage[data, TRUE,
"invalid switch--the default switch is ascending. "];
keySpecsString ¬ ARAccess.AppendChar[keySpecsString, 'a]
String.AppendCharAndGrow[
@keySpecsString, 'a, data.heap]
}
ELSE {
AdobeCommon.PostMessage[data, TRUE,
"no switch was entered--the default switch is ascending. "];
keySpecsString ¬ ARAccess.AppendChar[keySpecsString, 'a]
String.AppendCharAndGrow[@keySpecsString, 'a, data.heap]
};
keySpecsString ¬ ARAccess.AppendChar[keySpecsString, ' ];
String.AppendCharAndGrow[@keySpecsString, ' , data.heap];
numberOfKeySpecsEntered ¬ numberOfKeySpecsEntered + 1;
create another SortKeySpecItem and append to sortKeySpec
newSortKeySpec ¬ NEW[SortKeySpecItem];
newSortKeySpec.posInSortKeyFile ¬ FindPosOfKeySpec[
keySpecToken, data];
ClearString[keySpecToken];
keySpecToken ¬ NIL;
keySpecToken.length ← 0;
newSortKeySpec.ascending ¬ keySpecAscending;
newSortKeySpec.next ¬ NIL;
IF ptrToSortKeySpec­ = NIL THEN {
ptrToSortKeySpec­ ¬ newSortKeySpec;
sortKeySpec ¬ ptrToSortKeySpec­;
endOfSortKeySpec ¬ newSortKeySpec;
}
sortKeySpec ← newSortKeySpec; endOfSortKeySpec ← newSortKeySpec;}
ELSE {
endOfSortKeySpec.next ¬ newSortKeySpec;
endOfSortKeySpec ¬ newSortKeySpec};
index ¬ index + 1;
ENDLOOP};
formItem.flags.invisible ← FALSE;
sortData.keySpecs ¬ NIL;
sortData.keySpecs.length ← 0;
sortData.keySpecs ¬ sortData.keySpecs.Concat[keySpecsString];
String.AppendStringAndGrow[@sortData.keySpecs, keySpecsString, data.heap];
FormSW.DisplayItem[sortData.cmdSW, keyspecsPos];
this appends page pos, byte pos and length pos to sortKeySpec
numberOfSortKeys ¬ CountSortKeys[sortData.sortKeysSH];
FOR index IN [numberOfSortKeys..numberOfSortKeys + 2] DO
newSortKeySpec ¬ NEW[SortKeySpecItem];
newSortKeySpec.posInSortKeyFile ¬ index;
newSortKeySpec.next ¬ NIL;
endOfSortKeySpec.next ¬ newSortKeySpec;
endOfSortKeySpec ¬ newSortKeySpec;
ENDLOOP;
};
ENDCASE;
data.heap.FREE[@keySpecToken];
data.heap.FREE[@keySpecsString];
END; --of GetKeySpec
CopyInputFileToOutputFile: PROCEDURE [data: AdobeCommon.Handle,
outputFileName: Rope.ROPE, maxFileLength: LONG CARDINAL, tempstring: Rope.ROPE, headerLength: LONG CARDINAL] =
BEGIN
instanceData: AdobeCommonInternal.InstanceDataHandle ¬NARROW[data.instanceData];
WITH instanceData SELECT FROM
sortData: REF AdobeCommonInternal.InstanceData.sort => {
GetNumber: PROCEDURE RETURNS [LONG CARDINAL ¬ 0] =
BEGIN
tempstring ¬ NIL;
tempstring.length ← 0;
WHILE char # Ascii.NUL DO
tempstring ¬ ARAccess.AppendChar[tempstring, char];
String.AppendChar[tempstring, char];
char ¬ IO.GetChar[sortData.sortKeysSH];
ENDLOOP;
char ¬ IO.GetChar[sortData.sortKeysSH];
RETURN[Convert.CardFromRope[tempstring, 10]];
RETURN[String.StringToLongNumber[tempstring, 10]];
END; --of GetNumber
count: CARDINAL ¬ 0;
i, pagePos, bytePos, lenPos, currentFileLength: LONG
CARDINAL ¬ 0;
char: CHARACTER ¬ ' ;
numberOfSortKeys: CARDINAL ¬ CountSortKeys[
sortData.sortKeysSH];
UNTIL IO.EndOf[sortData.sortKeysSH] --MStream.EndOf[sortData.sortKeysSH]-- DO
WHILE count # numberOfSortKeys DO
WHILE char # Ascii.NUL DO
char ¬ IO.GetChar[sortData.sortKeysSH]; ENDLOOP;
count ¬ count + 1;
char ¬ IO.GetChar[sortData.sortKeysSH];
ENDLOOP;
pagePos ¬ GetNumber[];
bytePos ¬ GetNumber[];
lenPos ¬ GetNumber[];
count ¬ 0;
IF currentFileLength + lenPos > maxFileLength THEN {
sortData.outputSH ¬ CleanUpStream[sortData.outputSH];
currentFileLength ¬ GetNextFileAndPutHeader[
data, outputFileName, headerLength];
sortData.fileNumber ¬ sortData.fileNumber + 1};
IO.SetIndex[sortData.inputSH, (adobeBytesPerPage * pagePos + bytePos)];
Stream.SetPosition[
sortData.inputSH,
(adobeBytesPerPage * pagePos + bytePos)];
FOR i IN [1..lenPos] DO
IO.PutChar[
sortData.outputSH, IO.GetChar[sortData.inputSH]];
ENDLOOP; --of for loop
currentFileLength ¬ currentFileLength + lenPos;
ENDLOOP; --of until loop
};
ENDCASE => ERROR; --shouldn't happen
END; --of CopyInputFileToOutputFile
CountSortKeys: PROCEDURE [sortSH: IO.STREAM--MStream.Handle--]
RETURNS [count: CARDINAL ¬ 0] =
BEGIN
char: CHARACTER ¬ ' ;
IO.SetIndex[sortSH, 0];
Stream.SetPosition[sortSH, 0];
UNTIL char = Ascii.CR OR char = Ascii.LF DO
WHILE char # Ascii.NUL DO
char ¬ IO.GetChar[sortSH]; ENDLOOP;
count ¬ count + 1;
char ¬ IO.GetChar[sortSH];
ENDLOOP; --of until loop
END; --of CountSortKeys
FreeSortKeySpec: PROCEDURE [
data: AdobeCommon.Handle, sortKeySpec: SortKeySpec] =
BEGIN
tempSortKeySpec: SortKeySpec;
UNTIL sortKeySpec = NIL DO
tempSortKeySpec ¬ sortKeySpec.next;
data.heap.FREE[@sortKeySpec];
sortKeySpec ¬ tempSortKeySpec;
ENDLOOP;
END; --of FreeSortKeySpec
FindPosOfKeySpec: PROCEDURE [
token: Rope.ROPE, data: AdobeCommon.Handle]
RETURNS [pos: CARDINAL ¬ 0] =
BEGIN
char: CHARACTER ¬ ' ;
instanceData: AdobeCommonInternal.InstanceDataHandle ¬ NARROW[data.instanceData];
WITH instanceData SELECT FROM
sortData: REF AdobeCommonInternal.InstanceData.sort => {
field: Rope.ROPE ¬ NIL;
String.MakeString[data.heap, 20];
ClearString[field];
IO.SetIndex[sortData.sortKeysSH, 0];
Stream.SetPosition[sortData.sortKeysSH, 0];
UNTIL char = Ascii.CR OR char = Ascii.LF DO
WHILE
((char ¬ IO.GetChar[sortData.sortKeysSH]) # Ascii.NUL)
AND (char # Ascii.CR) AND (char # Ascii.LF) DO
field ¬ ARAccess.AppendChar[field, char];
String.AppendCharAndGrow[@field, char, data.heap];
ENDLOOP; --of while loop
IF Rope.Equal[token, field, FALSE] THEN {
data.heap.FREE[@field];
RETURN[pos]
};
IF String.Equivalent[token, field] THEN {
data.heap.FREE[@field]; RETURN[pos]};
pos ¬ pos + 1;
field ¬ NIL;
field.length ← 0;
ENDLOOP; --of until loop
AdobeCommon.PostMessage[data, FALSE, token];
AdobeCommon.PostMessage[data, TRUE,
"--this field cannot be used as a sortkey"];
ERROR ABORTED};
ENDCASE; --should not happen
END; --of FindPosOfKeySpec
GetSortKeysFile: PROCEDURE [data: AdobeCommon.Handle] =
BEGIN
sortKeyFile: Rope.ROPE ¬ NIL;
index: INTEGER ¬ 0;
mfRD: MStream.ReleaseData ← [NIL, NIL];
instanceData: AdobeCommonInternal.InstanceDataHandle ¬ NARROW[data.instanceData];
WITH instanceData SELECT FROM
sortData: REF AdobeCommonInternal.InstanceData.sort => {
sortKeyFile ¬ NIL;
String.MakeString[data.heap, sortData.sortInputFile.length + 9];
UNTIL index = sortData.sortInputFile.Length
OR sortData.sortInputFile.Fetch[index] = '. DO
sortKeyFile ¬ ARAccess.AppendChar[sortKeyFile, sortData.sortInputFile.Fetch[index]];
String.AppendChar[
sortKeyFile, sortData.sortInputFile[index]];
index ¬ index + 1;
ENDLOOP;
sortKeyFile ¬ sortKeyFile.Concat[".sortKeys"];
String.AppendString[sortKeyFile, ".sortKeys"];
sortData.sortKeysSH ¬ PFS.StreamOpen[
PFS.PathFromRope[sortKeyFile] !
PFS.Error => {
AdobeCommon.PostMessage[data, TRUE, " ERROR--file cannot be obtained"];
ERROR ABORTED}];
sortData.sortKeysSH ← MStream.ReadOnly[
sortKeyFile, mfRD !
MStream.Error => {
AdobeCommon.PostMessage[
data, TRUE, " ERROR--file cannot be obtained"];
ERROR ABORTED}];
data.heap.FREE[@sortKeyFile]
};
ENDCASE; --should not happen
END; --of GetSortKeysFile
GetHeaderLength: PROCEDURE [data: AdobeCommon.Handle, numberofSortKeys: CARDINAL]
RETURNS [n: LONG CARDINAL] =
BEGIN
count: CARDINAL ¬ 0;
char: CHARACTER ¬ ' ;
tempString: Rope.ROPE ¬ NIL;
String.MakeString[Heap.systemZone, 30];
page, byte: LONG CARDINAL ¬ 0;
instanceData: AdobeCommonInternal.InstanceDataHandle ¬ NARROW[data.instanceData];
WITH instanceData SELECT FROM
sortData: REF AdobeCommonInternal.InstanceData.sort => {
ReadFirstLine[sortData.sortKeysSH];
checks to see if there are any entries in the sortkeys file
char ¬ IO.GetChar[
sortData.sortKeysSH !
IO.EndOfStream => {
AdobeCommon.PostMessage[
data, TRUE,
"There are no ARs in the sortkeys file"];
ERROR ABORTED}];
IF char = Ascii.NUL THEN count ¬ count + 1;
UNTIL count = numberofSortKeys DO
WHILE (char ¬ IO.GetChar[sortData.sortKeysSH]) #
Ascii.NUL DO ENDLOOP;
count ¬ count + 1;
ENDLOOP;
WHILE (char ¬ IO.GetChar[sortData.sortKeysSH]) # Ascii.NUL
DO
tempString ¬ ARAccess.AppendChar[tempString, char];
String.AppendCharAndGrow[
@tempString, char, Heap.systemZone];
ENDLOOP;
page ¬ Convert.CardFromRope[tempString, 10];
page ← String.StringToLongNumber[tempString, 10];
tempString ¬ NIL;
tempString.length ← 0;
WHILE (char ¬ IO.GetChar[sortData.sortKeysSH]) # Ascii.NUL
DO
tempString ¬ ARAccess.AppendChar[tempString, char];
String.AppendCharAndGrow[
@tempString, char, Heap.systemZone];
ENDLOOP;
byte ¬ Convert.CardFromRope[tempString, 10];
byte ← String.StringToLongNumber[tempString, 10];
n ¬ page * adobeBytesPerPage + byte};
ENDCASE; --should not happen
Heap.systemZone.FREE[@tempString];
END; --of GetHeaderLength
PutHeader: PROCEDURE [
data: AdobeCommon.Handle, fileName: Rope.ROPE,
headerLength: LONG CARDINAL]
RETURNS [currentFileLength: LONG CARDINAL] =
BEGIN
char: CHARACTER ¬ Ascii.NUL;
instanceData: AdobeCommonInternal.InstanceDataHandle ¬ NARROW[data.instanceData];
WITH instanceData SELECT FROM
sortData: REF AdobeCommonInternal.InstanceData.sort => {
PutIt: PROCEDURE [s: Rope.ROPE, CR: BOOLEAN ¬ FALSE] =
BEGIN
IF s # NIL THEN
IO.PutRope[self: sortData.outputSH, r: s];
IF CR THEN IO.PutChar[sortData.outputSH, Ascii.CR];
END; --of PutIt
currentFileLength ¬ 0;
PutIt[s: "This is file: ", CR: FALSE];
PutIt[s: fileName, CR: TRUE];
PutIt[s: "Generated by AdobeSort using: ", CR: FALSE];
PutIt[s: sortData.keySpecs, CR: TRUE];
PutIt[s: "Original "];
IF sortData.keySpecs = NIL THEN
currentFileLength ¬ fileName.Length + 55
ELSE
currentFileLength ¬
fileName.Length + sortData.keySpecs.Length + 55;
IO.SetIndex[sortData.inputSH, 0];
Stream.SetPosition[sortData.inputSH, 0];
outputs report name which is the first line of the input file
THROUGH [0..headerLength) DO
IO.PutChar[
sortData.outputSH, IO.GetChar[sortData.inputSH]];
ENDLOOP;
currentFileLength ¬ currentFileLength + headerLength}
ENDCASE => ERROR; --shouldn't happen
END; --of PutHeader
CleanUpStream: PROCEDURE [streamHandle: IO.STREAM--MStream.Handle--]
RETURNS [IO.STREAM--MStream.Handle--] =
BEGIN
IO.Close[streamHandle];
Stream.Delete[streamHandle];
streamHandle ¬ NIL;
RETURN[streamHandle];
END; --of CleanUpStream
END.
14-May-87 13:54:20 - rlc - fix CompareStrings. remainder of proc wants to be part of the ELSE clause. As is was the key spec wasn't being incremented properly in the case of an empty field.