CDOut.mesa
Copyright © 1983, 1986 by Xerox Corporation. All rights reserved.
by Ch. Jacobi, December 12, 1983 2:02 pm
some code from Kim Rachmeler
Last Edited by: Jacobi, January 29, 1986 11:52:04 am PST
DIRECTORY
BasicTime,
CDIO,
CDIOExtras,
CD,
CDDirectory,
CDEvents,
CDExtras,
CDPrivate,
CDProperties,
CDValue,
Convert,
FileNames,
FS,
IO,
Process,
Rope,
TerminalIO,
TokenIO;
CDOut:
CEDAR
PROGRAM
--monitored by TokenIO
IMPORTS BasicTime, CD, CDIO, CDDirectory, CDEvents, CDExtras, CDProperties, CDValue, Convert, FileNames, FS, IO, Process, Rope, TerminalIO, TokenIO
EXPORTS CDIO, CDIOExtras
SHARES CDProperties =
BEGIN
xChipndaleFile: INT = 12121983;
xVersion: INT = 12;
propKeyForKey: REF = NEW[INT];
-- global variables
-- protected through attachment with TokenIO
gDesign: CD.Design ← NIL;
gDirectoryNum: INT;
gDirectoryMark: TokenIO.Mark;
gThisKey: REF ← NIL;
gLastKey: REF ← NIL;
gQuiet: BOOL ← FALSE;
WriteRope:
PROC [r: Rope.
ROPE] =
INLINE BEGIN
IF ~gQuiet THEN TerminalIO.WriteRope[r]
END;
WriteLayer:
PUBLIC PROC [l:
CD.Layer] =
BEGIN
TokenIO.WriteAtom[CD.LayerKey[l]];
END;
WritePos:
PUBLIC
PROC [p:
CD.Position] =
BEGIN
TokenIO.WriteInt[p.x];
TokenIO.WriteInt[p.y];
END;
WriteRect:
PUBLIC
PROC[r:
CD.Rect] =
BEGIN
TokenIO.WriteInt[r.x1];
TokenIO.WriteInt[r.y1];
TokenIO.WriteInt[r.x2];
TokenIO.WriteInt[r.y2];
END;
FakeCopyProperties:
PROC[propList:
CD.PropList]
RETURNS [newList:
CD.PropList←
NIL] =
INLINE
BEGIN
DoFakeCopy:
PROC[] = {
FOR l:
CD.PropList ← propList, l.rest
WHILE l#
NIL
DO
IF ISTYPE[l.first.key, ATOM] THEN newList ← CONS[l.first, newList];
ENDLOOP;
};
IF propList#NIL THEN CDProperties.DoWithinLock[DoFakeCopy]
END;
WriteProperties:
PUBLIC
PROC [props:
CD.PropList] =
BEGIN
--we make a copy! so GetProp's reorder does not fool us
FOR l:
CD.PropList ← FakeCopyProperties[props], l.rest
WHILE l#
NIL
DO
IF
ISTYPE[l.first.key,
ATOM]
THEN {
pp: CDProperties.PropertyProcs = CDProperties.FetchProcs[l.first.key];
IF pp#
NIL
AND pp.internalWrite#
NIL
THEN {
TokenIO.WriteAtom[$Property];
TokenIO.WritePushFlag[NARROW[l.first.key, ATOM]];
pp.internalWrite[l.first.key, l.first.val];
TokenIO.WritePopFlag[];
}
ELSE
WITH l.first.val
SELECT
FROM
r: Rope.
ROPE => {
TokenIO.WriteAtom[$DefaultProperty];
TokenIO.WriteAtom[NARROW[l.first.key, ATOM]];
TokenIO.WriteRope[r];
};
at:
ATOM => {
TokenIO.WriteAtom[$DefaultProperty];
TokenIO.WriteAtom[NARROW[l.first.key, ATOM]];
TokenIO.WriteAtom[at];
};
ri:
REF
INT => {
TokenIO.WriteAtom[$DefaultProperty];
TokenIO.WriteAtom[NARROW[l.first.key, ATOM]];
TokenIO.WriteInt[ri^];
};
pl: CDPrivate.LayerRef => {
TokenIO.WriteAtom[$DefaultProperty];
TokenIO.WritePushFlag[NARROW[l.first.key, ATOM]];
TokenIO.WriteAtom[$layer]; -- now comes a property list
WriteLayer[pl.number];
TokenIO.WritePopFlag[];
};
pl:
CD.PropList => {
TokenIO.WriteAtom[$DefaultProperty];
TokenIO.WritePushFlag[NARROW[l.first.key, ATOM]];
TokenIO.WriteAtom[$properties]; -- now comes a property list
WriteProperties[pl];
TokenIO.WritePopFlag[];
};
rl:
LIST
OF Rope.
ROPE => {
TokenIO.WriteAtom[$DefaultProperty];
TokenIO.WritePushFlag[NARROW[l.first.key, ATOM]];
TokenIO.WriteAtom[$ropeList]; -- now comes a property list
FOR l:
LIST
OF Rope.
ROPE ← rl, l.rest
WHILE l#
NIL
DO
TokenIO.WriteRope[l.first];
ENDLOOP;
TokenIO.WritePopFlag[];
};
ENDCASE => NULL;
};
ENDLOOP;
END;
WriteInstance:
PUBLIC
PROC [ap:
CD.Instance] =
BEGIN
TokenIO.WriteInt[ap.location.x];
TokenIO.WriteInt[ap.location.y];
CDIO.WriteOrientation[ap.orientation];
WriteProperties[ap.properties];
WriteObject[ap.ob];
END;
Length:
PROC [il:
CD.InstanceList]
RETURNS [leng:
INT𡤀] =
INLINE
BEGIN
FOR list:
CD.InstanceList ← il, list.rest
WHILE list#
NIL
DO
leng ← leng+1;
ENDLOOP;
END;
WriteInstanceList:
PUBLIC
PROC [list:
CD.InstanceList] =
BEGIN
TokenIO.WriteInt[Length[list]];
FOR l:
CD.InstanceList ← list, l.rest
WHILE l#
NIL
DO
WriteInstance[l.first];
ENDLOOP;
END;
WriteObject:
PUBLIC
PROC [ob:
CD.Object] =
BEGIN
Process.Yield[]; --should make output slower than redraw...
WITH CDProperties.GetPropFromObject[from: ob, prop: gThisKey]
SELECT
FROM
key:
REF
INT => { WriteRope["."];
TokenIO.WriteInt[key^];
};
ENDCASE => { WriteRope["x"];
DoWriteObject[ob]
};
END;
DoWriteObject:
PROC [ob:
CD.Object, number:
INT←-1] =
BEGIN
IF ob.class.internalWrite=
NIL
THEN {
ob1: CD.Object ← CDDirectory.ExpandHard[ob, gDesign, NIL];
IF ob1#
NIL
THEN {
WriteRope["*"];
WriteObject[ob1];
}
ELSE {
TokenIO.WritePushFlag[$Unknown];
TokenIO.WritePopFlag[];
WriteRope["*object not written\n"];
};
}
ELSE {
--object has internalWrite proc
TokenIO.WritePushFlag[ob.class.objectType];
ob.class.internalWrite[ob];
IF ob.class.inDirectory
THEN {
TokenIO.WriteRope[CDDirectory.Name[ob]];
};
WriteProperties[ob.properties];
TokenIO.WritePopFlag[];
};
IF number#-1
THEN
CDProperties.PutPropOnObject[onto: ob, prop: gThisKey, val: NEW[INT←number]];
CDProperties.PutPropOnObject[onto: ob, prop: gLastKey, val: NIL];
END;
WritePushLevel:
PROC [pl:
LIST
OF
CD.PushRec] =
BEGIN
IF pl=NIL THEN RETURN;
IF pl.rest#NIL THEN WritePushLevel[pl.rest];
TokenIO.WriteAtom[$Push];
IF pl.first.mightReplace#NIL THEN WriteInstance[pl.first.mightReplace]
ELSE TokenIO.WriteAtom[$Nil];
WriteObject[pl.first.dummyCell.ob];
WriteRect[pl.first.specific.dIr];
END;
EachChild: CDDirectory.EnumerateObjectsProc
--PROC [me: Object, x: REF]-- =
BEGIN
xx: REF = CDProperties.GetPropFromObject[from: me, prop: gThisKey];
IF xx#NIL THEN RETURN; -- it and its children are already out
IF me.class.inDirectory
THEN
CDDirectory.EnumerateChildObjects[me: me, p: EachChild, x: x];
gDirectoryNum ← gDirectoryNum+1;
TokenIO.WriteInt[gDirectoryNum];
DoWriteObject[me, gDirectoryNum];
END;
EachDirectoryEntry: CDDirectory.EachEntryAction
--[name: Rope.ROPE, ob: CD.Object] RETURNS [quit: BOOLLSE]-- =
BEGIN
EachChild[ob, NIL];
END;
InnerWriteDesign:
PROC [] =
BEGIN
ENABLE TokenIO.WritingStopped => {
RemoveOldProperties:
PROC[design:
CD.Design, key:
REF] = {
--at this place we know that CDExtras will fork and will set the priority down
CDExtras.RemoveProperties[design, key];
};
RemoveOldProperties[gDesign, gLastKey];
REJECT
};
PrepareOutput:
PROC = {
--uses globals
gThisKey ← NEW[INT];
gLastKey ← CDValue.Fetch[boundTo: gDesign, key: propKeyForKey, propagation: design];
IF gLastKey=NIL THEN {gLastKey←NEW[INT]};
CDValue.Store[boundTo: gDesign, key: propKeyForKey, value: gThisKey];
gDirectoryNum ← 0;
};
NameForOutput:
PROC [d:
CD.Design]
RETURNS [Rope.
ROPE] = {
IF Rope.Length[d.name]<=0 OR Rope.Fetch[d.name]='/ OR Rope.Fetch[d.name]='[ THEN RETURN[NIL]
ELSE RETURN [d.name];
};
TimeKey:
PROC []
RETURNS [Rope.
ROPE] = {
RETURN [Convert.RopeFromTime[from: BasicTime.Now[], end: seconds]];
};
PrepareOutput[];
TokenIO.WriteRope[NameForOutput[gDesign]];
TokenIO.WriteRope[TimeKey[]];
gDirectoryMark ← TokenIO.MarkAndWriteInt[gDirectoryNum]; -- number of entries in directory
[] ← CDDirectory.Enumerate[design: gDesign, action: EachDirectoryEntry];
WriteProperties[gDesign.properties^];
WritePushLevel[gDesign.actual];
TokenIO.UpdateMark[mark: gDirectoryMark, value: gDirectoryNum];
TokenIO.WriteAtom[$EndOfDesign];
END;
WriteHelpMessage:
PROC [] =
BEGIN
Write:
PROC [r: Rope.
ROPE] = {
TerminalIO.WriteRopes[r, "\n"];
};
Write["****************************************"];
Write["Help for saving a design:"];
Write["wait until the background saving is done and make a copy of it! (but don't rely on background saving alone; background saving is done WITHOUT requiring monitor locks, for obvious reasons.)"];
Write["then, try regular output again, before you proceed to any of the more drastic methods following:\n"];
Write["SHIFT-SHIFT-SWAT (>2 seconds) should save all designs"];
Write["or use a command tool: (the command tool commands require you to bringover the necesarry sources and bcd's first)"];
Write[" ""← CDSequencerImpl.savePeriod ← -1"" "];
Write[" prevents any further un-monitored automatic saving in parallel with your other command tool commands."];
Write[" ""← CDEmergencyHandling.SaveAll[]"" "];
Write[" or"];
Write[" ""← TokenIO.ReleaseWriter[]"" "];
Write[" to release the locks; after this try a regular output command"];
Write["****************************************"];
END;
OpenFile:
PROC [design:
CD.Design, to:
REF, emergency:
BOOL←
FALSE]
RETURNS [stream:
IO.
STREAM, mustClose:
BOOL←
TRUE, name: Rope.
ROPE←NIL] =
--opens the file but does not any output to it
BEGIN
CheckForOverWrite:
PROC [name: Rope.
ROPE] =
--Errors DontOverWrite if necessary
BEGIN
temfile: IO.STREAM;
temfile ← FS.StreamOpen[name ! FS.Error => IF error.group#bug THEN GOTO ok];
--file exists already
TerminalIO.WriteRopes["File ", name, " exists already; overwrite?"];
IF ~TerminalIO.Confirm[choice: "overwrite", label: "file exists"]
THEN
ERROR ABORTED;
TerminalIO.WriteRope[" yes\n"];
EXITS ok => NULL --this is the "normal" case of an not yet existing file
END;
InteractiveName:
PROC [wDir: Rope.
ROPE←
NIL]
RETURNS [name: Rope.
ROPE] =
BEGIN
TerminalIO.WriteRope["file name:"];
IF ~Rope.IsEmpty[wDir] THEN TerminalIO.WriteRopes[" (", wDir, ")"];
name ← TerminalIO.RequestRope[" > "];
name ← CDIO.MakeName[name, "dale", wDir];
CheckForOverWrite[name];
END;
wDir: Rope.ROPE ← CDIO.GetWorkingDirectory[design];
IF Rope.IsEmpty[wDir] THEN wDir ← FileNames.CurrentWorkingDirectory[];
IF to#
NIL
THEN
WITH to
SELECT
FROM
s: IO.STREAM => RETURN [stream←s, mustClose←FALSE, name←NIL];
r: Rope.ROPE => name ← r;
ENDCASE => TerminalIO.WriteRope["bad file parameter in WriteDesign\n"];
IF Rope.IsEmpty[name]
THEN
IF emergency THEN name ← "///temp/ChipNDale/emergency/emergency.dale"
ELSE name ← InteractiveName[wDir];
name ← CDIO.MakeName[name, "dale", wDir];
stream ←
FS.StreamOpen[fileName: name, accessOptions: $create, keep: 2 !
FS.Error => {
mustClose ← FALSE;
stream ← NIL;
IF error.group#bug THEN CONTINUE;
}
];
END;
Attach:
PROC [stream:
IO.
STREAM, emergency:
BOOL←
FALSE, quiet:
BOOL←
FALSE]
RETURNS [done:
BOOL←
TRUE] =
BEGIN
TokenIO.AttachWriter[stream ! TokenIO.Error => {done←FALSE; CONTINUE}];
IF ~done
AND emergency
THEN {
FOR i:
INT
IN [0..5]
DO
TokenIO.StopWriting[];
Process.Pause[Process.SecondsToTicks[1]];
TokenIO.StopWriting[];
Process.Pause[Process.SecondsToTicks[2]];
TerminalIO.WriteRope["TRIES TO BREAK THE LOCK\n"];
TokenIO.ReleaseWriter[]; -- XXXXXXX DANGEROUS
Process.Pause[Process.SecondsToTicks[1]];
done←TRUE;
TokenIO.AttachWriter[stream ! TokenIO.Error => {done←FALSE; CONTINUE}];
IF done THEN RETURN;
ENDLOOP;
};
IF ~done
AND ~quiet
THEN {
TerminalIO.WriteRope["ChipNDale internal locks are hold\n"];
WriteHelpMessage[];
};
END;
WriteDesign:
PUBLIC
PROC [design:
CD.Design, to:
REF, quiet:
BOOL, emergency:
BOOL]
RETURNS [done:
BOOL←
FALSE] =
--to is either a IO.STREAM, a Rope.ROPE, or NIL
--if emergency, some locks are ignored, interactive input is skipped; you better
--roll back after an emergency write is done
BEGIN
mustClose: BOOL←FALSE;
binFile: IO.STREAM;
fileName: Rope.ROPE;
sealMark: TokenIO.Mark;
[binFile, mustClose, fileName] ← OpenFile[design, to, emergency];
IF binFile=
NIL
THEN {
IF ~quiet THEN TerminalIO.WriteRope["file not created\n"];
RETURN
};
IF ~Attach[binFile, emergency, quiet] THEN RETURN;
[] ← CDEvents.ProcessEvent[ev: beforeOutputEvent, design: design, x: NIL, listenToDont: FALSE];
gQuiet ← quiet;
gDesign ← design;
TokenIO.WriteInt[xChipndaleFile];
TokenIO.WriteInt[xVersion];
sealMark ← TokenIO.MarkAndWriteInt[0]; -- this is an invalid seal; it will be fixed at the end
TokenIO.WriteAtom[gDesign.technology.key];
TokenIO.WriteRope[gDesign.technology.name];
IF CDEvents.ProcessEvent[ev: writeEvent, design: gDesign, x:
NIL, listenToDont:
TRUE].dont
THEN {
WriteRope["write not done\n"];
TokenIO.ReleaseWriter[];
IO.Close[binFile];
RETURN
};
InnerWriteDesign[];
TokenIO.UpdateMark[sealMark, -1]; -- validate seal
TokenIO.ReleaseWriter[];
IF mustClose THEN IO.Close[binFile];
WriteRope["\n"];
TerminalIO.WriteRopes["design ", gDesign.name, " written on file "];
TerminalIO.WriteRopes[fileName, "\n"];
done ← TRUE;
END;
-- Module initialization
writeEvent: CDEvents.EventRegistration = CDEvents.RegisterEventType[$WriteTechnologyPrivate];
beforeOutputEvent: CDEvents.EventRegistration = CDEvents.RegisterEventType[$BeforeOutput];
END.