DIRECTORY BasicTime USING [GMT, nullGMT], Commander USING [CommandProc, Handle, Register], CommandTool USING [ArgumentVector, Failed, Parse, StarExpansion], Convert USING [Error, IntFromRope], DFUtilities USING [DateToStream], FileNames USING [ConvertToSlashFormat, CurrentWorkingDirectory, DirectoryContaining, GetShortName, HomeDirectory, IsADirectory, IsAPattern, ResolveRelativePath, StripVersionNumber], FS USING [Close, Copy, Delete, EnumerateForNames, Error, ErrorDesc, ExpandName, FileInfo, GetDefaultWDir, Open, NameProc, nullOpenFile, OpenFile, Rename, SetDefaultWDir, SetKeep, StreamOpen], FSBackdoor USING [Flush, EntryPtr, Enumerate, EnumerateCacheForNames, MakeFName, NameProc, ScavengeDirectoryAndCache, SetFreeboard, TextFromTextRep, VolumePages], IO USING [Close, EndOf, EndOfStream, Error, GetBlock, PutBlock, PutChar, PutF, PutFR, PutRope, PutText, Reset, STREAM, TextFromTOS, TOS], List USING [Assoc, PutAssoc], ProcessExtras USING [CheckForAbort], ProcessProps USING [GetPropList], ReadEvalPrint USING [Handle], Rope USING [Cat, Concat, Equal, Fetch, Find, Flatten, Index, Length, Match, Replace, ROPE, Run, SkipTo, Substr, Text], ViewerOps USING [PaintViewer]; FSFileCommandsImpl: CEDAR PROGRAM IMPORTS Commander, CommandTool, Convert, DFUtilities, FileNames, FS, FSBackdoor, IO, List, ProcessExtras, ProcessProps, Rope, ViewerOps = BEGIN ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; FSErrorMsg: PROC [error: FS.ErrorDesc] RETURNS [ROPE] = { SELECT error.group FROM lock => RETURN[" -- locked!\n"]; ENDCASE => IF error.code = $unknownFile THEN RETURN [" -- not found!\n"] ELSE RETURN[Rope.Cat[" -- FS.Error: ", error.explanation, "\n"]]; }; FSErrorMsg1: PROC [error: FS.ErrorDesc] RETURNS [ROPE] = { SELECT error.group FROM lock => RETURN[" -- locked!"]; ENDCASE => IF error.code = $unknownFile THEN RETURN [" -- not found!"] ELSE RETURN[Rope.Concat["\n -- FS.Error: ", error.explanation]]; }; createKeep: CARDINAL _ 2; DeleteFiles: Commander.CommandProc = { out: STREAM = cmd.out; exactLevelMatch: BOOL _ FALSE; anglesRequired: INT _ 0; deleteIt: FS.NameProc = { ProcessExtras.CheckForAbort[]; continue _ TRUE; IF exactLevelMatch AND anglesRequired # CountAngles[fullFName] THEN RETURN; IO.PutRope[out, " deleting "]; IO.PutRope[out, fullFName]; FS.Delete[fullFName ! FS.Error => IF error.group # bug THEN {IO.PutRope[out, FSErrorMsg1[error]]; CONTINUE} ]; IO.PutRope[out, "\n"]; }; argv: CommandTool.ArgumentVector _ CommandTool.Parse[cmd ! CommandTool.Failed => {msg _ errorMsg; GO TO Die}]; FOR i: NAT IN [1..argv.argc) DO pattern: ROPE _ argv[i]; IF Rope.Match["-*", pattern] THEN { sense: BOOL _ TRUE; FOR i: INT IN [1..Rope.Length[pattern]) DO c: CHAR = Rope.Fetch[pattern, i]; SELECT c FROM '~ => {sense _ NOT sense; LOOP}; 'x, 'X => exactLevelMatch _ sense; ENDCASE; sense _ TRUE; ENDLOOP; LOOP; }; {ENABLE FS.Error => IF error.group # $bug THEN {msg _ FSErrorMsg[error]; GO TO Die}; pattern _ FileNames.ResolveRelativePath[pattern]; pattern _ FS.ExpandName[pattern].fullFName; IF pattern.Length[] = 0 THEN LOOP; IF exactLevelMatch THEN anglesRequired _ CountAngles[pattern]; IF Rope.Find[pattern, "!"] = -1 THEN pattern _ Rope.Concat[pattern, "!L"]; IF Rope.Find[pattern, "*"] = -1 THEN [] _ deleteIt[pattern] ELSE FS.EnumerateForNames[pattern, deleteIt]; }; ProcessExtras.CheckForAbort[]; ENDLOOP; EXITS Die => result _ $Failure; }; TypeFile: Commander.CommandProc = { block: REF TEXT _ NEW[TEXT[256]]; nBytesRead: NAT _ 0; argv: CommandTool.ArgumentVector _ ExpandAndParse[cmd ! CommandTool.Failed => {msg _ errorMsg; GO TO Die}]; IF argv.argc < 2 THEN GOTO Usage; FOR i: NAT IN [1..argv.argc) DO fileStream: STREAM _ FS.StreamOpen[FileNames.ResolveRelativePath[argv[i]] ! FS.Error => IF error.group # $bug THEN {msg _ FSErrorMsg[error]; GO TO Die}]; WHILE NOT IO.EndOf[fileStream] DO nBytesRead _ IO.GetBlock[fileStream, block, 0, 256 ! IO.EndOfStream => EXIT; IO.Error => EXIT; ]; IO.PutBlock[cmd.out, block, 0, nBytesRead ! IO.EndOfStream => EXIT; IO.Error => EXIT; ]; ProcessExtras.CheckForAbort[]; ENDLOOP; IF fileStream # NIL THEN IO.Close[fileStream]; ENDLOOP; EXITS Die => result _ $Failure; Usage => RETURN[$Failure, "Usage: Type list-of-patterns\n"]; }; PDWD: Commander.CommandProc = { RETURN[NIL, FS.GetDefaultWDir[]]; }; SDWD: Commander.CommandProc = { dir: ROPE; argv: CommandTool.ArgumentVector _ ExpandAndParse[cmd ! CommandTool.Failed => {msg _ errorMsg; GO TO Die}]; IF argv.argc # 2 THEN GOTO Usage; dir _ FileNames.ResolveRelativePath[argv[1]]; IF argv[1].Length < 3 THEN GOTO Usage; FS.SetDefaultWDir[dir ! FS.Error => IF error.group # $bug THEN {msg _ FSErrorMsg[error]; GOTO Die}]; [result, msg] _ PDWD[cmd]; EXITS Usage => RETURN[$Failure, "Usage: SetDefaultWorkingDirectory directoryName"]; Die => result _ $Failure; }; PrintFSCacheInfo: Commander.CommandProc = { volName: ROPE _ NIL; size, free, freeboard: INT; argv: CommandTool.ArgumentVector _ ExpandAndParse[cmd ! CommandTool.Failed => {msg _ errorMsg; GO TO Die}]; IF argv.argc > 2 THEN {msg _ "Usage: PrintFSCacheInfo {volumeName}"; GO TO Die}; IF argv.argc = 2 THEN volName _ argv[1]; [size, free, freeboard] _ FSBackdoor.VolumePages[volName ! FS.Error => IF error.group # $bug THEN {msg _ FSErrorMsg[error]; GOTO Die}]; msg _ IO.PutFR[" size = %g, free = %g, freeboard = %g\n", [integer[size]], [integer[free]], [integer[freeboard]] ]; EXITS Die => result _ $Failure; }; SetKeep: Commander.CommandProc = { pattern: ROPE; keep: INT; bangIndex: PROC [r: ROPE] RETURNS [INT] = { FOR i: INT DECREASING IN [0 .. Rope.Length[r]) DO IF Rope.Fetch[r, i] = '! THEN RETURN [i]; ENDLOOP; RETURN [Rope.Length[r]]; }; setIt: FS.NameProc = { fullFName _ Rope.Substr[fullFName, 0, bangIndex[fullFName]]; cmd.out.PutF[" processing %g", [rope[fullFName]]]; FS.SetKeep[fullFName, keep]; cmd.out.PutChar['\n]; RETURN[NOT AbortRequested[cmd]]; }; argv: CommandTool.ArgumentVector _ CommandTool.Parse[cmd: cmd ! CommandTool.Failed => {msg _ errorMsg; GO TO Die}]; IF argv.argc # 3 THEN GO TO Usage; keep _ Convert.IntFromRope[argv[1] ! Convert.Error => GO TO Usage]; pattern _ Rope.Replace[base: argv[2], start: bangIndex[argv[2]], with: "!H"]; FS.EnumerateForNames[pattern, setIt ! FS.Error => IF error.group # $bug THEN {msg _ FSErrorMsg[error]; GO TO Die}]; EXITS Die => result _ $Failure; Usage => RETURN[$Failure, "Usage: SetKeep keep pattern"]; }; SetFSFreeboard: Commander.CommandProc = { freeBoard: INT; argv: CommandTool.ArgumentVector _ ExpandAndParse[cmd ! CommandTool.Failed => {msg _ errorMsg; GO TO Die}]; IF argv.argc # 2 THEN GOTO Usage; freeBoard _ Convert.IntFromRope[argv[1] ! Convert.Error => GOTO Usage]; FSBackdoor.SetFreeboard[freeBoard ! FS.Error => IF error.group # $bug THEN {msg _ FSErrorMsg[error]; GOTO Die;}]; EXITS Usage => RETURN[$Failure, "Usage: SetFreeboard nPages"]; Die => result _ $Failure; }; PrintWorkingDirectory: Commander.CommandProc = { wDir: ROPE _ FileNames.CurrentWorkingDirectory[]; rep: ReadEvalPrint.Handle _ NARROW[List.Assoc[key: $ReadEvalPrintHandle, aList: cmd.propertyList]]; IF rep # NIL AND rep.viewer # NIL THEN { rep.viewer.name _ Rope.Concat["CommandTool: WD = ", wDir]; ViewerOps.PaintViewer[viewer: rep.viewer, hint: caption, clearClient: FALSE]; }; RETURN[NIL, wDir]; }; ChangeWorkingDirectory: Commander.CommandProc = { wDir: ROPE; root: BOOL _ FALSE; push: BOOL _ FALSE; argv: CommandTool.ArgumentVector _ ExpandAndParse[cmd ! CommandTool.Failed => {msg _ errorMsg; GO TO oops}]; list: LIST OF ROPE _ NARROW[List.Assoc[$WorkingDirectoryStack, cmd.propertyList]]; SELECT cmd.procData.clientData FROM $CDR => root _ TRUE; $PDR => root _ push _ TRUE; $PD => push _ TRUE; ENDCASE; IF push THEN list _ CONS[FileNames.CurrentWorkingDirectory[], list]; SELECT argv.argc FROM 1 => wDir _ IF root THEN "///" ELSE FileNames.HomeDirectory[]; 2 => { wDir _ argv[1]; IF root THEN wDir _ FS.ExpandName[wDir, "///" ! FS.Error => {msg _ FSErrorMsg[error]; GO TO oops}; ].fullFName; }; ENDCASE => {msg _ "Usage: ChangeWorkingDirectory directoryName"; GO TO oops}; [result, msg] _ SetWD[cmd, wDir]; IF result # $Failure AND push THEN cmd.propertyList _ List.PutAssoc[$WorkingDirectoryStack, list, cmd.propertyList]; EXITS oops => result _ $Failure; }; PopWD: Commander.CommandProc = { list: LIST OF ROPE _ NARROW[List.Assoc[key: $WorkingDirectoryStack, aList: cmd.propertyList]]; IF list = NIL THEN RETURN[NIL, FileNames.CurrentWorkingDirectory[]]; [result, msg] _ SetWD[cmd, list.first]; list _ list.rest; cmd.propertyList _ List.PutAssoc[key: $WorkingDirectoryStack, val: list, aList: cmd.propertyList]; }; SetWD: PROC [cmd: Commander.Handle, wDir: ROPE] RETURNS [result: REF ANY _ NIL, msg: ROPE _ NIL] = { rep: ReadEvalPrint.Handle; wDir _ FileNames.ResolveRelativePath[FileNames.ConvertToSlashFormat[wDir]]; IF wDir.Length[] = 0 THEN RETURN[$Failure, "empty working directory"]; IF wDir.Fetch[0] # '/ THEN wDir _ Rope.Concat[FileNames.CurrentWorkingDirectory[], wDir]; IF wDir.Fetch[wDir.Length[] - 1] # '/ THEN wDir _ Rope.Concat[wDir, "/"]; IF wDir.Length[] < 3 THEN RETURN[NIL]; msg _ wDir; [] _ List.PutAssoc[key: $WorkingDirectory, val: wDir, aList: ProcessProps.GetPropList[]]; rep _ NARROW[List.Assoc[key: $ReadEvalPrintHandle, aList: cmd.propertyList]]; IF rep # NIL AND rep.viewer # NIL THEN { rep.viewer.name _ Rope.Concat["CommandTool: WD = ", wDir]; ViewerOps.PaintViewer[viewer: rep.viewer, hint: caption, clearClient: FALSE]; }; }; FindExcessVersionsCommand: Commander.CommandProc = { out: STREAM = cmd.out; lagName: ROPE _ NIL; lagBang: INT _ 0; inner: FS.NameProc = { bang: INT _ Rope.Length[fullFName]; pos: INT _ bang; continue _ TRUE; WHILE pos > 0 DO IF Rope.Fetch[fullFName, pos _ pos - 1] = '! THEN {bang _ pos; EXIT}; ENDLOOP; IF lagBang = bang AND Rope.Run[lagName, 0, fullFName, 0, FALSE] >= bang THEN { IF kill THEN { IO.PutRope[cmd.out, "\ndeleting "]; IO.PutRope[cmd.out, lagName]; FS.Delete[lagName ! FS.Error => IF error.group # bug THEN {IO.PutRope[out, FSErrorMsg1[error]]; CONTINUE}]; } ELSE { IO.PutRope[cmd.out, lagName]; IO.PutRope[cmd.out, " "]; }; }; lagName _ fullFName; lagBang _ bang; }; kill: BOOL _ FALSE; DoPattern: PROC [pattern: ROPE] = { FS.EnumerateForNames[pattern: pattern, proc: inner ! FS.Error => IF error.group # bug THEN { IO.PutRope[cmd.out, FSErrorMsg1[error]]; CONTINUE; }]; IO.PutRope[cmd.out, "\n"]; }; argv: CommandTool.ArgumentVector _ CommandTool.Parse[cmd: cmd ! CommandTool.Failed => {msg _ errorMsg; GO TO bogus}]; WITH cmd.procData.clientData SELECT FROM opt: ATOM => SELECT opt FROM $Find => kill _ FALSE; $Kill => kill _ TRUE; ENDCASE; ENDCASE; IF argv = NIL OR argv.argc <= 1 THEN DoPattern["*"] ELSE FOR i: NAT IN [1..argv.argc) DO DoPattern[argv[i]]; ENDLOOP; EXITS bogus => result _ $Failure; }; CacheFindExcessVersionsCommand: Commander.CommandProc = { out: STREAM = cmd.out; lagName: ROPE _ NIL; lagBang: INT _ 0; inner: FS.NameProc = { bang: INT _ Rope.Length[fullFName]; pos: INT _ bang; continue _ TRUE; WHILE pos > 0 DO IF Rope.Fetch[fullFName, pos _ pos - 1] = '! THEN {bang _ pos; EXIT}; ENDLOOP; IF lagBang = bang AND Rope.Run[lagName, 0, fullFName, 0, FALSE] >= bang THEN { IF kill THEN { IO.PutRope[out, "\nflushing "]; IO.PutRope[out, lagName]; FSBackdoor.Flush[lagName ! FS.Error => IF error.group # bug THEN {IO.PutRope[out, FSErrorMsg1[error]]; CONTINUE}]; } ELSE { IO.PutRope[out, lagName]; IO.PutRope[out, " "]; }; }; lagName _ fullFName; lagBang _ bang; }; DoPattern: PROC [pattern: ROPE] = { FSBackdoor.EnumerateCacheForNames[pattern: pattern, proc: inner ! FS.Error => IF error.group # bug THEN {IO.PutRope[out, FSErrorMsg1[error]]; CONTINUE}]; IO.PutRope[cmd.out, "\n"]; }; kill: BOOL _ FALSE; argv: CommandTool.ArgumentVector _ CommandTool.Parse[cmd ! CommandTool.Failed => {msg _ errorMsg; GO TO bogus}]; WITH cmd.procData.clientData SELECT FROM opt: ATOM => SELECT opt FROM $Find => kill _ FALSE; $Kill => kill _ TRUE; ENDCASE; ENDCASE; IF argv = NIL OR argv.argc <= 1 THEN DoPattern["*"] ELSE FOR i: NAT IN [1..argv.argc) DO DoPattern[argv[i]]; ENDLOOP; EXITS bogus => result _ $Failure; }; FSFlushCache: Commander.CommandProc = { out: STREAM = cmd.out; pattern: ROPE; flushIt: FSBackdoor.NameProc = { out.PutF[" flushing %g", [rope[fullGName]]]; FSBackdoor.Flush[fullGName ! FS.Error => { IF error.group = $lock THEN { out.PutRope[" -- Locked!"]; CONTINUE; } ELSE out.PutRope[" -- Error!\n"]; }]; out.PutRope["\n"]; RETURN[NOT AbortRequested[cmd]]; }; argv: CommandTool.ArgumentVector _ CommandTool.Parse[cmd: cmd ! CommandTool.Failed => {msg _ errorMsg; GO TO Die}]; FOR i: NAT IN [1..argv.argc) DO pattern _ FileNames.ResolveRelativePath[argv[i]]; {ENABLE FS.Error => IF error.group # $bug THEN {msg _ FSErrorMsg[error]; GO TO Die}; IF Rope.Find[pattern, "*"] = -1 THEN [] _ flushIt[pattern] ELSE FSBackdoor.EnumerateCacheForNames[flushIt, NIL, pattern]; }; IF AbortRequested[cmd] THEN EXIT; ENDLOOP; EXITS Die => result _ $Failure; }; FSListBTree: Commander.CommandProc = { out: STREAM _ cmd.out; pattern: Rope.Text _ NIL; printLocal: BOOL _ FALSE; printAttached: BOOL _ FALSE; printCached: BOOL _ TRUE; anySwitch: BOOL _ FALSE; localText: REF TEXT _ NEW[TEXT[64]]; localOut: STREAM _ IO.TOS[localText]; accept: PROC RETURNS [stop: BOOL] = { IO.PutText[out, IO.TextFromTOS[localOut]]; RETURN[stop: AbortRequested[cmd]]; }; print: UNSAFE PROC [entry: FSBackdoor.EntryPtr] RETURNS [accept, stop: BOOL _ FALSE] = UNCHECKED { name: ROPE = FSBackdoor.MakeFName[ FSBackdoor.TextFromTextRep[@entry[entry.nameBody]], entry.version]; IO.Reset[localOut]; WITH e: entry^ SELECT FROM local => IF printLocal THEN { IO.PutF[localOut, "L %g\n", [rope[name]]]; accept _ TRUE; }; attached => IF printAttached THEN { toName: ROPE = FSBackdoor.TextFromTextRep[@entry[e.attachedTo]]; IO.PutF[localOut, "A %-30g (%g)\n", [rope[name]], [rope[toName]]]; accept _ TRUE; }; cached => IF printCached THEN { IO.PutF[localOut, "C %-40g ", [rope[name]]]; DFUtilities.DateToStream[localOut, [explicit, e.used] ]; IO.PutRope[localOut, "\n"]; accept _ TRUE; }; ENDCASE => ERROR; }; argv: CommandTool.ArgumentVector _ CommandTool.Parse[cmd ! CommandTool.Failed => {msg _ errorMsg; GO TO Die}]; IF argv.argc = 1 THEN FSBackdoor.Enumerate[ volName: NIL, nameBodyPattern: "[*", localOnly: FALSE, allVersions: TRUE, version: [0], matchProc: print, acceptProc: accept] ELSE FOR i: NAT IN [1..argv.argc) DO IF argv[i].Fetch[0] = '- THEN { IF NOT anySwitch THEN { printLocal _ FALSE; printAttached _ FALSE; printCached _ FALSE; anySwitch _ TRUE; }; FOR j: INT IN [1..argv[i].Length[]) DO SELECT argv[i].Fetch[j] FROM 'c => printCached _ TRUE; 'l => printLocal _ TRUE; 'a => printAttached _ TRUE; ENDCASE; ENDLOOP; ProcessExtras.CheckForAbort[]; LOOP; }; pattern _ Rope.Flatten[FileNames.ResolveRelativePath[argv[i]]]; FSBackdoor.Enumerate[ volName: NIL, nameBodyPattern: pattern, localOnly: FALSE, allVersions: TRUE, version: [0], matchProc: print, acceptProc: accept]; IF AbortRequested[cmd] THEN EXIT; ENDLOOP; EXITS Die => result _ $Failure; }; FSLoad: Commander.CommandProc = { argv: CommandTool.ArgumentVector; out: STREAM _ cmd.out; pattern: ROPE; fetch: FS.NameProc = { file: FS.OpenFile _ FS.nullOpenFile; out.PutRope[" loading "]; out.PutRope[fullFName]; { file _ FS.Open[fullFName, read ! FS.Error => IF error.group # bug THEN {IO.PutRope[out, FSErrorMsg1[error]]; GO TO err} ]; FS.Close[file]; EXITS err => {}}; out.PutRope["\n"]; RETURN[NOT AbortRequested[cmd]]; }; argv _ CommandTool.Parse[cmd: cmd ! CommandTool.Failed => {msg _ errorMsg; GO TO Die}]; FOR i: NAT IN [1..argv.argc) DO pattern _ FileNames.ResolveRelativePath[argv[i]]; IF pattern.Find["!"] = -1 THEN pattern _ Rope.Concat[pattern, "!H"]; FS.EnumerateForNames[pattern, fetch ! FS.Error => IF error.group # $bug THEN {msg _ FSErrorMsg1[error]; GO TO Die}]; IF AbortRequested[cmd] THEN EXIT; ENDLOOP; EXITS Die => result _ $Failure; }; FSScavenge: Commander.CommandProc = { volName: ROPE _ NIL; argv: CommandTool.ArgumentVector _ ExpandAndParse[cmd ! CommandTool.Failed => {msg _ errorMsg; GO TO Die}]; IF argv.argc > 1 THEN volName _ argv[1]; FSBackdoor.ScavengeDirectoryAndCache[ volName ! FS.Error => {msg _ FSErrorMsg[error]; GO TO Die}]; EXITS Die => result _ $Failure; }; CopyAndRename: Commander.CommandProc = { forceCopy: BOOL _ FALSE; destinationDirectory: ROPE _ NIL; leftArrowExists: BOOL; doACopy: BOOL _ cmd.procData.clientData = $Copy; doStore: BOOL _ cmd.procData.clientData = $Store; dirBeforeStar: ROPE; dirBeforeStarLength: INT; retainStructure: BOOL _ FALSE; updateOnly: BOOL _ FALSE; exactLevelMatch: BOOL _ FALSE; anglesRequired: INT _ 0; HandleAFile: PROC [to, from: ROPE] = { ProcessExtras.CheckForAbort[]; cmd.out.PutF[" %g _ %g", [rope[to]], [rope[from]]]; {ENABLE FS.Error => IF error.group # bug THEN {msg _ FSErrorMsg1[error]; GO TO skipIt}; IF updateOnly THEN { sourceTime: BasicTime.GMT _ BasicTime.nullGMT; destTime: BasicTime.GMT _ BasicTime.nullGMT; sourceTime _ FS.FileInfo[from].created; destTime _ FS.FileInfo[to ! FS.Error => IF error.group # bug THEN CONTINUE].created; IF sourceTime = destTime AND sourceTime # BasicTime.nullGMT THEN { cmd.out.PutRope["\n -- not copied, create dates match"]; GO TO skipIt; }; }; IF doACopy THEN FS.Copy[from: from, to: to, keep: createKeep, attach: NOT forceCopy] ELSE FS.Rename[from: from, to: to]; EXITS skipIt => {}; }; cmd.out.PutRope["\n"]; }; argv: CommandTool.ArgumentVector _ CommandTool.Parse[cmd ! CommandTool.Failed => {msg _ errorMsg; GO TO Die}]; nArgs: NAT _ argv.argc; { i: NAT _ 1; length: INT; Bump: PROC [scratch: NAT] = { FOR j: NAT IN (scratch..nArgs) DO argv[j - 1] _ argv[j]; ENDLOOP; nArgs _ nArgs - 1; }; WHILE i < nArgs DO length _ argv[i].Length[]; SELECT TRUE FROM length = 0 => Bump[i]; argv[i].Fetch[0] = '- => { FOR j: INT IN [1..length) DO SELECT argv[i].Fetch[j] FROM 'c, 'C => forceCopy _ TRUE; 'r, 'R => retainStructure _ TRUE; 'u, 'U => updateOnly _ TRUE; 'x, 'X => exactLevelMatch _ TRUE; ENDCASE; ENDLOOP; Bump[i]; }; ENDCASE => i _ i + 1; ENDLOOP; }; -- end of switch processing leftArrowExists _ nArgs >= 3 AND Rope.Equal[argv[2], "_"]; FOR i: NAT IN [1..nArgs) DO argv[i] _ FileNames.ConvertToSlashFormat[FileNames.ResolveRelativePath[argv[i]]]; ENDLOOP; IF leftArrowExists THEN { IF FileNames.IsADirectory[argv[1]] THEN destinationDirectory _ argv[1] ELSE { IF nArgs # 4 OR FileNames.IsAPattern[argv[1]] OR FileNames.IsAPattern[argv[3]] THEN RETURN[$Failure, "Bad syntax for copying a file"]; HandleAFile[from: argv[3], to: argv[1]]; RETURN[IF msg # NIL THEN $Failure ELSE NIL, msg]; }; } ELSE destinationDirectory _ FileNames.ConvertToSlashFormat[FileNames.CurrentWorkingDirectory[]]; FOR i: NAT IN [(IF leftArrowExists THEN 3 ELSE 1) .. nArgs) DO IF FileNames.IsADirectory[argv[i]] THEN { msg _ Rope.Concat["Cannot copy a directory: ", argv[i]]; GO TO Die; }; IF FileNames.IsAPattern[argv[i]] THEN { pattern: ROPE _ argv[i]; handleIt: FS.NameProc = { to: ROPE; short: ROPE _ NIL; continue _ TRUE; IF SkipFunny[fullFName] THEN RETURN; IF exactLevelMatch AND anglesRequired # CountAngles[fullFName] THEN RETURN; fullFName _ FileNames.ConvertToSlashFormat[fullFName]; IF retainStructure THEN to _ FileNames.StripVersionNumber[fullFName.Substr[dirBeforeStarLength]] ELSE to _ FileNames.GetShortName[fullFName]; to _ Rope.Concat[destinationDirectory, to]; HandleAFile[from: fullFName, to: to]; RETURN[msg = NIL]; }; IF pattern.Find["!"] = -1 THEN pattern _ Rope.Concat[pattern, "!H"]; IF pattern.Fetch[0] # '/ THEN pattern _ Rope.Concat[FileNames.CurrentWorkingDirectory[], pattern]; IF exactLevelMatch THEN anglesRequired _ CountAngles[pattern]; dirBeforeStar _ FileNames.DirectoryContaining[ path: pattern, pos: Rope.Index[s1: pattern, s2: "*"]]; dirBeforeStarLength _ dirBeforeStar.Length[]; FS.EnumerateForNames[pattern, handleIt ! FS.Error => IF error.group # $bug THEN {msg _ FSErrorMsg[error]; GO TO Die}]; } ELSE { to: ROPE _ Rope.Concat[destinationDirectory, FileNames.GetShortName[argv[i]]]; HandleAFile[from: argv[i], to: to]; IF msg # NIL THEN GO TO Die; }; ProcessExtras.CheckForAbort[]; ENDLOOP; EXITS Die => result _ $Failure; }; SkipFunny: PROC [fullFName: ROPE] RETURNS [BOOL] = { lastPoint, bangIndex: INT _ 0; FOR i: INT DECREASING IN [0 .. fullFName.Length[]) DO SELECT fullFName.Fetch[i] FROM '! => bangIndex _ i; '> => {lastPoint _ i; EXIT}; ENDCASE; ENDLOOP; RETURN[ bangIndex = lastPoint + 1 ]; -- skip [..]<..>!1 }; ExpandAndParse: PROC [cmd: Commander.Handle] RETURNS [CommandTool.ArgumentVector] = { CommandTool.StarExpansion[cmd]; RETURN [CommandTool.Parse[cmd]]; }; CountAngles: PROC [pattern: ROPE] RETURNS [count: INT _ 0] = { len: INT = Rope.Length[pattern]; pos: INT _ 0; WHILE pos < len DO pos _ Rope.SkipTo[pattern, pos+1, ">/"]; count _ count + 1; ENDLOOP; IF Rope.Match["/*", pattern] THEN count _ count - 1; }; AbortRequested: PROC [cmd: Commander.Handle] RETURNS [BOOL] = { ProcessExtras.CheckForAbort[]; RETURN [FALSE]; }; Init: PROCEDURE = { Commander.Register[ "///Commands/Copy", CopyAndRename, "Copy newFile _ oldFile, or Copy directory _ {pattern}*\n -c: force copy, -r: retain structure, -u: update only, -x: eXact level match", $Copy]; Commander.Register[ "///Commands/Del", DeleteFiles, "Delete {switch | pattern}*\n -x: eXact level match"]; Commander.Register[ "///Commands/Delete", DeleteFiles, "Delete {switch | pattern}*\n -x: eXact level match"]; Commander.Register[ "///Commands/Rename", CopyAndRename, "Rename newFile _ oldFile, or Rename directory _ {pattern}*\n -r: retain structure, -u: update only, -x: eXact level match"]; Commander.Register[ "///Commands/Type", TypeFile, "Type fileName (in executive window)"]; Commander.Register[ "///Commands/PrintDefaultWorkingDirectory", PDWD, "Print FS default working directory"]; Commander.Register[ "///Commands/SetDefaultWorkingDirectory", SDWD, "SetDefaultWorkingDirectory directoryName"]; Commander.Register[ "///Commands/PrintFSCacheInfo", PrintFSCacheInfo, "PrintFSCacheInfo {volumeName}, Print FS cache info"]; Commander.Register[ "///Commands/SetKeep", SetKeep, "SetKeep keep {pattern}*"]; Commander.Register[ "///Commands/SetFreeboard", SetFSFreeboard, "SetFreeboard nPages -- set FS cache limit"]; Commander.Register[ "///Commands/PrintWorkingDirectory", PrintWorkingDirectory, "Print working directory"]; Commander.Register[ "///Commands/PWD", PrintWorkingDirectory, "Print working directory"]; Commander.Register[ "///Commands/ChangeWorkingDirectory", ChangeWorkingDirectory, "Change working directory", $CD]; Commander.Register[ "///Commands/CD", ChangeWorkingDirectory, "Change working directory", $CD]; Commander.Register[ "///Commands/CDR", ChangeWorkingDirectory, "Change working directory (root relative)", $CDR]; Commander.Register[ "///Commands/PushWorkingDirectory", ChangeWorkingDirectory, "Push working directory", $PD]; Commander.Register[ "///Commands/Push", ChangeWorkingDirectory, "Push working directory", $PD]; Commander.Register[ "///Commands/PushR", ChangeWorkingDirectory, "Push working directory (root relative)", $PDR]; Commander.Register[ "///Commands/PopWorkingDirectory", PopWD, "Pop working directory"]; Commander.Register[ "///Commands/Pop", PopWD, "Pop working directory"]; Commander.Register[ "///Commands/FlushCache", FSFlushCache, "FlushCache {pattern}* -- flush FS cache"]; Commander.Register[ "///Commands/ListBTree", FSListBTree, "ListBTree {pattern}* -- list FS local directory and cache"]; Commander.Register[ "///Commands/FSLoad", FSLoad, "Load {pattern}* -- retrieve files"]; Commander.Register[ "///Commands/Scavenge", FSScavenge, "Scavenge {volumeName} -- FS scavenge"]; Commander.Register[ "///Commands/CacheFindExcessVersions", CacheFindExcessVersionsCommand, "lists excess versions of files to the command output.", $Find]; Commander.Register[ "///Commands/CacheKillExcessVersions", CacheFindExcessVersionsCommand, "deletes excess versions of files.", $Kill]; Commander.Register[ "///Commands/FindExcessVersions", FindExcessVersionsCommand, "lists excess versions of files to the command output.", $Find]; Commander.Register[ "///Commands/KillExcessVersions", FindExcessVersionsCommand, "deletes excess versions of files.", $Kill]; }; Init[]; END. ÒFSFileCommandsImpl.mesa Copyright c 1984 by Xerox Corporation. All rights reserved. L. Stewart, January 17, 1984 4:54 pm Russ Atkinson, September 14, 1984 12:40:21 pm PDT [cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: ROPE _ NIL] CommandObject = [ in, out, err: STREAM, commandLine, command: ROPE, propertyList: List.AList, procData: CommandProcHandle] [fullFName: ROPE] RETURNS [continue: BOOL] This is a switch, not a file name [cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: ROPE _ NIL] CommandObject = [ in, out, err: STREAM, commandLine, command: ROPE, propertyList: List.AList, procData: CommandProcHandle] [cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: ROPE _ NIL] CommandObject = [ in, out, err: STREAM, commandLine, command: ROPE, propertyList: List.AList, procData: CommandProcHandle] [cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: ROPE _ NIL] CommandObject = [ in, out, err: STREAM, commandLine, command: ROPE, propertyList: List.AList, procData: CommandProcHandle] [cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: ROPE _ NIL] CommandObject = [ in, out, err: STREAM, commandLine, command: ROPE, propertyList: List.AList, procData: CommandProcHandle] [cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: ROPE _ NIL] CommandObject = [ in, out, err: STREAM, commandLine, command: ROPE, propertyList: List.AList, procData: CommandProcHandle] [fullFName: ROPE] RETURNS [continue: BOOL] [cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: ROPE _ NIL] CommandObject = [ in, out, err: STREAM, commandLine, command: ROPE, propertyList: List.AList, procData: CommandProcHandle] [cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: ROPE _ NIL] CommandObject = [ in, out, err: STREAM, commandLine, command: ROPE, propertyList: List.AList, procData: CommandProcHandle] Now change the commander herald, if there is one [cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: ROPE _ NIL] CommandObject = [ in, out, err: STREAM, commandLine, command: ROPE, propertyList: List.AList, procData: CommandProcHandle] [cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: ROPE _ NIL] CommandObject = [ in, out, err: STREAM, commandLine, command: ROPE, propertyList: List.AList, procData: CommandProcHandle] Do a bit of checking! Now change the commander herald, if there is one [cmd: Handle] RETURNS [result: REF _ NIL, msg: ROPE _ NIL] CommandObject = [ in, out, err: STREAM, commandLine, command: ROPE, propertyList: List.AList, procData: CommandProcHandle] [fullFName: ROPE] RETURNS [continue: BOOL] We have a duplicate file name that presumably differs only in version number just list the name to the command output [cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: ROPE _ NIL] CommandObject = [ in, out, err: STREAM, commandLine, command: ROPE, propertyList: List.AList, procData: CommandProcHandle] [fullFName: ROPE] RETURNS [continue: BOOL] We have a duplicate file name that presumably differs only in version number just list the name to the command output [cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: ROPE _ NIL] CommandObject = [ in, out, err: STREAM, commandLine, command: ROPE, propertyList: List.AList, procData: CommandProcHandle] [cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: ROPE _ NIL] CommandObject = [ in, out, err: STREAM, commandLine, command: ROPE, propertyList: List.AList, procData: CommandProcHandle] [cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: ROPE _ NIL] CommandObject = [ in, out, err: STREAM, commandLine, command: ROPE, propertyList: List.AList, procData: CommandProcHandle] [fullFName: ROPE] RETURNS [continue: BOOL] [cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: ROPE _ NIL] CommandObject = [ in, out, err: STREAM, commandLine, command: ROPE, propertyList: List.AList, procData: CommandProcHandle] There will be three forms of Copy: Copy newFile _ oldFile copy a single file to a new name Copy directory _ list-of-files and patterns copy a group of files to a new directory each with the same shortname as before If the directory _ is omitted, files will be copied to the current working directory Copy -c -- Copy, don't use Attach [cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: ROPE _ NIL] CommandObject = [ in, out, err: STREAM, commandLine, command: ROPE, propertyList: List.AList, procData: CommandProcHandle] This file does not need a copy, since it has the same create date as the destination file. We have been instructed to trust the create date. Process switches First find out whether there is a _ anywhere. If there is, it must be the second arg. If we get here, then for each of the filenames and patterns, copy the file to the destination directory. [fullFName: ROPE] RETURNS [continue: BOOL] Count the number of slashes after the first *. This is a convenience proc to better deal with error handling from the combination of these procedures. This routine counts either angle brackets or slashes (not counting a slash in the first position, which does not correspond to a slash). The idea is that this gives a measure of the number of levels in a path name. The count is too high by 1 (we skipped the first char anyway), since the first two slashes indicate server delimiters rather than directory element delimiters. Initialization Ê#Û˜šœ™Jšœ Ïmœ1™Jšžœžœ'˜Kšžœ˜Jšžœ˜Jšžœžœ&˜-—J˜—Jšœ˜Jšžœ˜—šž˜Jšœ˜—J˜—J˜šœ#˜#Jš œžœžœ žœžœžœžœ™Ešœ™Jšœžœžœ™1Jšœ6™6—Jš œžœžœžœžœ˜!Jšœ žœ˜šœ ˜ Jšœ>žœ˜J—Jšžœžœžœ˜!šžœžœžœž˜šœ žœžœ2˜IJš œžœ žœžœžœžœ˜O—šžœžœžœž˜!šœ žœ%˜4JšžœÏrž ˜Jšžœ  ž ˜Jšœ˜—šžœ)˜+Jšžœ ž ˜Jšžœ  ž ˜Jšœ˜—Jšœ˜Jšžœ˜—Jšžœžœžœžœ˜.Jšžœ˜—šž˜Jšœ˜Jšœ žœ-˜<—J˜—J˜šžœ˜Jš œžœžœ žœžœžœžœ™Ešœ™Jšœžœžœ™1Jšœ6™6—Jšžœžœžœ˜!J˜J˜—šžœ˜Jš œžœžœ žœžœžœžœ™Ešœ™Jšœžœžœ™1Jšœ6™6—Jšœžœ˜ šœ5˜5Jšœ)žœžœ˜5—Jšžœžœžœ˜!Jšœ-˜-Jšžœžœžœ˜&šžœ˜Jš œžœ žœžœžœ˜N—Jšœžœ˜šž˜Jšœ žœ>˜MJšœ˜—J˜—J˜šœ+˜+Jš œžœžœ žœžœžœžœ™Ešœ™Jšœžœžœ™1Jšœ6™6—Jšœ žœžœ˜Jšœžœ˜šœ5˜5Jšœ)žœžœ˜5—Jšžœžœ0žœžœ˜PJšžœžœ˜(šœ8˜8Jš œžœ žœžœžœ˜N—šœžœ2˜:Jšœ9˜9—šž˜Jšœ˜—J˜J˜—šœ"˜"Jš œžœžœ žœžœžœžœ™Ešœ™Jšœžœžœ™1Jšœ6™6—Jšœ žœ˜Jšœžœ˜ š œ žœžœžœžœ˜+š žœžœž œžœž˜1Jšžœžœžœ˜)Jšžœ˜—Jšžœ˜J˜—šœžœ ˜Jšœ žœžœ žœ™*Jšœ<˜žœžœ˜K—Jš œžœžœžœžœ7˜Ršžœž˜#Jšœžœ˜Jšœžœ˜Jšœžœ˜Jšžœ˜—Jšžœžœžœ,˜Dšžœ ž˜Jšœ žœžœžœ˜>šœ˜Jšœ˜šžœžœžœ˜-Jšœžœ$žœžœ˜4Jšœ ˜ —J˜—Jšžœ:žœžœ˜M—Jšœ!˜!šžœžœž˜"JšœQ˜Q—Jšžœ˜ J˜—J˜šœ ˜ Jš œžœžœ žœžœžœžœ™Ešœ™Jšœžœžœ™1Jšœ6™6—Jš œžœžœžœžœC˜^Jš žœžœžœžœžœ'˜DJšœ'˜'Jšœ˜J˜bJ˜—J˜šŸœž˜ Jšœžœžœ žœžœžœžœžœ˜XJ˜JšœK˜KJ™Jšžœž œ&˜Fšžœž˜Jšœ>˜>—Jšžœ$žœ˜IJšžœžœžœžœ˜&Jšœ ˜ JšœY˜YJ™0JšœžœA˜Mš žœžœžœžœžœ˜(Jšœ:˜:JšœFžœ˜MJ˜—J˜—J˜˜4Jš œžœ žœžœžœžœ™:šœ™Jšœžœžœ™1Jšœ6™6—Jšœžœ ˜Jšœ žœžœ˜Jšœ žœ˜šœžœ ˜Jšœ žœžœ žœ™*Jšœžœ˜#Jšœžœ˜Jšœ žœ˜šžœ ž˜Jšžœ+žœžœ˜EJšžœ˜—šžœžœ$žœ žœ˜NJ™Lšžœ˜šžœ˜Jšžœ!˜#Jšžœ˜šžœ˜šœžœ ˜ Jšžœžœžœ#žœ˜K——J˜—šžœ˜Jšœ(™(Jšžœ˜Jšžœ˜J˜——J˜—Jšœ˜J˜J˜—Jšœžœžœ˜šŸ œžœ žœ˜#šžœ0˜2šœžœ žœžœ˜)Jšžœ&˜(Jšžœ˜ Jšœ˜——Jšžœ˜J˜—šœ=˜=Jšœ)žœžœ ˜7—šžœžœž˜(šœžœ˜ šžœž˜Jšœžœ˜Jšœžœ˜Jšžœ˜——Jšžœ˜—šžœžœžœ˜Jšžœ˜Jš žœžœžœžœžœžœ˜A—šž˜Jšœ˜—J˜J˜—˜9Jš œžœžœ žœžœžœžœ™Ešœ™Jšœžœžœ™1Jšœ6™6—Jšœžœ ˜Jšœ žœžœ˜Jšœ žœ˜šœžœ ˜Jšœ žœžœ žœ™*Jšœžœ˜#Jšœžœ˜Jšœ žœ˜šžœ ž˜Jšžœ+žœžœ˜EJšžœ˜—šžœžœ$žœ žœ˜NJ™Lšžœ˜šžœ˜Jšžœ˜Jšžœ˜šœ˜šœžœ ˜ Jšžœžœžœ#žœ˜K——J˜—šžœ˜Jšœ(™(Jšžœ˜Jšžœ˜J˜——J˜—Jšœ˜J˜J˜—šŸ œžœ žœ˜#šœ?˜?šœžœ ˜ Jšžœžœžœ#žœ˜K——Jšžœ˜J˜—Jšœžœžœ˜šœ8˜8Jšœ)žœžœ ˜7—šžœžœž˜(šœžœ˜ šžœž˜Jšœžœ˜Jšœžœ˜Jšžœ˜——Jšžœ˜—šžœžœžœ˜Jšžœ˜Jš žœžœžœžœžœžœ˜A—šž˜Jšœ˜—J˜J˜—šœ'˜'Jš œžœžœ žœžœžœžœ™Ešœ™Jšœžœžœ™1Jšœ6™6—Jšœžœ ˜Jšœ žœ˜šœ ˜ Kšœ-˜-šœžœ ˜*šžœžœ˜Kšœ˜Kšžœ˜ K˜—Kšžœ˜"Kšœ˜—Kšœ˜Kšžœžœ˜ Kšœ˜—šœ=˜=Jšœ)žœžœ˜5—šžœžœžœž˜Kšœ1˜1š œžœžœ žœžœžœžœ˜Tšžœ˜Kšžœ˜Kšžœ,žœ ˜>—K˜—Kšžœžœžœ˜!Kšžœ˜—šž˜Kšœ˜—J˜—J˜šœ&˜&Jš œžœžœ žœžœžœžœ™Ešœ™Jšœžœžœ™1Jšœ6™6—Jšœžœ ˜Jšœžœ˜Jšœ žœžœ˜Jšœžœžœ˜Jšœ žœžœ˜Jšœ žœžœ˜Jš œ žœžœžœžœ˜$Jšœ žœžœžœ ˜%šœžœžœžœ˜%Kšžœžœ˜*Kšžœ˜"K˜—šœžœž˜Kš œžœžœžœž œ˜Ošœžœ˜"KšœC˜C—Kšžœ˜šžœ žœž˜šœ žœ žœ˜Kšžœ(˜*Kšœ žœ˜K˜—šœ žœžœ˜#Kšœžœ4˜@Kšžœ@˜BKšœ žœ˜K˜—šœ žœ žœ˜Kšžœ+˜-Kšœ8˜8Kšœ˜Kšœ žœ˜K˜—Kšžœžœ˜—Kšœ˜—šœ8˜8Jšœ)žœžœ˜5—šžœ˜šž˜šœ˜Jšœ žœ$žœžœ5˜}——šž˜šžœžœžœž˜šžœžœ˜šžœžœ žœ˜Jšœ žœ˜Jšœžœ˜Jšœžœ˜Jšœ žœ˜J˜—šžœžœžœž˜&šžœž˜Jšœžœ˜Jšœžœ˜Jšœžœ˜Jšžœ˜—Jšžœ˜—Kšœ˜Jšžœ˜J˜—Kšœ?˜?šœ˜Jšœ žœ'žœžœ6˜—Kšžœžœžœ˜!Kšžœ˜———šž˜Kšœ˜—J˜—J˜šœ!˜!Jš œžœžœ žœžœžœžœ™Ešœ™Jšœžœžœ™1Jšœ6™6—Jšœ!˜!Jšœžœ ˜Jšœ žœ˜šœžœ ˜Jšœ žœžœ žœ™*Kšœžœ žœ˜$Kšœ˜Kšœ˜˜šœžœ˜šœžœ ˜ Kš žœžœžœ#žœžœ˜J—Kšœ˜—Kšžœ ˜Kšžœ ˜—Kšœ˜Kšžœžœ˜ Kšœ˜—šœ!˜!Jšœ)žœžœ˜5—šžœžœžœž˜Kšœ1˜1Jšžœžœ&˜Dšžœ!˜#Jš œžœ žœžœžœžœ˜P—Jšžœžœžœ˜!Kšžœ˜—šž˜Jšœ˜—Kšœ˜—J˜šœ%˜%Jš œžœžœ žœžœžœžœ™Ešœ™Jšœžœžœ™1Jšœ6™6—Kšœ žœžœ˜šœ ˜ Jšœ>žœžœ˜J—Kšžœžœ˜(šœ%˜%Kšœ žœ$žœ˜<—šž˜Kšœ˜—J˜—K™™"™K™ —™+K™OK™U—Kšœ Ïc™"—K˜šœ(˜(Jš œžœžœ žœžœžœžœ™Ešœ™Jšœžœžœ™1Jšœ6™6—Jšœ žœžœ˜Jšœžœžœ˜!Jšœžœ˜Jšœ žœ#˜0Jšœ žœ$˜1Jšœžœ˜Jšœžœ˜Jšœžœžœ˜Jšœ žœžœ˜Jšœžœžœ˜Jšœžœ˜J˜šŸ œžœ žœ˜&J˜Jšœ4˜4šœž˜šžœ ˜ Jšžœžœžœžœ ˜C—šžœ žœ˜Jšœžœ˜.Jšœžœ˜,Jšœ žœ˜'Jš œ žœžœ žœžœžœ ˜Tšžœžœ žœ˜BJšœ™Jšœ9˜9Jšžœžœ˜ J˜—J˜—šžœ˜ Jšžœžœ4žœ ˜IJšžœžœ˜#—šž˜J˜ —K˜—Jšœ˜K˜—J˜šœ8˜8Jšœ)žœžœ˜5—Jšœžœ ˜J˜˜J™Jšœžœ˜ Jšœžœ˜ šŸœžœ žœ˜šžœžœžœž˜!J˜Jšžœ˜—J˜J˜—J™šžœ ž˜J˜šžœžœž˜Jšœ˜šœ˜šžœžœžœ ž˜šžœž˜Jšœžœ˜Jšœžœ˜!Jšœžœ˜Jšœžœ˜!Jšžœ˜—Jšžœ˜—J˜J˜—Jšžœ˜—Kšžœ˜—Jšœ¡˜—J™J™VJ˜Jšœžœ˜:šžœžœžœ ž˜JšœQ˜QJšžœ˜—J˜šžœ˜šžœ˜šžœ ˜"Jšžœ˜#šžœ˜šžœ žœžœ˜NJšžœžœ,˜7—Jšœ(˜(Jš žœžœžœžœ žœžœ˜1J˜——J˜—šžœ˜JšœD˜D——J˜J™hš žœžœžœžœžœžœž˜>šžœ!žœ˜)Jšœ8˜8Jšžœžœ˜ J˜—šžœ˜ šžœ˜Jšœ žœ ˜šœ žœ ˜Jšœ žœžœ žœ™*Jšœžœ˜ Jšœžœžœ˜Jšœ žœ˜Jšžœžœžœ˜$Jšžœžœ)žœžœ˜KJšœ6˜6šžœ˜JšžœI˜MJšžœ(˜,—Jšœ+˜+Jšœ%˜%Jšžœžœ˜J˜—Jšžœžœ&˜Dšžœž˜JšœD˜D—Jšžœžœ'˜>šœ.˜.Jšœ6˜6—šœ-˜-J™.—šžœ$˜&Jš œžœ žœžœžœžœ˜O—Jšœ˜—šžœ˜JšœžœF˜NJšœ#˜#Jš žœžœžœžœžœ˜J˜——J˜Jšžœ˜—šž˜Jšœ˜—K˜—K˜š Ÿ œžœ žœžœžœ˜4Jšœžœ˜š žœžœž œžœž˜5šžœž˜Jšœ˜Jšœžœ˜Jšžœ˜—Jšžœ˜—Jšžœžœ¡˜7K˜K˜—šŸœžœžœ!˜UJšœg™gJšœ˜Jšžœ˜ J˜—J˜š Ÿ œžœ žœžœ žœ ˜>Jšœ×™×Jšœžœ˜ Jšœžœ˜ šžœ ž˜Jšœ(˜(Jšœ˜Jšžœ˜—šžœž˜!JšœŸ™ŸJšœ˜—J˜J˜—šŸœžœžœžœ˜?J˜Jšžœžœ˜J˜—J˜šŸœž œ˜šœ˜Jšœ"˜"Jšœ‘˜‘—šœ˜Jšœ˜Jšœ7˜7—šœ˜Jšœ"˜"Jšœ7˜7—šœ˜Jšœ$˜$Jšœ~˜~—šœ˜Jšœ˜Jšœ'˜'—šœ˜Jšœ,žœ˜1Jšœ&˜&—šœ˜Jšœ*žœ˜/Jšœ,˜,—šœ˜Jšœ1˜1Jšœ6˜6—šœ˜Jšœ˜Jšœ˜—šœ˜Jšœ+˜+Jšœ-˜-—šœ˜Jšœ;˜;Jšœ˜—šœ˜Jšœ)˜)Jšœ˜—šœ˜Jšœ=˜=Jšœ!˜!—šœ˜Jšœ)˜)Jšœ!˜!—šœ˜Jšœ*˜*Jšœ2˜2—šœ˜Jšœ;˜;Jšœ˜—šœ˜Jšœ+˜+Jšœ˜—šœ˜Jšœ,˜,Jšœ0˜0—šœ˜Jšœ)˜)Jšœ˜—šœ˜Jšœ˜Jšœ˜—šœ˜Jšœ'˜'Jšœ+˜+—šœ˜Jšœ%˜%Jšœ=˜=—šœ˜JšœC˜C—šœ˜Jšœ$˜$Jšœ(˜(—šœ˜JšœF˜FJšœ8˜8Jšœ˜—šœ˜JšœF˜FJšœ$˜$Jšœ˜—šœ˜Jšœ<˜