ptype.pas 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715
  1. {
  2. $Id$
  3. Copyright (c) 1998-2000 by Florian Klaempfl
  4. Does parsing types for Free Pascal
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2 of the License, or
  8. (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program; if not, write to the Free Software
  15. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  16. ****************************************************************************
  17. }
  18. unit ptype;
  19. {$i defines.inc}
  20. interface
  21. uses
  22. globtype,symtype;
  23. const
  24. { forward types should only be possible inside a TYPE statement }
  25. typecanbeforward : boolean = false;
  26. var
  27. { hack, which allows to use the current parsed }
  28. { object type as function argument type }
  29. testcurobject : byte;
  30. curobjectname : stringid;
  31. { reads a string, file type or a type id and returns a name and }
  32. { tdef }
  33. procedure single_type(var tt:ttype;var s : string;isforwarddef:boolean);
  34. procedure read_type(var tt:ttype;const name : stringid);
  35. { reads a type definition }
  36. { to a appropriating tdef, s gets the name of }
  37. { the type to allow name mangling }
  38. procedure id_type(var tt : ttype;var s : string;isforwarddef:boolean);
  39. implementation
  40. uses
  41. { common }
  42. cutils,cpuinfo,
  43. { global }
  44. globals,tokens,verbose,
  45. systems,
  46. { symtable }
  47. symconst,symbase,symdef,symsym,symtable,types,
  48. { pass 1 }
  49. node,pass_1,
  50. nmat,nadd,ncal,nset,ncnv,ninl,ncon,nld,nflw,
  51. { parser }
  52. scanner,
  53. pbase,pexpr,pdecsub,pdecvar,pdecobj;
  54. procedure id_type(var tt : ttype;var s : string;isforwarddef:boolean);
  55. { reads a type definition }
  56. { to a appropriating tdef, s gets the name of }
  57. { the type to allow name mangling }
  58. var
  59. is_unit_specific : boolean;
  60. pos : tfileposinfo;
  61. srsym : tsym;
  62. srsymtable : tsymtable;
  63. begin
  64. s:=pattern;
  65. pos:=akttokenpos;
  66. { classes can be used also in classes }
  67. if (curobjectname=pattern) and is_class_or_interface(aktobjectdef) then
  68. begin
  69. tt.setdef(aktobjectdef);
  70. consume(_ID);
  71. exit;
  72. end;
  73. { objects can be parameters }
  74. if (testcurobject=2) and (curobjectname=pattern) then
  75. begin
  76. tt.setdef(aktobjectdef);
  77. consume(_ID);
  78. exit;
  79. end;
  80. { try to load the symbol to see if it's a unitsym }
  81. is_unit_specific:=false;
  82. searchsym(s,srsym,srsymtable);
  83. consume(_ID);
  84. if assigned(srsym) and
  85. (srsym.typ=unitsym) then
  86. begin
  87. is_unit_specific:=true;
  88. consume(_POINT);
  89. if srsym.owner.unitid=0 then
  90. begin
  91. srsym:=searchsymonlyin(tunitsym(srsym).unitsymtable,pattern);
  92. pos:=akttokenpos;
  93. s:=pattern;
  94. end
  95. else
  96. srsym:=nil;
  97. consume(_ID);
  98. end;
  99. { are we parsing a possible forward def ? }
  100. if isforwarddef and
  101. not(is_unit_specific) then
  102. begin
  103. tt.setdef(tforwarddef.create(s,pos));
  104. exit;
  105. end;
  106. { unknown sym ? }
  107. if not assigned(srsym) then
  108. begin
  109. Message1(sym_e_id_not_found,s);
  110. tt:=generrortype;
  111. exit;
  112. end;
  113. { type sym ? }
  114. if (srsym.typ<>typesym) then
  115. begin
  116. Message(type_e_type_id_expected);
  117. tt:=generrortype;
  118. exit;
  119. end;
  120. { Types are first defined with an error def before assigning
  121. the real type so check if it's an errordef. if so then
  122. give an error }
  123. if (ttypesym(srsym).restype.def.deftype=errordef) then
  124. begin
  125. Message(sym_e_error_in_type_def);
  126. tt:=generrortype;
  127. exit;
  128. end;
  129. { Only use the definitions for system/current unit, becuase
  130. they can be refered from the parameters and symbols are not
  131. loaded at that time. A symbol reference to an other unit
  132. is still possible, because it's already loaded (PFV)
  133. can't use in [] here, becuase unitid can be > 255 }
  134. if (ttypesym(srsym).owner.unitid=0) or
  135. (ttypesym(srsym).owner.unitid=1) then
  136. tt.setdef(ttypesym(srsym).restype.def)
  137. else
  138. tt.setsym(srsym);
  139. end;
  140. procedure single_type(var tt:ttype;var s : string;isforwarddef:boolean);
  141. { reads a string, file type or a type id and returns a name and }
  142. { tdef }
  143. var
  144. hs : string;
  145. t2 : ttype;
  146. begin
  147. case token of
  148. _STRING:
  149. begin
  150. string_dec(tt);
  151. s:='STRING';
  152. end;
  153. _FILE:
  154. begin
  155. consume(_FILE);
  156. if token=_OF then
  157. begin
  158. consume(_OF);
  159. single_type(t2,hs,false);
  160. tt.setdef(tfiledef.createtyped(t2));
  161. s:='FILE$OF$'+hs;
  162. end
  163. else
  164. begin
  165. tt:=cfiletype;
  166. s:='FILE';
  167. end;
  168. end;
  169. else
  170. begin
  171. id_type(tt,s,isforwarddef);
  172. end;
  173. end;
  174. end;
  175. { reads a record declaration }
  176. function record_dec : tdef;
  177. var
  178. symtable : tsymtable;
  179. storetypecanbeforward : boolean;
  180. begin
  181. { create recdef }
  182. symtable:=trecordsymtable.create;
  183. record_dec:=trecorddef.create(symtable);
  184. { update symtable stack }
  185. symtable.next:=symtablestack;
  186. symtablestack:=symtable;
  187. { parse record }
  188. consume(_RECORD);
  189. storetypecanbeforward:=typecanbeforward;
  190. { for tp mode don't allow forward types }
  191. if m_tp in aktmodeswitches then
  192. typecanbeforward:=false;
  193. read_var_decs(true,false,false);
  194. consume(_END);
  195. typecanbeforward:=storetypecanbeforward;
  196. { may be scale record size to a size of n*4 ? }
  197. symtablestack.datasize:=align(symtablestack.datasize,symtablestack.dataalignment);
  198. { restore symtable stack }
  199. symtablestack:=symtable.next;
  200. end;
  201. { reads a type definition and returns a pointer to it }
  202. procedure read_type(var tt : ttype;const name : stringid);
  203. var
  204. pt : tnode;
  205. tt2 : ttype;
  206. aktenumdef : tenumdef;
  207. ap : tarraydef;
  208. s : stringid;
  209. l,v : TConstExprInt;
  210. oldaktpackrecords : longint;
  211. hs : string;
  212. defpos,storepos : tfileposinfo;
  213. procedure expr_type;
  214. var
  215. pt1,pt2 : tnode;
  216. begin
  217. { use of current parsed object ? }
  218. if (token=_ID) and (testcurobject=2) and (curobjectname=pattern) then
  219. begin
  220. consume(_ID);
  221. tt.setdef(aktobjectdef);
  222. exit;
  223. end;
  224. { classes can be used also in classes }
  225. if (curobjectname=pattern) and is_class_or_interface(aktobjectdef) then
  226. begin
  227. tt.setdef(aktobjectdef);
  228. consume(_ID);
  229. exit;
  230. end;
  231. { we can't accept a equal in type }
  232. pt1:=comp_expr(not(ignore_equal));
  233. if (token=_POINTPOINT) then
  234. begin
  235. consume(_POINTPOINT);
  236. { get high value of range }
  237. pt2:=comp_expr(not(ignore_equal));
  238. { make both the same type }
  239. inserttypeconv(pt1,pt2.resulttype);
  240. { both must be evaluated to constants now }
  241. if (pt1.nodetype=ordconstn) and
  242. (pt2.nodetype=ordconstn) then
  243. begin
  244. { Check bounds }
  245. if tordconstnode(pt2).value<tordconstnode(pt1).value then
  246. Message(cg_e_upper_lower_than_lower)
  247. else
  248. begin
  249. { All checks passed, create the new def }
  250. case pt1.resulttype.def.deftype of
  251. enumdef :
  252. tt.setdef(tenumdef.create_subrange(tenumdef(pt1.resulttype.def),tordconstnode(pt1).value,tordconstnode(pt2).value));
  253. orddef :
  254. begin
  255. if is_char(pt1.resulttype.def) then
  256. tt.setdef(torddef.create(uchar,tordconstnode(pt1).value,tordconstnode(pt2).value))
  257. else
  258. if is_boolean(pt1.resulttype.def) then
  259. tt.setdef(torddef.create(bool8bit,tordconstnode(pt1).value,tordconstnode(pt2).value))
  260. else
  261. tt.setdef(torddef.create(uauto,tordconstnode(pt1).value,tordconstnode(pt2).value));
  262. end;
  263. end;
  264. end;
  265. end
  266. else
  267. Message(sym_e_error_in_type_def);
  268. pt2.free;
  269. end
  270. else
  271. begin
  272. { a simple type renaming }
  273. if (pt1.nodetype=typen) then
  274. tt:=ttypenode(pt1).resulttype
  275. else
  276. Message(sym_e_error_in_type_def);
  277. end;
  278. pt1.free;
  279. end;
  280. procedure array_dec;
  281. var
  282. lowval,
  283. highval : longint;
  284. arraytype : ttype;
  285. ht : ttype;
  286. procedure setdefdecl(const t:ttype);
  287. begin
  288. case t.def.deftype of
  289. enumdef :
  290. begin
  291. lowval:=tenumdef(t.def).min;
  292. highval:=tenumdef(t.def).max;
  293. arraytype:=t;
  294. end;
  295. orddef :
  296. begin
  297. if torddef(t.def).typ in [uchar,
  298. u8bit,u16bit,
  299. s8bit,s16bit,s32bit,
  300. bool8bit,bool16bit,bool32bit,
  301. uwidechar] then
  302. begin
  303. lowval:=torddef(t.def).low;
  304. highval:=torddef(t.def).high;
  305. arraytype:=t;
  306. end
  307. else
  308. Message1(parser_e_type_cant_be_used_in_array_index,t.def.gettypename);
  309. end;
  310. else
  311. Message(sym_e_error_in_type_def);
  312. end;
  313. end;
  314. begin
  315. consume(_ARRAY);
  316. { open array? }
  317. if token=_LECKKLAMMER then
  318. begin
  319. consume(_LECKKLAMMER);
  320. { defaults }
  321. arraytype:=generrortype;
  322. lowval:=longint($80000000);
  323. highval:=$7fffffff;
  324. tt.reset;
  325. repeat
  326. { read the expression and check it, check apart if the
  327. declaration is an enum declaration because that needs to
  328. be parsed by readtype (PFV) }
  329. if token=_LKLAMMER then
  330. begin
  331. read_type(ht,'');
  332. setdefdecl(ht);
  333. end
  334. else
  335. begin
  336. pt:=expr;
  337. if pt.nodetype=typen then
  338. setdefdecl(pt.resulttype)
  339. else
  340. begin
  341. if (pt.nodetype=rangen) then
  342. begin
  343. if (trangenode(pt).left.nodetype=ordconstn) and
  344. (trangenode(pt).right.nodetype=ordconstn) then
  345. begin
  346. lowval:=tordconstnode(trangenode(pt).left).value;
  347. highval:=tordconstnode(trangenode(pt).right).value;
  348. if highval<lowval then
  349. begin
  350. Message(parser_e_array_lower_less_than_upper_bound);
  351. highval:=lowval;
  352. end;
  353. arraytype:=trangenode(pt).right.resulttype;
  354. end
  355. else
  356. Message(type_e_cant_eval_constant_expr);
  357. end
  358. else
  359. Message(sym_e_error_in_type_def)
  360. end;
  361. pt.free;
  362. end;
  363. { create arraydef }
  364. if not assigned(tt.def) then
  365. begin
  366. ap:=tarraydef.create(lowval,highval,arraytype);
  367. tt.setdef(ap);
  368. end
  369. else
  370. begin
  371. ap.elementtype.setdef(tarraydef.create(lowval,highval,arraytype));
  372. ap:=tarraydef(ap.elementtype.def);
  373. end;
  374. if token=_COMMA then
  375. consume(_COMMA)
  376. else
  377. break;
  378. until false;
  379. consume(_RECKKLAMMER);
  380. end
  381. else
  382. begin
  383. ap:=tarraydef.create(0,-1,s32bittype);
  384. ap.IsDynamicArray:=true;
  385. tt.setdef(ap);
  386. end;
  387. consume(_OF);
  388. read_type(tt2,'');
  389. { if no error, set element type }
  390. if assigned(ap) then
  391. ap.elementtype:=tt2;
  392. end;
  393. var
  394. p : tnode;
  395. enumdupmsg : boolean;
  396. begin
  397. tt.reset;
  398. case token of
  399. _STRING,_FILE:
  400. begin
  401. single_type(tt,hs,false);
  402. end;
  403. _LKLAMMER:
  404. begin
  405. consume(_LKLAMMER);
  406. { allow negativ value_str }
  407. l:=-1;
  408. enumdupmsg:=false;
  409. aktenumdef:=tenumdef.create;
  410. repeat
  411. s:=orgpattern;
  412. defpos:=akttokenpos;
  413. consume(_ID);
  414. { only allow assigning of specific numbers under fpc mode }
  415. if (m_fpc in aktmodeswitches) and
  416. (token=_ASSIGNMENT) then
  417. begin
  418. consume(_ASSIGNMENT);
  419. v:=get_intconst;
  420. { please leave that a note, allows type save }
  421. { declarations in the win32 units ! }
  422. if (v<=l) and (not enumdupmsg) then
  423. begin
  424. Message(parser_n_duplicate_enum);
  425. enumdupmsg:=true;
  426. end;
  427. l:=v;
  428. end
  429. else if (m_delphi in aktmodeswitches) and
  430. (token=_EQUAL) then
  431. begin
  432. consume(_EQUAL);
  433. p:=comp_expr(true);
  434. if (p.nodetype=ordconstn) then
  435. begin
  436. { we expect an integer or an enum of the
  437. same type }
  438. if is_integer(p.resulttype.def) or
  439. is_equal(p.resulttype.def,aktenumdef) then
  440. l:=tordconstnode(p).value
  441. else
  442. Message2(type_e_incompatible_types,p.resulttype.def.typename,s32bittype.def.typename);
  443. end
  444. else
  445. Message(cg_e_illegal_expression);
  446. p.free;
  447. end
  448. else
  449. inc(l);
  450. storepos:=akttokenpos;
  451. akttokenpos:=defpos;
  452. constsymtable.insert(tenumsym.create(s,aktenumdef,l));
  453. akttokenpos:=storepos;
  454. until not try_to_consume(_COMMA);
  455. tt.setdef(aktenumdef);
  456. consume(_RKLAMMER);
  457. end;
  458. _ARRAY:
  459. begin
  460. array_dec;
  461. end;
  462. _SET:
  463. begin
  464. consume(_SET);
  465. consume(_OF);
  466. read_type(tt2,'');
  467. if assigned(tt2.def) then
  468. begin
  469. case tt2.def.deftype of
  470. { don't forget that min can be negativ PM }
  471. enumdef :
  472. if tenumdef(tt2.def).min>=0 then
  473. tt.setdef(tsetdef.create(tt2,tenumdef(tt2.def).max))
  474. else
  475. Message(sym_e_ill_type_decl_set);
  476. orddef :
  477. begin
  478. case torddef(tt2.def).typ of
  479. uchar :
  480. tt.setdef(tsetdef.create(tt2,255));
  481. u8bit,u16bit,u32bit,
  482. s8bit,s16bit,s32bit :
  483. begin
  484. if (torddef(tt2.def).low>=0) then
  485. tt.setdef(tsetdef.create(tt2,torddef(tt2.def).high))
  486. else
  487. Message(sym_e_ill_type_decl_set);
  488. end;
  489. else
  490. Message(sym_e_ill_type_decl_set);
  491. end;
  492. end;
  493. else
  494. Message(sym_e_ill_type_decl_set);
  495. end;
  496. end
  497. else
  498. tt:=generrortype;
  499. end;
  500. _CARET:
  501. begin
  502. consume(_CARET);
  503. single_type(tt2,hs,typecanbeforward);
  504. tt.setdef(tpointerdef.create(tt2));
  505. end;
  506. _RECORD:
  507. begin
  508. tt.setdef(record_dec);
  509. end;
  510. _PACKED:
  511. begin
  512. consume(_PACKED);
  513. if token=_ARRAY then
  514. array_dec
  515. else
  516. begin
  517. oldaktpackrecords:=aktalignment.recordalignmax;
  518. aktalignment.recordalignmax:=1;
  519. if token in [_CLASS,_OBJECT] then
  520. tt.setdef(object_dec(name,nil))
  521. else
  522. tt.setdef(record_dec);
  523. aktalignment.recordalignmax:=oldaktpackrecords;
  524. end;
  525. end;
  526. _CLASS,
  527. _CPPCLASS,
  528. _INTERFACE,
  529. _OBJECT:
  530. begin
  531. tt.setdef(object_dec(name,nil));
  532. end;
  533. _PROCEDURE:
  534. begin
  535. consume(_PROCEDURE);
  536. tt.setdef(tprocvardef.create);
  537. if token=_LKLAMMER then
  538. parameter_dec(tprocvardef(tt.def));
  539. if token=_OF then
  540. begin
  541. consume(_OF);
  542. consume(_OBJECT);
  543. include(tprocvardef(tt.def).procoptions,po_methodpointer);
  544. end;
  545. end;
  546. _FUNCTION:
  547. begin
  548. consume(_FUNCTION);
  549. tt.def:=tprocvardef.create;
  550. if token=_LKLAMMER then
  551. parameter_dec(tprocvardef(tt.def));
  552. consume(_COLON);
  553. single_type(tprocvardef(tt.def).rettype,hs,false);
  554. if token=_OF then
  555. begin
  556. consume(_OF);
  557. consume(_OBJECT);
  558. include(tprocvardef(tt.def).procoptions,po_methodpointer);
  559. end;
  560. end;
  561. else
  562. expr_type;
  563. end;
  564. if tt.def=nil then
  565. tt:=generrortype;
  566. end;
  567. end.
  568. {
  569. $Log$
  570. Revision 1.28 2001-07-09 21:15:41 peter
  571. * Length made internal
  572. * Add array support for Length
  573. Revision 1.27 2001/07/01 20:16:16 peter
  574. * alignmentinfo record added
  575. * -Oa argument supports more alignment settings that can be specified
  576. per type: PROC,LOOP,VARMIN,VARMAX,CONSTMIN,CONSTMAX,RECORDMIN
  577. RECORDMAX,LOCALMIN,LOCALMAX. It is possible to set the mimimum
  578. required alignment and the maximum usefull alignment. The final
  579. alignment will be choosen per variable size dependent on these
  580. settings
  581. Revision 1.26 2001/06/04 18:06:38 peter
  582. * fix for enum with assignment
  583. Revision 1.25 2001/06/04 11:51:59 peter
  584. * enum type declarations assignments can also be of the same enum
  585. type
  586. Revision 1.24 2001/06/03 20:16:19 peter
  587. * allow int64 in range declaration for new types
  588. Revision 1.23 2001/04/13 01:22:13 peter
  589. * symtable change to classes
  590. * range check generation and errors fixed, make cycle DEBUG=1 works
  591. * memory leaks fixed
  592. Revision 1.22 2001/04/04 22:43:53 peter
  593. * remove unnecessary calls to firstpass
  594. Revision 1.21 2001/04/02 21:20:34 peter
  595. * resulttype rewrite
  596. Revision 1.20 2001/03/22 22:35:42 florian
  597. + support for type a = (a=1); in Delphi mode added
  598. + procedure p(); in Delphi mode supported
  599. + on isn't keyword anymore, it can be used as
  600. id etc. now
  601. Revision 1.19 2001/03/12 12:49:01 michael
  602. + Patches from peter
  603. Revision 1.18 2001/03/11 22:58:50 peter
  604. * getsym redesign, removed the globals srsym,srsymtable
  605. Revision 1.17 2000/12/07 17:19:43 jonas
  606. * new constant handling: from now on, hex constants >$7fffffff are
  607. parsed as unsigned constants (otherwise, $80000000 got sign extended
  608. and became $ffffffff80000000), all constants in the longint range
  609. become longints, all constants >$7fffffff and <=cardinal($ffffffff)
  610. are cardinals and the rest are int64's.
  611. * added lots of longint typecast to prevent range check errors in the
  612. compiler and rtl
  613. * type casts of symbolic ordinal constants are now preserved
  614. * fixed bug where the original resulttype.def wasn't restored correctly
  615. after doing a 64bit rangecheck
  616. Revision 1.16 2000/11/29 00:30:38 florian
  617. * unused units removed from uses clause
  618. * some changes for widestrings
  619. Revision 1.15 2000/11/14 23:43:38 florian
  620. * fixed 1238
  621. Revision 1.14 2000/11/04 14:25:21 florian
  622. + merged Attila's changes for interfaces, not tested yet
  623. Revision 1.13 2000/10/31 22:02:51 peter
  624. * symtable splitted, no real code changes
  625. Revision 1.12 2000/10/26 21:54:03 peter
  626. * fixed crash with error in child definition (merged)
  627. Revision 1.11 2000/10/21 18:16:12 florian
  628. * a lot of changes:
  629. - basic dyn. array support
  630. - basic C++ support
  631. - some work for interfaces done
  632. ....
  633. Revision 1.10 2000/10/14 10:14:52 peter
  634. * moehrendorf oct 2000 rewrite
  635. Revision 1.9 2000/09/24 15:06:25 peter
  636. * use defines.inc
  637. Revision 1.8 2000/08/27 20:19:39 peter
  638. * store strings with case in ppu, when an internal symbol is created
  639. a '$' is prefixed so it's not automatic uppercased
  640. Revision 1.7 2000/08/27 16:11:52 peter
  641. * moved some util functions from globals,cobjects to cutils
  642. * splitted files into finput,fmodule
  643. Revision 1.6 2000/08/16 18:33:54 peter
  644. * splitted namedobjectitem.next into indexnext and listnext so it
  645. can be used in both lists
  646. * don't allow "word = word" type definitions (merged)
  647. Revision 1.5 2000/08/06 14:17:15 peter
  648. * overload fixes (merged)
  649. Revision 1.4 2000/07/30 17:04:43 peter
  650. * merged fixes
  651. Revision 1.3 2000/07/13 12:08:27 michael
  652. + patched to 1.1.0 with former 1.09patch from peter
  653. Revision 1.2 2000/07/13 11:32:47 michael
  654. + removed logs
  655. }