DEADSOFTWARE

First tiny steps to implementing Oberon Compound Document (.ODC) reader
[odcread.git] / odcread.cc
1 #include <iostream>
2 #include <fstream>
3 #include <stdint.h>
5 namespace odc {
6 /**
7 * INTEGER: 4 bytes (-2147483648..2147483647)
8 */
9 typedef int32_t INTEGER;
11 bool isBigEndian() { // http://stackoverflow.com/questions/1001307/detecting-endianness-programmatically-in-a-c-program
12 union {
13 uint32_t i;
14 uint8_t c[4];
15 } test = {0x01020304};
16 return test.c[0] == 1;
17 }
18 bool isLittleEndian() {
19 return !isBigEndian();
20 }
22 class Domain {
23 };
25 class Reader;
27 /**
28 * TYPE Store
29 * ABSTRACT
30 * Storable extensible data types like Views.View or TextModels.Text are derived from Store.
31 * Stores are typically allocated by suitable directories, e.g., Views.Directory or TextModels.Directory.
32 * Stores are used as base types for all objects that must be both extensible and persistent.
33 */
34 class Store {
35 public:
36 /**
37 * PROCEDURE (s: Store) Domain (): Domain
38 * NEW
39 * A store may be associated with a domain. This is done by the procedure InitDomain, which assigns a domain to the store.
40 * Domain may be called by arbitrary clients.
41 */
42 Domain* getDomain() {
43 return 0;
44 }
46 /**
47 * PROCEDURE (s: Store) CopyFrom- (source: Store)
48 * NEW, EMPTY
49 * Copy the contents of source to s. Copying is a deep copy.
50 *
51 * Pre
52 * source # NIL guaranteed
53 * TYP(source) = TYP(s) guaranteed
54 * s.Domain() = NIL guaranteed
55 * s is not yet initialized guaranteed
56 */
57 // FIXME
58 /**
59 * PROCEDURE (s: Store) Internalize- (VAR rd: Reader)
60 * NEW, EMPTY
61 * (For backward compatibility, this method is actually still EXTENSIBLE. This may change in the future.)
62 * Reads the contents of s from reader rd. Internalize must read the same (amount of) data as is written by the corresponding Externalize procedure.
63 * Internalize is called locally.
64 * Internalize is extended by various persistent object types, e.g., models, views, and controllers.
65 *
66 * Pre
67 * source.Domain() = NIL guaranteed
68 * source is not yet initialized guaranteed
69 */
70 void internalize(Reader &reader) {
71 // PROCEDURE (s: Store) Internalize- (VAR rd: Reader), NEW, EXTENSIBLE;
72 // VAR thisVersion: INTEGER;
73 // BEGIN
74 // rd.ReadVersion(minVersion, maxStoreVersion, thisVersion);
75 // IF ~rd.cancelled & s.isElem THEN
76 // rd.ReadVersion(minVersion, maxStoreVersion, thisVersion)
77 // (* works since maxStoreVersion = maxElemVersion = 0 in pre-1.3 *)
78 // END
79 // END Internalize;
80 }
82 /**
83 * PROCEDURE (s: Store) Externalize- (VAR wr: Writer)
84 * NEW, EMPTY
85 * (For backward compatibility, this method is actually still EXTENSIBLE. This may change in the future.)
86 * Write the contents of s to writer wr. Externalize must write the same (amount of) data as is read by the corresponding Internalize procedure.
87 * Externalize ist called locally.
88 * Externalize is extended by various persistent object types, e.g., models, views, and controllers.
89 */
90 // FIXME
92 /**
93 * PROCEDURE (s: Store) ExternalizeAs- (VAR s1: Store)
94 * NEW, EMPTY
95 * Before a store's Externalize procedure is called, its ExternalizeAs procedure is called, which gives the store the opportunity to denote another store that should be externalized in its place (a "proxy"). It is also possible to set s1 to NIL, which means that the store should not be externalized at all. This is used e.g. for compiler error markers, which are never stored.
96 * ExternalizeAs ist called locally.
97 *
98 * Pre
99 * s1 = s guaranteed
100 */
101 // FIXME
102 };
104 /**
105 * TYPE Reader
106 * Reader for Component Pascal values like integers, reals, or sets. A reader contains a Files.Reader, to which it forwards most operations.
107 * Readers are used in the Store.Internalize procedure.
108 * Readers are not extensible.
109 */
110 class Reader {
111 private:
112 /*
113 * rider-: Files.Reader
114 * The file rider which links a Reader to a file.
115 */
116 std::istream &d_rider;
118 /*
119 * cancelled-: BOOLEAN valid during a Store.Internalize call
120 * Tells whether the currently executing Internalize has been called by ReadVersion or TurnIntoAlien.
121 */
122 bool d_cancelled;
124 /**
125 * readAlien-: BOOLEAN
126 * Tells whether any alien has been read since the last ConnectTo.
127 */
128 bool d_readAlien;
130 public:
131 Reader(std::istream &rider): d_rider(rider), d_cancelled(false), d_readAlien(false) {}
133 /**
134 * PROCEDURE (VAR rd: Reader) ConnectTo (f: Files.File)
135 * NEW
136 * Connect the reader to a file. All the following operations require connected readers, i.e., rd.rider # NIL. This precondition is not checked explicitly, however. After connecting, the reader's position is at the beginning of the file. If the same reader should be reused on another file, it must first be closed, by connecting it to NIL.
137 * ConnectTo is used internally.
138 *
139 * Pre
140 * 20 (f = NIL) OR (rd.rider = NIL)
141 *
142 * Post
143 * f = NIL
144 * rd.rider = NIL
145 * f # NIL
146 * (rd.rider # NIL) & (rd.rider.Base() = f)
147 * rd.Pos() = 0
148 */
149 // FIXME
151 /**
152 * PROCEDURE (VAR rd: Reader) Pos (): INTEGER
153 * NEW
154 * Returns the reader's current position.
155 *
156 * Post
157 * 0 <= result <= rd.rider.Base().Length()
158 */
159 // FIXME
161 /**
162 * PROCEDURE (VAR rd: Reader) SetPos (pos: INTEGER)
163 * NEW
164 * Sets the reader's current position to pos.
165 *
166 * Pre
167 * 20 pos >= 0
168 * 21 pos <= rd.rider.Base().Length()
169 *
170 * Post
171 * rd.Pos() = pos
172 * ~rd.rider.eof
173 */
174 // FIXME
176 /**
177 * PROCEDURE (VAR rd: Reader) ReadBool (OUT x: BOOLEAN)
178 * NEW
179 * Reads a Boolean value.
180 *
181 * PROCEDURE (VAR rd: Reader) ReadSChar (OUT x: SHORTCHAR)
182 * NEW
183 * Reads a short character (00X..0FFX).
184 *
185 * PROCEDURE (VAR rd: Reader) ReadXChar (OUT x: CHAR)
186 * NEW
187 * Same as ReadSChar, but has a CHAR-type parameter.
188 * This procedure is provided to simplify migration from Release 1.2 to 1.3.
189 *
190 * PROCEDURE (VAR rd: Reader) ReadChar (OUT x: CHAR)
191 * NEW
192 * Reads a character (0000X..0FFFFX).
193 *
194 * PROCEDURE (VAR rd: Reader) ReadByte (OUT x: BYTE)
195 * NEW
196 * Reads a very short integer (-128..127).
197 *
198 * PROCEDURE (VAR rd: Reader) ReadSInt (OUT x: SHORTINT)
199 * NEW
200 * Reads a short integer (-32768..32767).
201 *
202 * PROCEDURE (VAR rd: Reader) ReadXInt (OUT x: INTEGER)
203 * NEW
204 * Same as ReadSInt, but has an INTEGER-type parameter.
205 * This procedure is provided to simplify migration from Release 1.2 to 1.3.
206 */
208 /**
209 * PROCEDURE (VAR rd: Reader) ReadInt (OUT x: INTEGER)
210 * NEW
211 * Reads an integer (-2147483648..2147483647).
212 */
213 INTEGER readInt() {
214 char *buf = new char[4];
215 d_rider.read(buf, 4);
216 if (isLittleEndian()) {
217 return *(INTEGER *)buf;
218 } else {
219 char *out = new char[4];
220 out[0] = buf[3]; out[1] = buf[2]; out[2] = buf[1]; out[3] = buf[0];
221 return *(INTEGER *)out;
225 /*
226 * PROCEDURE (VAR rd: Reader) ReadLong (OUT x: LONGINT)
227 * NEW
228 * Reads a long integer (-9223372036854775808..9223372036854775807).
229 *
230 * PROCEDURE (VAR rd: Reader) ReadSReal (OUT x: SHORTREAL)
231 * NEW
232 * Reads a short real (32-bit IEEE number).
233 *
234 * PROCEDURE (VAR rd: Reader) ReadXReal (OUT x: REAL)
235 * NEW
236 * Same as ReadSReal, but has a REAL-type parameter.
237 * This procedure is provided to simplify migration from Release 1.2 to 1.3.
238 *
239 * PROCEDURE (VAR rd: Reader) ReadReal (OUT x: REAL)
240 * NEW
241 * Reads a real (64-bit IEEE number).
242 *
243 * PROCEDURE (VAR rd: Reader) ReadSet (OUT x: SET)
244 * NEW
245 * Reads a set (32 elements).
246 *
247 * PROCEDURE (VAR rd: Reader) ReadSString (OUT x: ARRAY OF SHORTCHAR)
248 * NEW
249 * Reads a 0X-terminated short string.
250 *
251 * Pre
252 * invalid index LEN(x) > Length(string)
253 *
254 * PROCEDURE (VAR rd: Reader) ReadXString (OUT x: ARRAY OF CHAR)
255 * NEW
256 * Same as ReadSString, but has a string-type parameter.
257 * This procedure is provided to simplify migration from Release 1.2 to 1.3.
258 *
259 * PROCEDURE (VAR rd: Reader) ReadString (OUT x: ARRAY OF CHAR)
260 * NEW
261 * Reads a 0X-terminated string.
262 *
263 * Pre
264 * invalid index LEN(x) > Length(string)
265 *
266 * PROCEDURE (VAR rd: Reader) ReadStore (OUT x: Store)
267 * NEW
268 * Reads a store's type, allocates it, and then reads its contents, by calling the store's Internalize procedure. x may also be NIL, or an alien if the store's module cannot be loaded, or if internalization has been cancelled by the Internalize procedure.
269 * If the store has already been read in, a pointer to the same store is returned instead of allocating a new one. This means that arbitrary graphs that have been written with WriteStore are reconstructed correctly, including alias pointers to the same store, cycles, etc.
270 * If the file on which the reader operates does not contain correct input, then an assertion trap will be caused (traps 101 to trap 106).
271 *
272 * Pre
273 * 20 the reader is at the start position of a new store
274 *
275 * Post
276 * empty store on file
277 * x = NIL
278 * non-empty store on file
279 * x # NIL
280 * x IS Alien
281 * x.cause # 0
282 * x.type # ""
283 * x.file # NIL
284 * x.pos >= 0 beginning of store's data
285 * x.len >= 0 length of store's data
286 * alien store contents are on x.file in the range [x.pos .. x.pos + x.len[.
287 * These data include only the store's contents, not its prefix
288 * ~(x IS Alien)
289 * x was read successfully
290 */
291 Store *readStore() {
292 return new Store();
294 // PROCEDURE (VAR rd: Reader) ReadStore* (OUT x: Store), NEW;
295 // VAR a: Alien; t: Kernel.Type;
296 // len, pos, pos1, id, comment, next, down, downPos, nextTypeId, nextElemId, nextStoreId: INTEGER;
297 // kind: SHORTCHAR; path: TypePath; type: TypeName;
298 // save: ReaderState;
299 // BEGIN
300 // rd.ReadSChar(kind);
301 // IF kind = nil THEN
302 // rd.ReadInt(comment); rd.ReadInt(next);
303 // rd.st.end := rd.Pos();
304 // IF (next > 0) OR ((next = 0) & ODD(comment)) THEN rd.st.next := rd.st.end + next ELSE rd.st.next := 0 END;
305 // x := NIL
306 // ELSIF kind = link THEN
307 // rd.ReadInt(id); rd.ReadInt(comment); rd.ReadInt(next);
308 // rd.st.end := rd.Pos();
309 // IF (next > 0) OR ((next = 0) & ODD(comment)) THEN rd.st.next := rd.st.end + next ELSE rd.st.next := 0 END;
310 // x := ThisStore(rd.eDict, id)
311 // ELSIF kind = newlink THEN
312 // rd.ReadInt(id); rd.ReadInt(comment); rd.ReadInt(next);
313 // rd.st.end := rd.Pos();
314 // IF (next > 0) OR ((next = 0) & ODD(comment)) THEN rd.st.next := rd.st.end + next ELSE rd.st.next := 0 END;
315 // x := ThisStore(rd.sDict, id)
316 // ELSIF (kind = store) OR (kind = elem) THEN
317 // IF kind = elem THEN
318 // id := rd.nextElemId; INC(rd.nextElemId)
319 // ELSE
320 // id := rd.nextStoreId; INC(rd.nextStoreId)
321 // END;
322 // ReadPath(rd, path); type := path[0];
323 // nextTypeId := rd.nextTypeId; nextElemId := rd.nextElemId; nextStoreId := rd.nextStoreId;
324 // rd.ReadInt(comment);
325 // pos1 := rd.Pos();
326 // rd.ReadInt(next); rd.ReadInt(down); rd.ReadInt(len);
327 // pos := rd.Pos();
328 // IF next > 0 THEN rd.st.next := pos1 + next + 4 ELSE rd.st.next := 0 END;
329 // IF down > 0 THEN downPos := pos1 + down + 8 ELSE downPos := 0 END;
330 // rd.st.end := pos + len;
331 // rd.cause := 0;
332 // ASSERT(len >= 0, 101);
333 // IF next # 0 THEN
334 // ASSERT(rd.st.next > pos1, 102);
335 // IF down # 0 THEN
336 // ASSERT(downPos < rd.st.next, 103)
337 // END
338 // END;
339 // IF down # 0 THEN
340 // ASSERT(downPos > pos1, 104);
341 // ASSERT(downPos < rd.st.end, 105)
342 // END;
343 // t := ThisType(type);
344 // IF t # NIL THEN
345 // x := NewStore(t); x.isElem := kind = elem
346 // ELSE
347 // rd.cause := thisTypeRes; AlienTypeReport(rd.cause, type);
348 // x := NIL
349 // END;
350 // IF x # NIL THEN
351 // IF SamePath(t, path) THEN
352 // IF kind = elem THEN
353 // x.id := id; AddStore(rd.eDict, rd.eHead, x)
354 // ELSE
355 // x.id := id; AddStore(rd.sDict, rd.sHead, x)
356 // END;
357 // save := rd.st; rd.cause := 0; rd.cancelled := FALSE;
358 // x.Internalize(rd);
359 // rd.st := save;
360 // IF rd.cause # 0 THEN x := NIL
361 // ELSIF (rd.Pos() # rd.st.end) OR rd.rider.eof THEN
362 // rd.cause := inconsistentVersion; AlienReport(rd.cause);
363 // x := NIL
364 // END
365 // ELSE
366 // rd.cause := inconsistentType; AlienTypeReport(rd.cause, type);
367 // x := NIL
368 // END
369 // END;
370 //
371 // IF x # NIL THEN
372 // IF rd.noDomain THEN
373 // rd.store := x;
374 // rd.noDomain := FALSE
375 // ELSE
376 // Join(rd.store, x)
377 // END
378 // ELSE (* x is an alien *)
379 // rd.SetPos(pos);
380 // ASSERT(rd.cause # 0, 107);
381 // NEW(a); a.path := path; a.cause := rd.cause; a.file := rd.rider.Base();
382 // IF rd.noDomain THEN
383 // rd.store := a;
384 // rd.noDomain := FALSE
385 // ELSE
386 // Join(rd.store, a)
387 // END;
388 // IF kind = elem THEN
389 // a.id := id; AddStore(rd.eDict, rd.eHead, a)
390 // ELSE
391 // a.id := id; AddStore(rd.sDict, rd.sHead, a)
392 // END;
393 // save := rd.st;
394 // rd.nextTypeId := nextTypeId; rd.nextElemId := nextElemId; rd.nextStoreId := nextStoreId;
395 // InternalizeAlien(rd, a.comps, downPos, pos, len);
396 // rd.st := save;
397 // x := a;
398 // ASSERT(rd.Pos() = rd.st.end, 108);
399 // rd.cause := 0; rd.cancelled := FALSE; rd.readAlien := TRUE
400 // END
401 // ELSE
402 // pos := rd.Pos();
403 // HALT(20)
404 // END
405 // END ReadStore;
406 /**
407 * PROCEDURE (VAR rd: Reader) ReadVersion (min, max: INTEGER; OUT version: INTEGER)
408 * NEW
409 * Read a version byte and return it in version. If version is not in the specified range [min .. max], the store currently being read is turned into an alien, with cause = alienVersion.
410 *
411 * Pre
412 * 20 0 <= min <= max
413 *
414 * Post
415 * min <= version <= max
416 * legal version
417 * (version < min) OR (version > max)
418 * illegal version
419 * rd.cause = alienVersion
420 * rd.cancelled
421 * rd.readAlien
422 *
423 * PROCEDURE (VAR rd: Reader) TurnIntoAlien (cause: INTEGER)
424 * NEW
425 * A store which is currently being internalized can turn itself into an alien, e.g., if it has read a component store which is an alien.
426 *
427 * Pre
428 * 20 cause > 0
429 */
430 };
433 Store* importDocument(std::istream &is) {
434 const INTEGER docTag = 0x6F4F4443;
435 const INTEGER docVersion = 0;
436 Reader r(is);
437 INTEGER tag = r.readInt();
438 if (tag == docTag) {
439 INTEGER version = r.readInt();
440 if (version != docVersion) {
441 throw 100;
443 Store *s = r.readStore();
444 return s;
446 return 0;
450 int main(int argc, char *argv[]) {
451 std::ifstream in(argv[1], std::ios::in | std::ios::binary);
452 odc::Store* s = odc::importDocument(in);
453 std::cout << s << std::endl;
454 return 0;