Alexander Shiryaev 12 роки тому
батько
коміт
dc9d663946

+ 361 - 0
BlackBox/Dev/Mod/Commanders.txt

@@ -0,0 +1,361 @@
+MODULE DevCommanders;
+
+	(* THIS IS TEXT COPY OF BlackBox 1.6-rc6 Dev/Mod/Commanders.odc *)
+	(* DO NOT EDIT *)
+
+	IMPORT
+		Kernel, Fonts, Ports, Stores, Models, Views, Controllers, Properties, Dialog, Controls,
+		TextModels, TextSetters, TextMappers, Services, StdLog;
+
+	CONST
+		(* additional Scan types *)
+		ident = 19; qualident = 20; execMark = 21;
+
+		point = Ports.point;
+
+		minVersion = 0; maxVersion = 0; maxStdVersion = 0;
+
+
+	TYPE
+		View* = POINTER TO ABSTRACT RECORD (Views.View)
+		END;
+		EndView* = POINTER TO ABSTRACT RECORD (Views.View)
+		END;
+
+		Par* = POINTER TO RECORD
+			text*: TextModels.Model;
+			beg*, end*: INTEGER
+		END;
+
+		Directory* = POINTER TO ABSTRACT RECORD END;
+
+
+		StdView = POINTER TO RECORD (View) END;
+		StdEndView = POINTER TO RECORD (EndView) END;
+
+		StdDirectory = POINTER TO RECORD (Directory) END;
+
+		Scanner = RECORD
+			s: TextMappers.Scanner;
+			ident: ARRAY LEN(Kernel.Name) OF CHAR;
+			qualident: ARRAY LEN(Kernel.Name) * 2 - 1 OF CHAR
+		END;
+		
+		TrapCleaner = POINTER TO RECORD (Kernel.TrapCleaner) END;
+
+	VAR
+		par*: Par;
+		dir-, stdDir-: Directory;
+		
+		cleaner: TrapCleaner;
+		cleanerInstalled: BOOLEAN;
+
+
+	(** Cleaner **)
+
+	PROCEDURE (c: TrapCleaner) Cleanup;
+	BEGIN
+		par := NIL;
+		cleanerInstalled := FALSE;
+	END Cleanup;
+	
+	(** View **)
+
+	PROCEDURE (v: View) Externalize- (VAR wr: Stores.Writer), EXTENSIBLE;
+	BEGIN
+		v.Externalize^(wr);
+		wr.WriteVersion(maxVersion);
+		wr.WriteXInt(execMark)
+	END Externalize;
+
+	PROCEDURE (v: View) Internalize- (VAR rd: Stores.Reader), EXTENSIBLE;
+		VAR thisVersion, type: INTEGER;
+	BEGIN
+		v.Internalize^(rd);
+		IF rd.cancelled THEN RETURN END;
+		rd.ReadVersion(minVersion, maxVersion, thisVersion);
+		IF rd.cancelled THEN RETURN END;
+		rd.ReadXInt(type)
+	END Internalize;
+
+
+	(** Directory **)
+
+	PROCEDURE (d: Directory) New* (): View, NEW, ABSTRACT;
+	PROCEDURE (d: Directory) NewEnd* (): EndView, NEW, ABSTRACT;
+
+
+	(* auxilliary procedures *)
+
+	PROCEDURE IsIdent (VAR s: ARRAY OF CHAR): BOOLEAN;
+		VAR i: INTEGER; ch: CHAR;
+	BEGIN
+		ch := s[0]; i := 1;
+		IF ("A" <= CAP(ch)) & (CAP(ch) <= "Z") OR (ch >= 0C0X) & (ch # "×") & (ch # "÷") & (ch <= 0FFX) OR (ch = "_") THEN
+			REPEAT
+				ch := s[i]; INC(i)
+			UNTIL ~( ("0" <= ch) & (ch <= "9") OR ("A" <= CAP(ch)) & (CAP(ch) <= "Z")
+						OR (ch >= 0C0X) & (ch # "×") & (ch # "÷") & (ch <= 0FFX) OR (ch = "_") );
+			RETURN (ch = 0X) & (i <= LEN(Kernel.Name))
+		ELSE
+			RETURN FALSE
+		END
+	END IsIdent;
+
+	PROCEDURE Scan (VAR s: Scanner);
+		VAR done: BOOLEAN;
+	BEGIN
+		s.s.Scan;
+		IF (s.s.type = TextMappers.view) THEN
+			IF Properties.ThisType(s.s.view, "DevCommanders.View") # NIL THEN s.s.type := execMark END
+		ELSIF (s.s.type = TextMappers.string) & TextMappers.IsQualIdent(s.s.string) THEN
+			s.s.type := qualident; s.qualident := s.s.string$
+		ELSIF (s.s.type = TextMappers.string) & IsIdent(s.s.string) THEN
+			s.ident := s.s.string$;
+			TextMappers.ScanQualIdent(s.s, s.qualident, done);
+			IF done THEN s.s.type := qualident ELSE s.s.type := ident END
+		END
+	END Scan;
+
+	PROCEDURE GetParExtend (r: TextModels.Reader; VAR end: INTEGER);
+		VAR v, v1: Views.View;
+	BEGIN
+		REPEAT r.ReadView(v); 
+			IF v # NIL THEN 
+				v1 := v;
+				v := Properties.ThisType(v1, "DevCommanders.View") ;
+				IF v = NIL THEN v := Properties.ThisType(v1, "DevCommanders.EndView")  END
+			END
+		UNTIL r.eot OR (v # NIL);
+		end := r.Pos(); IF ~r.eot THEN DEC(end) END
+	END GetParExtend;
+
+	PROCEDURE Unload (cmd: Dialog.String);
+		VAR modname: Kernel.Name; str: Dialog.String; i: INTEGER; ch: CHAR; mod: Kernel.Module;
+	BEGIN
+		i := 0; ch := cmd[0];
+		WHILE (ch # 0X) & (ch # ".") DO modname[i] := SHORT(ch); INC(i); ch := cmd[i] END;
+		modname[i] := 0X;
+		mod := Kernel.ThisLoadedMod(modname);
+		IF mod # NIL THEN
+			Kernel.UnloadMod(mod);
+			IF mod.refcnt < 0 THEN
+				str := modname$;
+				Dialog.MapParamString("#Dev:Unloaded", str, "", "", str);
+				StdLog.String(str); StdLog.Ln;
+				Controls.Relink
+			ELSE
+				str := modname$;
+				Dialog.ShowParamMsg("#Dev:UnloadingFailed", str, "", "")
+			END
+		END
+	END Unload;
+
+	PROCEDURE Execute (t: TextModels.Model; pos: INTEGER; VAR end: INTEGER; unload: BOOLEAN);
+		VAR s: Scanner; beg, res: INTEGER; cmd: Dialog.String;
+	BEGIN
+		end := t.Length();
+		s.s.ConnectTo(t); s.s.SetPos(pos); s.s.SetOpts({TextMappers.returnViews});
+		Scan(s); ASSERT(s.s.type = execMark, 100);
+		Scan(s);
+		IF s.s.type IN {qualident, TextMappers.string} THEN
+			beg := s.s.Pos() - 1; GetParExtend(s.s.rider, end);
+			ASSERT(~cleanerInstalled, 101);
+			Kernel.PushTrapCleaner(cleaner); cleanerInstalled := TRUE;
+			NEW(par); par.text := t; par.beg := beg; par.end := end;
+			IF s.s.type = qualident THEN cmd := s.qualident$ ELSE cmd := s.s.string$ END;
+			IF unload (* & (s.s.type = qualident)*) THEN Unload(cmd) END;
+			Dialog.Call(cmd, " ",  res);
+			par := NIL;
+			Kernel.PopTrapCleaner(cleaner); cleanerInstalled := FALSE;
+		END
+	END Execute;
+
+	PROCEDURE Track (v: View; f: Views.Frame; x, y: INTEGER; buttons: SET);
+		VAR c: Models.Context; w, h, end: INTEGER; isDown, in, in0: BOOLEAN; m: SET;
+	BEGIN
+		c := v.context; c.GetSize(w, h); in0 := FALSE; in := TRUE;
+		REPEAT
+			IF in # in0 THEN
+				f.MarkRect(0, 0, w, h, Ports.fill, Ports.invert, Ports.show); in0 := in
+			END;
+			f.Input(x, y, m, isDown);
+			in := (0 <= x) & (x < w) & (0 <= y) & (y < h)
+		UNTIL ~isDown;
+		IF in0 THEN
+			f.MarkRect(0, 0, w, h, Ports.fill, Ports.invert, Ports.hide);
+			WITH c:TextModels.Context DO
+				Execute(c.ThisModel(), c.Pos(), end,Controllers.modify IN buttons)
+			ELSE Dialog.Beep
+			END
+		END
+	END Track;
+
+	(* StdView *)
+
+	PROCEDURE (v: StdView) Externalize (VAR wr: Stores.Writer);
+	BEGIN
+		v.Externalize^(wr);
+		wr.WriteVersion(maxStdVersion)
+	END Externalize;
+
+	PROCEDURE (v: StdView) Internalize (VAR rd: Stores.Reader);
+		VAR thisVersion: INTEGER;
+	BEGIN
+		v.Internalize^(rd);
+		IF rd.cancelled THEN RETURN END;
+		rd.ReadVersion(minVersion, maxStdVersion, thisVersion)
+	END Internalize;
+
+	PROCEDURE (v: StdView) Restore (f: Views.Frame; l, t, r, b: INTEGER);
+		CONST u = point;
+		VAR c: Models.Context; a: TextModels.Attributes; font: Fonts.Font; color: Ports.Color;
+			size, d, w, asc, dsc, fw: INTEGER; s: ARRAY 2 OF CHAR;
+	BEGIN
+		ASSERT(v.context # NIL, 20);
+		c := v.context;
+		WITH c: TextModels.Context DO a := c.Attr(); font := a.font; color := a.color
+		ELSE font := Fonts.dir.Default(); color := Ports.defaultColor
+		END;
+		font.GetBounds(asc, dsc, fw);
+		size := asc + dsc; d := size DIV 2;
+		f.DrawOval(u, 0, u + size, size, Ports.fill, color);
+		s := "!";
+		w := font.StringWidth(s);
+		f.DrawString(u + d - w DIV 2, size - dsc, Ports.background, s, font)
+	END Restore;
+
+	PROCEDURE (v: StdView) HandleCtrlMsg (f: Views.Frame; VAR msg: Controllers.Message;
+																		VAR focus: Views.View);
+	BEGIN
+		WITH msg: Controllers.TrackMsg DO
+			Track(v, f, msg.x, msg.y, msg.modifiers)
+		| msg: Controllers.PollCursorMsg DO
+			msg.cursor := Ports.refCursor
+		ELSE
+		END
+	END HandleCtrlMsg;
+
+	PROCEDURE (v: StdView) HandlePropMsg (VAR msg: Properties.Message);
+		VAR c: Models.Context; a: TextModels.Attributes; font: Fonts.Font; asc, dsc, fw: INTEGER;
+	BEGIN
+		WITH msg: Properties.Preference DO
+			WITH msg: Properties.SizePref DO
+				c := v.context;
+				IF (c # NIL) & (c IS TextModels.Context) THEN
+					a := c(TextModels.Context).Attr(); font := a.font
+				ELSE font := Fonts.dir.Default()
+				END;
+				font.GetBounds(asc, dsc, fw);
+				msg.h := asc + dsc; msg.w := msg.h + 2 * point
+			| msg: Properties.ResizePref DO
+				msg.fixed := TRUE
+			| msg: Properties.FocusPref DO
+				msg.hotFocus := TRUE
+			| msg: TextSetters.Pref DO
+				c := v.context;
+				IF (c # NIL) & (c IS TextModels.Context) THEN
+					a := c(TextModels.Context).Attr(); font := a.font
+				ELSE font := Fonts.dir.Default()
+				END;
+				font.GetBounds(asc, msg.dsc, fw)
+			| msg: Properties.TypePref DO
+				IF Services.Is(v, msg.type) THEN msg.view := v END
+			ELSE
+			END
+		ELSE
+		END
+	END HandlePropMsg;
+	
+	
+	(* StdEndView *)
+
+	PROCEDURE (v: StdEndView) Restore (f: Views.Frame; l, t, r, b: INTEGER);
+		CONST u = point;
+		VAR c: Models.Context; a: TextModels.Attributes; font: Fonts.Font; color: Ports.Color;
+			size, w, asc, dsc, fw: INTEGER; s: ARRAY 2 OF CHAR;
+			points: ARRAY 3 OF Ports.Point;
+	BEGIN
+		ASSERT(v.context # NIL, 20);
+		c := v.context;
+		WITH c: TextModels.Context DO a := c.Attr(); font := a.font; color := a.color
+		ELSE font := Fonts.dir.Default(); color := Ports.defaultColor
+		END;
+		font.GetBounds(asc, dsc, fw);
+		size := asc + dsc;
+		points[0].x := 0; points[0].y := size;
+		points[1].x := u + (size DIV 2); points[1].y := size DIV 2;
+		points[2].x := u + (size DIV 2); points[2].y := size;     
+		f.DrawPath(points, 3, Ports.fill, color, Ports.closedPoly)
+	END Restore;
+	
+	PROCEDURE (v: StdEndView) HandlePropMsg (VAR msg: Properties.Message);
+		VAR c: Models.Context; a: TextModels.Attributes; font: Fonts.Font; asc, dsc, fw: INTEGER;
+	BEGIN
+		WITH msg: Properties.Preference DO
+			WITH msg: Properties.SizePref DO
+				c := v.context;
+				IF (c # NIL) & (c IS TextModels.Context) THEN
+					a := c(TextModels.Context).Attr(); font := a.font
+				ELSE font := Fonts.dir.Default()
+				END;
+				font.GetBounds(asc, dsc, fw);
+				msg.h := asc + dsc; msg.w := (msg.h + 2 * point) DIV 2
+			| msg: Properties.ResizePref DO
+				msg.fixed := TRUE
+			| msg: Properties.FocusPref DO
+				msg.hotFocus := TRUE
+			| msg: TextSetters.Pref DO
+				c := v.context;
+				IF (c # NIL) & (c IS TextModels.Context) THEN
+					a := c(TextModels.Context).Attr(); font := a.font
+				ELSE font := Fonts.dir.Default()
+				END;
+				font.GetBounds(asc, msg.dsc, fw)
+			| msg: Properties.TypePref DO
+				IF Services.Is(v, msg.type) THEN msg.view := v END
+			ELSE
+			END
+		ELSE
+		END
+	END HandlePropMsg;
+
+	(* StdDirectory *)
+
+	PROCEDURE (d: StdDirectory) New (): View;
+		VAR v: StdView;
+	BEGIN
+		NEW(v); RETURN v
+	END New;
+	
+	PROCEDURE (d: StdDirectory) NewEnd (): EndView;
+		VAR v: StdEndView;
+	BEGIN
+		NEW(v); RETURN v
+	END NewEnd;
+
+	PROCEDURE Deposit*;
+	BEGIN
+		Views.Deposit(dir.New())
+	END Deposit;
+
+	PROCEDURE DepositEnd*;
+	BEGIN
+		Views.Deposit(dir.NewEnd())
+	END DepositEnd;
+
+	PROCEDURE SetDir* (d: Directory);
+	BEGIN
+		dir := d
+	END SetDir;
+
+	PROCEDURE Init;
+		VAR d: StdDirectory;
+	BEGIN
+		NEW(d); dir := d; stdDir := d;
+		NEW(cleaner); cleanerInstalled := FALSE;
+	END Init;
+
+BEGIN
+	Init
+END DevCommanders.

+ 348 - 0
BlackBox/Dev/Mod/Compiler.txt

@@ -0,0 +1,348 @@
+MODULE DevCompiler;
+
+	(* THIS IS TEXT COPY OF BlackBox 1.6-rc6 Dev/Mod/Compiler.odc *)
+	(* DO NOT EDIT *)
+
+	IMPORT Kernel,
+		Files, Views, Dialog, Controls,
+		TextModels, TextMappers, TextViews, TextControllers,
+		StdLog, StdDialog,
+		DevMarkers, DevCommanders, DevSelectors,
+		DevCPM, DevCPT, DevCPB, DevCPP, DevCPE, DevCPV := DevCPV486;
+
+	CONST
+		(* compiler options: *)
+		checks = 0; allchecks = 1; assert = 2; obj = 3; ref = 4; allref = 5; srcpos = 6; reallib = 7; signatures = 8;
+		hint = 29; oberon = 30; errorTrap = 31;
+		defopt = {checks, assert, obj, ref, allref, srcpos, signatures};
+
+		(* additional scanner types *)
+		import = 100; module = 101; semicolon = 102; becomes = 103; comEnd = 104;
+
+	VAR
+		sourceR: TextModels.Reader;
+		s: TextMappers.Scanner;
+		str: Dialog.String;
+		found: BOOLEAN;	(* DevComDebug was found -> DTC *)
+
+	PROCEDURE Module (source: TextModels.Reader; opt: SET; log: TextModels.Model; VAR error: BOOLEAN);
+		VAR ext, new: BOOLEAN; p: DevCPT.Node;
+	BEGIN
+		DevCPM.Init(source, log);
+		IF found THEN INCL(DevCPM.options, DevCPM.comAware) END;
+		IF errorTrap IN opt THEN INCL(DevCPM.options, DevCPM.trap) END;
+		IF oberon IN opt THEN INCL(DevCPM.options, DevCPM.oberon) END;
+		DevCPT.Init(opt);
+		DevCPB.typSize := DevCPV.TypeSize;
+		DevCPT.processor := DevCPV.processor;
+		DevCPP.Module(p);
+		IF DevCPM.noerr THEN
+			IF DevCPT.libName # "" THEN EXCL(opt, obj) END;
+(*
+			IF errorTrap IN opt THEN DevCPDump.DumpTree(p) END;
+*)
+			DevCPV.Init(opt); DevCPV.Allocate; DevCPT.Export(ext, new);
+			IF DevCPM.noerr & (obj IN opt) THEN
+				DevCPV.Module(p)
+			END;
+			DevCPV.Close
+		END;
+		IF DevCPM.noerr & (new OR ext) THEN DevCPM.RegisterNewSym
+		ELSE DevCPM.DeleteNewSym
+		END;
+		DevCPT.Close;
+		error := ~DevCPM.noerr;
+		DevCPM.Close;
+		p := NIL;
+		Kernel.FastCollect;
+		IF error THEN
+			DevCPM.InsertMarks(source.Base());
+			DevCPM.LogWLn; DevCPM.LogWStr(" ");
+			IF DevCPM.errors = 1 THEN
+				Dialog.MapString("#Dev:OneErrorDetected", str)
+			ELSE
+				DevCPM.LogWNum(DevCPM.errors, 0); Dialog.MapString("#Dev:ErrorsDetected", str)
+			END;
+			StdLog.String(str)
+		ELSE
+			IF hint IN opt THEN DevCPM.InsertMarks(source.Base()) END;
+			DevCPM.LogWStr("  "); DevCPM.LogWNum(DevCPE.pc, 8);
+			DevCPM.LogWStr("  "); DevCPM.LogWNum(DevCPE.dsize, 8)
+		END;
+		DevCPM.LogWLn
+	END Module;
+
+	PROCEDURE Scan (VAR s: TextMappers.Scanner);
+	BEGIN
+		s.Scan;
+		IF s.type = TextMappers.string THEN
+			IF s.string = "MODULE" THEN s.type := module END
+		ELSIF s.type = TextMappers.char THEN
+			IF s.char = "(" THEN
+				IF s.rider.char = "*" THEN
+					s.rider.Read;
+					REPEAT Scan(s) UNTIL (s.type = TextMappers.eot) OR (s.type = comEnd);
+					Scan(s)
+				END
+			ELSIF s.char = "*" THEN
+				IF s.rider.char = ")" THEN s.rider.Read; s.type := comEnd END
+			END
+		END
+	END Scan;
+
+	PROCEDURE Do (source, log: TextModels.Model; beg: INTEGER; opt: SET; VAR error: BOOLEAN);
+		VAR s: TextMappers.Scanner;
+	BEGIN
+		Dialog.MapString("#Dev:Compiling", str);
+		StdLog.String(str); StdLog.Char(" ");
+		s.ConnectTo(source); s.SetPos(beg);
+		Scan(s);
+		WHILE (s.type # TextMappers.eot) & (s.type # module) DO Scan(s) END;
+		IF s.type = module THEN
+			Scan(s);
+			IF s.type = TextMappers.string THEN
+				StdLog.Char('"'); StdLog.String(s.string); StdLog.Char('"')
+			END
+		END;
+		sourceR := source.NewReader(NIL); sourceR.SetPos(beg);
+		Module(sourceR, opt, log, error)
+	END Do;
+
+
+	PROCEDURE Open;
+	BEGIN
+		Dialog.ShowStatus("#Dev:Compiling");
+		StdLog.buf.Delete(0, StdLog.buf.Length())
+	END Open;
+
+	PROCEDURE Close;
+	BEGIN
+		StdLog.text.Append(StdLog.buf);
+		IF DevCPM.noerr THEN Dialog.ShowStatus("#Dev:Ok")
+		END;
+		sourceR := NIL;
+		Kernel.Cleanup
+	END Close;
+
+	PROCEDURE Compile*;
+		VAR t: TextModels.Model; error: BOOLEAN;
+	BEGIN
+		Open;
+		t := TextViews.FocusText();
+		IF t # NIL THEN
+			Do(t, StdLog.text, 0, defopt, error);
+			IF error THEN DevMarkers.ShowFirstError(t, TextViews.focusOnly) END
+		ELSE Dialog.ShowMsg("#Dev:NoTextViewFound")
+		END;
+		Close
+	END Compile;
+
+	PROCEDURE CompileOpt* (opt: ARRAY OF CHAR);
+		VAR t: TextModels.Model; error: BOOLEAN; i: INTEGER; opts: SET;
+	BEGIN
+		i := 0; opts := defopt;
+		WHILE opt[i] # 0X DO
+			IF opt[i] = "-" THEN
+				IF srcpos IN opts THEN EXCL(opts, srcpos)
+				ELSIF allref IN opts THEN EXCL(opts, allref)
+				ELSIF ref IN opts THEN EXCL(opts, ref)
+				ELSE EXCL(opts, obj)
+				END
+			ELSIF opt[i] = "!" THEN
+				IF assert IN opts THEN EXCL(opts, assert)
+				ELSE EXCL(opts, checks)
+				END
+			ELSIF opt[i] = "+" THEN INCL(opts, allchecks)
+			ELSIF opt[i] = "?" THEN INCL(opts, hint)
+			ELSIF opt[i] = "@" THEN INCL(opts, errorTrap)
+			ELSIF opt[i] = "$" THEN INCL(opts, oberon)
+			END;
+			INC(i)
+		END;
+		Open;
+		t := TextViews.FocusText();
+		IF t # NIL THEN
+			Do(t, StdLog.text, 0, opts, error);
+			IF error THEN DevMarkers.ShowFirstError(t, TextViews.focusOnly) END
+		ELSE Dialog.ShowMsg("#Dev:NoTextViewFound")
+		END;
+		Close
+	END CompileOpt;
+
+	PROCEDURE CompileText* (text: TextModels.Model; beg: INTEGER; OUT error: BOOLEAN);
+	BEGIN
+		ASSERT(text # NIL, 20); ASSERT((beg >= 0) & (beg < text.Length()), 21);
+		Open;
+		Do(text, StdLog.text, beg, defopt, error);
+		IF error THEN DevMarkers.ShowFirstError(text, TextViews.focusOnly) END;
+		Close
+	END CompileText;
+
+	PROCEDURE CompileAndUnload*;
+		VAR t: TextModels.Model; error: BOOLEAN; mod: Kernel.Module; n: ARRAY 256 OF CHAR;
+	BEGIN
+		Open;
+		t := TextViews.FocusText();
+		IF t # NIL THEN
+			Do(t, StdLog.text, 0, defopt, error);
+			IF error THEN DevMarkers.ShowFirstError(t, TextViews.focusOnly)
+			ELSE
+				mod := Kernel.ThisLoadedMod(DevCPT.SelfName);
+				IF mod # NIL THEN
+					Kernel.UnloadMod(mod);
+					n := DevCPT.SelfName$;
+					IF mod.refcnt < 0 THEN
+						Dialog.MapParamString("#Dev:Unloaded", n, "", "", str);
+						StdLog.String(str); StdLog.Ln;
+						Controls.Relink
+					ELSE
+						Dialog.MapParamString("#Dev:UnloadingFailed", n, "", "", str);
+						StdLog.String(str); StdLog.Ln
+					END
+				END
+			END
+		ELSE Dialog.ShowMsg("#Dev:NoTextViewFound")
+		END;
+		Close
+	END CompileAndUnload;
+
+	PROCEDURE CompileSelection*;
+		VAR c: TextControllers.Controller; t: TextModels.Model; beg, end: INTEGER; error: BOOLEAN;
+	BEGIN
+		Open;
+		c := TextControllers.Focus();
+		IF c # NIL THEN
+			t := c.text;
+			IF c.HasSelection() THEN
+				c.GetSelection(beg, end); Do(t, StdLog.text, beg, defopt, error);
+				IF error THEN DevMarkers.ShowFirstError(t, TextViews.focusOnly) END
+			ELSE Dialog.ShowMsg("#Dev:NoSelectionFound")
+			END
+		ELSE Dialog.ShowMsg("#Dev:NoTextViewFound")
+		END;
+		Close
+	END CompileSelection;
+
+	PROCEDURE CompileList (beg, end: INTEGER; c: TextControllers.Controller);
+		VAR v: Views.View; i: INTEGER; error, one: BOOLEAN; name: Files.Name; loc: Files.Locator;
+			t: TextModels.Model; opts: SET; title, entry: ARRAY 64 OF CHAR;
+	BEGIN
+		s.SetPos(beg); s.Scan; one := FALSE;
+		WHILE (s.start < end) & (s.type = TextMappers.string) & (s.len < LEN(name)) DO
+			s.Scan; one := TRUE;
+			WHILE (s.start < end) & (s.type = TextMappers.char) &
+				((s.char = "-") OR (s.char = "+") OR
+				(s.char = "!") OR (s.char = "*") OR (s.char = "?") OR (s.char = "^") OR (s.char = "("))
+			DO
+				IF s.char = "(" THEN
+					WHILE (s.start < end) & ((s.type # TextMappers.char) OR (s.char # ")")) DO s.Scan END
+				END;
+				s.Scan
+			END
+		END;
+		IF one & (s.start >= end) THEN
+			s.SetPos(beg); s.Scan; error := FALSE;
+			WHILE (s.start < end) & (s.type = TextMappers.string) & ~error DO
+				i := 0; WHILE i < LEN(name) DO name[i] := 0X; INC(i) END;
+				StdDialog.GetSubLoc(s.string, "Mod", loc, name);
+				t := NIL;
+				IF loc # NIL THEN
+					v := Views.OldView(loc, name);
+					IF v # NIL THEN
+						WITH v: TextViews.View DO t := v.ThisModel()
+						ELSE Dialog.ShowParamMsg("#Dev:NoTextFileFound", name, "", ""); error := TRUE
+						END
+					ELSE Dialog.ShowParamMsg("#Dev:CannotOpenFile", name, "", ""); error := TRUE
+					END
+				ELSE Dialog.ShowParamMsg("#System:FileNotFound", name, "", ""); error := TRUE
+				END;
+				s.Scan; opts := defopt;
+				WHILE (s.start < end) & (s.type = TextMappers.char) DO
+					IF s.char = "-" THEN
+						IF srcpos IN opts THEN EXCL(opts, srcpos)
+						ELSIF allref IN opts THEN EXCL(opts, allref)
+						ELSIF ref IN opts THEN EXCL(opts, ref)
+						ELSE EXCL(opts, obj)
+						END
+					ELSIF s.char = "!" THEN
+						IF assert IN opts THEN EXCL(opts, assert)
+						ELSE EXCL(opts, checks)
+						END
+					ELSIF s.char = "+" THEN INCL(opts, allchecks)
+					ELSIF s.char = "?" THEN INCL(opts, hint)
+					ELSIF s.char = "@" THEN INCL(opts, errorTrap)
+					ELSIF s.char = "$" THEN INCL(opts, oberon)
+					ELSIF s.char = "(" THEN
+						s.Scan;
+						WHILE (s.start < end) & (s.type = TextMappers.string) DO
+							title := s.string$; s.Scan;
+							IF (s.start < end) & (s.type = TextMappers.char) & (s.char = ":") THEN
+								s.Scan;
+								IF (s.start < end) & (s.type = TextMappers.string) THEN
+									entry := s.string$; s.Scan;
+									IF t # NIL THEN DevSelectors.ChangeTo(t, title, entry) END
+								END
+							END;
+							IF (s.start < end) & (s.type = TextMappers.char) & (s.char = ",") THEN s.Scan END
+						END
+					END;
+					s.Scan
+				END;
+				IF t # NIL THEN
+					Do(t, StdLog.text, 0, opts, error)
+				END
+			END
+		ELSE Dialog.ShowMsg("#Dev:NotOnlyFileNames")
+		END;
+		s.ConnectTo(NIL);
+		IF error & (c # NIL) & c.HasSelection() & (s.start < end) THEN
+			c.SetSelection(s.start, end)
+		END;
+		IF error & (v # NIL) THEN
+			Views.Open(v, loc, name, NIL);
+			DevMarkers.ShowFirstError(t, TextViews.any)
+		END
+	END CompileList;
+
+	PROCEDURE CompileModuleList*;
+		VAR c: TextControllers.Controller; beg, end: INTEGER;
+	BEGIN
+		Open;
+		c := TextControllers.Focus();
+		IF c # NIL THEN
+			s.ConnectTo(c.text);
+			IF c.HasSelection() THEN c.GetSelection(beg, end)
+			ELSE beg := 0; end := c.text.Length()
+			END;
+			CompileList(beg, end, c)
+		ELSE Dialog.ShowMsg("#Dev:NoTextViewFound")
+		END;
+		Close
+	END CompileModuleList;
+
+	PROCEDURE CompileThis*;
+		VAR p: DevCommanders.Par; beg, end: INTEGER;
+	BEGIN
+		Open;
+		p := DevCommanders.par;
+		IF p # NIL THEN
+			DevCommanders.par := NIL;
+			s.ConnectTo(p.text); beg := p.beg; end := p.end;
+			CompileList(beg, end, NIL)
+		ELSE Dialog.ShowMsg("#Dev:NoTextViewFound")
+		END;
+		Close
+	END CompileThis;
+
+	PROCEDURE Init;
+		VAR loc: Files.Locator; f: Files.File;
+	BEGIN
+		loc := Files.dir.This("Dev"); loc := loc.This("Code");
+		f := Files.dir.Old(loc, "ComDebug.ocf", TRUE);
+		found := f # NIL;
+		IF f # NIL THEN f.Close END
+	END Init;
+
+BEGIN
+	Init
+END DevCompiler.

+ 1740 - 0
BlackBox/Dev/Mod/ElfLinker16.txt

@@ -0,0 +1,1740 @@
+MODULE DevElfLinker;
+
+	(* THIS IS TEXT COPY OF OpenBUGS Dev/Mod/ElfLinker16.odc *)
+	(* DO NOT EDIT *)
+
+(*
+	DevElfLinker version compatible with BlackBox Component Builder release 1.6.
+	This module will replace DevElfLinker, once the final version of BlackBox 1.6 will be released.
+*)
+
+	IMPORT
+		Strings,
+		Kernel, Files, Dialog,
+		TextMappers,
+		StdLog, DevCommanders;
+
+	CONST
+		NewRecFP = 4E27A847H;
+		NewArrFP = 76068C78H;
+
+		OFdir = "Code";
+		SYSdir = "System";
+
+		(* meta interface consts *)
+		mConst = 1; mTyp = 2; mVar = 3; mProc = 4;
+		mInternal = 1; mExported = 4;
+
+		(* mod desc fields *)
+		modOpts = 4; modRefcnt = 8; modTerm = 40; modNames = 84; modImports = 92; modExports = 96;
+
+		(* .dynsym entries *)
+		stbLocal = 0; stbGlobal = 1;
+		sttNotype = 0; sttObject = 1; sttFunc = 2; sttSection = 3;
+		shnUnd = 0; shnAbs = 0FFF1H;
+
+		fixup = 0;
+		noSymbol = MIN(INTEGER);
+		noAddr = MIN(INTEGER);
+		firstDllSymbolVal = 12;
+
+		(* distinguished section header indexes. *)
+		textIndexVal = 1;	(* index of the .text section header in the section header table *)
+		rodataIndexVal = 3;	(* index of the .rodata section header in the section header table *)
+		dynsymIndexVal = 5;	(* index of the .dynsym section header in the section header table *)
+		dynstrIndexVal = 6;	(* index of the .dynstr section header in the section header table *)
+
+		(* fixed elements dimensions *)
+		elfHeaderSizeVal = 52;	(* size of the ELF file header *)
+		shEntrySizeVal = 40;	(* size of an entry in the section header table *)
+		dynsymEntrySizeVal = 16; (* size of a symbol table entry *)
+		dynamicEntrySizeVal = 8; (* size of an entry in the dynamic section *)
+		gotEntrySizeVal = 4; (* size of an entry in the got section *)
+		relEntrySizeVal = 8; (* size of an entry in a relocation section *)
+		phEntrySizeVal = 32; (* size of an entry in the program header *)
+
+		shNumVal = 12; (* number of entries in the section header table. See WriteSectionHeaderTable *)
+		shStrndxVal = shNumVal - 1; (* index of the string table for section names. See WriteSectionHeaderTable *)
+		phNumVal = 3; (* number of entries in the program header table *)
+
+		(* sections alignments (in bytes) *)
+		textAlign = 4H;
+		dynsymAlign = 4H;
+		dynstrAlign = 1H;
+		hashAlign = 4H;
+		gotAlign = 4H;
+		dynamicAlign = 4H;
+		shstrtabAlign = 1H;
+		bssAlign = 4H;
+		rodataAlign = 8H;
+		relAlign = 4H;
+
+		pageSize = 1000H; (* I386 page size *)
+
+		r38632 = 1; r386pc32 = 2; r386Relative = 8; (* ELF relocation types *)
+
+	TYPE
+		Name = ARRAY 40 OF SHORTCHAR;
+
+		Export = POINTER TO RECORD
+			next: Export;
+			name: Name;
+			adr: INTEGER
+		END;
+
+		Module = POINTER TO RECORD
+			next: Module;
+			name: Name;
+			fileName: Files.Name;
+			file: Files.File;
+			hs, ms, ds, cs, vs, ni, ma, ca, va: INTEGER;
+			dll, intf: BOOLEAN;
+			exp: Export;
+			imp: POINTER TO ARRAY OF Module;
+			data: POINTER TO ARRAY OF BYTE
+		END;
+
+		Strtab = RECORD
+			tab: ARRAY 4096 OF SHORTCHAR;
+			cur: INTEGER
+		END;
+
+		Relocation = RECORD
+			offset, type: INTEGER
+		END;
+
+		RelTab = RECORD
+			tab: ARRAY 65536 OF Relocation;
+			cur: INTEGER
+		END;
+
+		Section = RECORD
+			fileOffset,
+			memOffset,
+			size: INTEGER
+		END;
+
+	VAR
+		W: TextMappers.Formatter;
+		Out: Files.File;
+		R: Files.Reader;
+		Ro: Files.Writer;
+		error, isDll, isStatic: BOOLEAN;
+		modList, kernel, main, last, impg, impd: Module;
+		numMod, lastTerm: INTEGER;
+		firstExp, lastExp: Export;
+		CodeSize, DataSize, ConSize: INTEGER;
+		maxCode, numExp: INTEGER;
+		newRec, newArr: Name;
+		code: POINTER TO ARRAY OF BYTE;
+
+		(* fixup positions *)
+		entryPos,
+		expPos,
+		shstrtabPos,
+		finiPos: INTEGER;
+
+		(* sections *)
+		text, reltext, relrodata, rodata, dynstr, shstrtab, hash, got, dynsym, dynamic, bss: Section;
+
+		(* distinguished file and memory offsets *)
+		shOffsetVal,	(* section header table file offset *)
+		phOffsetVal,	(* program header table file offset *)
+		finiMemOffsetVal: INTEGER;	(* memory offset (aka virtual address) of the finalization code (CLOSE sections) *)
+
+		dynsymInfoVal,	(* value of the info field for the .dynsym section *)
+		sonameStrIndexVal: INTEGER;	(* string table index of the name of hte library *)
+
+		(* segment dimensions *)
+		textSegmentSizeVal,
+		dataSegmentSizeVal,
+		dynamicSegmentSizeVal: INTEGER;
+
+		headerstrtab, dynstrtab: Strtab;
+		hashtab: ARRAY 256 OF Name;
+
+		neededIdx: ARRAY 256 OF INTEGER;
+
+		relTextTab, relRodataTab: RelTab;
+
+		soName: Name;
+
+		doWrite: BOOLEAN;
+
+	PROCEDURE (VAR t: Strtab) AddName (IN s: ARRAY OF SHORTCHAR; OUT idx: INTEGER), NEW;
+		VAR i: INTEGER;
+	BEGIN
+		ASSERT((t.cur + LEN(s$)) <= LEN(t.tab), 20); (* table buffer not large enough: TODO enlarge? *)
+		idx := t.cur;
+		i := 0;
+		WHILE s[i] # 0X DO
+			t.tab[t.cur] := s[i];
+			INC(i); INC(t.cur)
+		END;
+		t.tab[t.cur] := s[i]; (* copy the 0X *)
+		INC(t.cur)
+	END AddName;
+
+	PROCEDURE (VAR t: RelTab) Add (offset, type: INTEGER), NEW;
+	BEGIN
+		ASSERT(t.cur < LEN(t.tab), 20); (* table buffer not large enough: TODO enlarge? *)
+		t.tab[t.cur].offset := offset;		
+		t.tab[t.cur].type := type;
+		INC(t.cur)
+	END Add;
+
+	PROCEDURE AddNeededIdx (idx: INTEGER);
+		VAR i, len: INTEGER;
+	BEGIN
+		ASSERT(idx > 0, 20);	(* index must be positive *)
+		len := LEN(neededIdx);
+		i := 0;
+		WHILE (i # len) & (neededIdx[i] # 0) DO INC(i) END;
+		IF i # len THEN
+			neededIdx[i] := idx
+		ELSE
+			HALT(21)	(* no more space for indexes *)
+		END
+	END AddNeededIdx;
+
+	PROCEDURE ThisFile (modname: ARRAY OF CHAR): Files.File;
+		VAR dir, name: Files.Name; loc: Files.Locator; f: Files.File;
+	BEGIN
+		Kernel.SplitName(modname, dir, name);
+		Kernel.MakeFileName(name, Kernel.objType);
+		loc := Files.dir.This(dir); loc := loc.This(OFdir);
+		f := Files.dir.Old(loc, name, TRUE);
+		IF (f = NIL) & (dir = "") THEN
+			loc := Files.dir.This(SYSdir); loc := loc.This(OFdir);
+			f := Files.dir.Old(loc, name, TRUE)
+		END;
+		RETURN f
+	END ThisFile;
+
+	PROCEDURE Read4 (VAR x: INTEGER);
+		VAR b: BYTE;
+	BEGIN
+		R.ReadByte(b); x := b MOD 256;
+		R.ReadByte(b); x := x + 100H * (b MOD 256);
+		R.ReadByte(b); x := x + 10000H * (b MOD 256);
+		R.ReadByte(b); x := x + 1000000H * b
+	END Read4;
+
+	PROCEDURE ReadName (VAR name: ARRAY OF SHORTCHAR);
+		VAR i: INTEGER; b: BYTE;
+	BEGIN i := 0;
+		REPEAT
+			R.ReadByte(b); name[i] := SHORT(CHR(b)); INC(i)
+		UNTIL b = 0
+	END ReadName;
+
+	PROCEDURE RNum (VAR i: INTEGER);
+		VAR b: BYTE; s, y: INTEGER;
+	BEGIN
+		s := 0; y := 0; R.ReadByte(b);
+		WHILE b < 0 DO INC(y, ASH(b + 128, s)); INC(s, 7); R.ReadByte(b) END;
+		i := ASH((b + 64) MOD 128 - 64, s) + y
+	END RNum;
+
+	PROCEDURE WriteCh (ch: SHORTCHAR);
+	BEGIN
+		IF doWrite THEN
+		Ro.WriteByte(SHORT(ORD(ch)))
+		END
+	END WriteCh;
+
+	PROCEDURE Write2 (x: INTEGER);
+	BEGIN
+		IF doWrite THEN
+			Ro.WriteByte(SHORT(SHORT(x MOD 256))); x := x DIV 256;
+			Ro.WriteByte(SHORT(SHORT(x MOD 256)))
+		END
+	END Write2;
+
+	PROCEDURE Write4 (x: INTEGER);
+	BEGIN
+		IF doWrite THEN
+			Ro.WriteByte(SHORT(SHORT(x MOD 256))); x := x DIV 256;
+			Ro.WriteByte(SHORT(SHORT(x MOD 256))); x := x DIV 256;
+			Ro.WriteByte(SHORT(SHORT(x MOD 256))); x := x DIV 256;
+			Ro.WriteByte(SHORT(SHORT(x MOD 256)))
+		END
+	END Write4;
+
+	PROCEDURE WriteBytes (IN x: ARRAY OF BYTE; beg, len: INTEGER);
+	BEGIN
+		IF doWrite THEN
+			Ro.WriteBytes(x, beg, len)
+		END
+	END WriteBytes;
+
+	PROCEDURE Align (alignment: INTEGER);
+	BEGIN
+		WHILE Ro.Pos() MOD alignment # 0 DO WriteCh(0X) END
+	END Align;
+	
+	PROCEDURE Aligned (pos, alignment: INTEGER): INTEGER;
+	BEGIN
+		RETURN (pos + (alignment - 1)) DIV alignment * alignment
+	END Aligned;
+	
+	PROCEDURE Put (mod: Module; a, x: INTEGER);
+	BEGIN
+		ASSERT((mod.data # NIL) & ((a >= 0) & (a <= LEN(mod.data))), 20);
+		mod.data[a] := SHORT(SHORT(x)); INC(a); x := x DIV 256;
+		mod.data[a] := SHORT(SHORT(x)); INC(a); x := x DIV 256;
+		mod.data[a] := SHORT(SHORT(x)); INC(a); x := x DIV 256;
+		mod.data[a] := SHORT(SHORT(x))
+	END Put;
+
+	PROCEDURE Get (mod: Module; a: INTEGER; VAR x: INTEGER);
+	BEGIN
+		ASSERT((mod.data # NIL) & ((a >= 0) & (a + 3 <= LEN(mod.data))), 20);
+		x := ((mod.data[a + 3] * 256 +
+			(mod.data[a + 2] MOD 256)) * 256 +
+			(mod.data[a + 1] MOD 256)) * 256 +
+			(mod.data[a] MOD 256)
+	END Get;
+
+	PROCEDURE CheckDllImports (mod: Module);
+		VAR i, x, y: INTEGER; name: Name; imp: Module; exp: Export;
+
+		PROCEDURE SkipLink;
+			VAR a: INTEGER;
+		BEGIN
+			RNum(a);
+			WHILE a # 0 DO RNum(a); RNum(a) END
+		END SkipLink;
+
+	BEGIN
+		R := mod.file.NewReader(R);
+		R.SetPos(mod.hs + mod.ms + mod.ds + mod.cs);
+		SkipLink; SkipLink; SkipLink; SkipLink; SkipLink; SkipLink;
+		i := 0;
+		WHILE i < mod.ni DO
+			imp := mod.imp[i];
+			IF imp # NIL THEN
+				RNum(x);
+				WHILE x # 0 DO
+					ReadName(name); RNum(y);
+					IF x = mVar THEN
+						SkipLink;
+						IF imp.dll THEN
+							exp := imp.exp;
+							WHILE (exp # NIL) & (exp.name # name) DO exp := exp.next END;
+							IF exp = NIL THEN
+								NEW(exp); exp.name := name$;
+								exp.next := imp.exp; imp.exp := exp
+							 END
+						END
+					ELSIF x = mTyp THEN RNum(y);
+						IF imp.dll THEN
+							RNum(y);
+							IF y # 0 THEN
+								W.WriteString("type descriptor (");
+								W.WriteString(imp.name$); W.WriteChar(".");
+								W.WriteSString(name);
+								W.WriteString(") imported from DLL in ");
+								W.WriteString(mod.name$);
+								W.WriteLn; StdLog.text.Append(StdLog.buf); error := TRUE;
+								RETURN
+							END
+						ELSE SkipLink
+						END
+					ELSIF x = mProc THEN
+						IF imp.dll THEN
+							SkipLink;
+							exp := imp.exp;
+							WHILE (exp # NIL) & (exp.name # name) DO exp := exp.next END;
+							IF exp = NIL THEN
+								NEW(exp); exp.name := name$;
+								exp.next := imp.exp; imp.exp := exp
+							 END
+						END
+					END;
+					RNum(x)
+				END
+			END;
+			INC(i)
+		END
+	END CheckDllImports;
+
+	PROCEDURE ReadHeaders;
+		VAR mod, im, t: Module; x, i, pos: INTEGER; impdll: BOOLEAN; name: Name;
+	BEGIN
+		ASSERT(isDll, 126);
+		mod := modList; modList := NIL; numMod := 0;
+		WHILE mod # NIL DO	(* reverse mod list & count modules *)
+			IF ~mod.dll THEN INC(numMod) END;
+			t := mod; mod := t.next; t.next := modList; modList := t
+		END;
+		IF isStatic THEN
+			CodeSize :=
+				6 + 5 * numMod + 2	(* _init() *)
+				+ 1 + 5 * numMod + 2	(* _fini() *)
+		ELSE
+			CodeSize :=
+				6 + 5 + 2	(* _init() *)
+				+ 1 + 5 + 2	(* _fini() *)
+		END;
+		DataSize := 0; ConSize := 0;
+		maxCode := 0; numExp := 0;
+		mod := modList;
+		WHILE mod # NIL DO
+			IF ~mod.dll THEN
+				mod.file := ThisFile(mod.fileName);
+				IF mod.file # NIL THEN
+					R := mod.file.NewReader(R); R.SetPos(0);
+					Read4(x);
+					IF x = 6F4F4346H THEN
+						Read4(x);
+						Read4(mod.hs); Read4(mod.ms); Read4(mod.ds); Read4(mod.cs);
+						Read4(mod.vs); RNum(mod.ni); ReadName(mod.name); impdll := FALSE;
+						IF mod.ni > 0 THEN
+							NEW(mod.imp, mod.ni);
+							x := 0;
+							WHILE x < mod.ni DO
+								ReadName(name);
+								IF name = "$$" THEN
+									IF (mod # kernel) & (kernel # NIL) THEN
+										mod.imp[x] := kernel
+									ELSE
+										W.WriteSString("no kernel"); W.WriteLn;
+										StdLog.text.Append(StdLog.buf); error := TRUE
+									END
+								ELSIF name[0] = "$" THEN
+									StdLog.String(name$); 
+									i := 1;
+									WHILE name[i] # 0X DO name[i-1] := name[i]; INC(i) END;
+									name[i-1] := 0X; 
+									IF i # 1 THEN
+										Strings.Find(name$, ".so", 0, pos);
+										IF pos = -1 THEN
+											name[i - 1] := "."; name[i] := "s"; name[i + 1] := "o"; name[i + 2] := 0X
+										END
+									END;
+									StdLog.String("  "); StdLog.String(name$); StdLog.Ln;
+									impdll := TRUE; im := modList;
+									WHILE (im # mod) & (im.name # name) DO im := im.next END;
+									IF (im = NIL) OR ~im.dll THEN
+										NEW(im); im.next := modList; modList := im;
+										im.dll := TRUE;
+										im.name := name$; 
+										dynstrtab.AddName(name, i);
+										AddNeededIdx(i)
+									END;
+									mod.imp[x] := im
+								ELSE
+									im := modList;
+									WHILE (im # mod) & (im.name # name) DO im := im.next END;
+									IF im # mod THEN
+										mod.imp[x] := im
+									ELSE
+										W.WriteSString(name);
+										W.WriteString(" not present (imported in ");
+										W.WriteString(mod.name$); W.WriteChar(")");
+										W.WriteLn; StdLog.text.Append(StdLog.buf); error := TRUE
+									END
+								END;
+								INC(x)
+							END
+						END;
+						IF impdll & ~error THEN CheckDllImports(mod) END;
+						mod.ma := ConSize; INC(ConSize, mod.ms + mod.ds);
+						mod.va := DataSize; INC(DataSize, mod.vs);
+						mod.ca := CodeSize; INC(CodeSize, mod.cs);
+						IF mod.cs > maxCode THEN maxCode := mod.cs END
+					ELSE
+						W.WriteString(mod.name$); W.WriteString(": wrong file type"); 
+						W.WriteLn; StdLog.text.Append(StdLog.buf); error := TRUE
+					END;
+					mod.file.Close; mod.file := NIL
+				ELSE
+					W.WriteString(mod.name$); W.WriteString(" not found"); 
+					W.WriteLn; StdLog.text.Append(StdLog.buf); error := TRUE
+				END;
+				last := mod
+			END;
+			mod := mod.next
+		END;
+		IF ~isStatic & (main = NIL) THEN
+			W.WriteSString("no main module specified"); W.WriteLn;
+			StdLog.text.Append(StdLog.buf); error := TRUE
+		END;
+		IF DataSize = 0 THEN DataSize := 1 END
+	END ReadHeaders;
+
+	PROCEDURE WriteElfHeader;
+	BEGIN
+		ASSERT(Ro.Pos() = 0, 100);
+		dynstrtab.AddName(soName$, sonameStrIndexVal);
+		Write4(464C457FH); Write4(00010101H); Write4(0); Write4(0); (* Magic *)
+		Write2(3); (* ET_DYN e_type Object file type *)
+		Write2(3); (* EM_386 e_machine Architecture *)
+		Write4(1); (* EV_CURRENT e_version Object file version *)
+		Write4(text.memOffset); (* e_entry Entry point virtual address *)
+		entryPos := Ro.Pos();
+		Write4(fixup); (* e_phoff Program header table file offset *)
+		Write4(fixup); (* e_shoff: Section header table file offset *)
+		Write4(0); (* e_flags Processor-specific flags *)
+		Write2(elfHeaderSizeVal); (* e_ehsize ELF header size in bytes *)
+		Write2(phEntrySizeVal); (* e_phentsize Program header table entry size *)
+		Write2(phNumVal); (* e_phnum Program header table entry count *)
+		Write2(shEntrySizeVal); (* e_shentsize Section header table entry size *)
+		Write2(shNumVal); (* e_shnum Section header table entry count *)
+		Write2(shStrndxVal); (* e_shstrndx Section header string table index *)
+		ASSERT(Ro.Pos() = elfHeaderSizeVal, 101)
+	END WriteElfHeader;
+
+	PROCEDURE FixupElfHeader;
+	BEGIN
+		Ro.SetPos(entryPos);
+		Write4(phOffsetVal);
+		Write4(shOffsetVal)
+	END FixupElfHeader;
+
+	PROCEDURE WriteNullSectionHeader;
+	BEGIN
+		Write4(0); (* sh_name Section name (string tbl index) *)
+		Write4(0); (* SHT_NULL sh_type Section type *)
+		Write4(0); (* sh_flags Section flags *)
+		Write4(0); (* ELF header + program header table; sh_addr Section virtual addr at execution *)
+		Write4(0); (* sh_offset Section file offset *)
+		Write4(0); (* sh_size Section size in bytes *)
+		Write4(0); (* SHN_UNDEF sh_link Link to another section *)
+		Write4(0); (* sh_info Additional section information *)
+		Write4(0); (* sh_addralign Section alignment *)
+		Write4(0) (* sh_entsize Entry size if section holds table *)
+	END WriteNullSectionHeader;
+
+	PROCEDURE WriteTextSectionHeader;
+		VAR i: INTEGER;
+	BEGIN
+		headerstrtab.AddName(".text", i);
+		Write4(i); (* sh_name Section name (string tbl index) *)
+		Write4(1); (* SHT_PROGBITS sh_type Section type *)
+		Write4(2H + 4H); (* SHF_ALLOC + SHF_EXECINSTR sh_flags Section flags *)
+		Write4(text.memOffset); (* sh_addr Section virtual addr at execution *)
+		Write4(text.fileOffset); (* sh_offset Section file offset *)
+		Write4(text.size); (* sh_size Section size in bytes *)
+		Write4(0); (* SHN_UNDEF sh_link Link to another section *)
+		Write4(0); (* sh_info Additional section information *)
+		Write4(textAlign); (* sh_addralign Section alignment *)
+		Write4(0) (* sh_entsize Entry size if section holds table *)
+	END WriteTextSectionHeader;
+
+	PROCEDURE WriteRelTextSectionHeader;
+		VAR i: INTEGER;
+	BEGIN
+		headerstrtab.AddName(".rel.text", i);
+		Write4(i); (* sh_name Section name (string tbl index) *)
+		Write4(9); (* SHT_REL sh_type Section type *)
+		Write4(2H); (* SHF_ALLOC sh_flags Section flags *)
+		Write4(reltext.memOffset); (* sh_addr Section virtual addr at execution *)
+		Write4(reltext.fileOffset); (* sh_offset Section file offset *)
+		Write4(reltext.size); (* sh_size Section size in bytes *)
+		Write4(dynsymIndexVal); (* sh_link Link to another section -> index of the associated symbol table *)
+		Write4(textIndexVal); (* sh_info Additional section information -> index of the relocated section *)
+		Write4(relAlign); (* sh_addralign Section alignment *)
+		Write4(relEntrySizeVal) (* sh_entsize Entry size if section holds table *)
+	END WriteRelTextSectionHeader;
+
+	PROCEDURE WriteRelRodataSectionHeader;
+		VAR i: INTEGER;
+	BEGIN
+		headerstrtab.AddName(".rel.rodata", i);
+		Write4(i); (* sh_name Section name (string tbl index) *)
+		Write4(9); (* SHT_REL sh_type Section type *)
+		Write4(2H); (* SHF_ALLOC sh_flags Section flags *)
+		Write4(relrodata.memOffset); (* sh_addr Section virtual addr at execution *)
+		Write4(relrodata.fileOffset); (* sh_offset Section file offset *)
+		Write4(relrodata.size); (* sh_size Section size in bytes *)
+		Write4(dynsymIndexVal); (* sh_link Link to another section -> index of the associated symbol table *)
+		Write4(rodataIndexVal); (* sh_info Additional section information -> index of the relocated section *)
+		Write4(relAlign); (* sh_addralign Section alignment *)
+		Write4(relEntrySizeVal) (* sh_entsize Entry size if section holds table *)
+	END WriteRelRodataSectionHeader;
+
+	PROCEDURE WriteRodataSectionHeader;
+		VAR i: INTEGER;
+	BEGIN
+		headerstrtab.AddName(".rodata", i);
+		Write4(i); (* sh_name Section name (string tbl index) *)
+		Write4(1); (* SHT_PROGBITS sh_type Section type *)
+		Write4(2H); (* SHF_ALLOC sh_flags Section flags *)
+		Write4(rodata.memOffset); (* sh_addr Section virtual addr at execution *)
+		Write4(rodata.fileOffset); (* sh_offset Section file offset *)
+		Write4(rodata.size); (* sh_size Section size in bytes *)
+		Write4(0); (* sh_link Link to another section *)
+		Write4(0); (* sh_info Additional section information *)
+		Write4(rodataAlign); (* sh_addralign Section alignment *)
+		Write4(0) (* sh_entsize Entry size if section holds table *)
+	END WriteRodataSectionHeader;
+
+	PROCEDURE WriteDynsymSectionHeader;
+		VAR i: INTEGER;
+	BEGIN
+		headerstrtab.AddName(".dynsym", i);
+		Write4(i); (* sh_name Section name (string tbl index) *)
+		Write4(11); (* SHT_DYNSYM sh_type Section type *)
+		Write4(2H); (* SHF_ALLOC sh_flags Section flags *)
+		Write4(dynsym.memOffset); (* sh_addr Section virtual addr at execution *)
+		Write4(dynsym.fileOffset); (* sh_offset Section file offset *)
+		Write4(dynsym.size); (* sh_size Section size in bytes *)
+		Write4(dynstrIndexVal); (* sh_link Link to another section -> index of the associated string table *)
+		expPos := Ro.Pos();
+		Write4(fixup); (* sh_info Additional section information -> see docu 4-17 *)
+		Write4(dynsymAlign); (* sh_addralign Section alignment *)
+		Write4(dynsymEntrySizeVal) (* sh_entsize Entry size if section holds table *)
+	END WriteDynsymSectionHeader;
+
+	PROCEDURE FixupDynsymSectionHeader;
+	BEGIN
+		Ro.SetPos(expPos);
+		Write4(dynsymInfoVal)
+	END FixupDynsymSectionHeader;
+	
+	PROCEDURE WriteDynstrSectionHeader;
+		VAR i: INTEGER;
+	BEGIN
+		headerstrtab.AddName(".dynstr", i);
+		Write4(i); (* sh_name Section name (string tbl index) *)
+		Write4(3); (* SHT_STRTAB sh_type Section type *)
+		Write4(2H); (* SHF_ALLOC sh_flags Section flags *)
+		Write4(dynstr.memOffset); (* sh_addr Section virtual addr at execution *)
+		Write4(dynstr.fileOffset); (* sh_offset Section file offset *)
+		Write4(dynstr.size); (* sh_size Section size in bytes *)
+		Write4(0); (* SHN_UNDEF sh_link Link to another section *)
+		Write4(0); (* sh_info Additional section information *)
+		Write4(dynstrAlign); (* sh_addralign Section alignment *)
+		Write4(0) (* sh_entsize Entry size if section holds table *)
+	END WriteDynstrSectionHeader;
+	
+	PROCEDURE WriteHashSectionHeader;
+		VAR i: INTEGER;
+	BEGIN
+		headerstrtab.AddName(".hash", i);
+		Write4(i); (* sh_name Section name (string tbl index) *)
+		Write4(5); (* SHT_HASH sh_type Section type *)
+		Write4(2H); (* SHF_ALLOC sh_flags Section flags *)
+		Write4(hash.memOffset); (* sh_addr Section virtual addr at execution *)
+		Write4(hash.fileOffset); (* sh_offset Section file offset *)
+		Write4(hash.size); (* sh_size Section size in bytes *)
+		Write4(dynsymIndexVal); (* sh_link Link to another section *)
+		Write4(0); (* sh_info Additional section information *)
+		Write4(hashAlign); (* sh_addralign Section alignment *)
+		Write4(4H) (* sh_entsize Entry size if section holds table *)
+	END WriteHashSectionHeader;
+
+	PROCEDURE WriteGotSectionHeader;
+		VAR i: INTEGER;
+	BEGIN
+		headerstrtab.AddName(".got", i);
+		Write4(i); (* sh_name Section name (string tbl index) *)
+		Write4(1); (* SHT_PROGBITS sh_type Section type *)
+		Write4(2H + 1H); (* SHF_ALLOC + SHF_WRITE sh_flags Section flags *)
+		Write4(got.memOffset); (* sh_addr Section virtual addr at execution *)
+		Write4(got.fileOffset); (* sh_offset Section file offset *)
+		Write4(got.size); (* sh_size Section size in bytes *)
+		Write4(0); (* SHN_UNDEF sh_link Link to another section *)
+		Write4(0); (* sh_info Additional section information *)
+		Write4(gotAlign); (* sh_addralign Section alignment *)
+		Write4(gotEntrySizeVal) (* sh_entsize Entry size if section holds table *)
+	END WriteGotSectionHeader;
+	
+	PROCEDURE WriteBssSectionHeader;
+		VAR i: INTEGER;
+	BEGIN
+		headerstrtab.AddName(".bss", i);
+		Write4(i); (* sh_name Section name (string tbl index) *)
+		Write4(8); (* SHT_NOBITS sh_type Section type *)
+		Write4(2H + 1H); (* SHF_ALLOC + SHF_WRITE sh_flags Section flags *)
+		Write4(bss.memOffset); (* sh_addr Section virtual addr at execution *)
+		Write4(bss.fileOffset); (* sh_offset Section file offset *)
+		Write4(bss.size); (* sh_size Section size in bytes *)
+		Write4(0); (* SHN_UNDEF sh_link Link to another section *)
+		Write4(0); (* sh_info Additional section information *)
+		Write4(bssAlign); (* sh_addralign Section alignment *)
+		Write4(0) (* sh_entsize Entry size if section holds table *)
+	END WriteBssSectionHeader;
+	
+	PROCEDURE WriteDynamicSectionHeader;
+		VAR i: INTEGER;
+	BEGIN
+		headerstrtab.AddName(".dynamic", i);
+		Write4(i); (* sh_name Section name (string tbl index) *)
+		Write4(6); (* SHT_DYNAMIC sh_type Section type *)
+		Write4(2H); (* SHF_ALLOC sh_flags Section flags *)
+		Write4(dynamic.memOffset); (* sh_addr Section virtual addr at execution *)
+		Write4(dynamic.fileOffset); (* sh_offset Section file offset *)
+		Write4(dynamic.size); (* sh_size Section size in bytes *)
+		Write4(dynstrIndexVal); (* sh_link Link to another section -> index of the associated symbol table *)
+		Write4(0); (* sh_info Additional section information *)
+		Write4(dynamicAlign); (* sh_addralign Section alignment *)
+		Write4(dynamicEntrySizeVal) (* sh_entsize Entry size if section holds table *)
+	END WriteDynamicSectionHeader;
+	
+	PROCEDURE WriteShstrtabSectionHeader;
+		VAR i: INTEGER;
+	BEGIN
+		headerstrtab.AddName(".shstrtab", i);
+		Write4(i); (* sh_name Section name (string tbl index) *)
+		Write4(3); (* SHT_STRTAB sh_type Section type *)
+		Write4(0); (* sh_flags Section flags *)
+		Write4(0); (* sh_addr Section virtual addr at execution *)
+		Write4(shstrtab.fileOffset); (* sh_offset Section file offset *)
+		shstrtabPos := Ro.Pos();
+		Write4(fixup); (* sh_size Section size in bytes *)
+		Write4(0); (* SHN_UNDEF sh_link Link to another section *)
+		Write4(0); (* sh_info Additional section information *)
+		Write4(shstrtabAlign); (* sh_addralign Section alignment *)
+		Write4(0) (* sh_entsize Entry size if section holds table *)
+	END WriteShstrtabSectionHeader;
+	
+	PROCEDURE FixupShstrtabSectionHeader;
+	BEGIN
+		Ro.SetPos(shstrtabPos);
+		Write4(shstrtab.size)
+	END FixupShstrtabSectionHeader;
+
+	PROCEDURE WriteRelSectionHeaders;
+	BEGIN
+		WriteRelTextSectionHeader;
+		WriteRelRodataSectionHeader
+	END WriteRelSectionHeaders;
+	
+	PROCEDURE WriteSectionHeaderTable;
+	BEGIN
+		shOffsetVal := Ro.Pos();
+		WriteNullSectionHeader;
+		WriteTextSectionHeader;
+		WriteRodataSectionHeader;
+		WriteRelSectionHeaders;
+		WriteDynsymSectionHeader;
+		WriteDynstrSectionHeader;
+		WriteHashSectionHeader;
+		WriteGotSectionHeader;
+		WriteDynamicSectionHeader;
+		WriteBssSectionHeader;
+		WriteShstrtabSectionHeader	(* see shStrndxVal *)
+		(* see shNumVal *)
+	END WriteSectionHeaderTable;
+
+	PROCEDURE FixupSectionHeaderTable;
+	BEGIN
+		FixupDynsymSectionHeader;
+		FixupShstrtabSectionHeader
+	END FixupSectionHeaderTable;
+
+	PROCEDURE WriteTextSegment;
+	BEGIN
+		Write4(1); (* PT_LOAD *)
+		Write4(0); (* offset *)
+		Write4(0); (* vaddr *)
+		Write4(0); (* paddr *)
+		Write4(textSegmentSizeVal); (* file size *)
+		Write4(textSegmentSizeVal); (* mem size *)
+		Write4(4H + 1H + 2H); (* flags: R+E+W *)
+		Write4(pageSize) (* I386 page size *)
+	END WriteTextSegment;
+	
+	PROCEDURE WriteDataSegment;
+	BEGIN
+		Write4(1); (* PT_LOAD *)
+		Write4(got.fileOffset); (* offset text segment size *)
+		Write4(got.memOffset); (* vaddr: offset + alignment * nof pages of text segment *)
+		Write4(got.memOffset); (* paddr: offset + alignment * nof pages of text segment *)
+		Write4(dataSegmentSizeVal); (* file size *)
+		Write4(dataSegmentSizeVal + bss.size); (* mem size -> dataSegmentSizeVal + NOBITS sections *)
+		Write4(4H + 2H); (* flags: R+W *)
+		Write4(pageSize) (* I386 page size *)
+	END WriteDataSegment;
+	
+	PROCEDURE WriteDynamicSegment;
+	BEGIN
+		Write4(2); (* PT_DYNAMIC *)
+		Write4(dynamic.fileOffset); (* offset text segment size *)
+		Write4(dynamic.memOffset); (* vaddr: offset of .dynamic section *)
+		Write4(dynamic.memOffset); (* paddr: vaddr + alignment * nof pages of text segment *)
+		Write4(dynamicSegmentSizeVal); (* file size *)
+		Write4(dynamicSegmentSizeVal); (* mem size *)
+		Write4(4H + 2H); (* flags: R+W *)
+		Write4(dynamicAlign) (* dynamic section alignement*)
+	END WriteDynamicSegment;
+	
+	PROCEDURE WriteProgramHeaderTable;
+	BEGIN
+		phOffsetVal := Ro.Pos();
+		WriteTextSegment; (* .text .rel.text .rodata .dynsym .dynstr .hash *)
+		WriteDataSegment; (* .got .dynamic .bss *)
+		WriteDynamicSegment (* .dynamic *)
+	END WriteProgramHeaderTable;
+	
+	PROCEDURE SearchObj (mod: Module; VAR name: ARRAY OF SHORTCHAR; m, fp, opt: INTEGER; VAR adr: INTEGER);
+		VAR dir, len, ntab, f, id, l, r, p, n, i, j: INTEGER; nch, och: SHORTCHAR;
+	BEGIN
+		Get(mod, mod.ms + modExports, dir); DEC(dir, rodata.memOffset + mod.ma); Get(mod, dir, len); INC(dir, 4);
+		Get(mod, mod.ms + modNames, ntab); DEC(ntab, rodata.memOffset + mod.ma);
+		IF name # "" THEN
+			l := 0; r := len;
+			WHILE l < r DO	(* binary search *)
+				n := (l + r) DIV 2; p := dir + n * 16;
+				Get(mod, p + 8, id);
+				i := 0; j := ntab + id DIV 256; nch := name[0]; och := SHORT(CHR(mod.data[j]));
+				WHILE (nch = och) & (nch # 0X) DO INC(i); INC(j); nch := name[i]; och := SHORT(CHR(mod.data[j])) END;
+				IF och = nch THEN
+					IF id MOD 16 = m THEN
+						Get(mod, p, f);
+						IF m = mTyp THEN
+							IF ODD(opt) THEN Get(mod, p + 4, f) END;
+							IF (opt > 1) & (id DIV 16 MOD 16 # mExported) THEN
+								W.WriteString(mod.name$); W.WriteChar("."); W.WriteSString(name);
+								W.WriteString(" imported from "); W.WriteString(impg.name$);
+								W.WriteString(" has wrong visibility"); W.WriteLn; error := TRUE
+							END;
+							Get(mod, p + 12, adr)
+						ELSIF m = mVar THEN
+							Get(mod, p + 4, adr); INC(adr, bss.memOffset + mod.va)
+						ELSIF m = mProc THEN
+							Get(mod, p + 4, adr); INC(adr, text.memOffset + mod.ca)
+						END;
+						IF f # fp THEN
+							W.WriteString(mod.name$); W.WriteChar("."); W.WriteSString(name);
+							W.WriteString(" imported from "); W.WriteString(impg.name$);
+							W.WriteString(" has wrong fprint"); W.WriteLn; error := TRUE
+						END
+					ELSE
+						W.WriteString(mod.name$); W.WriteChar("."); W.WriteSString(name);
+						W.WriteString(" imported from "); W.WriteString(impg.name$);
+						W.WriteString(" has wrong class"); W.WriteLn; error := TRUE
+					END;
+					RETURN
+				END;
+				IF och < nch THEN l := n + 1 ELSE r := n END
+			END;
+			W.WriteString(mod.name$); W.WriteChar("."); W.WriteSString(name);
+			W.WriteString(" not found (imported from "); W.WriteString(impg.name$);
+			W.WriteChar(")"); W.WriteLn; error := TRUE
+		ELSE (* anonymous type *)
+			WHILE len > 0 DO
+				Get(mod, dir + 4, f); Get(mod, dir + 8, id);
+				IF (f = fp) & (id MOD 16 = mTyp) & (id DIV 256 = 0) THEN
+					Get(mod, dir + 12, adr); RETURN
+				END;
+				DEC(len); INC(dir, 16)
+			END;
+			W.WriteString("anonymous type in "); W.WriteString(mod.name$);
+			W.WriteString(" not found"); W.WriteLn; error := TRUE
+		END
+	END SearchObj;
+	
+	PROCEDURE CollectExports (mod: Module);
+		VAR dir, len, ntab, id, i, j, n: INTEGER; e, exp: Export;
+	BEGIN
+		ASSERT(mod.intf & ~mod.dll, 20);
+		Get(mod, mod.ms + modExports, dir);
+		DEC(dir, rodata.memOffset + mod.ma); Get(mod, dir, len); INC(dir, 4);
+		Get(mod, mod.ms + modNames, ntab); DEC(ntab, rodata.memOffset + mod.ma); n := 0;
+		WHILE n < len DO
+			Get(mod, dir + 8, id);
+			IF (id DIV 16 MOD 16 # mInternal) & (id MOD 16 = mProc) THEN	(* exported procedure *)
+				NEW(exp);
+				i := 0; j := ntab + id DIV 256;
+				WHILE mod.data[j] # 0 DO exp.name[i] := SHORT(CHR(mod.data[j])); INC(i); INC(j) END;
+				exp.name[i] := 0X;
+				Get(mod, dir + 4, exp.adr);
+				IF id MOD 16 = mProc THEN
+					INC(exp.adr, text.memOffset + mod.ca)
+				ELSE
+					HALT(126);
+					ASSERT(id MOD 16 = mVar); INC(exp.adr, bss.memOffset + mod.va)
+				END;
+				IF (firstExp = NIL) OR (exp.name < firstExp.name) THEN
+					exp.next := firstExp; firstExp := exp;
+					IF lastExp = NIL THEN lastExp := exp END
+				ELSE
+					e := firstExp;
+					WHILE (e.next # NIL) & (exp.name > e.next.name) DO e := e.next END;
+					exp.next := e.next; e.next := exp;
+					IF lastExp = e THEN lastExp := exp END
+				END;
+				INC(numExp)
+			END;
+			INC(n); INC(dir, 16)
+		END
+	END CollectExports;
+
+	PROCEDURE Relocate0 (link, adr, sym: INTEGER);
+		CONST
+			absolute = 100; relative = 101; copy = 102; table = 103; tableend = 104; (* BB fixup types *)
+			noElfType = MIN(INTEGER);
+		VAR
+			offset, linkadr, bbType, elfType, n, x: INTEGER; relText: BOOLEAN;
+	BEGIN
+		WHILE link # 0 DO
+			RNum(offset);
+			WHILE link # 0 DO
+				IF link > 0 THEN
+					n := (code[link] MOD 256) + (code[link+1] MOD 256) * 256 + code[link+2] * 65536;
+					bbType := code[link+3];
+					linkadr := text.memOffset + impg.ca + link
+				ELSE
+					n := (impg.data[-link] MOD 256) + (impg.data[-link+1] MOD 256) * 256 + impg.data[-link+2] * 65536;
+					bbType := impg.data[-link+3];
+					linkadr := rodata.memOffset + impg.ma - link
+				END;
+				elfType := noElfType;
+				IF bbType = absolute THEN
+					IF sym = noSymbol THEN
+						x := adr + offset;
+						elfType := r386Relative
+					ELSE
+						x := 0H;
+						elfType := r38632 + sym * 256
+					END
+				ELSIF bbType = relative THEN
+					IF sym = noSymbol THEN
+						x := adr + offset - linkadr - 4
+					ELSE
+						x := 0FFFFFFFCH;
+						elfType := r386pc32 + sym * 256
+					END
+				ELSIF bbType = copy THEN
+					Get(impd, adr + offset - rodata.memOffset - impd.ma, x);
+					IF x # 0 THEN elfType := r386Relative END
+				ELSIF bbType = table THEN
+					x := adr + n; n := link + 4;
+					elfType := r386Relative
+				ELSIF bbType = tableend THEN
+					x := adr + n; n := 0;
+					elfType := r386Relative
+				ELSE HALT(99)
+				END;
+				relText := link > 0; 
+				IF link > 0 THEN
+					code[link] := SHORT(SHORT(x));
+					code[link+1] := SHORT(SHORT(x DIV 100H));
+					code[link+2] := SHORT(SHORT(x DIV 10000H));
+					code[link+3] := SHORT(SHORT(x DIV 1000000H))
+				ELSE
+					link := -link;
+					impg.data[link] := SHORT(SHORT(x));
+					impg.data[link+1] := SHORT(SHORT(x DIV 100H));
+					impg.data[link+2] := SHORT(SHORT(x DIV 10000H));
+					impg.data[link+3] := SHORT(SHORT(x DIV 1000000H))
+				END;
+				IF elfType # noElfType THEN
+					IF relText THEN
+						relTextTab.Add(linkadr, elfType)
+					ELSE
+						relRodataTab.Add(linkadr, elfType)
+					END
+				END;
+				link := n
+			END;
+			RNum(link)
+		END
+	END Relocate0;
+	
+	PROCEDURE Relocate (adr: INTEGER);
+		VAR link: INTEGER;
+	BEGIN
+		RNum(link); Relocate0(link, adr, noSymbol)
+	END Relocate;
+
+	PROCEDURE RelocateSymbol (adr, sym: INTEGER);
+		VAR link: INTEGER;
+	BEGIN
+		RNum(link); Relocate0(link, adr, sym)
+	END RelocateSymbol;
+	
+	PROCEDURE SymbolIndex (IN name: Name): INTEGER;
+		VAR n: INTEGER; exp: Export; m: Module;
+	BEGIN
+		n := 0; exp := NIL;
+		m := modList;
+		WHILE (m # NIL) & (exp = NIL) DO
+			IF m.dll THEN
+				exp := m.exp;
+				WHILE (exp # NIL) & (exp.name$ # name$) DO
+					INC(n);
+					exp := exp.next
+				END
+			END;
+			m := m.next
+		END;
+		ASSERT((exp # NIL) & (exp.name$ = name$), 60);
+		RETURN firstDllSymbolVal + n
+	END SymbolIndex;
+
+	PROCEDURE WriteTextSection;
+		VAR mod, m: Module; i, x, a, sym, fp, opt: INTEGER; exp: Export; name: Name;
+	BEGIN
+		ASSERT(isDll, 126);
+		ASSERT(~doWrite OR (Ro.Pos() = text.fileOffset), 100);
+		WriteCh(053X);	(* push ebx *)	(* _init() *)
+		a := 1;
+		WriteCh(0BBX); Write4(rodata.memOffset  + last.ma + last.ms);	(* mov bx, modlist *)
+		relTextTab.Add(text.memOffset + a + 1, r386Relative);
+		INC(a, 5);
+		IF isStatic THEN
+			m := modList;
+			WHILE m # NIL DO
+				IF ~m.dll THEN
+					WriteCh(0E8X); INC(a, 5); Write4(m.ca - a)	(* call body *)
+				END;
+				m := m.next
+			END
+		ELSE
+			WriteCh(0E8X); INC(a, 5); Write4(main.ca - a)	(* call main *)
+		END;
+		WriteCh(05BX); 	(* pop ebx *)
+		WriteCh(0C3X);	(* ret *)
+		INC(a, 2);
+		finiMemOffsetVal := text.memOffset + a;
+		WriteCh(053X);	(* push ebx *)	(* _fini() *)
+		INC(a);
+		finiPos := text.memOffset + a;
+		IF isStatic THEN
+			i := 0;
+			WHILE i < numMod DO	(* nop for call terminator *)
+				WriteCh(02DX); Write4(0);	(* sub EAX, 0 *)
+				INC(i); INC(a, 5)
+			END
+		ELSE
+			WriteCh(02DX); Write4(0);	(* sub EAX, 0 *)
+			INC(a, 5)
+		END;
+		lastTerm := a;
+		WriteCh(05BX); 	(* pop ebx *)
+		WriteCh(0C3X);	(* ret *)	
+		IF ~doWrite THEN NEW(code, maxCode) END;
+		mod := modList;
+		WHILE mod # NIL DO
+			impg := mod;
+			impd := mod;
+			IF ~mod.dll THEN
+				mod.file := ThisFile(mod.fileName);
+				R := mod.file.NewReader(R);
+				R.SetPos(mod.hs);
+				IF ~doWrite THEN NEW(mod.data, mod.ms + mod.ds) END;
+				R.ReadBytes(mod.data^, 0, mod.ms + mod.ds);
+				R.ReadBytes(code^, 0, mod.cs);
+				RNum(x);
+				IF x # 0 THEN
+					IF (mod # kernel) & (kernel # NIL) THEN
+						SearchObj(kernel, newRec, mProc, NewRecFP, 0, a);
+						IF error THEN RETURN END;
+						Relocate0(x, a, noSymbol)
+					ELSE
+						W.WriteSString("no kernel"); W.WriteLn;
+						StdLog.text.Append(StdLog.buf);
+						error := TRUE;
+						RETURN
+					END
+				END;
+				RNum(x);
+				IF x # 0 THEN
+					IF (mod # kernel) & (kernel # NIL) THEN
+						SearchObj(kernel, newArr, mProc, NewArrFP, 0, a);
+						IF error THEN RETURN END;
+						Relocate0(x, a, noSymbol)
+					ELSE
+						W.WriteSString("no kernel"); W.WriteLn;
+						StdLog.text.Append(StdLog.buf); error := TRUE;
+						RETURN
+					END
+				END;
+				Relocate(rodata.memOffset + mod.ma); (* metalink *)
+				Relocate(rodata.memOffset + mod.ma + mod.ms); (* desclink *)
+				Relocate(text.memOffset + mod.ca); (* codelink *)
+				Relocate(bss.memOffset + mod.va); (* datalink *)
+				i := 0;
+				WHILE i < mod.ni DO
+					m := mod.imp[i]; impd := m; RNum(x);
+					WHILE x # 0 DO
+						ReadName(name); RNum(fp); opt := 0;
+						IF x = mTyp THEN RNum(opt) END;
+						sym := noSymbol;
+						IF m.dll THEN
+							IF (x = mProc) OR (x = mVar) THEN
+								exp := m.exp;
+								WHILE exp.name # name DO exp := exp.next END;
+								a := noAddr;
+								sym := SymbolIndex(name)
+							END
+						ELSE
+							SearchObj(m, name, x, fp, opt, a);
+							IF error THEN RETURN END
+						END;
+						IF x # mConst THEN
+							RelocateSymbol(a, sym)
+						END;
+						RNum(x)
+					END;
+					IF ~m.dll THEN
+						Get(mod, mod.ms + modImports, x); DEC(x, rodata.memOffset + mod.ma); INC(x, 4 * i);
+						Put(mod, x, rodata.memOffset + m.ma + m.ms);	(* imp ref *)
+						relRodataTab.Add(rodata.memOffset + mod.ma + x, r386Relative);
+						Get(m, m.ms + modRefcnt, x); Put(m, m.ms + modRefcnt, x + 1)	(* inc ref count *)
+					END;
+					INC(i)
+				END;
+				WriteBytes(code^, 0, mod.cs);
+				IF mod.intf THEN CollectExports(mod) END;
+				mod.file.Close; mod.file := NIL
+			END;
+			mod := mod.next
+		END;
+		ASSERT(~doWrite OR (text.size = Ro.Pos() - text.fileOffset), 101)
+	END WriteTextSection;
+
+	PROCEDURE WriteTermCode (m: Module; i: INTEGER);
+		VAR x: INTEGER;
+	BEGIN
+		IF m # NIL THEN
+			IF m.dll THEN WriteTermCode(m.next, i)
+			ELSE
+				IF isStatic THEN WriteTermCode(m.next, i + 1) END;
+				Get(m, m.ms + modTerm, x);	(* terminator address in mod desc*)
+				IF x = 0 THEN
+					WriteCh(005X); Write4(0)	(* add EAX, 0 (nop) *)
+				ELSE
+					WriteCh(0E8X); Write4(x - lastTerm + 5 * i - text.memOffset)	(* call term *)
+				END
+			END
+		END
+	END WriteTermCode;
+
+	PROCEDURE FixupTextSection;
+	BEGIN
+		ASSERT(isDll, 126);
+		Ro.SetPos(finiPos);
+		IF isStatic THEN
+			WriteTermCode(modList, 0)
+		ELSE
+			WriteTermCode(main, 0)
+		END
+	END FixupTextSection;
+
+	PROCEDURE WriteRelSection (IN s: Section; IN t: RelTab);
+		VAR i: INTEGER;
+	BEGIN
+		ASSERT(s.fileOffset = Ro.Pos(), 100);
+		i := 0;
+		WHILE i # t.cur DO
+			Write4(t.tab[i].offset);
+			Write4(t.tab[i].type);
+			INC(i)
+		END;
+		ASSERT(s.size = Ro.Pos() - s.fileOffset, 101)
+	END WriteRelSection;
+
+	PROCEDURE WriteRelSections;
+	BEGIN
+		WriteRelSection(reltext, relTextTab);
+		WriteRelSection(relrodata, relRodataTab)
+	END WriteRelSections;
+	
+	PROCEDURE WriteRodataSection;
+		VAR mod, lastMod: Module; x: INTEGER;
+	BEGIN
+		ASSERT(~doWrite OR (rodata.fileOffset = Ro.Pos()), 100);
+		mod := modList; lastMod := NIL;
+		WHILE mod # NIL DO
+			IF ~mod.dll THEN
+				IF lastMod # NIL THEN
+					Put(mod, mod.ms, rodata.memOffset + lastMod.ma + lastMod.ms);	(* mod list *)
+					relRodataTab.Add(rodata.memOffset + mod.ma + mod.ms, r386Relative)
+				END;
+				Get(mod, mod.ms + modOpts, x);
+				IF isStatic THEN INC(x, 10000H) END;	(* set init bit (16) *)
+				IF isDll THEN INC(x, 1000000H) END;	(* set dll bit (24) *)
+				Put(mod, mod.ms + modOpts, x);
+				WriteBytes(mod.data^, 0, mod.ms + mod.ds);
+				lastMod := mod
+			END;
+			mod := mod.next
+		END;		
+		ASSERT(~doWrite OR (rodata.size = Ro.Pos() - rodata.fileOffset), 101)
+	END WriteRodataSection;
+		
+	PROCEDURE WriteSymbolTableEntry (IN name: ARRAY OF SHORTCHAR; val, size: INTEGER; bind, type: BYTE; shndx: INTEGER);
+		VAR i: INTEGER; info: SHORTCHAR;
+	BEGIN
+		IF name # "" THEN dynstrtab.AddName(name, i)
+		ELSE i := 0
+		END;
+		Write4(i);
+		Write4(val);
+		Write4(size);
+		info := SHORT(CHR(bind * 16 + type));
+		WriteCh(info);
+		WriteCh(0X); (* Symbol visibility *)
+		Write2(shndx)
+	END WriteSymbolTableEntry;
+	
+	PROCEDURE FixupSymbolTableEntry (val, size: INTEGER; bind, type: BYTE; shndx: INTEGER);
+		VAR info: SHORTCHAR;
+	BEGIN
+		Ro.SetPos(Ro.Pos() + 4); (* skip name *)
+		Write4(val);
+		Write4(size);
+		info := SHORT(CHR(bind * 16 + type));
+		WriteCh(info);
+		WriteCh(0X); (* Symbol visibility *)
+		Write2(shndx)
+	END FixupSymbolTableEntry;
+	
+	PROCEDURE WriteDynsymSection;
+		VAR e: Export; m: Module; i: INTEGER;
+	BEGIN
+		ASSERT(Ro.Pos() = dynsym.fileOffset, 100);
+		WriteSymbolTableEntry("", 0, 0, 0, 0, 0);
+		WriteSymbolTableEntry("", text.memOffset, 0, stbLocal, sttSection, 1); (* .text section *)
+		WriteSymbolTableEntry("", rodata.memOffset, 0, stbLocal, sttSection, 2); (* .rodata section *)
+		WriteSymbolTableEntry("", reltext.memOffset, 0, stbLocal, sttSection, 3); (* .rel.text.section *)
+		WriteSymbolTableEntry("", relrodata.memOffset, 0, stbLocal, sttSection, 4); (* .rel.rodata section *)
+		WriteSymbolTableEntry("", dynsym.memOffset, 0, stbLocal, sttSection, 5); (* .dynsym section *)
+		WriteSymbolTableEntry("", dynstr.memOffset, 0, stbLocal, sttSection, 6); (* .dynstr section *)
+		WriteSymbolTableEntry("", hash.memOffset, 0, stbLocal, sttSection, 7); (* .hash section *)
+		WriteSymbolTableEntry("", got.memOffset, 0, stbLocal, sttSection, 8); (* .got section *)
+		WriteSymbolTableEntry("", dynamic.memOffset, 0, stbLocal, sttSection, 9); (* .dynamic section *)
+		WriteSymbolTableEntry("", bss.memOffset, 0, stbLocal, sttSection, 10); (* .bss section *)
+		dynsymInfoVal := 11;
+		i := dynsymInfoVal;
+		WriteSymbolTableEntry("_DYNAMIC", dynamic.memOffset, 0, stbGlobal, sttObject, shnAbs);
+		hashtab[i] := "_DYNAMIC";
+		INC(i);
+		ASSERT(i = firstDllSymbolVal);
+		m := modList;
+		WHILE m # NIL DO
+			IF m.dll THEN
+				e := m.exp;
+				WHILE e # NIL DO
+					WriteSymbolTableEntry(e.name, 0, 0, stbGlobal, sttNotype, shnUnd);
+					hashtab[i] := e.name$;
+					INC(i);
+					e := e.next
+				END
+			END;
+			m := m.next
+		END;
+		e := firstExp;
+		WHILE e # NIL DO
+			WriteSymbolTableEntry(e.name, fixup, 0, stbGlobal, sttFunc, textIndexVal);
+			hashtab[i] := e.name$; INC(i);
+			e := e.next
+		END;
+		WriteSymbolTableEntry("_GLOBAL_OFFSET_TABLE_", got.memOffset, 0, stbGlobal, sttObject, shnAbs);
+		hashtab[i] := "_GLOBAL_OFFSET_TABLE_";
+		ASSERT(dynsym.size = Ro.Pos() - dynsym.fileOffset, 101)
+	END WriteDynsymSection;
+	
+	PROCEDURE FixupDynsymSection;
+		VAR e: Export; m: Module;
+	BEGIN
+		Ro.SetPos(dynsym.fileOffset + dynsymEntrySizeVal * firstDllSymbolVal);
+		m := modList;
+		WHILE m # NIL DO
+			IF m.dll THEN
+				e := m.exp;
+				WHILE e # NIL DO
+					Ro.SetPos(Ro.Pos() + dynsymEntrySizeVal);
+					e := e.next
+				END
+			END;
+			m := m.next
+		END;
+		Ro.SetPos(Ro.Pos() + 4);
+		e := firstExp;
+		WHILE e # NIL DO
+			Write4(e.adr);
+			Ro.SetPos(Ro.Pos() + 12);
+			e := e.next
+		END
+	END FixupDynsymSection;
+
+	PROCEDURE WriteStringTable (IN t: Strtab);
+		VAR i: INTEGER;
+	BEGIN
+		i := 0;
+		WHILE i # t.cur DO
+			WriteCh(t.tab[i]);
+			INC(i)
+		END
+	END WriteStringTable;
+
+	PROCEDURE WriteDynstrSection;
+	BEGIN
+		ASSERT(Ro.Pos() = dynstr.fileOffset, 100);
+		WriteStringTable(dynstrtab);
+		ASSERT(dynstr.size = Ro.Pos() - dynstr.fileOffset, 101)
+	END WriteDynstrSection;
+
+	PROCEDURE Hash (name: ARRAY OF SHORTCHAR): INTEGER;
+		VAR i, h, g: INTEGER;
+	BEGIN
+		h := 0; i := 0;
+		WHILE name[i] # 0X DO
+			h := ASH(h, 4) + ORD(name[i]);
+			g := ORD(BITS(h) * BITS(0F0000000H));
+			IF g # 0 THEN
+				h := ORD(BITS(h) / BITS(SHORT((g MOD 100000000L) DIV 1000000H)))
+			END;
+			h := ORD(BITS(h) * (-BITS(g)));
+			INC(i)
+		END;
+		RETURN h
+	END Hash;
+
+	PROCEDURE AddToChain (VAR c: ARRAY OF INTEGER; i, idx: INTEGER);
+		VAR k: INTEGER;
+	BEGIN
+		IF c[i] # 0 THEN
+			k := i;
+			WHILE c[k] # 0 DO k := c[k] END;
+			c[k] := idx
+		ELSE
+			c[i] := idx
+		END
+	END AddToChain;
+
+	PROCEDURE WriteHashSection;
+		VAR n, i, hi: INTEGER; b, c: POINTER TO ARRAY OF INTEGER;
+	BEGIN
+		ASSERT(hash.fileOffset = Ro.Pos(), 100);
+		n := dynsym.size DIV dynsymEntrySizeVal; (* number of enties in the symbol table *)
+		NEW(b, n);
+		NEW(c, n);
+		i := 0;
+		WHILE i # n DO
+			c[i] := 0; (* STN_UNDEF *)
+			IF hashtab[i] # "" THEN
+				hi := Hash(hashtab[i]) MOD n;
+				IF b[hi] # 0 THEN (* another word has the same index *)
+					AddToChain(c, i, b[hi])  (*c[i] := b[hi]*)
+				END;
+				b[hi] := i
+			END;
+			INC(i)
+		END;
+		Write4(n); (* nbucket *)
+		Write4(n); (* nchain *)
+		i := 0;
+		WHILE i # n DO
+			Write4(b[i]);
+			INC(i)
+		END;
+		i := 0;
+		WHILE i # n DO
+			Write4(c[i]);
+			INC(i)
+		END;
+		ASSERT(hash.size = Ro.Pos() - hash.fileOffset, 101)
+	END WriteHashSection;
+	
+	PROCEDURE WriteGotSection;
+	BEGIN
+		ASSERT(got.fileOffset = Ro.Pos(), 100);
+		Write4(dynamic.memOffset); (* addr of .dynamic section *)
+		Write4(0); (* reserved for ? *)
+		Write4(0); (* reserved for ? *)
+		ASSERT(got.size = Ro.Pos() - got.fileOffset, 101)
+	END WriteGotSection;
+	
+	PROCEDURE WriteDynamicSectionEntry (tag, val: INTEGER);
+	BEGIN
+		Write4(tag);
+		Write4(val)
+	END WriteDynamicSectionEntry;
+	
+	PROCEDURE WriteDynamicSection;
+		CONST dtNull = 0; dtNeeded = 1; dtHash = 4; dtStrtab = 5; dtSymtab = 6;
+			dtStrsz = 10; dtSyment = 11; dtInit = 12; dtFini = 13; dtSoname = 14; dtRel = 17; dtRelsz = 18; dtRelent = 19;
+			dtTextrel = 22;
+		VAR i: INTEGER;
+	BEGIN
+		ASSERT(dynamic.fileOffset = Ro.Pos(), 100);
+		WriteDynamicSectionEntry(dtSoname, fixup);
+		WriteDynamicSectionEntry(dtFini, fixup);
+		WriteDynamicSectionEntry(dtInit, text.memOffset);
+		WriteDynamicSectionEntry(dtHash, hash.memOffset);
+		WriteDynamicSectionEntry(dtStrtab, dynstr.memOffset);
+		WriteDynamicSectionEntry(dtSymtab, dynsym.memOffset);
+		WriteDynamicSectionEntry(dtStrsz, dynstr.size);
+		WriteDynamicSectionEntry(dtSyment, dynsymEntrySizeVal);
+		WriteDynamicSectionEntry(dtRel, reltext.memOffset);
+		WriteDynamicSectionEntry(dtRelsz, reltext.size + relrodata.size);
+		WriteDynamicSectionEntry(dtRelent, relEntrySizeVal);
+		i := 0;
+		WHILE neededIdx[i] # 0 DO
+			WriteDynamicSectionEntry(dtNeeded, neededIdx[i]);
+			INC(i)
+		END;
+		WriteDynamicSectionEntry(dtTextrel, 0);
+		WriteDynamicSectionEntry(dtNull, 0); (* DT_NULL: marks the end *)
+		ASSERT(dynamic.size = Ro.Pos() - dynamic.fileOffset, 101)
+	END WriteDynamicSection;
+	
+	PROCEDURE FixupDynamicSection;
+		VAR i: INTEGER;
+	BEGIN
+		Ro.SetPos(dynamic.fileOffset + 4);
+		Write4(sonameStrIndexVal);
+		Ro.SetPos(Ro.Pos() + 4);
+		Write4(finiMemOffsetVal)
+	END FixupDynamicSection;
+	
+	PROCEDURE WriteBssSection;
+	BEGIN
+(*
+		The .bss section does not take space in the file.
+		This procedure serves consistency-check purposes.
+*)
+		ASSERT(bss.fileOffset = Ro.Pos(), 100)
+	END WriteBssSection;
+
+	PROCEDURE WriteShstrtabSection;
+	BEGIN
+		ASSERT(shstrtab.fileOffset = Ro.Pos(), 100);
+		WriteStringTable(headerstrtab);
+		shstrtab.size := Ro.Pos() - shstrtab.fileOffset
+	END WriteShstrtabSection;
+
+	PROCEDURE GetImpListSize (OUT len: INTEGER; OUT count: INTEGER);
+		VAR m: Module; e: Export;
+	BEGIN
+		len := 0; count := 0;
+		m := modList;
+		WHILE m # NIL DO
+			IF m.dll THEN
+				e := m.exp;
+				WHILE e # NIL DO
+					INC(len, LEN(e.name$) + 1);
+					INC(count);
+					e := e.next
+				END
+			END;
+			m := m.next
+		END
+	END GetImpListSize;
+	
+	PROCEDURE GetExpListSize (OUT len: INTEGER; OUT count: INTEGER);
+		VAR e: Export;
+	BEGIN
+		count := 0; len := 0;
+		e := firstExp;
+		WHILE e # NIL DO
+			INC(len, LEN(e.name$) + 1);
+			INC(count);
+			e := e.next
+		END
+	END GetExpListSize;
+	
+	PROCEDURE DynsymSize (init: INTEGER): INTEGER;
+		VAR size: INTEGER;
+	BEGIN
+		size := init;
+		INC(size, dynsymEntrySizeVal * 11); (* sections entries *)
+		INC(size, dynsymEntrySizeVal); (* _DYNAMIC symbol *)
+		INC(size, dynsymEntrySizeVal); (* _GLOBAL_OFFSET_TABLE_ symbol *)
+		RETURN size
+	END DynsymSize;
+	
+	PROCEDURE DynstrSize (init: INTEGER): INTEGER;
+		VAR size: INTEGER;
+	BEGIN
+		size := init + 1;
+		INC(size, dynstrtab.cur - 1);
+		INC(size, LEN(soName$) + 1); (* library name *)
+		INC(size, 9); (* "_DYNAMIC" symbol + 0X *)
+		INC(size, 21 + 1); (* "_GLOBAL_OFFSET_TABLE_" symbol + trailing 0X *)
+		RETURN size
+	END DynstrSize;
+	
+	PROCEDURE DynamicSize (init: INTEGER): INTEGER;
+		VAR i, size: INTEGER;
+	BEGIN
+		size := init;
+		i := 0;
+		WHILE neededIdx[i] # 0 DO
+			INC(size, dynamicEntrySizeVal);
+			INC(i)
+		END;
+		RETURN size
+	END DynamicSize;
+	
+	PROCEDURE CalculateLayout;
+		VAR headerSize, impCount, expCount, impLen, expLen: INTEGER;
+	BEGIN
+		ASSERT(~error, 20);
+		headerSize := elfHeaderSizeVal + shEntrySizeVal * shNumVal + phEntrySizeVal * phNumVal;
+		text.fileOffset := Aligned(headerSize, textAlign);
+		text.memOffset := text.fileOffset;
+		text.size := CodeSize;
+		rodata.fileOffset := Aligned(text.fileOffset + text.size, rodataAlign);
+		rodata.memOffset := rodata.fileOffset;
+		rodata.size := ConSize;
+		reltext.fileOffset := Aligned(rodata.fileOffset + rodata.size, relAlign);
+		reltext.memOffset := reltext.fileOffset;
+		doWrite := FALSE;
+		WriteTextSection;	(* this only calculates the number of text relocations *)
+		IF error THEN RETURN END;
+		reltext.size := relEntrySizeVal * relTextTab.cur;
+		relrodata.fileOffset := reltext.fileOffset + reltext.size;
+		relrodata.memOffset := relrodata.fileOffset;
+		IF ~error THEN
+			WriteRodataSection	(* this only calculates the number of data relocations *)
+		ELSE
+			RETURN
+		END;
+		relrodata.size := relEntrySizeVal * relRodataTab.cur;
+		dynsym.fileOffset := Aligned(relrodata.fileOffset + relrodata.size, dynsymAlign);
+		dynsym.memOffset := dynsym.fileOffset;
+		GetImpListSize(impLen, impCount);
+		GetExpListSize(expLen, expCount);
+		dynsym.size := DynsymSize((impCount + expCount) * dynsymEntrySizeVal);
+		dynstr.fileOffset := Aligned(dynsym.fileOffset + dynsym.size, dynstrAlign);
+		dynstr.memOffset := dynstr.fileOffset;
+		dynstr.size := DynstrSize(impLen + expLen);
+		hash.fileOffset := Aligned(dynstr.fileOffset + dynstr.size, hashAlign);
+		hash.memOffset := hash.fileOffset;
+		hash.size := 8 + dynsym.size DIV dynsymEntrySizeVal * 4 * 2;
+		got.fileOffset := Aligned(hash.fileOffset + hash.size, gotAlign);
+		got.memOffset := Aligned(got.fileOffset, pageSize) + got.fileOffset MOD pageSize;
+		got.size := 3 * gotEntrySizeVal;
+		dynamic.fileOffset := Aligned(got.fileOffset + got.size, dynamicAlign);
+		dynamic.memOffset := got.memOffset + dynamic.fileOffset - got.fileOffset;
+		dynamic.size := DynamicSize(13 * dynamicEntrySizeVal);
+		bss.fileOffset := Aligned(dynamic.fileOffset + dynamic.size, bssAlign);
+		bss.memOffset := dynamic.memOffset + bss.fileOffset - dynamic.fileOffset;		
+		bss.size := DataSize;
+		shstrtab.fileOffset := Aligned(bss.fileOffset, shstrtabAlign);
+		shstrtab.size := fixup;
+		textSegmentSizeVal := got.fileOffset;
+		dataSegmentSizeVal := shstrtab.fileOffset - got.fileOffset;
+		dynamicSegmentSizeVal := shstrtab.fileOffset - dynamic.fileOffset;
+		relTextTab.cur := 0;
+		relRodataTab.cur := 0;
+		firstExp := NIL; lastExp := NIL;
+		doWrite := TRUE
+	END CalculateLayout;
+
+	PROCEDURE WriteOut;
+		VAR res: INTEGER;
+	BEGIN
+		ASSERT(~error, 20);
+		Out := Files.dir.New(Files.dir.This(""), Files.ask);
+		IF Out # NIL THEN
+			Ro := Out.NewWriter(Ro); Ro.SetPos(0);
+			CalculateLayout;
+			IF ~error THEN WriteElfHeader END;
+			IF ~error THEN WriteSectionHeaderTable END;
+			IF ~error THEN WriteProgramHeaderTable END;
+			IF ~error THEN Align(textAlign); WriteTextSection END;
+			IF ~error THEN Align(rodataAlign); WriteRodataSection END;
+			IF ~error THEN Align(relAlign); WriteRelSections END;
+			IF ~error THEN Align(dynsymAlign); WriteDynsymSection END;
+			IF ~error THEN Align(dynstrAlign); WriteDynstrSection END;
+			IF ~error THEN Align(hashAlign); WriteHashSection END;
+			IF ~error THEN Align(gotAlign); WriteGotSection END;
+			IF ~error THEN Align(dynamicAlign); WriteDynamicSection END;
+			IF ~error THEN Align(bssAlign); WriteBssSection END;
+			IF ~error THEN Align(shstrtabAlign); WriteShstrtabSection END;
+
+			IF ~error THEN FixupElfHeader END;
+			IF ~error THEN FixupSectionHeaderTable END;
+			IF ~error THEN FixupTextSection END;
+			IF ~error THEN FixupDynsymSection END;
+			IF ~error THEN FixupDynamicSection END;
+			Out.Register(soName$, "so", Files.ask, res);
+			IF res # 0 THEN error := TRUE END
+		ELSE
+			error := TRUE
+		END
+	END WriteOut;
+	
+	PROCEDURE ResetHashtab;
+		VAR i: INTEGER;
+	BEGIN
+		i := 0;
+		WHILE i # LEN(hashtab) DO
+			hashtab[i] := "";
+			INC(i)
+		END
+	END ResetHashtab;
+
+	PROCEDURE ResetNeededIdx;
+		VAR i: INTEGER;
+	BEGIN
+		i := 0;
+		WHILE i # LEN(neededIdx) DO
+			neededIdx[i] := 0;
+			INC(i)
+		END
+	END ResetNeededIdx;
+
+	PROCEDURE MakeSoName (VAR name: ARRAY OF CHAR; type: ARRAY OF CHAR);
+		VAR i, j: INTEGER; ext: Files.Name; ch: CHAR;
+	BEGIN
+		ASSERT((type = "") OR (type[0] = "."), 20);
+		i := 0;
+		WHILE (name[i] # 0X) & (name[i] # ".") DO INC(i) END;
+		IF name[i] = "." THEN
+			IF name[i + 1] = 0X THEN name[i] := 0X END
+		ELSIF i < LEN(name) - (LEN(type$) + 1) THEN
+			IF type = "" THEN ext := ".so" ELSE ext := type$ END;
+			j := 0; ch := ext[0];
+			WHILE ch # 0X DO
+				IF (ch >= "A") & (ch <= "Z") THEN
+					ch := CHR(ORD(ch) + ORD("a") - ORD("A"))
+				END;
+				name[i] := ch; INC(i); INC(j); ch := ext[j]
+			END;
+			name[i] := 0X
+		END
+	END MakeSoName;
+	
+	PROCEDURE ParseExt (IN S: TextMappers.Scanner; OUT ext: Files.Name);
+		VAR ch: CHAR; i: INTEGER;
+	BEGIN
+		ext := "";
+		S.rider.ReadPrevChar(ch);
+		IF ch = "." THEN
+			S.rider.ReadChar(ch);
+			i := 0;
+			WHILE (ch # 20X) & (ch # 9X) DO
+				ext[i] := ch;
+				INC(i);
+				S.rider.ReadChar(ch)
+			END;
+			ext[i] := 0X
+		ELSIF (ch # 20X) & (ch # 9X) THEN
+			W.WriteSString("Invalid character '");W.WriteChar(ch); W.WriteSString("' for file name.");
+			W.WriteLn; StdLog.text.Append(StdLog.buf); error := TRUE
+		END;
+		S.SetPos(S.rider.Pos())
+	END ParseExt;
+
+	PROCEDURE ParseModList (S: TextMappers.Scanner; end: INTEGER);
+		VAR mod: Module;
+	BEGIN
+		WHILE (S.start < end) & (S.type = TextMappers.string) DO
+			NEW(mod); mod.fileName := S.string$;
+			mod.next := modList; modList := mod;
+			S.Scan;
+			WHILE (S.start < end) & (S.type = TextMappers.char) &
+				((S.char = "*") OR (S.char = "+") OR (S.char = "$") OR (S.char = "#")) DO
+				IF S.char = "*" THEN mod.dll := TRUE
+				ELSIF S.char = "+" THEN kernel := mod
+				ELSIF S.char = "$" THEN main := mod
+				ELSE mod.intf := TRUE;
+					ASSERT(isDll, 126);
+					IF ~isDll THEN
+						W.WriteSString("Exports from Exe not possible. Use LinkDll or LinkDynDll.");
+						W.WriteLn; StdLog.text.Append(StdLog.buf); error := TRUE
+					END
+				END;
+				S.Scan
+			END
+		END
+	END ParseModList;
+
+	PROCEDURE LinkIt;
+		VAR S: TextMappers.Scanner; name, ext: Files.Name; end: INTEGER;
+	BEGIN
+		doWrite := TRUE;
+		headerstrtab.tab[0] := 0X;
+		headerstrtab.cur := 1;
+		dynstrtab.tab[0] := 0X;
+		dynstrtab.cur := 1;
+		relTextTab.cur := 0;
+		relRodataTab.cur := 0;
+		ResetHashtab;
+		ResetNeededIdx;
+		modList := NIL; kernel := NIL; main := NIL;
+		last := NIL; impg := NIL; impd := NIL;
+		firstExp := NIL; lastExp := NIL;
+		Dialog.ShowStatus("linking");
+		error := FALSE; modList := NIL;
+		IF DevCommanders.par = NIL THEN RETURN END;
+		S.ConnectTo(DevCommanders.par.text);
+		S.SetPos(DevCommanders.par.beg);
+		end := DevCommanders.par.end;
+		DevCommanders.par := NIL;
+		W.ConnectTo(StdLog.buf); S.Scan;
+		IF S.type = TextMappers.string THEN
+			name := S.string$;
+			ext := "";
+			ParseExt(S, ext); S.Scan;
+			IF ~error THEN
+				MakeSoName(name, ext);
+				IF (S.type = TextMappers.char) & (S.char = ":") THEN S.Scan;
+					IF (S.type = TextMappers.char) & (S.char = "=") THEN S.Scan;
+						ParseModList(S, end);
+						ReadHeaders;
+						soName := SHORT(name$);
+						IF ~error THEN
+							WriteOut
+						END;
+						IF ~error THEN
+							W.WriteString("Library " + name + " written: ");
+							W.WriteInt(Out.Length()); W.WriteString("    "); W.WriteInt(text.size)
+						END
+					ELSE
+						error := TRUE;
+						W.WriteString(" := missing")
+					END
+				ELSE
+					error := TRUE;
+					W.WriteString(" := missing")
+				END;
+				W.WriteLn; StdLog.text.Append(StdLog.buf)
+			END
+		END;
+		IF error THEN Dialog.ShowStatus("Failed to write library") ELSE Dialog.ShowStatus("Ok") END;
+		W.ConnectTo(NIL); S.ConnectTo(NIL);
+		modList := NIL; kernel := NIL; main := NIL; firstExp := NIL; lastExp := NIL;
+		last := NIL; impg := NIL; impd := NIL; code := NIL
+	END LinkIt;
+
+(*
+	exes are not supported
+
+	PROCEDURE Link*;
+	BEGIN
+		HALT(126);
+		isDll := FALSE; isStatic := FALSE;
+		LinkIt
+	END Link;
+	
+	PROCEDURE LinkExe*;
+	BEGIN
+		HALT(126);
+		isDll := FALSE; isStatic := TRUE;
+		LinkIt
+	END LinkExe;
+*)
+	
+	PROCEDURE LinkDll*;
+	BEGIN
+		isDll := TRUE; isStatic := TRUE;
+		LinkIt
+	END LinkDll;
+	
+	PROCEDURE LinkDynDll*;
+	BEGIN
+		isDll := TRUE; isStatic := FALSE;
+		LinkIt
+	END LinkDynDll;
+		
+BEGIN
+	newRec := "NewRec"; newArr := "NewArr"
+END DevElfLinker.
+
+LinTestSo LinTestSo2 LinKernel
+
+(!)DevElfLinker.LinkDynDll libtestbb.so := LinKernel+$ LinTestSo2 LinTestSo# ~
+(!)DevElfLinker.LinkDll libtestbb.so := LinTestSo2 LinTestSo# ~
+

+ 411 - 0
BlackBox/Dev/Mod/Selectors.txt

@@ -0,0 +1,411 @@
+MODULE DevSelectors;
+
+	(* THIS IS TEXT COPY OF BlackBox 1.6-rc6 Dev/Mod/Selectors.odc *)
+	(* DO NOT EDIT *)
+
+	IMPORT
+		Ports, Stores, Models, Views, Controllers, Fonts, Properties, TextModels, TextViews, TextSetters;
+		
+
+	CONST
+		left* = 1; middle* = 2; right* = 3;
+
+		minVersion = 0; currentVersion = 0;
+		
+		changeSelectorsKey = "#Dev:Change Selectors";
+
+
+	TYPE
+		Selector* = POINTER TO RECORD (Views.View)
+			position-: INTEGER;	(* left, middle, right *)
+			leftHidden: TextModels.Model;	(* valid iff (position = left) *)
+			rightHidden: TextModels.Model	(* valid iff (position = left) *)
+		END;
+
+		Directory* = POINTER TO ABSTRACT RECORD END;
+
+		StdDirectory = POINTER TO RECORD (Directory) END;
+		
+		
+	VAR
+		dir-, stdDir-: Directory;
+
+
+		PROCEDURE (d: Directory) New* (position: INTEGER): Selector, NEW, ABSTRACT;
+
+
+	PROCEDURE GetFirst (selector: Selector; OUT first: Selector; OUT pos: INTEGER);
+		VAR c: Models.Context; rd: TextModels.Reader; v: Views.View; nest: INTEGER;
+	BEGIN
+		c := selector.context; first := NIL; pos := 0;
+		WITH c: TextModels.Context DO
+			IF selector.position = left THEN
+				first := selector
+			ELSE
+				rd := c.ThisModel().NewReader(NIL); rd.SetPos(c.Pos());
+				nest := 1; pos := 1; rd.ReadPrevView(v);
+				WHILE (v # NIL) & (nest > 0) DO
+					WITH v: Selector DO
+						IF v.position = left THEN DEC(nest);
+							IF nest = 0 THEN first := v END
+						ELSIF v.position = right THEN INC(nest)
+						ELSIF nest = 1 THEN INC(pos)
+						END
+					ELSE
+					END;
+					rd.ReadPrevView(v)
+				END
+			END
+		ELSE (* selector not embedded in a text *)
+		END;
+		ASSERT((first = NIL) OR (first.position = left), 100)
+	END GetFirst;
+	
+	PROCEDURE GetNext (rd: TextModels.Reader; OUT next: Selector);
+		VAR nest: INTEGER; v: Views.View;
+	BEGIN
+		nest := 1; next := NIL; rd.ReadView(v);
+		WHILE v # NIL DO
+			WITH v: Selector DO
+				IF v.position = left THEN INC(nest)
+				ELSIF nest = 1 THEN next := v; RETURN
+				ELSIF v.position = right THEN DEC(nest)
+				END
+			ELSE
+			END;
+			rd.ReadView(v)
+		END
+	END GetNext;
+
+	PROCEDURE CalcSize (f: Selector; OUT w, h: INTEGER);
+		VAR c: Models.Context; a: TextModels.Attributes; font: Fonts.Font; asc, dsc, fw: INTEGER;
+	BEGIN
+		c := f.context;
+		IF (c # NIL) & (c IS TextModels.Context) THEN
+			a := c(TextModels.Context).Attr();
+			font := a.font
+		ELSE font := Fonts.dir.Default();
+		END;
+		font.GetBounds(asc, dsc, fw);
+		h := asc + dsc; w := 3 * h DIV 4
+	END CalcSize;
+
+	PROCEDURE GetSection (first: Selector; rd: TextModels.Reader; n: INTEGER; OUT name: ARRAY OF CHAR);
+		VAR i, p0, p1: INTEGER; ch: CHAR; sel: Selector;
+	BEGIN
+		sel := first;
+		IF first.leftHidden.Length() > 0 THEN
+			rd := first.leftHidden.NewReader(rd); rd.SetPos(0);
+			REPEAT p0 := rd.Pos(); GetNext(rd, sel); DEC(n) UNTIL (n < 0) OR (sel = NIL);
+			IF sel = NIL THEN INC(n) END;
+			p1 := rd.Pos() - 1
+		END;
+		IF n >= 0 THEN
+			rd := first.context(TextModels.Context).ThisModel().NewReader(rd);
+			rd.SetPos(first.context(TextModels.Context).Pos() + 1);
+			REPEAT p0 := rd.Pos(); GetNext(rd, sel); DEC(n) UNTIL (n < 0) OR (sel = NIL) OR (sel.position = right);
+			p1 := rd.Pos() - 1
+		END;
+		IF (n >= 0) & (first.rightHidden.Length() > 0) THEN
+			rd := first.rightHidden.NewReader(rd); rd.SetPos(1);
+			REPEAT p0 := rd.Pos(); GetNext(rd, sel); DEC(n) UNTIL (n < 0) OR (sel = NIL);
+			p1 := rd.Pos() - 1;
+			IF sel = NIL THEN p1 := first.rightHidden.Length() END
+		END;
+		IF n < 0 THEN
+			rd.SetPos(p0); rd.ReadChar(ch); i := 0;
+			WHILE (ch <= " ") & (rd.Pos() <= p1) DO rd.ReadChar(ch) END;
+			WHILE (i < LEN(name) - 1) & (rd.Pos() <= p1) & (ch # 0X) DO
+				IF ch >= " " THEN name[i] := ch; INC(i) END;
+				rd.ReadChar(ch)
+			END;
+			WHILE (i > 0) & (name[i - 1] = " ") DO DEC(i) END;
+			name[i] := 0X
+		ELSE
+			name := 7FX + ""
+		END
+	END GetSection;
+	
+
+	PROCEDURE ChangeSelector (first: Selector; rd: TextModels.Reader; selection: INTEGER);
+		VAR pos, p0, len, s: INTEGER; text: TextModels.Model; sel: Selector;
+	BEGIN
+		text := rd.Base();
+		pos := first.context(TextModels.Context).Pos() + 1;
+		(* expand *)
+		rd.SetPos(pos);
+		REPEAT GetNext(rd, sel) UNTIL (sel = NIL) OR (sel.position = right);
+		IF sel # NIL THEN
+			len := first.rightHidden.Length();
+			IF len > 0 THEN text.Insert(rd.Pos() - 1, first.rightHidden, 0, len) END;
+			len := first.leftHidden.Length();
+			IF len > 0 THEN text.Insert(pos, first.leftHidden, 0, len) END;
+			IF selection # 0 THEN	(* collapse *)
+				rd.SetPos(pos); s := 0;
+				REPEAT GetNext(rd, sel); INC(s) UNTIL (s = selection) OR (sel = NIL) OR (sel.position = right);
+				IF (sel # NIL) & (sel.position = middle) THEN
+					first.leftHidden.Insert(0, text, pos, rd.Pos());
+					rd.SetPos(pos); GetNext(rd, sel);
+					p0 := rd.Pos() - 1;
+					WHILE (sel # NIL) & (sel.position # right) DO GetNext(rd, sel) END;
+					IF sel # NIL THEN
+						first.rightHidden.Insert(0, text, p0, rd.Pos() - 1)
+					END
+				END
+			END
+		END;
+		rd.SetPos(pos)
+	END ChangeSelector;
+	
+	PROCEDURE ChangeThis (
+		text: TextModels.Model; rd, rd1: TextModels.Reader; title: ARRAY OF CHAR; selection: INTEGER
+	);
+		VAR v: Views.View; str: ARRAY 256 OF CHAR;
+	BEGIN
+		rd := text.NewReader(rd);
+		rd.SetPos(0); rd.ReadView(v);
+		WHILE v # NIL DO
+			WITH v: Selector DO
+				IF v.position = left THEN
+					GetSection(v, rd1, 0, str);
+					IF str = title THEN
+						ChangeSelector(v, rd, selection)
+					END;
+					IF v.leftHidden.Length() > 0 THEN ChangeThis(v.leftHidden, NIL, rd1, title, selection) END;
+					IF v.rightHidden.Length() > 0 THEN ChangeThis(v.rightHidden, NIL, rd1, title, selection) END
+				END
+			ELSE
+			END;
+			rd.ReadView(v)
+		END
+	END ChangeThis;
+	
+	PROCEDURE Change* (text: TextModels.Model; title: ARRAY OF CHAR; selection: INTEGER);
+		VAR rd, rd1: TextModels.Reader; script: Stores.Operation;
+	BEGIN
+		rd := text.NewReader(NIL);
+		rd1 := text.NewReader(NIL);
+		Models.BeginModification(Models.clean, text);
+		Models.BeginScript(text, changeSelectorsKey, script);
+		ChangeThis(text, rd, rd1, title, selection);
+		Models.EndScript(text, script);
+		Models.EndModification(Models.clean, text);
+	END Change;
+	
+	PROCEDURE ChangeTo* (text: TextModels.Model; title, entry: ARRAY OF CHAR);
+		VAR rd, rd1: TextModels.Reader; str: ARRAY 256 OF CHAR; v: Views.View; sel: INTEGER;
+	BEGIN
+		rd := text.NewReader(NIL);
+		rd1 := text.NewReader(NIL);
+		rd.SetPos(0); rd.ReadView(v);
+		WHILE v # NIL DO
+			WITH v: Selector DO
+				IF v.position = left THEN
+					GetSection(v, rd1, 0, str);
+					IF title = str THEN
+						sel := 0;
+						REPEAT
+							INC(sel); GetSection(v, rd1, sel, str)
+						UNTIL (str[0] = 7FX) OR (str = entry);
+						IF str[0] # 7FX THEN
+							Change(text, title, sel);
+							RETURN
+						END
+					END
+				END
+			ELSE
+			END;
+			rd.ReadView(v)
+		END
+	END ChangeTo;
+
+
+	PROCEDURE (selector: Selector) HandlePropMsg- (VAR msg: Properties.Message);
+		VAR c: Models.Context; a: TextModels.Attributes; asc, w: INTEGER;
+	BEGIN
+		WITH msg: Properties.SizePref DO CalcSize(selector, msg.w, msg.h)
+		| msg: Properties.ResizePref DO msg.fixed := TRUE;
+		| msg: Properties.FocusPref DO msg.hotFocus := TRUE;
+		| msg: TextSetters.Pref DO c := selector.context;
+			IF (c # NIL) & (c IS TextModels.Context) THEN
+				a := c(TextModels.Context).Attr();
+				a.font.GetBounds(asc, msg.dsc, w)
+			END
+		ELSE (*selector.HandlePropMsg^(msg);*)
+		END
+	END HandlePropMsg;
+
+	PROCEDURE Track (selector: Selector; f: Views.Frame; x, y: INTEGER; buttons: SET; VAR hit: BOOLEAN);
+		VAR a: TextModels.Attributes; font: Fonts.Font; c: Models.Context;
+			w, h, asc, dsc, fw: INTEGER; isDown, in, in0: BOOLEAN; modifiers: SET;
+	BEGIN
+		c := selector.context; hit := FALSE;
+		WITH c: TextModels.Context DO
+			a := c.Attr(); font := a.font;
+			c.GetSize(w, h); in0 := FALSE;
+			in := (0 <= x) & (x < w) & (0 <= y) & (y < h);
+			REPEAT
+				IF in # in0 THEN
+					f.MarkRect(0, 0, w, h, Ports.fill, Ports.hilite, FALSE); in0 := in
+				END;
+				f.Input(x, y, modifiers, isDown);
+				in := (0 <= x) & (x < w) & (0 <= y) & (y < h)
+			UNTIL ~isDown;
+			IF in0 THEN hit := TRUE;
+				font.GetBounds(asc, dsc, fw);
+				f.MarkRect(0, 0, w, asc + dsc, Ports.fill, Ports.hilite, FALSE);
+			END
+		ELSE
+		END
+	END Track;
+
+	PROCEDURE (selector: Selector) HandleCtrlMsg* (
+		f: Views.Frame; VAR msg: Views.CtrlMessage; VAR focus: Views.View
+	);
+		VAR hit: BOOLEAN; sel, pos: INTEGER; text: TextModels.Model; title: ARRAY 256 OF CHAR; first: Selector;
+	BEGIN
+		WITH msg: Controllers.TrackMsg DO
+			IF selector.context IS TextModels.Context THEN
+				Track(selector, f, msg.x, msg.y, msg.modifiers, hit);
+				IF hit THEN
+					text := selector.context(TextModels.Context).ThisModel();
+					GetFirst(selector, first, pos);
+					IF first # NIL THEN
+						GetSection(first, NIL, 0, title);
+						IF selector.position = middle THEN sel := pos ELSE sel := 0 END;
+						Change(text, title, sel);
+						text := selector.context(TextModels.Context).ThisModel();
+						IF TextViews.FocusText() = text THEN
+							pos := selector.context(TextModels.Context).Pos();
+							TextViews.ShowRange(text, pos, pos+1, TRUE)
+						END
+					END
+				END
+			END
+		| msg: Controllers.PollCursorMsg DO
+			msg.cursor := Ports.refCursor;
+		ELSE
+		END
+	END HandleCtrlMsg;
+
+	PROCEDURE (selector: Selector) Restore* (f: Views.Frame; l, t, r, b: INTEGER);
+		VAR w, h, d: INTEGER;
+	BEGIN
+		selector.context.GetSize(w, h);
+(*
+		GetFirst(selector, first, pos);
+*)
+		w := w - w MOD f.unit; d := 2 * f.dot;
+		f.DrawLine(d, d, w - d, d, d, Ports.grey25);
+		f.DrawLine(d, h - d, w - d, h - d, d, Ports.grey25);
+		IF selector.position # right THEN f.DrawLine(d, d, d, h - d, d, Ports.grey25) END;
+		IF selector.position # left THEN f.DrawLine(w - d, d, w - d, h - d, d, Ports.grey25) END
+	END Restore;
+
+	PROCEDURE (selector: Selector) CopyFromSimpleView- (source: Views.View);
+	BEGIN
+		(* selector.CopyFrom^(source); *)
+		WITH source: Selector DO
+			selector.position := source.position;
+			IF source.leftHidden # NIL THEN
+				selector.leftHidden := TextModels.CloneOf(source.leftHidden);
+				selector.leftHidden.InsertCopy(0, source.leftHidden, 0, source.leftHidden.Length())
+			END;
+			IF source.rightHidden # NIL THEN
+				selector.rightHidden := TextModels.CloneOf(source.rightHidden);
+				selector.rightHidden.InsertCopy(0, source.rightHidden, 0, source.rightHidden.Length())
+			END
+		END
+	END CopyFromSimpleView;
+
+	PROCEDURE (selector: Selector) InitContext* (context: Models.Context);
+	BEGIN
+		selector.InitContext^(context);
+		IF selector.position = left THEN
+			WITH context: TextModels.Context DO
+				IF selector.leftHidden = NIL THEN 
+					selector.leftHidden := TextModels.CloneOf(context.ThisModel());
+					Stores.Join(selector, selector.leftHidden);
+				END;
+				IF selector.rightHidden = NIL THEN
+					selector.rightHidden := TextModels.CloneOf(context.ThisModel());
+					Stores.Join(selector, selector.rightHidden)
+				END
+			ELSE
+			END
+		END
+	END InitContext;
+	
+	PROCEDURE (selector: Selector) Internalize- (VAR rd: Stores.Reader);
+		VAR version: INTEGER; store: Stores.Store;
+	BEGIN
+		selector.Internalize^(rd);
+		IF rd.cancelled THEN RETURN END;
+		rd.ReadVersion(minVersion, currentVersion, version);
+		IF rd.cancelled THEN RETURN END;
+		rd.ReadInt(selector.position);
+		rd.ReadStore(store);
+		IF store # NIL THEN selector.leftHidden := store(TextModels.Model)
+		ELSE selector.leftHidden := NIL
+		END;
+		rd.ReadStore(store);
+		IF store # NIL THEN selector.rightHidden := store(TextModels.Model)
+		ELSE selector.rightHidden := NIL
+		END
+	END Internalize;
+
+	PROCEDURE (selector: Selector) Externalize- (VAR wr: Stores.Writer);
+	BEGIN
+		selector.Externalize^(wr);
+		wr.WriteVersion(currentVersion);
+		wr.WriteInt(selector.position);
+		wr.WriteStore(selector.leftHidden);
+		wr.WriteStore(selector.rightHidden)
+	END Externalize;
+
+
+	PROCEDURE (d: StdDirectory) New (position: INTEGER): Selector;
+		VAR selector: Selector;
+	BEGIN
+		NEW(selector);
+		selector.position := position;
+		RETURN selector
+	END  New;
+
+	PROCEDURE SetDir* (d: Directory);
+	BEGIN
+		ASSERT(d # NIL, 20);
+		dir := d
+	END SetDir;
+	
+
+	PROCEDURE DepositLeft*;
+	BEGIN
+		Views.Deposit(dir.New(left))
+	END DepositLeft;
+
+	PROCEDURE DepositMiddle*;
+	BEGIN
+		Views.Deposit(dir.New(middle))
+	END DepositMiddle;
+
+	PROCEDURE DepositRight*;
+	BEGIN
+		Views.Deposit(dir.New(right))
+	END DepositRight;
+
+
+	PROCEDURE InitMod;
+		VAR d: StdDirectory;
+	BEGIN
+		NEW(d); dir := d; stdDir := d;
+	END InitMod;
+
+BEGIN
+	InitMod
+END DevSelectors.
+
+
+	"Insert Left"	"*F5"	"DevSelectors.DepositLeft; StdCmds.PasteView"	"StdCmds.PasteViewGuard"
+	"Insert Middle"	"*F6"	"DevSelectors.DepositMiddle; StdCmds.PasteView"	"StdCmds.PasteViewGuard"
+	"Insert Right"	"*F7"	"DevSelectors.DepositRight; StdCmds.PasteView"	"StdCmds.PasteViewGuard"

+ 25 - 5
BlackBox/Init-Interp.txt

@@ -1,10 +1,30 @@
 MODULE Init;
 
-	IMPORT Interp, HostFonts (* required for Texts *), HostWindows (* required for Windows *), HostDates (* for Dates.SetHook *), HostDialog (* required for Dialog.SetShowHook *), StdLog, StdDialog (* required for Views *), Converters (* .odc *);
+	IMPORT
+		LinConsole,
+
+		HostFonts (* Fonts.SetHook; required for Texts *),
+		HostWindows (* Windows.SetHook *),
+		HostDates (* Dates.SetHook *),
+		HostDialog (* Dialog.SetShowHook *),
+		StdLog,
+		StdInterpreter, (* Dialog.SetCallHook *)
+		StdDialog (* Views.SetViewHook *),
+
+		Converters (* .odc *),
+		Dialog,
+
+		TextViews, TextControllers;
+
+	PROCEDURE Init*;
+		VAR res: INTEGER;
+	BEGIN
+		(* StdLog.Open; *)
+		Converters.Register("Documents.ImportDocument", "Documents.ExportDocument", "", "odc", {});
+		(* Converters.Register("StdETHConv.ImportETHDoc", "", "TextViews.View", "eth", {Converters.importAll}); *)
+		Dialog.Call("Interp.Run", " ", res)
+	END Init;
 
 BEGIN
-	(* StdLog.Open; *)
-	Converters.Register("Documents.ImportDocument", "Documents.ExportDocument", "", "odc", {});
-	Converters.Register("StdETHConv.ImportETHDoc", "", "TextViews.View", "eth", {Converters.importAll});
-	Interp.Init
+	Init
 END Init.

+ 66 - 17
BlackBox/Interp.txt

@@ -2,18 +2,73 @@ MODULE Interp;
 
 	(*
 		A. V. Shiryaev, 2012.09
-
-		(Std)Interpreter on (Lin)Console
 	*)
 
-	IMPORT Console, LinConsole (* required *), Strings, Dialog, StdInterpreter (* required (Dialog.SetCallHook) *);
+	IMPORT
+		Console,
+		Strings, Dialog,
+		DevCommanders, TextModels;
+
+	PROCEDURE Call1 (IN s: ARRAY OF CHAR; i: INTEGER): BOOLEAN;
+		VAR j: INTEGER;
+			res: INTEGER;
+			par: DevCommanders.Par;
+			m: TextModels.Model; w: TextModels.Writer;
+	BEGIN
+		(* ASSERT 0X in s[ i:LEN(s) ) *)
+		j := i;
+		WHILE s[j] # 0X DO INC(j) END;
+		IF j > i THEN
+			m := TextModels.dir.New();
+			w := m.NewWriter(NIL);
+			WHILE i < j DO
+				w.WriteChar(s[i]);
+				INC(i)
+			END;
+			NEW(par); par.text := m; par.beg := 0; par.end := m.Length() - 1;
+			DevCommanders.par := par
+		END;
+		Dialog.Call(s, " ", res);
+		DevCommanders.par := NIL;
+	RETURN res = 0
+	END Call1;
 
-	PROCEDURE WriteInt (x: INTEGER);
-		VAR s: ARRAY 16 OF CHAR;
+	PROCEDURE Call0 (VAR s: ARRAY OF CHAR): BOOLEAN;
+		VAR i: INTEGER;
+			res: BOOLEAN;
+			inStr: BOOLEAN;
 	BEGIN
-		Strings.IntToString(x, s);
-		Console.WriteStr(s)
-	END WriteInt;
+		(* ASSERT s is 0X terminated and not empty *)
+		i := 0;
+		WHILE (s[i] # 0X) & (s[i] # ' ') & (s[i] # '(') DO
+			INC(i)
+		END;
+		IF s[i] = 0X THEN
+			res := Call1(s, i)
+		ELSIF s[i] = ' ' THEN
+			s[i] := 0X;
+			res := Call1(s, i + 1)
+		ELSE (* s[i] = '(' *)
+			INC(i);
+			inStr := FALSE;
+			WHILE (s[i] # 0X) & ~(~inStr & (s[i] = ')')) DO
+				IF s[i] = "'" THEN inStr := ~inStr END;
+				INC(i)
+			END;
+			IF s[i] # 0X THEN
+				INC(i);
+				IF s[i] = 0X THEN
+					res := Call1(s, i)
+				ELSE
+					s[i] := 0X;
+					res := Call1(s, i + 1)
+				END
+			ELSE
+				res := FALSE
+			END
+		END;
+	RETURN res
+	END Call0;
 
 	PROCEDURE Call (VAR s: ARRAY OF CHAR): BOOLEAN;
 		VAR i: INTEGER;
@@ -26,13 +81,7 @@ MODULE Interp;
 		IF (i < LEN(s)) & (s[i] # 0X) THEN
 			IF (i > 0) & (s[0] # '#') THEN
 				s[i] := 0X;
-				Dialog.Call(s, " ", i);
-				IF i = 0 THEN
-					res := TRUE
-				ELSE
-					(* WriteInt(i); Console.WriteLn; *)
-					res := FALSE (* stop on Dialog.Call error *)
-				END
+				res := Call0(s)
 			ELSE (* skip empty strings and comments *)
 				res := TRUE
 			END
@@ -42,13 +91,13 @@ MODULE Interp;
 	RETURN res
 	END Call;
 
-	PROCEDURE Init*;
+	PROCEDURE Run*;
 		VAR s: ARRAY 1024 OF CHAR;
 	BEGIN
 		Console.ReadLn(s);
 		WHILE Call(s) DO
 			Console.ReadLn(s)
 		END
-	END Init;
+	END Run;
 
 END Interp.

+ 54 - 0
BlackBox/Lindev/Mod/Interp.txt

@@ -0,0 +1,54 @@
+MODULE LindevInterp;
+
+	(*
+		A. V. Shiryaev, 2012.09
+
+		(Std)Interpreter on (Lin)Console
+	*)
+
+	IMPORT Console, LinConsole (* required *), Strings, Dialog, StdInterpreter (* required (Dialog.SetCallHook) *);
+
+	PROCEDURE WriteInt (x: INTEGER);
+		VAR s: ARRAY 16 OF CHAR;
+	BEGIN
+		Strings.IntToString(x, s);
+		Console.WriteStr(s)
+	END WriteInt;
+
+	PROCEDURE Call (VAR s: ARRAY OF CHAR): BOOLEAN;
+		VAR i: INTEGER;
+			res: BOOLEAN;
+	BEGIN
+		i := 0;
+		WHILE (i < LEN(s)) & (s[i] # 0AX) & (s[i] # 0DX) & (s[i] # 0X) DO
+			INC(i)
+		END;
+		IF (i < LEN(s)) & (s[i] # 0X) THEN
+			IF (i > 0) & (s[0] # '#') THEN
+				s[i] := 0X;
+				Dialog.Call(s, "", i);
+				IF i = 0 THEN
+					res := TRUE
+				ELSE
+					WriteInt(i); Console.WriteLn;
+					res := FALSE (* stop on Dialog.Call error *)
+				END
+			ELSE (* skip empty strings and comments *)
+				res := TRUE
+			END
+		ELSE (* end of input *)
+			res := FALSE
+		END;
+	RETURN res
+	END Call;
+
+	PROCEDURE Init*;
+		VAR s: ARRAY 1024 OF CHAR;
+	BEGIN
+		Console.ReadLn(s);
+		WHILE Call(s) DO
+			Console.ReadLn(s)
+		END
+	END Init;
+
+END LindevInterp.

+ 108 - 10
BlackBox/StdLog.txt

@@ -2,28 +2,126 @@ MODULE StdLog;
 
 	(* for Dev *)
 
-	IMPORT Console, Strings;
+	IMPORT Console, Strings, TextModels, TextMappers;
+
+	VAR
+		text-, buf-: TextModels.Model;
+		out: TextMappers.Formatter;
+		textR: TextModels.Reader;
+
+	PROCEDURE Flush;
+		VAR c: CHAR;
+	BEGIN
+		text.Append(buf);
+
+		textR.SetPos(0);
+		textR.ReadChar(c);
+		WHILE ~textR.eot DO
+			IF c = 0DX THEN c := 0AX END;
+			Console.WriteChar(c);
+			textR.ReadChar(c)
+		END;
+		text.Delete(0, text.Length())
+	END Flush;
 
 	PROCEDURE Char* (c: CHAR);
 	BEGIN
-		Console.WriteChar(c)
+		out.WriteChar(c); Flush
 	END Char;
 
-	PROCEDURE String* (IN s: ARRAY OF CHAR);
+	PROCEDURE Int* (i: LONGINT);
+	BEGIN
+		out.WriteChar(" "); out.WriteInt(i); Flush
+	END Int;
+
+	PROCEDURE Real* (x: REAL);
+	BEGIN
+		out.WriteChar(" "); out.WriteReal(x); Flush
+	END Real;
+
+	PROCEDURE String* (IN str: ARRAY OF CHAR);
 	BEGIN
-		Console.WriteStr(s)
+		out.WriteString(str); Flush
 	END String;
 
-	PROCEDURE Int* (x: LONGINT);
-		VAR s: ARRAY 32 OF CHAR;
+	PROCEDURE Bool* (x: BOOLEAN);
 	BEGIN
-		Strings.IntToString(x, s);
-		Console.WriteStr(s)
-	END Int;
+		out.WriteChar(" "); out.WriteBool(x); Flush
+	END Bool;
+
+	PROCEDURE Set* (x: SET);
+	BEGIN
+		out.WriteChar(" "); out.WriteSet(x); Flush
+	END Set;
+
+	PROCEDURE IntForm* (x: LONGINT; base, minWidth: INTEGER; fillCh: CHAR; showBase: BOOLEAN);
+	BEGIN
+		out.WriteIntForm(x, base, minWidth, fillCh, showBase); Flush
+	END IntForm;
+
+	PROCEDURE RealForm* (x: REAL; precision, minW, expW: INTEGER; fillCh: CHAR);
+	BEGIN
+		out.WriteRealForm(x, precision, minW, expW, fillCh); Flush
+	END RealForm;
+
+	PROCEDURE Tab*;
+	BEGIN
+		out.WriteTab; Flush
+	END Tab;
 
 	PROCEDURE Ln*;
 	BEGIN
-		Console.WriteLn
+		out.WriteLn; Flush;
+		(* TextViews.ShowRange(text, text.Length(), text.Length(), TextViews.any) *)
 	END Ln;
 
+	PROCEDURE Para*;
+	BEGIN
+		out.WritePara; Flush;
+		(* TextViews.ShowRange(text, text.Length(), text.Length(), TextViews.any) *)
+	END Para;
+
+(*
+	PROCEDURE View* (v: Views.View);
+	BEGIN
+		out.WriteView(v); Flush
+	END View;
+
+	PROCEDURE ViewForm* (v: Views.View; w, h: INTEGER);
+	BEGIN
+		out.WriteViewForm(v, w, h); Flush
+	END ViewForm;
+*)
+
+	PROCEDURE ParamMsg* (IN msg, p0, p1, p2: ARRAY OF CHAR);
+	BEGIN
+		out.WriteParamMsg(msg, p0, p1, p2); Flush
+	END ParamMsg;
+
+	PROCEDURE Msg* (IN msg: ARRAY OF CHAR);
+	BEGIN
+		out.WriteMsg(msg); Flush
+	END Msg;
+
+
+	PROCEDURE Open*;
+	BEGIN
+	END Open;
+
+	PROCEDURE Clear*;
+	BEGIN
+		text.Delete(0, text.Length());
+		buf.Delete(0, buf.Length());
+	END Clear;
+
+	PROCEDURE Init;
+	BEGIN
+		text := TextModels.dir.New();
+		textR := text.NewReader(NIL);
+		buf := TextModels.CloneOf(text);
+		out.ConnectTo(buf)
+	END Init;
+
+BEGIN
+	Init
 END StdLog.

+ 12 - 28
BlackBox/build

@@ -1,15 +1,19 @@
 #!/bin/sh
 
-./run-interp <<DATA
+./run-lindev <<DATA
 LindevCompiler.Compile('Lin/Mod', 'Obsd.Dl.txt')
 LindevCompiler.Compile('Lin/Mod', 'Obsd.Libc.txt')
 LindevCompiler.Compile('Lin/Mod', 'Obsd.linKernel.txt')
 
 LindevCompiler.Compile('System/Mod', 'Files.txt')
-LindevCompiler.Compile('System/Mod', 'Dialog.txt')
+LindevCompiler.Compile('System/Mod', 'Console.txt')
 LindevCompiler.Compile('System/Mod', 'Math.txt')
 LindevCompiler.Compile('System/Mod', 'Strings.txt')
 LindevCompiler.Compile('System/Mod', 'Meta.txt')
+LindevCompiler.Compile('System/Mod', 'Dialog.txt')
+
+LindevCompiler.Compile('Lin/Mod', 'Console.txt')
+LindevCompiler.Compile('Lin/Mod', 'Obsd.linHostFiles.txt')
 
 LindevCompiler.Compile('System/Mod', 'Stores.txt')
 LindevCompiler.Compile('System/Mod', 'Converters.txt')
@@ -67,26 +71,13 @@ LindevCompiler.Compile('System/Mod', 'Console.txt')
 LindevCompiler.Compile('Lin/Mod', 'Console.txt')
 LindevCompiler.Compile('Lin/Mod', 'Kernel_so_init.txt')
 
-LindevCompiler.Compile('Lindev/Mod', 'CPM.txt')
-LindevCompiler.Compile('Lindev/Mod', 'CPT.txt')
-LindevCompiler.Compile('Lindev/Mod', 'CPS.txt')
-LindevCompiler.Compile('Lindev/Mod', 'CPH.txt')
-LindevCompiler.Compile('Lindev/Mod', 'CPB.txt')
-LindevCompiler.Compile('Lindev/Mod', 'CPP.txt')
-LindevCompiler.Compile('Lindev/Mod', 'CPE.txt')
-LindevCompiler.Compile('Lindev/Mod', 'CPL486.txt')
-LindevCompiler.Compile('Lindev/Mod', 'CPC486.txt')
-LindevCompiler.Compile('Lindev/Mod', 'CPV486.txt')
-
-LindevCompiler.Compile('Lindev/Mod', 'Compiler.txt')
-LindevCompiler.Compile('Lindev/Mod', 'ElfLinker16.txt')
-
 # LindevCompiler.Compile('Std/Mod', 'Log.txt')
 LindevCompiler.Compile('', 'StdLog.txt')
 # LindevCompiler.Compile('Std/Mod', 'Out.txt')
 
 LindevCompiler.Compile('Dev/Mod', 'Markers.txt')
-# LindevCompiler.Compile('Dev/Mod', 'Commanders.txt')
+LindevCompiler.Compile('Dev/Mod', 'Commanders.txt')
+LindevCompiler.Compile('Dev/Mod', 'Selectors.txt')
 LindevCompiler.Compile('Dev/Mod', 'CPM.txt')
 LindevCompiler.Compile('Dev/Mod', 'CPT.txt')
 LindevCompiler.Compile('Dev/Mod', 'CPH.txt')
@@ -97,18 +88,9 @@ LindevCompiler.Compile('Dev/Mod', 'CPP.txt')
 LindevCompiler.Compile('Dev/Mod', 'CPL486.txt')
 LindevCompiler.Compile('Dev/Mod', 'CPC486.txt')
 LindevCompiler.Compile('Dev/Mod', 'CPV486.txt')
+LindevCompiler.Compile('Dev/Mod', 'Compiler.txt')
+LindevCompiler.Compile('Dev/Mod', 'ElfLinker16.txt')
 
-### simple dev interpreter (include LindevCompiler and LindevElfLinker)
-
-LindevCompiler.Compile('', 'Views.txt')
-LindevCompiler.Compile('Std/Mod', 'Interpreter.txt')
-
-LindevCompiler.Compile('', 'Interp.txt')
-
-# LindevElfLinker.LinkDll('libBBInterp.so := Kernel+ Kernel_so_init# Console Math Strings LinConsole Files HostFiles LindevCPM LindevCPT LindevCPS LindevCPH LindevCPB LindevCPP LindevCPE LindevCPL486 LindevCPC486 LindevCPV486 LindevCompiler LindevElfLinker Dialog Meta Views StdInterpreter Interp#')
-LindevElfLinker.LinkDll('libBBInterp.so := Kernel+ Console Math Strings LinConsole Files HostFiles LindevCPM LindevCPT LindevCPS LindevCPH LindevCPB LindevCPP LindevCPE LindevCPL486 LindevCPC486 LindevCPV486 LindevCompiler LindevElfLinker Dialog Meta Views StdInterpreter Interp#')
-
-LindevCompiler.Compile('System/Mod', 'Views.txt')
 LindevCompiler.Compile('Std/Mod', 'Interpreter.txt')
 
 ### BlackBox
@@ -119,6 +101,8 @@ LindevCompiler.Compile('', 'HostWindows.txt')
 # HostDates:
 LindevCompiler.Compile('Lin/Mod', 'Dates.txt')
 
+LindevCompiler.Compile('', 'Interp.txt')
+
 LindevCompiler.Compile('', 'Init-Interp.txt')
 
 LindevElfLinker.LinkDll('libBB.so := Kernel+ Files HostFiles StdLoader')

+ 41 - 0
BlackBox/build-lindev

@@ -0,0 +1,41 @@
+#!/bin/sh
+
+./run-lindev <<DATA
+LindevCompiler.Compile('Lin/Mod', 'Obsd.Dl.txt')
+LindevCompiler.Compile('Lin/Mod', 'Obsd.Libc.txt')
+LindevCompiler.Compile('Lin/Mod', 'Obsd.linKernel.txt')
+
+LindevCompiler.Compile('System/Mod', 'Files.txt')
+LindevCompiler.Compile('System/Mod', 'Console.txt')
+LindevCompiler.Compile('System/Mod', 'Math.txt')
+LindevCompiler.Compile('System/Mod', 'Strings.txt')
+LindevCompiler.Compile('System/Mod', 'Meta.txt')
+LindevCompiler.Compile('System/Mod', 'Dialog.txt')
+
+LindevCompiler.Compile('Lindev/Mod', 'CPM.txt')
+LindevCompiler.Compile('Lindev/Mod', 'CPT.txt')
+LindevCompiler.Compile('Lindev/Mod', 'CPS.txt')
+LindevCompiler.Compile('Lindev/Mod', 'CPH.txt')
+LindevCompiler.Compile('Lindev/Mod', 'CPB.txt')
+LindevCompiler.Compile('Lindev/Mod', 'CPP.txt')
+LindevCompiler.Compile('Lindev/Mod', 'CPE.txt')
+LindevCompiler.Compile('Lindev/Mod', 'CPL486.txt')
+LindevCompiler.Compile('Lindev/Mod', 'CPC486.txt')
+LindevCompiler.Compile('Lindev/Mod', 'CPV486.txt')
+
+LindevCompiler.Compile('Lindev/Mod', 'Compiler.txt')
+LindevCompiler.Compile('Lindev/Mod', 'ElfLinker16.txt')
+
+LindevCompiler.Compile('Lin/Mod', 'Console.txt')
+LindevCompiler.Compile('Lin/Mod', 'Obsd.linHostFiles.txt')
+
+### simple dev interpreter (include LindevCompiler and LindevElfLinker)
+
+LindevCompiler.Compile('', 'Views.txt')
+LindevCompiler.Compile('Std/Mod', 'Interpreter.txt')
+
+LindevCompiler.Compile('Lindev/Mod', 'Interp.txt')
+
+# LindevElfLinker.LinkDll('libBBInterp.so := Kernel+ Kernel_so_init# Console Math Strings LinConsole Files HostFiles LindevCPM LindevCPT LindevCPS LindevCPH LindevCPB LindevCPP LindevCPE LindevCPL486 LindevCPC486 LindevCPV486 LindevCompiler LindevElfLinker Dialog Meta Views StdInterpreter Interp#')
+LindevElfLinker.LinkDll('libBB0.so := Kernel+ Console Math Strings LinConsole Files HostFiles LindevCPM LindevCPT LindevCPS LindevCPH LindevCPB LindevCPP LindevCPE LindevCPL486 LindevCPC486 LindevCPV486 LindevCompiler LindevElfLinker Dialog Meta Views StdInterpreter LindevInterp#')
+DATA

+ 0 - 1
BlackBox/interp

@@ -1 +0,0 @@
-../c/interp

+ 1 - 0
BlackBox/lindev

@@ -0,0 +1 @@
+../c/lindev

+ 1 - 0
BlackBox/run-lindev

@@ -0,0 +1 @@
+../c/run-lindev


+ 4 - 4
c/Makefile

@@ -1,10 +1,10 @@
-all: interp BlackBox
+all: lindev BlackBox
 
 BlackBox: BlackBox.c openbsd.c
 	${CC} ${CFLAGS} -O0 -g -o ${.TARGET} ${.ALLSRC} -L. -lBB -Wl,-E
 
-interp: BlackBox1.c openbsd.c
-	${CC} ${CFLAGS} -O0 -g -o ${.TARGET} ${.ALLSRC} -L . -lBBInterp
+lindev: BlackBox1.c openbsd.c
+	${CC} ${CFLAGS} -O0 -g -o ${.TARGET} ${.ALLSRC} -L . -lBB0
 
 clean:
-	rm -f interp BlackBox
+	rm -f lindev BlackBox

+ 1 - 0
c/libBB0.so

@@ -0,0 +1 @@
+../BlackBox/libBB0.so


+ 0 - 4
c/run-interp

@@ -1,4 +0,0 @@
-#!/bin/sh
-
-# env LD_LIBRARY_PATH=. env LD_DEBUG=1 ./interp
-env LD_LIBRARY_PATH=. ./interp

+ 4 - 0
c/run-lindev

@@ -0,0 +1,4 @@
+#!/bin/sh
+
+# env LD_LIBRARY_PATH=. env LD_DEBUG=1 ./lindev
+env LD_LIBRARY_PATH=. ./lindev