CDEnvironmentImpl.mesa (part of ChipNDale)
Copyright © 1983, 1986 by Xerox Corporation. All rights reserved.
by Christian Jacobi, August 11, 1983 11:32 am
Last Edited by: Christian Jacobi, September 4, 1986 4:47:32 pm PDT
DIRECTORY
Ascii,
Atom,
CD,
CDEnvironment,
CDEvents,
CDValue,
Commander,
CommandTool,
FileNames,
FS,
Icons,
IO,
List,
ProcessProps,
Rope,
RuntimeError,
TerminalIO,
TEditImpl USING [ReloadTable], --crazy tiptable load procedure
TIPUser,
UserProfile;
CDEnvironmentImpl: CEDAR PROGRAM
IMPORTS Atom, CD, CDEvents, CDValue, CommandTool, FileNames, FS, Icons, IO, List, ProcessProps, RuntimeError, Rope, TEditImpl, TerminalIO, TIPUser, UserProfile
EXPORTS CDEnvironment =
BEGIN
-- tip tables ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SetTipTable: PUBLIC PROC [for: REF, tipTable: Rope.ROPE] = {
tipTab: TIPUser.TIPTable ← NIL;
IF Rope.IsEmpty[tipTable] THEN tipTable ← "Standard";
CDValue.Store[for, $TipTableName, tipTable];
IF Rope.Equal[tipTable, "Standard"] THEN tipTab ← InstallStandardTip[for]
ELSE tipTab ← TryExplicitTipTable[for, tipTable];
IF tipTab#NIL THEN CDValue.Store[for, $TipTable, tipTab];
};
TryExplicitTipTable: PROC [for: REF, name: Rope.ROPE] RETURNS [tipTable: TIPUser.TIPTable] = {
name ← MakeName[base: name, ext: "tip", wDir: TechWDir[for]];
tipTable ← TIPUser.InstantiateNewTIPTable[name !
FS.Error => {
TerminalIO.WriteRopes["Tip-table not installed; ", error.explanation, "\n"];
GOTO oops
};
TIPUser.InvalidTable => {
TerminalIO.WriteRopes["Tip-table not installed; ", errorMsg, "\n"];
GOTO oops
};
];
EXITS oops => NULL
};
InstallStandardTip: PROC [for: REF] RETURNS [tipTable: TIPUser.TIPTable ← NIL] = {
WITH for SELECT FROM
tech: CD.Technology => {
profileKey: Rope.ROPE ← Rope.Cat["ChipNDale.", tech.name, ".TIP"];
techPart: Rope.ROPE ← MakeName[base: "ChipNDale", ext: "tip", modifier: tech.name, wDir: GetWorkingDirectory[tech]];
base: Rope.ROPE ← MakeName[base: "ChipNDale", ext: "tip", wDir: GetWorkingDirectory[NIL]];
default: Rope.ROPE ← Rope.Cat[techPart, " ", base];
tipTable ← TEditImpl.ReloadTable[oldTIP: NIL, profileKey: profileKey, default: default];
IF tipTable=NIL THEN {
TerminalIO.WriteRopes["Tip-table for ", tech.name, " not installed\n"];
tipTable ← TryExplicitTipTable[for, "ChipNDale.tip"];
};
};
d: CD.Design => tipTable ← InstallStandardTip[d.technology];
ENDCASE => TerminalIO.WriteRope["Tip-table not installed\n"];
};
GetTipTable: PUBLIC PROC [for: REF] RETURNS [TIPUser.TIPTable] = {
WITH CDValue.Fetch[boundTo: for, key: $TipTable, propagation: global] SELECT FROM
tipTable: TIPUser.TIPTable => RETURN [tipTable];
ENDCASE => RETURN [NIL];
};
NoteProfileChange: UserProfile.ProfileChangedProc = {
EachTech: CD.TechnologyEnumerator = {
WITH CDValue.Fetch[tech, $TipTableName] SELECT FROM
r: Rope.ROPE => IF Rope.Equal[r, "Standard"] THEN SetTipTable[tech, r];
ENDCASE => NULL;
};
[] ← CD.EnumerateTechnologies[EachTech];
};
-- Icons ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
FetchIcon: PROC [for: REF, file: Rope.ROPE, n: NAT] RETURNS [icon: REF Icons.IconFlavor] = {
if: Icons.IconFlavor;
icon ← NEW[Icons.IconFlavor←tool];
file ← MakeName[base: file, ext: "icon", wDir: TechWDir[for]];
if ← Icons.NewIconFromFile[file, n ! RuntimeError.UNCAUGHT => GOTO Oops];
icon^ ← if;
EXITS Oops => TerminalIO.WriteRope["**Icon not loaded\n"];
};
SetIcon: PUBLIC PROC [for: REF, file: Rope.ROPE, n: NAT] = {
icon: REF Icons.IconFlavor ← FetchIcon[for, file, n];
CDValue.Store[for, $Icon, icon];
};
GetIcon: PUBLIC PROC [for: REF] RETURNS [Icons.IconFlavor] = {
WITH CDValue.Fetch[for, $Icon, global] SELECT FROM
ip: REF Icons.IconFlavor => RETURN [ip^];
ENDCASE => RETURN [Icons.IconFlavor[unInit]]
};
SetPanelIcon: PUBLIC PROC [for: REF, file: Rope.ROPE, n: NAT] = {
icon: REF Icons.IconFlavor ← FetchIcon[for, file, n];
CDValue.Store[for, $PanelIcon, icon];
};
GetPanelIcon: PUBLIC PROC [for: REF] RETURNS [Icons.IconFlavor] = {
WITH CDValue.Fetch[for, $PanelIcon, global] SELECT FROM
ip: REF Icons.IconFlavor => RETURN [ip^];
ENDCASE => RETURN [Icons.IconFlavor[unInit]]
};
-- Working Directories ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CheckWorkingDirectory: PROC [wDir: Rope.ROPE] RETURNS [slashWDir: Rope.ROPE] = {
--if wDir is a directory, assign it to slashWDir
--else slashWDir ← nil
length: INT;
IF FileNames.IsADirectory[wDir] AND NOT FileNames.IsAPattern[wDir] THEN {
slashWDir ← FileNames.ConvertToSlashFormat[wDir];
length ← Rope.Length[slashWDir];
IF slashWDir=wDir AND length>0 AND slashWDir.Fetch[length-1]='/ THEN {
RETURN [slashWDir];
}
};
RETURN [NIL]
};
SetWorkingDirectory: PUBLIC PROC [for: REF, wDir: Rope.ROPE] = {
IF wDir=NIL THEN {
WITH for SELECT FROM
d: CD.Design => NULL;
t: CD.Technology => {
techName: Rope.ROPE ← t.name;
IF techName=NIL THEN techName ← Atom.GetPName[t.key];
wDir ← UserProfile.Token[Rope.Cat["ChipNDale.", techName, ".BaseDirectory"]];
};
ENDCASE => IF for=NIL THEN {
wDir ← UserProfile.Token["ChipNDale.BaseDirectory"];
};
IF wDir=NIL THEN wDir ← FileNames.CurrentWorkingDirectory[];
};
CDValue.Store[for, $WorkingDirectory, CheckWorkingDirectory[wDir]];
};
GetWorkingDirectory: PUBLIC PROC [for: REF] RETURNS [wDir: Rope.ROPENIL] = {
WITH CDValue.Fetch[for, $WorkingDirectory, global] SELECT FROM
r: Rope.ROPE => wDir ← CheckWorkingDirectory[r];
ENDCASE => NULL;
};
DoWithWDir: PUBLIC PROC [wDir: Rope.ROPE, proc: PROC] = {
ProcessProps.AddPropList[
propList: Atom.PutPropOnList[NIL, $WorkingDirectory, wDir],
inner: proc
];
};
-- Names ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
MakeName: PUBLIC PROC [base: Rope.ROPE, ext: Rope.ROPENIL, wDir: Rope.ROPENIL, modifier: Rope.ROPENIL] RETURNS [Rope.ROPE] = {
TrailingChar: PROC [base: Rope.ROPE, char: CHAR] RETURNS [INT] = {
--position of last "char", only before '!, '], '>, '/ considered
len: INT ← Rope.Length[base];
pos: INT ← len;
WHILE pos > 0 DO
SELECT Rope.Fetch[base, pos ← pos - 1] FROM
char => RETURN [pos];
'!, '], '>, '/ => EXIT;
ENDCASE;
ENDLOOP;
RETURN [len];
};
bang: INT = TrailingChar[base, '!];
--remove version number
r: Rope.ROPE ← Rope.Substr[base: base, len: bang];
--include modifier
IF ~modifier.IsEmpty[] THEN r ← Rope.Concat[r, modifier];
--include extension
IF ~ext.IsEmpty[] AND (TrailingChar[r, '.]>=Rope.Length[r]) THEN {
dot2: INT ← TrailingChar[ext, '.];
IF dot2>=Rope.Length[ext] THEN r ← Rope.Cat[r, ".", ext]
ELSE r ← Rope.Concat[r, ext.Substr[dot2]]
};
--include working directory
IF wDir#NIL THEN {
IF Rope.IsEmpty[r] OR (Rope.Fetch[r]#'/ AND Rope.Fetch[r]#'[) THEN
r ← Rope.Concat[FileNames.Directory[wDir], r]
};
--put version number back
IF bang<Rope.Length[base] THEN {
r ← Rope.Concat[r, Rope.Substr[base, bang]]
};
RETURN [r]
};
-- Loader ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SplitLine: PUBLIC PROC [line: Rope.ROPE] RETURNS [key, rest: Rope.ROPENIL] = {
--splits a line into a keyword and the rest
--returns
-- key: keyword at beginning of line;
-- (leading spaces and trailing separator [space or colon] removed)
-- rest: rest of line (after separator)
--  (leading spaces removed)
leng: INT ← Rope.Length[line];
nextPos: INT ← 0;
startPos: INT ← 0;
--skip leading spaces
WHILE startPos<leng DO
SELECT Rope.Fetch[line, startPos] FROM
Ascii.SP, Ascii.TAB => startPos ← startPos+1;
ENDCASE => EXIT;
ENDLOOP;
--find position of separator
nextPos ← startPos;
WHILE nextPos<leng DO
SELECT Rope.Fetch[line, nextPos] FROM
Ascii.SP, ':, Ascii.TAB => EXIT;
ENDCASE => nextPos ← nextPos+1;
ENDLOOP;
IF startPos<nextPos THEN key ← Rope.Substr[line, startPos, nextPos-startPos];
nextPos ← nextPos+1;
--skip leading spaces again
WHILE nextPos<leng DO
SELECT Rope.Fetch[line, nextPos] FROM
Ascii.SP, Ascii.TAB => nextPos ← nextPos+1;
ENDCASE => {rest ← Rope.Substr[line, nextPos, leng-nextPos]; RETURN}
ENDLOOP;
};
FetchKeyLine: PUBLIC PROC [fileName: Rope.ROPE, key: Rope.ROPE] RETURNS [entry: Rope.ROPENIL] = {
--Searches for a line starting with key in file
--Returns the rest of the line found or NIL if not found
line, first, rest: Rope.ROPE; file: IO.STREAM;
file ← FS.StreamOpen[fileName ! FS.Error => GOTO finish];
DO
line ← IO.GetLineRope[file ! IO.EndOfStream => GOTO finish];
IF ~Rope.IsEmpty[line] THEN {
[first, rest] ← SplitLine[line];
IF Rope.Equal[first, key] THEN RETURN [rest]
}
ENDLOOP;
EXITS finish => NULL;
};
StuffToCommandTool: PUBLIC PROC [r: Rope.ROPE, wDir: Rope.ROPENIL, searchPath: LIST OF Rope.ROPENIL] RETURNS [result: REFNIL] = {
RopeListPath: PROC [rl: LIST OF Rope.ROPE] RETURNS [path: List.LORANIL] = {
last: Rope.ROPENIL;
FOR l: LIST OF Rope.ROPE ← rl, l.rest WHILE l#NIL DO
IF ~Rope.IsEmpty[l.first] AND ~Rope.Equal[last, l.first, FALSE] THEN {
last ← l.first;
path ← CONS[last, path];
}
ENDLOOP;
RETURN [List.DReverse[path]]
};
out: Rope.ROPE; --for the result rope returned by the CommandTool
cmd: Commander.Handle ← NEW[Commander.CommandObject ← [
out: TerminalIO.TOS[],
err: TerminalIO.TOS[],
in: IO.noInputStream,
propertyList: List.PutAssoc[key: $SearchRules, val: RopeListPath[searchPath], aList: NIL]
]];
Exec: PROC [] = {
[out, result] ← CommandTool.DoCommandRope[commandLine: r, parent: cmd];
};
IF Rope.IsEmpty[r] THEN TerminalIO.WriteRopes["executes empty command\n"]
ELSE {
TerminalIO.WriteRopes["executes: """, r, """\n"];
DoWithWDir[wDir, Exec];
TerminalIO.WriteRopes["\n{", out, "}\n"];
};
};
ExecFileEntry: PUBLIC PROC [key: Rope.ROPE, technology: CD.Technology←NIL, modifier: Rope.ROPENIL] = {
--checks whether a particular key is mentioned in a .CDLoadList file
--if particular key is found, executes the rest of the line with a command tool
--technology and modifier are used to make the name of the used .CDLoadList file
--(using modifier .CDLoadList files for particular feature classes can be distinguished)
--building of name for the .CDLoadList files:
-- ChipNDale-CD.CDLoadList if {technology=NIL, modifier=NIL}
-- ChipNDale-CD-modifier.CDLoadList if {technology=NIL, modifier#NIL}
-- ChipNDale-technologyName.CDLoadList if {technology#NIL, modifier=NIL}
-- ChipNDale-technologyName-modifier.CDLoadList if {technology#NIL, modifier#NIL}
wDir: Rope.ROPE; --for working directories
entry: Rope.ROPE; --for the line which will be stuffed into a command tool
Fetch: PROC [mod: Rope.ROPE] = {
--internal procedure
--makes up the full path name of the .CDLoadList file
--and looks for a particular entry line in it
--mod: name for the technology
IF modifier#NIL THEN mod ← Rope.Cat[mod, "-", modifier];
mod ← MakeName["ChipNDale-", "CDLoadList", wDir, mod];
entry ← FetchKeyLine[mod, key];
};
searchPath: LIST OF Rope.ROPELIST[GetWorkingDirectory[NIL], "///Commands/"];
IF technology#NIL THEN {
wDir ← GetWorkingDirectory[technology];
searchPath ← CONS[wDir, searchPath];
Fetch[technology.name];
};
IF entry=NIL THEN {
wDir ← GetWorkingDirectory[NIL];
Fetch["CD"];
};
IF entry=NIL THEN TerminalIO.WriteRopes["command line for ", key, " not found\n"]
ELSE [] ← StuffToCommandTool[entry, wDir, searchPath];
};
LoadTechnology: PUBLIC PROC [key: ATOM, name: Rope.ROPE] RETURNS [tech: CD.Technology←NIL] = {
--makes all the necessary messages if not loaded
IF key#NIL THEN tech ← CD.FetchTechnology[key];
IF tech=NIL THEN {
autoLoadDefault: BOOL ← UserProfile.Boolean["ChipNDale.AutoLoad.Default", TRUE];
IF name=NIL THEN name ← Atom.GetPName[key];
IF UserProfile.Boolean[Rope.Concat["ChipNDale.AutoLoad.", name], autoLoadDefault] THEN {
TerminalIO.WriteRopes["load technology """, name, """ \n"];
ExecFileEntry[name];
tech ← CD.FetchTechnology[key];
};
IF tech=NIL THEN TerminalIO.WriteRopes["technology '", name, "' not loaded\n"];
};
};
-- internals ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
TechWDir: PROC [for: REF] RETURNS [Rope.ROPE] = {
WITH for SELECT FROM
d: CD.Design => for ← d.technology
ENDCASE => NULL;
RETURN [GetWorkingDirectory[for]]
};
TechnologyHasBeenRegisterd: CDEvents.EventProc = {
WITH x SELECT FROM
t: CD.Technology => {
SetWorkingDirectory[t, NIL];
Do not! allow tech without tiptable have no error message SetTipTable[t, "Standard"];
};
ENDCASE => NULL
};
NewDesignHasBeenCreated: CDEvents.EventProc = {
IF design#NIL THEN SetWorkingDirectory[design, FileNames.CurrentWorkingDirectory[]];
};
CDValue.RegisterKey[$TipTable, NIL, $CD];
CDValue.RegisterKey[$TipTableName, NIL, $CD];
CDValue.RegisterKey[$Icon, NIL, $CD];
CDValue.RegisterKey[$WorkingDirectory, NIL, $CD];
SetIcon[NIL, "ChipNDale.icons", 0];
SetPanelIcon[NIL, "ChipNDale.icons", 1];
SetWorkingDirectory[NIL, NIL];
UserProfile.CallWhenProfileChanges[NoteProfileChange];
CDEvents.RegisterEventProc[$CreateNewDesign, NewDesignHasBeenCreated];
CDEvents.RegisterEventProc[$RegisterTechnology, TechnologyHasBeenRegisterd];
END.