sysos2.pas 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820
  1. {****************************************************************************
  2. Free Pascal -- OS/2 runtime library
  3. Copyright (c) 1999-2000 by Florian Klaempfl
  4. Copyright (c) 1999-2000 by Daniel Mantione
  5. Free Pascal is distributed under the GNU Public License v2. So is this unit.
  6. The GNU Public License requires you to distribute the source code of this
  7. unit with any product that uses it. We grant you an exception to this, and
  8. that is, when you compile a program with the Free Pascal Compiler, you do not
  9. need to ship source code with that program, AS LONG AS YOU ARE USING
  10. UNMODIFIED CODE! If you modify this code, you MUST change the next line:
  11. <This an official, unmodified Free Pascal source code file.>
  12. Send us your modified files, we can work together if you want!
  13. Free Pascal is distributed in the hope that it will be useful,
  14. but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. Library GNU General Public License for more details.
  17. You should have received a copy of the Library GNU General Public License
  18. along with Free Pascal; see the file COPYING.LIB. If not, write to
  19. the Free Software Foundation, 59 Temple Place - Suite 330,
  20. Boston, MA 02111-1307, USA.
  21. ****************************************************************************}
  22. unit sysos2;
  23. {Changelog:
  24. People:
  25. DM - Daniel Mantione
  26. Date: Description of change: Changed by:
  27. - First released version 0.1. DM
  28. Coding style:
  29. My coding style is a bit unusual for Pascal. Nevertheless I friendly ask
  30. you to try to make your changes not look all to different. In general,
  31. set your IDE to use tab characters, optimal fill on and a tabsize of 4.}
  32. interface
  33. {Link the startup code.}
  34. {$l prt1.oo2}
  35. {$I SYSTEMH.INC}
  36. {$I heaph.inc}
  37. type Tos=(osDOS,osOS2,osDPMI);
  38. var os_mode:Tos;
  39. first_meg:pointer;
  40. type Psysthreadib=^Tsysthreadib;
  41. Pthreadinfoblock=^Tthreadinfoblock;
  42. Pprocessinfoblock=^Tprocessinfoblock;
  43. Tbytearray=array[0..$ffff] of byte;
  44. Pbytearray=^Tbytearray;
  45. Tsysthreadib=record
  46. tid,
  47. priority,
  48. version:longint;
  49. MCcount,
  50. MCforceflag:word;
  51. end;
  52. Tthreadinfoblock=record
  53. pexchain,
  54. stack,
  55. stacklimit:pointer;
  56. tib2:Psysthreadib;
  57. version,
  58. ordinal:longint;
  59. end;
  60. Tprocessinfoblock=record
  61. pid,
  62. parentpid,
  63. hmte:longint;
  64. cmd,
  65. env:Pbytearray;
  66. flstatus,
  67. ttype:longint;
  68. end;
  69. const UnusedHandle=$ffff;
  70. StdInputHandle=0;
  71. StdOutputHandle=1;
  72. StdErrorHandle=2;
  73. FileNameCaseSensitive : boolean = false;
  74. var
  75. { C-compatible arguments and environment }
  76. argc : longint;external name '_argc';
  77. argv : ppchar;external name '_argv';
  78. envp : ppchar;external name '_environ';
  79. implementation
  80. {$I SYSTEM.INC}
  81. procedure DosGetInfoBlocks (var Atib: PThreadInfoBlock;
  82. var Apib: PProcessInfoBlock); cdecl;
  83. external 'DOSCALLS' index 312;
  84. function DosSetRelMaxFH (var ReqCount, CurMaxFH: longint): longint; cdecl;
  85. external 'DOSCALLS' index 382;
  86. {This is the correct way to call external assembler procedures.}
  87. procedure syscall; external name '___SYSCALL';
  88. {***************************************************************************
  89. Runtime error checking related routines.
  90. ***************************************************************************}
  91. {$S-}
  92. procedure st1(stack_size:longint);[public,alias: 'STACKCHECK'];
  93. begin
  94. { called when trying to get local stack }
  95. { if the compiler directive $S is set }
  96. {$ASMMODE DIRECT}
  97. asm
  98. movl stack_size,%ebx
  99. movl %esp,%eax
  100. subl %ebx,%eax
  101. {$ifdef SYSTEMDEBUG}
  102. movl U_SYSOS2_LOWESTSTACK,%ebx
  103. cmpl %eax,%ebx
  104. jb Lis_not_lowest
  105. movl %eax,U_SYSOS2_LOWESTSTACK
  106. Lis_not_lowest:
  107. {$endif SYSTEMDEBUG}
  108. cmpb $2,U_SYSOS2_OS_MODE
  109. jne Lrunning_in_dos
  110. movl U_SYSOS2_STACKBOTTOM,%ebx
  111. jmp Lrunning_in_os2
  112. Lrunning_in_dos:
  113. movl __heap_brk,%ebx
  114. Lrunning_in_os2:
  115. cmpl %eax,%ebx
  116. jae Lshort_on_stack
  117. leave
  118. ret $4
  119. Lshort_on_stack:
  120. end ['EAX','EBX'];
  121. {$ASMMODE ATT}
  122. { this needs a local variable }
  123. { so the function called itself !! }
  124. { Writeln('low in stack ');}
  125. HandleError(202);
  126. end;
  127. {no stack check in system }
  128. {****************************************************************************
  129. Miscellaneous related routines.
  130. ****************************************************************************}
  131. {$asmmode intel}
  132. procedure system_exit; assembler;
  133. asm
  134. mov ah, 04ch
  135. mov al, byte ptr exitcode
  136. call syscall
  137. end;
  138. {$asmmode att}
  139. {$asmmode direct}
  140. function paramcount:longint;assembler;
  141. asm
  142. movl _argc,%eax
  143. decl %eax
  144. end ['EAX'];
  145. function paramstr(l:longint):string;
  146. function args:pointer;assembler;
  147. asm
  148. movl _argv,%eax
  149. end ['EAX'];
  150. var p:^Pchar;
  151. begin
  152. if (l>=0) and (l<=paramcount) then
  153. begin
  154. p:=args;
  155. paramstr:=strpas(p[l]);
  156. end
  157. else paramstr:='';
  158. end;
  159. {$asmmode att}
  160. procedure randomize;
  161. var hl:longint;
  162. begin
  163. asm
  164. movb $0x2c,%ah
  165. call syscall
  166. movw %cx,-4(%ebp)
  167. movw %dx,-2(%ebp)
  168. end;
  169. randseed:=hl;
  170. end;
  171. {****************************************************************************
  172. Heap management releated routines.
  173. ****************************************************************************}
  174. { this function allows to extend the heap by calling
  175. syscall $7f00 resizes the brk area}
  176. function sbrk(size:longint):longint; assembler;
  177. asm
  178. movl size,%edx
  179. movw $0x7f00,%ax
  180. call syscall
  181. end;
  182. {$ASMMODE direct}
  183. function getheapstart:pointer;assembler;
  184. asm
  185. movl __heap_base,%eax
  186. end ['EAX'];
  187. function getheapsize:longint;assembler;
  188. asm
  189. movl HEAPSIZE,%eax
  190. end ['EAX'];
  191. {$ASMMODE ATT}
  192. {$i heap.inc}
  193. {****************************************************************************
  194. Low Level File Routines
  195. ****************************************************************************}
  196. procedure allowslash(p:Pchar);
  197. {Allow slash as backslash.}
  198. var i:longint;
  199. begin
  200. for i:=0 to strlen(p) do
  201. if p[i]='/' then p[i]:='\';
  202. end;
  203. procedure do_close(h:longint);
  204. begin
  205. { Only three standard handles under real OS/2 }
  206. if (h > 4) or
  207. (os_MODE = osOS2) and (h > 2) then
  208. begin
  209. asm
  210. movb $0x3e,%ah
  211. movl h,%ebx
  212. call syscall
  213. end;
  214. end;
  215. end;
  216. procedure do_erase(p:Pchar);
  217. begin
  218. allowslash(p);
  219. asm
  220. movl P,%edx
  221. movb $0x41,%ah
  222. call syscall
  223. jnc .LERASE1
  224. movw %ax,inoutres;
  225. .LERASE1:
  226. end;
  227. end;
  228. procedure do_rename(p1,p2:Pchar);
  229. begin
  230. allowslash(p1);
  231. allowslash(p2);
  232. asm
  233. movl P1, %edx
  234. movl P2, %edi
  235. movb $0x56,%ah
  236. call syscall
  237. jnc .LRENAME1
  238. movw %ax,inoutres;
  239. .LRENAME1:
  240. end;
  241. end;
  242. function do_read(h,addr,len:longint):longint; assembler;
  243. asm
  244. movl len,%ecx
  245. movl addr,%edx
  246. movl h,%ebx
  247. movb $0x3f,%ah
  248. call syscall
  249. jnc .LDOSREAD1
  250. movw %ax,inoutres;
  251. xorl %eax,%eax
  252. .LDOSREAD1:
  253. end;
  254. function do_write(h,addr,len:longint) : longint; assembler;
  255. asm
  256. movl len,%ecx
  257. movl addr,%edx
  258. movl h,%ebx
  259. movb $0x40,%ah
  260. call syscall
  261. jnc .LDOSWRITE1
  262. movw %ax,inoutres;
  263. .LDOSWRITE1:
  264. end;
  265. function do_filepos(handle:longint): longint; assembler;
  266. asm
  267. movw $0x4201,%ax
  268. movl handle,%ebx
  269. xorl %edx,%edx
  270. call syscall
  271. jnc .LDOSFILEPOS
  272. movw %ax,inoutres;
  273. xorl %eax,%eax
  274. .LDOSFILEPOS:
  275. end;
  276. procedure do_seek(handle,pos:longint); assembler;
  277. asm
  278. movw $0x4200,%ax
  279. movl handle,%ebx
  280. movl pos,%edx
  281. call syscall
  282. jnc .LDOSSEEK1
  283. movw %ax,inoutres;
  284. .LDOSSEEK1:
  285. end;
  286. function do_seekend(handle:longint):longint; assembler;
  287. asm
  288. movw $0x4202,%ax
  289. movl handle,%ebx
  290. xorl %edx,%edx
  291. call syscall
  292. jnc .Lset_at_end1
  293. movw %ax,inoutres;
  294. xorl %eax,%eax
  295. .Lset_at_end1:
  296. end;
  297. function do_filesize(handle:longint):longint;
  298. var aktfilepos:longint;
  299. begin
  300. aktfilepos:=do_filepos(handle);
  301. do_filesize:=do_seekend(handle);
  302. do_seek(handle,aktfilepos);
  303. end;
  304. procedure do_truncate(handle,pos:longint); assembler;
  305. asm
  306. (* DOS function 40h isn't safe for this according to EMX documentation
  307. movl $0x4200,%eax
  308. movl 8(%ebp),%ebx
  309. movl 12(%ebp),%edx
  310. call syscall
  311. jc .LTruncate1
  312. movl 8(%ebp),%ebx
  313. movl 12(%ebp),%edx
  314. movl %ebp,%edx
  315. xorl %ecx,%ecx
  316. movb $0x40,%ah
  317. call syscall
  318. *)
  319. movl $0x7F25,%eax
  320. movl Handle,%ebx
  321. movl Pos,%edx
  322. call syscall
  323. inc %eax
  324. movl %ecx, %eax
  325. jnz .LTruncate1
  326. (* File position is undefined after truncation, move to the end. *)
  327. movl $0x4202,%eax
  328. movl Handle,%ebx
  329. movl $0,%edx
  330. call syscall
  331. jnc .LTruncate2
  332. .LTruncate1:
  333. movw %ax,inoutres;
  334. .LTruncate2:
  335. end;
  336. const
  337. FileHandleCount: longint = 20;
  338. function Increase_File_Handle_Count: boolean;
  339. var Err: word;
  340. L1, L2: longint;
  341. begin
  342. if os_mode = osOS2 then
  343. begin
  344. L1 := 10;
  345. if DosSetRelMaxFH (L1, L2) <> 0 then
  346. Increase_File_Handle_Count := false
  347. else
  348. if L2 > FileHandleCount then
  349. begin
  350. FileHandleCount := L2;
  351. Increase_File_Handle_Count := true;
  352. end
  353. else
  354. Increase_File_Handle_Count := false;
  355. end
  356. else
  357. begin
  358. Inc (FileHandleCount, 10);
  359. Err := 0;
  360. asm
  361. movl $0x6700, %eax
  362. movl FileHandleCount, %ebx
  363. call syscall
  364. jnc .LIncFHandles
  365. movw %ax, Err
  366. .LIncFHandles:
  367. end;
  368. if Err <> 0 then
  369. begin
  370. Increase_File_Handle_Count := false;
  371. Dec (FileHandleCount, 10);
  372. end
  373. else
  374. Increase_File_Handle_Count := true;
  375. end;
  376. end;
  377. procedure do_open(var f;p:pchar;flags:longint);
  378. {
  379. filerec and textrec have both handle and mode as the first items so
  380. they could use the same routine for opening/creating.
  381. when (flags and $100) the file will be append
  382. when (flags and $1000) the file will be truncate/rewritten
  383. when (flags and $10000) there is no check for close (needed for textfiles)
  384. }
  385. var Action: longint;
  386. begin
  387. allowslash(p);
  388. { close first if opened }
  389. if ((flags and $10000)=0) then
  390. begin
  391. case filerec(f).mode of
  392. fminput,fmoutput,fminout : Do_Close(filerec(f).handle);
  393. fmclosed:;
  394. else
  395. begin
  396. inoutres:=102; {not assigned}
  397. exit;
  398. end;
  399. end;
  400. end;
  401. { reset file handle }
  402. filerec(f).handle := UnusedHandle;
  403. Action := 0;
  404. { convert filemode to filerec modes }
  405. case (flags and 3) of
  406. 0 : filerec(f).mode:=fminput;
  407. 1 : filerec(f).mode:=fmoutput;
  408. 2 : filerec(f).mode:=fminout;
  409. end;
  410. if (flags and $1000)<>0 then
  411. Action := $50000; (* Create / replace *)
  412. { empty name is special }
  413. if p[0]=#0 then
  414. begin
  415. case FileRec(f).mode of
  416. fminput :
  417. FileRec(f).Handle:=StdInputHandle;
  418. fminout, { this is set by rewrite }
  419. fmoutput :
  420. FileRec(f).Handle:=StdOutputHandle;
  421. fmappend :
  422. begin
  423. FileRec(f).Handle:=StdOutputHandle;
  424. FileRec(f).mode:=fmoutput; {fool fmappend}
  425. end;
  426. end;
  427. exit;
  428. end;
  429. Action := Action or (Flags and $FF);
  430. (* DenyAll if sharing not specified. *)
  431. if Flags and 112 = 0 then
  432. Action := Action or 16;
  433. asm
  434. movl $0x7f2b, %eax
  435. movl Action, %ecx
  436. movl p, %edx
  437. call syscall
  438. cmpl $0xffffffff, %eax
  439. jnz .LOPEN1
  440. movw %cx, InOutRes
  441. movw UnusedHandle, %ax
  442. .LOPEN1:
  443. movl f,%edx
  444. movw %ax,(%edx)
  445. end;
  446. if (InOutRes = 4) and Increase_File_Handle_Count then
  447. (* Trying again after increasing amount of file handles *)
  448. asm
  449. movl $0x7f2b, %eax
  450. movl Action, %ecx
  451. movl p, %edx
  452. call syscall
  453. cmpl $0xffffffff, %eax
  454. jnz .LOPEN2
  455. movw %cx, InOutRes
  456. movw UnusedHandle, %ax
  457. .LOPEN2:
  458. movl f,%edx
  459. movw %ax,(%edx)
  460. end;
  461. { for systems that have more handles }
  462. if FileRec (F).Handle > FileHandleCount then
  463. FileHandleCount := FileRec (F).Handle;
  464. if (flags and $100)<>0 then
  465. begin
  466. do_seekend(filerec(f).handle);
  467. FileRec (F).Mode := fmOutput; {fool fmappend}
  468. end;
  469. end;
  470. {$ASMMODE INTEL}
  471. function do_isdevice (Handle: longint): boolean; assembler;
  472. (*
  473. var HT, Attr: longint;
  474. begin
  475. if os_mode = osOS2 then
  476. begin
  477. if DosQueryHType (Handle, HT, Attr) <> 0 then HT := 1;
  478. end
  479. else
  480. *)
  481. asm
  482. mov ebx, Handle
  483. mov eax, 4400h
  484. call syscall
  485. mov eax, 1
  486. jc @IsDevEnd
  487. test edx, 80h
  488. jnz @IsDevEnd
  489. dec eax
  490. @IsDevEnd:
  491. end;
  492. {$ASMMODE ATT}
  493. {*****************************************************************************
  494. UnTyped File Handling
  495. *****************************************************************************}
  496. {$i file.inc}
  497. {*****************************************************************************
  498. Typed File Handling
  499. *****************************************************************************}
  500. {$i typefile.inc}
  501. {*****************************************************************************
  502. Text File Handling
  503. *****************************************************************************}
  504. {$DEFINE EOF_CTRLZ}
  505. {$i text.inc}
  506. {****************************************************************************
  507. Directory related routines.
  508. ****************************************************************************}
  509. {*****************************************************************************
  510. Directory Handling
  511. *****************************************************************************}
  512. procedure dosdir(func:byte;const s:string);
  513. var buffer:array[0..255] of char;
  514. begin
  515. move(s[1],buffer,length(s));
  516. buffer[length(s)]:=#0;
  517. allowslash(Pchar(@buffer));
  518. asm
  519. leal buffer,%edx
  520. movb func,%ah
  521. call syscall
  522. jnc .LDOS_DIRS1
  523. movw %ax,inoutres;
  524. .LDOS_DIRS1:
  525. end;
  526. end;
  527. procedure mkdir(const s : string);
  528. begin
  529. DosDir($39,s);
  530. end;
  531. procedure rmdir(const s : string);
  532. begin
  533. DosDir($3a,s);
  534. end;
  535. procedure chdir(const s : string);
  536. begin
  537. DosDir($3b,s);
  538. end;
  539. procedure getdir(drivenr : byte;var dir : shortstring);
  540. {Written by Michael Van Canneyt.}
  541. var temp:array[0..255] of char;
  542. sof:Pchar;
  543. i:byte;
  544. begin
  545. sof:=pchar(@dir[4]);
  546. { dir[1..3] will contain '[drivenr]:\', but is not }
  547. { supplied by DOS, so we let dos string start at }
  548. { dir[4] }
  549. { Get dir from drivenr : 0=default, 1=A etc... }
  550. asm
  551. movb drivenr,%dl
  552. movl sof,%esi
  553. mov $0x47,%ah
  554. call syscall
  555. end;
  556. { Now Dir should be filled with directory in ASCIIZ, }
  557. { starting from dir[4] }
  558. dir[0]:=#3;
  559. dir[2]:=':';
  560. dir[3]:='\';
  561. i:=4;
  562. {Conversion to Pascal string }
  563. while (dir[i]<>#0) do
  564. begin
  565. { convert path name to DOS }
  566. if dir[i]='/' then
  567. dir[i]:='\';
  568. dir[0]:=char(i);
  569. inc(i);
  570. end;
  571. { upcase the string (FPC function) }
  572. if not (FileNameCaseSensitive) then dir:=upcase(dir);
  573. if drivenr<>0 then { Drive was supplied. We know it }
  574. dir[1]:=char(65+drivenr-1)
  575. else
  576. begin
  577. { We need to get the current drive from DOS function 19H }
  578. { because the drive was the default, which can be unknown }
  579. asm
  580. movb $0x19,%ah
  581. call syscall
  582. addb $65,%al
  583. movb %al,i
  584. end;
  585. dir[1]:=char(i);
  586. end;
  587. end;
  588. {****************************************************************************
  589. System unit initialization.
  590. ****************************************************************************}
  591. function GetFileHandleCount: longint;
  592. var L1, L2: longint;
  593. begin
  594. L1 := 0; (* Don't change the amount, just check. *)
  595. if DosSetRelMaxFH (L1, L2) <> 0 then GetFileHandleCount := 50
  596. else GetFileHandleCount := L2;
  597. end;
  598. var pib:Pprocessinfoblock;
  599. tib:Pthreadinfoblock;
  600. begin
  601. {Determine the operating system we are running on.}
  602. asm
  603. movl $0,os_mode
  604. movw $0x7f0a,%ax
  605. call syscall
  606. testw $512,%bx {Bit 9 is OS/2 flag.}
  607. setnzb os_mode
  608. testw $4096,%bx
  609. jz .LnoRSX
  610. movl $2,os_mode
  611. .LnoRSX:
  612. end;
  613. {$ASMMODE DIRECT}
  614. {Enable the brk area by initializing it with the initial heap size.}
  615. asm
  616. movw $0x7f01,%ax
  617. movl HEAPSIZE,%edx
  618. addl __heap_base,%edx
  619. call ___SYSCALL
  620. cmpl $-1,%eax
  621. jnz Lheapok
  622. pushl $204
  623. {call RUNERROR$$WORD}
  624. Lheapok:
  625. end;
  626. {$ASMMODE ATT}
  627. {Now request, if we are running under DOS,
  628. read-access to the first meg. of memory.}
  629. if os_mode in [osDOS,osDPMI] then
  630. asm
  631. movw $0x7f13,%ax
  632. xorl %ebx,%ebx
  633. movl $0xfff,%ecx
  634. xorl %edx,%edx
  635. call syscall
  636. movl %eax,first_meg
  637. end
  638. else
  639. begin
  640. first_meg := nil;
  641. (* Initialize the amount of file handles *)
  642. FileHandleCount := GetFileHandleCount;
  643. end;
  644. {At 0.9.2, case for enumeration does not work.}
  645. case os_mode of
  646. osDOS:
  647. stackbottom:=0; {In DOS mode, heap_brk is also the
  648. stack bottom.}
  649. osOS2:
  650. begin
  651. dosgetinfoblocks(tib,pib);
  652. stackbottom:=longint(tib^.stack);
  653. end;
  654. osDPMI:
  655. stackbottom:=0; {Not sure how to get it, but seems to be
  656. always zero.}
  657. end;
  658. exitproc:=nil;
  659. {Initialize the heap.}
  660. initheap;
  661. { ... and exceptions }
  662. InitExceptions;
  663. { to test stack depth }
  664. loweststack:=maxlongint;
  665. OpenStdIO(Input,fmInput,StdInputHandle);
  666. OpenStdIO(Output,fmOutput,StdOutputHandle);
  667. OpenStdIO(StdOut,fmOutput,StdOutputHandle);
  668. OpenStdIO(StdErr,fmOutput,StdErrorHandle);
  669. { no I/O-Error }
  670. inoutres:=0;
  671. end.
  672. {
  673. $Log$
  674. Revision 1.3 2000-09-29 21:49:41 jonas
  675. * removed warnings
  676. Revision 1.2 2000/07/14 10:33:11 michael
  677. + Conditionals fixed
  678. Revision 1.1 2000/07/13 06:31:07 michael
  679. + Initial import
  680. }