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 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]; SortCommandProc: PUBLIC PROCEDURE [h: AdobeCommon.Handle] = { windowData: AdobeCommon.Handle ¬ h; --AdobeCommon.GetDataSW[sw]; instanceData: AdobeCommonInternal.InstanceDataHandle ¬ NARROW[windowData.instanceData]; 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; 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] = { {ENABLE UNWIND => windowData.isBusy ¬ FALSE; --sortPos => --Sort[windowData]; windowData.isBusy ¬ FALSE; AdobeCommon.PostMessage[windowData, TRUE, "Done. "]; }; }; 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]; 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]; 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]}; 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]; 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; IF sortStringBuffer.Length = 0 THEN RETURN[NIL]; UNTIL keySpec = NIL DO pos, index: CARDINAL ¬ 0; 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; WHILE s.Fetch[index] # Ascii.NUL DO tempstring ¬ ARAccess.AppendChar[tempstring, s.Fetch[index]]; index ¬ index + 1; ENDLOOP; index ¬ index + 1; RETURN[Convert.CardFromRope[tempstring, 10]]; }; --of GetString 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)]; 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]}; 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 AdobeCommon.PostMessage[data, FALSE, "Sorting..."]; [currentFileLength, maxFileLength, headerLength, numberOfKeySpecsEntered, outputFileName] ¬ InitializeSortVariables[data, sortKeySpecRef]; ReadFirstLine[sortData.sortKeysSH]; reportData ¬ GetSortData[]; sortStringBuffer ¬ NIL; ReadFirstLine[sortData.sortKeysSH]; IF sortKeySpecRef­ = NIL THEN CopyInputFileToOutputFile[ data, outputFileName, maxFileLength, tempstring, headerLength] 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; }; 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]}; 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; 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]; } }; 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]; sortKey ¬ NIL; char ¬ IO.GetChar[sortData.sortKeysSH]; IF char # Ascii.CR AND char # Ascii.LF THEN { AdobeCommon.PostMessage[data, FALSE, ", "]; sortKey ¬ ARAccess.AppendChar[sortKey, char] } ELSE EXIT; } ELSE sortKey ¬ ARAccess.AppendChar[sortKey, char]; 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 }; --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; [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}]; 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 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; }] ELSE PFS.StreamOpen[PFS.PathFromRope[fileName], write ! PFS.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]; newFileName: Rope.ROPE ¬ outputFileName; WITH instanceData SELECT FROM sortData: REF AdobeCommonInternal.InstanceData.sort => { newFileName ¬ newFileName.Concat[Convert.RopeFromCard[sortData.fileNumber, 10, FALSE]]; sortData.outputSH ¬ PFS.StreamOpen[PFS.PathFromRope[newFileName], write]; currentFileLength ¬ 0; currentFileLength ¬ PutHeader[data, newFileName, headerLength]; } ENDCASE => ERROR; --shouldn't happen }; --of GetNextFileAndPutHeader ReadFirstLine: PROCEDURE [sortSH: IO.STREAM--MStream.Handle--] = BEGIN char: CHARACTER ¬ ' ; IO.SetIndex[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; instanceData: AdobeCommonInternal.InstanceDataHandle ¬ NARROW[data.instanceData]; WITH instanceData SELECT FROM sortData: REF AdobeCommonInternal.InstanceData.sort => { numberOfSortKeys ¬ numberOfKeySpecsEntered ¬ 0; IF sortData.keySpecs = NIL OR sortData.keySpecs.Length = 0 THEN RETURN; { ENABLE UNWIND => { }; keySpecsString ¬ NIL; 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]]; index ¬ index + 1; ENDLOOP; keySpecsString ¬ ARAccess.AppendChar[keySpecsString, '/]; 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] }; ENDCASE => { AdobeCommon.PostMessage[data, TRUE, "invalid switch--the default switch is ascending. "]; keySpecsString ¬ ARAccess.AppendChar[keySpecsString, 'a] } ELSE { AdobeCommon.PostMessage[data, TRUE, "no switch was entered--the default switch is ascending. "]; keySpecsString ¬ ARAccess.AppendChar[keySpecsString, 'a] }; keySpecsString ¬ ARAccess.AppendChar[keySpecsString, ' ]; numberOfKeySpecsEntered ¬ numberOfKeySpecsEntered + 1; newSortKeySpec ¬ NEW[SortKeySpecItem]; newSortKeySpec.posInSortKeyFile ¬ FindPosOfKeySpec[ keySpecToken, data]; keySpecToken ¬ NIL; newSortKeySpec.ascending ¬ keySpecAscending; newSortKeySpec.next ¬ NIL; IF ptrToSortKeySpec­ = NIL THEN { ptrToSortKeySpec­ ¬ newSortKeySpec; sortKeySpec ¬ ptrToSortKeySpec­; endOfSortKeySpec ¬ newSortKeySpec; } ELSE { endOfSortKeySpec.next ¬ newSortKeySpec; endOfSortKeySpec ¬ newSortKeySpec}; index ¬ index + 1; ENDLOOP}; sortData.keySpecs ¬ NIL; sortData.keySpecs ¬ sortData.keySpecs.Concat[keySpecsString]; 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; 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; WHILE char # Ascii.NUL DO tempstring ¬ ARAccess.AppendChar[tempstring, char]; char ¬ IO.GetChar[sortData.sortKeysSH]; ENDLOOP; char ¬ IO.GetChar[sortData.sortKeysSH]; RETURN[Convert.CardFromRope[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)]; 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]; 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; 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; IO.SetIndex[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]; ENDLOOP; --of while loop IF Rope.Equal[token, field, FALSE] THEN { RETURN[pos] }; pos ¬ pos + 1; field ¬ NIL; 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; instanceData: AdobeCommonInternal.InstanceDataHandle ¬ NARROW[data.instanceData]; WITH instanceData SELECT FROM sortData: REF AdobeCommonInternal.InstanceData.sort => { sortKeyFile ¬ NIL; UNTIL index = sortData.sortInputFile.Length OR sortData.sortInputFile.Fetch[index] = '. DO sortKeyFile ¬ ARAccess.AppendChar[sortKeyFile, sortData.sortInputFile.Fetch[index]]; index ¬ index + 1; ENDLOOP; sortKeyFile ¬ sortKeyFile.Concat[".sortKeys"]; sortData.sortKeysSH ¬ PFS.StreamOpen[ PFS.PathFromRope[sortKeyFile] ! PFS.Error => { AdobeCommon.PostMessage[data, TRUE, " ERROR--file cannot be obtained"]; ERROR ABORTED}]; }; 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; page, byte: LONG CARDINAL ¬ 0; instanceData: AdobeCommonInternal.InstanceDataHandle ¬ NARROW[data.instanceData]; WITH instanceData SELECT FROM sortData: REF AdobeCommonInternal.InstanceData.sort => { ReadFirstLine[sortData.sortKeysSH]; 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]; ENDLOOP; page ¬ Convert.CardFromRope[tempString, 10]; tempString ¬ NIL; WHILE (char ¬ IO.GetChar[sortData.sortKeysSH]) # Ascii.NUL DO tempString ¬ ARAccess.AppendChar[tempString, char]; ENDLOOP; byte ¬ Convert.CardFromRope[tempString, 10]; n ¬ page * adobeBytesPerPage + byte}; ENDCASE; --should not happen 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]; 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]; 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. $4 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 globals for sort CreateSortSW: PUBLIC PROCEDURE [ w: Window.Handle, makeCmdSW: BOOLEAN _ TRUE] RETURNS [initialMsg: Rope.ROPE _ NIL] = 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; MsgSW.Clear[windowData.msgSW]; IF instanceData.background AND ~TajoMisc.toolDriverRunning THEN Process.Detach[ windowData.processID _ FORK AdobeCommon.CatchErrors[ sw, item, index, windowData, ChooseSortCmd]] ELSE { windowData.processID _ Process.GetCurrent[]; FormSW.ProcType = windowData: AdobeCommon.Handle _ AdobeCommon.GetDataSW[sw]; SELECT index FROM showPos => ShowSortKeys[windowData]; ENDCASE => ERROR; windowData.processID _ NIL; New Data structures for use with a different sort interface sortKeySpec: SortKeySpec _ NIL; Heap.systemZone.FREE[@sortStringBuffer]; Heap.systemZone.FREE[@sortKeyStringBuffer]; data.heap.FREE[@tempstring]; data.heap.FREE[@outputFileName]; p1.Fetch[index] _ UpperCase[p1.Fetch[index]]; p2.Fetch[index] _ UpperCase[p2.Fetch[index]]; --put a single line of the sortkeys file into a buffer called sortStringBuffer this creates a new string with the fields in sorted order finds the position of the field in the sortstringbuffer tempstring.length _ 0; String.AppendChar[tempstring, s[index]]; RETURN[String.StringToLongNumber[tempstring, 10]]; 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 Stream.SetPosition[ sortData.inputSH, (adobeBytesPerPage * pagePos + bytePos)]; p1.Fetch[index] _ UpperCase[p1.Fetch[index]]; p2.Fetch[index] _ UpperCase[p2.Fetch[index]]; 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 WHILE maxWordsThisString # 0 DO maxWordsThisString _ SortKeysGetString[sortKeyStringBuffer]; maxWordsAllStrings _ MAX[ maxWordsThisString, maxWordsAllStrings]; ENDLOOP; maxCharsAllStrings _ 2 * maxWordsAllStrings; String.MakeString[Heap.systemZone, maxCharsAllStrings]; no sorting necessary GSort.Sort[ get: LOOPHOLE[SortKeysGetString], put: LOOPHOLE[SortKeysPutString], compare: LOOPHOLE[CompareStrings], expectedItemSize: maxWordsAllStrings, maxItemSize: maxWordsAllStrings, pagesInHeap: 127]; free storage Heap.systemZone.FREE[@sortStringBuffer]; Heap.systemZone.FREE[@sortKeyStringBuffer]; data.heap.FREE[@tempstring]; data.heap.FREE[@outputFileName]; not sure about the size of string String.FreeString[data.heap, sortKey] --ClearString[sortKey]; sortKey.length _ 0; String.AppendCharAndGrow[@sortKey, char, data.heap] String.AppendCharAndGrow[@sortKey, char, data.heap]; String.FreeString[data.heap, sortKey]; outputFileName _ String.CopyToNewString[sortData.sortOutputFile, data.heap]; maxFileLength _ String.StringToLongNumber[sortData.maxLen, 10]; mfRD: MStream.ReleaseData _ [NIL, NIL]; MStream.ReadOnly[fileName, mfRD ! MStream.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; }]; mfRD: MStream.ReleaseData _ [NIL, NIL]; String.CopyToNewString[s: outputFileName, z: Heap.systemZone, longer: 5]; String.AppendNumber[newFileName, sortData.fileNumber]; sortData.outputSH _ MStream.WriteOnly[ newFileName, mfRD, text]; Heap.systemZone.FREE[@newFileName]; read first line of the sortSH file stops at the Ascii.CR which is the current character Stream.SetPosition[sortSH, 0]; String.MakeString[data.heap, 20]; formItem: FormSW.ItemHandle; formItem _ FormSW.FindItem[instanceData.cmdSW, keyspecsPos]; formItem.flags.invisible _ TRUE; formItem.flags.invisible _ FALSE; data.heap.FREE[@keySpecsString]; FormSW.DisplayItem[instanceData.cmdSW, keyspecsPos] String.MakeString[data.heap, sortData.keySpecs.length+10]; sortData.keySpecs.length _ 0; String.AppendCharAndGrow[ @keySpecToken, sortData.keySpecs[index], data.heap]; String.AppendCharAndGrow[ @keySpecsString, sortData.keySpecs[index], data.heap]; String.AppendCharAndGrow[@keySpecsString, '/, data.heap]; String.AppendCharAndGrow[ @keySpecsString, 'a, data.heap] }; String.AppendCharAndGrow[ @keySpecsString, 'a, data.heap] String.AppendCharAndGrow[@keySpecsString, 'a, data.heap] String.AppendCharAndGrow[@keySpecsString, ' , data.heap]; create another SortKeySpecItem and append to sortKeySpec ClearString[keySpecToken]; keySpecToken.length _ 0; sortKeySpec _ newSortKeySpec; endOfSortKeySpec _ newSortKeySpec;} formItem.flags.invisible _ FALSE; sortData.keySpecs.length _ 0; String.AppendStringAndGrow[@sortData.keySpecs, keySpecsString, data.heap]; FormSW.DisplayItem[sortData.cmdSW, keyspecsPos]; this appends page pos, byte pos and length pos to sortKeySpec data.heap.FREE[@keySpecToken]; data.heap.FREE[@keySpecsString]; tempstring.length _ 0; String.AppendChar[tempstring, char]; RETURN[String.StringToLongNumber[tempstring, 10]]; Stream.SetPosition[ sortData.inputSH, (adobeBytesPerPage * pagePos + bytePos)]; Stream.SetPosition[sortSH, 0]; data.heap.FREE[@sortKeySpec]; String.MakeString[data.heap, 20]; ClearString[field]; Stream.SetPosition[sortData.sortKeysSH, 0]; String.AppendCharAndGrow[@field, char, data.heap]; data.heap.FREE[@field]; IF String.Equivalent[token, field] THEN { data.heap.FREE[@field]; RETURN[pos]}; field.length _ 0; mfRD: MStream.ReleaseData _ [NIL, NIL]; String.MakeString[data.heap, sortData.sortInputFile.length + 9]; String.AppendChar[ sortKeyFile, sortData.sortInputFile[index]]; String.AppendString[sortKeyFile, ".sortKeys"]; sortData.sortKeysSH _ MStream.ReadOnly[ sortKeyFile, mfRD ! MStream.Error => { AdobeCommon.PostMessage[ data, TRUE, " ERROR--file cannot be obtained"]; ERROR ABORTED}]; data.heap.FREE[@sortKeyFile] String.MakeString[Heap.systemZone, 30]; checks to see if there are any entries in the sortkeys file String.AppendCharAndGrow[ @tempString, char, Heap.systemZone]; page _ String.StringToLongNumber[tempString, 10]; tempString.length _ 0; String.AppendCharAndGrow[ @tempString, char, Heap.systemZone]; byte _ String.StringToLongNumber[tempString, 10]; Heap.systemZone.FREE[@tempString]; Stream.SetPosition[sortData.inputSH, 0]; outputs report name which is the first line of the input file Stream.Delete[streamHandle]; Κ%Š•NewlineDelimiter –(cedarcode) style™šœ3™3Jšœ Οeœ1™™%J™2——Jšœ ™ Jšœžœ™J™"™"J™Jšžœžœžœž™1Jšžœžœžœžœ™8Jšœ žœžœ&™;—Jšžœ™——˜šŸ œ™'Jšžœžœ™Jšœ;™;™6J™—šžœžœž™+™ J™=Jšœ žœ™™J™6J™—™J™,J™—™!J™8J™—™J™J™1Jšœžœ™—™J™(J™8Jšœžœ™—™J™9Jšœ4žœ™:—™ J™1Jšœ2žœ™9——JšžœžœΟc™%—Jšžœ™——˜šŸœžœž œ˜=Kšœ$ ˜@˜6Kšžœ˜ —J™šžœžœžœ ˜EKšžœž˜ Kšžœ"žœ˜*Kšœ$žœ˜BKšžœ˜—šžœž˜Kšœ$žœ5˜]—šžœ˜Kšœžœ˜šžœžœž™?™Jšœžœ™4J™,——šžœ™J™,˜Kš œ ˜4———Kšœ˜——˜š Ÿ œž œžœžœžœ˜1šžœžœ ž˜Kšžœ˜—Kšžœžœ˜K˜K˜—šœž œ#˜;Jšœ™Kšœ˜Jšœ;™;Kšœžœžœžœ˜,šžœž™Kš œ˜ J™$Jšžœžœ™—Kšœžœ˜Jšœžœ™Kšœ$žœ ˜4Kšœ˜Kšœ˜——˜Jšœ;™;Kšœ žœžœ˜%šœžœžœ˜Kšœžœ˜Kšœ žœž˜K˜K˜—šŸœž œ˜.Kšœžœžœ˜ Kšœžœžœžœ˜9Jšœžœ™Kšœžœ˜&Kšœžœ  ˜:Kšœ0žœ˜=šœ(žœžœ˜8K˜ —Kšœžœžœ *˜MKšœžœžœ *˜PKšœžœžœ #˜@Kšœžœ˜K˜˜6Kšžœ˜—šžœžœž˜Kšœ žœ+˜8šžœ˜šžœ˜ šžœžœž˜K˜3—šžœžœž˜K˜5—šžœžœž˜!˜$K˜——Jšœžœ™(Jšœžœ™+Jšœ žœ™Jšœ žœ™ šžœžœž˜K˜'—K˜—Kšœ˜—K˜šŸ œž œžœžœ˜>Kšœžœ˜Kšœžœžœ˜Kšœžœ˜"K˜šžœ žœž˜šžœ žœžœ˜Kšœ žœ˜K˜K˜—šžœ˜Kšœ žœ˜ K˜K˜—K˜Kšœžœ˜"Kšžœ˜—K˜K˜—šŸœž œžœžœžœžœžœ˜6Kšœžœ˜šžœžœžœ˜Kšžœ ˜—šž˜Kšžœ ˜—K˜—K˜šŸœž œ žœžœ˜*Kšœžœ˜Kšœžœ˜šžœž ˜K˜ —šž˜K˜—K˜K˜—š Ÿœž œžœžœžœ!˜QKšœžœ˜Kšœžœ˜Kšœ žœ ˜Kšœ žœ ˜Kšœžœ˜K˜*šž˜š žœžœžœžœžœ˜EK˜K˜K˜Kšžœ!žœžœ  ˜G—šžœ˜šžœžœžœ˜%Kšžœžœžœ˜)Kšžœžœ ˜—šž˜šžœžœžœ˜%Kšžœžœžœ ˜,Kšžœžœ˜——J™-J™-šžœžœž˜˜:Kšžœžœžœ˜)Kšžœžœ ˜—˜:Kšžœžœžœ ˜,Kšžœžœ˜—Kšžœ˜—K˜—Kšžœ ˜—Kšœ ˜—K˜š Ÿœž œžœžœ žœ˜UKšœž œ˜Kšœžœ˜K˜'Kš œžœžœžœžœžœ˜*JšœN™NKšœžœ˜Kšœžœ˜šžœž˜$Kšœžœ˜'—š žœžœžœžœžœž˜MK˜?Kšœžœ˜'Kšžœ˜—Jšœ9™9K˜Kšžœžœžœžœ˜0šžœ žœž˜Kšœ žœ˜Jšœ7™7šžœ ž˜'šžœ'žœž˜2Kšœžœ˜—K˜K˜Kšžœ ˜—šžœ'žœž˜2˜$Kšœ.˜.—K˜Kšžœ ˜—Kšœ9žœ˜>K˜Kšžœ ˜—Kšžœ˜Kšœ ˜——˜šŸœž œ žœžœ˜>Kšœžœ˜Kšœžœ˜Kšœžœžœ˜/K˜š Ÿ œž œžœžœžœ ˜4šœ žœ˜J™—šžœžœž˜#˜=J™(—K˜Kšžœ˜—K˜šžœ'˜-Jšžœ,™2—Kšœ ˜—K˜Jšœ=™=Jšœ™Jšœ ™ Jšœ™Jšœ-™-šžœ!ž˜(Kšžœžœžœžœ˜?K˜K˜Kšžœ˜—K˜K˜K˜šžœ,žœ˜4K˜5˜,K˜$—K˜/—KšžœE˜G™J™J™)—šžœžœ ž˜Kšœžœ˜!šžœ žœž˜Kšœ žœ˜ —Kšžœ˜!Kšžœ  ˜—K˜/Kšœ ˜———˜šŸœž œ žœ žœ˜8Kšžœžœ˜Kšž˜Kšœžœ˜K˜*šž˜š žœžœžœžœžœ˜EK˜K˜K˜Kš žœ!žœžœžœ ˜D—šžœ˜šžœžœžœ˜%Kšžœžœžœžœ˜'Kšžœžœžœ˜—šž˜šžœžœžœ˜%Kšžœžœžœžœ˜'Kšžœžœžœ˜——J™-J™-šžœžœž˜˜:Kšžœžœžœžœ˜'Kšžœžœžœ˜—˜:Kšžœžœžœžœ˜'Kšžœžœžœ˜—Kšžœ˜—K˜—Kšžœ ˜—Kšžœ ˜K˜—šŸœž œ žœ žœ™8Jšžœžœ™Jšž™Jšœžœ™J™&šž™š žœžœžœžœžœ™EJ™J™J™Jš žœ!žœžœžœ ™D—šžœ™šžœžœžœ™%Jšžœžœžœžœ™'Jšžœžœžœ™—šž™šžœžœžœ™%Jšžœžœžœžœ™'Jšžœžœžœ™——J™-J™-šžœžœž™™$Jšžœžœžœžœ™'Jšžœžœžœ™—™$Jšžœžœžœžœ™'Jšžœžœžœ™—Jšžœ™—J™—Jšžœ ™—Jšžœ ™——K˜˜Kšœžœ˜3˜0K˜*Kšœ.˜.—K˜#K˜šžœž™J™<šœžœ™J™(—Jšžœ™—J™,šœžœ˜Jš œ%™7—K˜#šžœžœž˜˜K˜0K˜ —Jšœ™—šžœ˜˜Kšœžœ˜ Kšœžœ˜ KšŸœ ˜KšŸœ ˜KšŸœ ˜—K˜šžœžœž˜K˜&K˜Kšžœ˜—K˜™ Jšœžœ™!Jšœžœ™!Jšœ žœ™"J™%J™3——K˜š žœžœžœžœžœ˜-Kšœžœ˜7Kšœžœ˜5Kšœžœ ˜,Kšœžœ˜5Kšœžœ˜=—Jšœ ™ Jšœžœ™(Jšœžœ™+Jšœ žœ™Jšœ žœ™ Kšžœžœžœ(˜EK˜9K˜3K˜5K˜Kšžœ˜Kšœ  ˜ ——˜šŸ œžœžœ ˜:K˜"Kšœž œ˜Jšœ!™!Kšœžœžœ #˜=˜6Kšžœ˜—K˜šžœžœž˜šœ žœ+˜8šžœ˜šžœ˜ šžœžœž˜!˜$K˜——J™%K˜—K˜—K˜;šžœžœ#žœ˜KKšœžœ˜@Kšžœž˜ Kšœ˜—K˜šžœžœžœ˜#Kšœžœ#˜FKšœžœ˜'š žœžœžœžœž˜,šžœžœžœ˜Kšœžœ ˜.Jšœ™šœ žœ˜J™—Kšœžœ˜'š žœžœžœžœžœ˜-Kšœžœ˜+K˜,J™3K˜—Kšžœžœ˜ K˜—šžœ˜K˜-Jšœ4™4—Kšœžœ˜'Kšžœ˜—Kšœžœ˜(Kšœ˜—šžœ˜Kšœžœ&˜HKšžœž˜ Kšœ˜—K˜9K˜—Kšžœ ˜J™&Kšœ ˜———˜š Ÿœž œ/žœ žœžœžœ˜ŒKš œžœžœžœžœ˜mK˜˜6Kšžœ˜—Kšœžœ˜K˜šžœžœž˜šœ žœ+˜8K˜;šžœžœ#žœ˜KKšœžœ%˜GKšžœž˜ Kšœ˜—K˜<šžœžœ$žœ˜MKšœžœ&˜HKšžœž˜ Kšœ˜—K˜6šžœžœžœžœ˜AKšœžœ)˜KKšžœž˜ Kšœ˜—Kšœ9žœ˜?K˜˜)J™L—K˜QK˜7K˜K˜4šžœžœžœžœ˜=Kšœžœžœžœ˜$Kšœ;žœ˜BK˜B—šžœ˜šœ]žœžœžœ˜tJ™?—K˜PK˜-K˜—K˜—Kšžœ ˜—Kšœ ˜ ——˜šŸœž œ+žœ˜BKš œžœžœžœžœ œžœ˜IKšž˜Jšœžœžœ™'˜ šžœžœ˜ šžœ žœ˜+˜Kšœžœ ˜/Kšœžœ˜Kšžœ žœ˜Kšž˜Kšœž œ˜šžœ˜J™—š žœžœžœžœž˜+šžœžœž˜Kšœžœžœ˜#—K˜Kšœžœ˜Kšžœ ˜—Kšžœ ˜——˜šŸœž œ˜Kšœ5˜5Kšž˜K˜šžœžœž˜K˜#Jšœ žœ™K˜Kšžœ˜—Kšžœ ˜——K˜K˜˜šŸœž œ˜Kšœ žœ˜+Kšžœžœ˜Kšž˜Kšœž œ˜Kšœ7žœ˜Qšžœžœž˜šœ žœ+˜8šœ žœžœ˜Jšœ!™!—Jšœ™šžœ"˜$J™+—š žœžœžœžœž˜+šž˜Kšœ žœ'žœ˜6Kš žœžœžœžœž˜.˜)J™2—Kšžœ ˜—šžœžœžœ˜)Jšœ žœ ™Kšžœ˜ Kšœ˜—šžœ!žœ™)Jšœ žœ žœ™%—K˜šœžœ˜ J™—Kšžœ ˜—Kšœžœ ˜,šœžœ˜#K˜,—Kšžœžœ˜—Kšžœ ˜—Kšžœ ˜——˜šŸœž œ˜7Kšž˜Kšœžœžœ˜Kšœžœ˜Jšœžœžœ™'Kšœ7žœ˜Qšžœžœž˜šœ žœ+˜8šœžœ˜Jšœ@™@—šžœ&˜+Kšžœ*ž˜.K˜T™J™,—K˜Kšžœ˜—˜.J™.—˜%šžœ˜šžœ ˜Kšœžœ%˜GKšžœžœ˜———™'J™™™Jšœžœ%™/—Jšžœžœ™——Jšœ žœ™Kšœ˜—Kšžœ ˜—Kšžœ ˜——˜šŸœž œ.žœ˜QKšžœžœžœ˜Kšž˜Kšœžœ˜Kšœž œ˜šœžœžœ˜Jšœ'™'—Kšœ žœžœ˜Kšœ7žœ˜Qšžœžœž˜šœ žœ+˜9K˜#Jšœ;™;šœžœ ˜K˜šžœ˜˜Kšœžœ˜ K˜)—Kšžœžœ˜——Kšžœžœžœ˜+šžœž˜!šžœ žœ ˜0Kšœžœžœžœ˜—K˜Kšžœ˜—šžœ žœ'ž˜:Kšž˜K˜3™J™$—Kšžœ˜—˜,J™1—šœ žœ˜J™—šžœ žœ'ž˜:Kšž˜K˜3™J™$—Kšžœ˜—˜,J™1—K˜%—Kšžœ ˜—Jšœžœ™"Kšžœ ˜——˜šŸ œž œ˜Kšœ)žœ˜.Kšœžœžœ˜Kšžœžœžœ˜,Kšž˜—˜Kšœž œ žœ˜Kšœ7žœ˜Qšžœžœž˜Kšœ žœ+˜8——˜š Ÿœž œ žœžœžœžœ˜6Kšž˜šžœžœž˜Kšžœ(˜*—Kš žœžœžœžœ"žœ˜3Kšžœ  ˜——˜K˜Kšœžœžœ˜&Kšœžœžœ˜Kšœ+žœžœ˜6Kšœžœžœ˜&K˜šžœžœž˜K˜(—šž˜˜K˜0——šžœ˜!J™(—Jšœ=™=šžœž˜šžœ ˜ Kšœžœ˜1—Kšžœ˜—K˜5Kšžœžœ ˜%Kšžœ ˜——˜š Ÿ œž œžœž œ˜DKšžœžœž œ˜'Kšž˜šžœ˜Jšœ™—Kšœžœ˜Kšžœ˜Kšžœ ˜——K˜˜Kšžœ˜K˜—KšœYžœ`˜½K˜—…—`Vͺ