sysutils.pp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743
  1. {
  2. $Id$
  3. This file is part of the Free Pascal run time library.
  4. Copyright (c) 1999-2000 by Florian Klaempfl
  5. member of the Free Pascal development team
  6. Sysutils unit for Go32v2
  7. See the file COPYING.FPC, included in this distribution,
  8. for details about the copyright.
  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.
  12. **********************************************************************}
  13. unit sysutils;
  14. interface
  15. {$MODE objfpc}
  16. { force ansistrings }
  17. {$H+}
  18. uses
  19. go32,dos;
  20. { Include platform independent interface part }
  21. {$i sysutilh.inc}
  22. implementation
  23. { Include platform independent implementation part }
  24. {$i sysutils.inc}
  25. {****************************************************************************
  26. File Functions
  27. ****************************************************************************}
  28. { some internal constants }
  29. const
  30. ofRead = $0000; { Open for reading }
  31. ofWrite = $0001; { Open for writing }
  32. ofReadWrite = $0002; { Open for reading/writing }
  33. faFail = $0000; { Fail if file does not exist }
  34. faCreate = $0010; { Create if file does not exist }
  35. faOpen = $0001; { Open if file exists }
  36. faOpenReplace = $0002; { Clear if file exists }
  37. Type
  38. PSearchrec = ^Searchrec;
  39. { converts S to a pchar and copies it to the transfer-buffer. }
  40. procedure StringToTB(const S: string);
  41. var
  42. P: pchar;
  43. Len: integer;
  44. begin
  45. Len := Length(S) + 1;
  46. P := StrPCopy(StrAlloc(Len), S);
  47. SysCopyToDos(longint(P), Len);
  48. StrDispose(P);
  49. end ;
  50. { Native OpenFile function.
  51. if return value <> 0 call failed. }
  52. function OpenFile(const FileName: string; var Handle: longint; Mode, Action: word): longint;
  53. var
  54. Regs: registers;
  55. begin
  56. result := 0;
  57. Handle := 0;
  58. StringToTB(FileName);
  59. if LFNSupport then Regs.Eax:=$716c
  60. else Regs.Eax:=$6c00;
  61. Regs.Edx := Action; { Action if file exists/not exists }
  62. Regs.Ds := tb_segment;
  63. Regs.Esi := tb_offset;
  64. Regs.Ebx := $2000 + (Mode and $ff); { file open mode }
  65. Regs.Ecx := $20; { Attributes }
  66. RealIntr($21, Regs);
  67. if Regs.Flags and CarryFlag <> 0 then result := Regs.Ax
  68. else Handle := Regs.Ax;
  69. end ;
  70. Function FileOpen (Const FileName : string; Mode : Integer) : Longint;
  71. var
  72. e: integer;
  73. Begin
  74. e := OpenFile(FileName, result, Mode, faOpen);
  75. if e <> 0 then
  76. result := -1;
  77. end;
  78. Function FileCreate (Const FileName : String) : Longint;
  79. var
  80. e: integer;
  81. begin
  82. e := OpenFile(FileName, result, ofReadWrite, faCreate or faOpenReplace);
  83. if e <> 0 then
  84. result := -1;
  85. end;
  86. Function FileRead (Handle : Longint; Var Buffer; Count : longint) : Longint;
  87. var
  88. regs : registers;
  89. size,
  90. readsize : longint;
  91. begin
  92. readsize:=0;
  93. while Count > 0 do
  94. begin
  95. if Count>tb_size then
  96. size:=tb_size
  97. else
  98. size:=Count;
  99. regs.realecx:=size;
  100. regs.realedx:=tb_offset;
  101. regs.realds:=tb_segment;
  102. regs.realebx:=Handle;
  103. regs.realeax:=$3f00;
  104. RealIntr($21,regs);
  105. if (regs.realflags and carryflag) <> 0 then
  106. begin
  107. Result:=-1;
  108. exit;
  109. end;
  110. syscopyfromdos(Longint(@Buffer)+readsize,lo(regs.realeax));
  111. inc(readsize,lo(regs.realeax));
  112. dec(Count,lo(regs.realeax));
  113. { stop when not the specified size is read }
  114. if lo(regs.realeax)<size then
  115. break;
  116. end;
  117. Result:=readsize;
  118. end;
  119. Function FileWrite (Handle : Longint; const Buffer; Count : Longint) : Longint;
  120. var
  121. regs : registers;
  122. size,
  123. writesize : longint;
  124. begin
  125. writesize:=0;
  126. while Count > 0 do
  127. begin
  128. if Count>tb_size then
  129. size:=tb_size
  130. else
  131. size:=Count;
  132. syscopytodos(Longint(@Buffer)+writesize,size);
  133. regs.realecx:=size;
  134. regs.realedx:=tb_offset;
  135. regs.realds:=tb_segment;
  136. regs.realebx:=Handle;
  137. regs.realeax:=$4000;
  138. RealIntr($21,regs);
  139. if (regs.realflags and carryflag) <> 0 then
  140. begin
  141. Result:=-1;
  142. exit;
  143. end;
  144. inc(writesize,lo(regs.realeax));
  145. dec(Count,lo(regs.realeax));
  146. { stop when not the specified size is written }
  147. if lo(regs.realeax)<size then
  148. break;
  149. end;
  150. Result:=WriteSize;
  151. end;
  152. Function FileSeek (Handle, FOffset, Origin : Longint) : Longint;
  153. var
  154. Regs: registers;
  155. begin
  156. Regs.Eax := $4200;
  157. Regs.Al := Origin;
  158. Regs.Edx := Lo(FOffset);
  159. Regs.Ecx := Hi(FOffset);
  160. Regs.Ebx := Handle;
  161. RealIntr($21, Regs);
  162. if Regs.Flags and CarryFlag <> 0 then
  163. result := -1
  164. else begin
  165. LongRec(result).Lo := Regs.Ax;
  166. LongRec(result).Hi := Regs.Dx;
  167. end ;
  168. end;
  169. Function FileSeek (Handle : Longint; FOffset,Origin : Int64) : Int64;
  170. begin
  171. {$warning need to add 64bit call }
  172. FileSeek:=FileSeek(Handle,Longint(FOffset),Longint(Origin));
  173. end;
  174. Procedure FileClose (Handle : Longint);
  175. var
  176. Regs: registers;
  177. begin
  178. if Handle<=4 then
  179. exit;
  180. Regs.Eax := $3e00;
  181. Regs.Ebx := Handle;
  182. RealIntr($21, Regs);
  183. end;
  184. Function FileTruncate (Handle,Size: Longint) : boolean;
  185. var
  186. regs : trealregs;
  187. begin
  188. FileSeek(Handle,Size,0);
  189. Regs.realecx := 0;
  190. Regs.realedx := tb_offset;
  191. Regs.ds := tb_segment;
  192. Regs.ebx := Handle;
  193. Regs.eax:=$4000;
  194. RealIntr($21, Regs);
  195. FileTruncate:=(regs.realflags and carryflag)=0;
  196. end;
  197. Function FileAge (Const FileName : String): Longint;
  198. var Handle: longint;
  199. begin
  200. Handle := FileOpen(FileName, 0);
  201. if Handle <> -1 then
  202. begin
  203. result := FileGetDate(Handle);
  204. FileClose(Handle);
  205. end
  206. else
  207. result := -1;
  208. end;
  209. Function FileExists (Const FileName : String) : Boolean;
  210. var Handle: longint;
  211. begin
  212. //!! This can be done quicker, need to find out how
  213. Result := (OpenFile(FileName, Handle, ofRead, faOpen) = 0);
  214. if Handle <> 0 then
  215. FileClose(Handle);
  216. end;
  217. Function FindFirst (Const Path : String; Attr : Longint; Var Rslt : TSearchRec) : Longint;
  218. Var Sr : PSearchrec;
  219. begin
  220. //!! Sr := New(PSearchRec);
  221. getmem(sr,sizeof(searchrec));
  222. Rslt.FindHandle := longint(Sr);
  223. DOS.FindFirst(Path, Attr, Sr^);
  224. result := -DosError;
  225. if result = 0 then
  226. begin
  227. Rslt.Time := Sr^.Time;
  228. Rslt.Size := Sr^.Size;
  229. Rslt.Attr := Sr^.Attr;
  230. Rslt.ExcludeAttr := 0;
  231. Rslt.Name := Sr^.Name;
  232. end ;
  233. end;
  234. Function FindNext (Var Rslt : TSearchRec) : Longint;
  235. var
  236. Sr: PSearchRec;
  237. begin
  238. Sr := PSearchRec(Rslt.FindHandle);
  239. if Sr <> nil then
  240. begin
  241. DOS.FindNext(Sr^);
  242. result := -DosError;
  243. if result = 0 then
  244. begin
  245. Rslt.Time := Sr^.Time;
  246. Rslt.Size := Sr^.Size;
  247. Rslt.Attr := Sr^.Attr;
  248. Rslt.ExcludeAttr := 0;
  249. Rslt.Name := Sr^.Name;
  250. end;
  251. end;
  252. end;
  253. Procedure FindClose (Var F : TSearchrec);
  254. var
  255. Sr: PSearchRec;
  256. begin
  257. Sr := PSearchRec(F.FindHandle);
  258. if Sr <> nil then
  259. begin
  260. //!! Dispose(Sr);
  261. // This call is non dummy if LFNSupport is true PM
  262. DOS.FindClose(SR^);
  263. freemem(sr,sizeof(searchrec));
  264. end;
  265. F.FindHandle := 0;
  266. end;
  267. Function FileGetDate (Handle : Longint) : Longint;
  268. var
  269. Regs: registers;
  270. begin
  271. //!! for win95 an alternative function is available.
  272. Regs.Ebx := Handle;
  273. Regs.Eax := $5700;
  274. RealIntr($21, Regs);
  275. if Regs.Flags and CarryFlag <> 0 then
  276. result := -1
  277. else
  278. begin
  279. LongRec(result).Lo := Regs.cx;
  280. LongRec(result).Hi := Regs.dx;
  281. end ;
  282. end;
  283. Function FileSetDate (Handle, Age : Longint) : Longint;
  284. var
  285. Regs: registers;
  286. begin
  287. Regs.Ebx := Handle;
  288. Regs.Eax := $5701;
  289. Regs.Ecx := Lo(Age);
  290. Regs.Edx := Hi(Age);
  291. RealIntr($21, Regs);
  292. if Regs.Flags and CarryFlag <> 0 then
  293. result := -Regs.Ax
  294. else
  295. result := 0;
  296. end;
  297. Function FileGetAttr (Const FileName : String) : Longint;
  298. var
  299. Regs: registers;
  300. begin
  301. StringToTB(FileName);
  302. Regs.Edx := tb_offset;
  303. Regs.Ds := tb_segment;
  304. if LFNSupport then
  305. begin
  306. Regs.Ax := $7143;
  307. Regs.Bx := 0;
  308. end
  309. else
  310. Regs.Ax := $4300;
  311. RealIntr($21, Regs);
  312. if Regs.Flags and CarryFlag <> 0 then
  313. result := -1
  314. else
  315. result := Regs.Cx;
  316. end;
  317. Function FileSetAttr (Const Filename : String; Attr: longint) : Longint;
  318. var
  319. Regs: registers;
  320. begin
  321. StringToTB(FileName);
  322. Regs.Edx := tb_offset;
  323. Regs.Ds := tb_segment;
  324. if LFNSupport then
  325. begin
  326. Regs.Ax := $7143;
  327. Regs.Bx := 1;
  328. end
  329. else
  330. Regs.Ax := $4301;
  331. Regs.Cx := Attr;
  332. RealIntr($21, Regs);
  333. if Regs.Flags and CarryFlag <> 0 then
  334. result := -Regs.Ax
  335. else
  336. result := 0;
  337. end;
  338. Function DeleteFile (Const FileName : String) : Boolean;
  339. var
  340. Regs: registers;
  341. begin
  342. StringToTB(FileName);
  343. Regs.Edx := tb_offset;
  344. Regs.Ds := tb_segment;
  345. if LFNSupport then
  346. Regs.Eax := $7141
  347. else
  348. Regs.Eax := $4100;
  349. Regs.Esi := 0;
  350. Regs.Ecx := 0;
  351. RealIntr($21, Regs);
  352. result := (Regs.Flags and CarryFlag = 0);
  353. end;
  354. Function RenameFile (Const OldName, NewName : String) : Boolean;
  355. var
  356. Regs: registers;
  357. begin
  358. StringToTB(OldName + #0 + NewName);
  359. Regs.Edx := tb_offset;
  360. Regs.Ds := tb_segment;
  361. Regs.Edi := tb_offset + Length(OldName) + 1;
  362. Regs.Es := tb_segment;
  363. if LFNSupport then
  364. Regs.Eax := $7156
  365. else
  366. Regs.Eax := $5600;
  367. Regs.Ecx := $ff;
  368. RealIntr($21, Regs);
  369. result := (Regs.Flags and CarryFlag = 0);
  370. end;
  371. Function FileSearch (Const Name, DirList : String) : String;
  372. begin
  373. result := DOS.FSearch(Name, DirList);
  374. end;
  375. {****************************************************************************
  376. Disk Functions
  377. ****************************************************************************}
  378. TYPE ExtendedFat32FreeSpaceRec=packed Record
  379. RetSize : WORD; { (ret) size of returned structure}
  380. Strucversion : WORD; {(call) structure version (0000h)
  381. (ret) actual structure version (0000h)}
  382. SecPerClus, {number of sectors per cluster}
  383. BytePerSec, {number of bytes per sector}
  384. AvailClusters, {number of available clusters}
  385. TotalClusters, {total number of clusters on the drive}
  386. AvailPhysSect, {physical sectors available on the drive}
  387. TotalPhysSect, {total physical sectors on the drive}
  388. AvailAllocUnits, {Available allocation units}
  389. TotalAllocUnits : DWORD; {Total allocation units}
  390. Dummy,Dummy2 : DWORD; {8 bytes reserved}
  391. END;
  392. function do_diskdata(drive : byte; Free : BOOLEAN) : Int64;
  393. VAR S : String;
  394. Rec : ExtendedFat32FreeSpaceRec;
  395. regs : registers;
  396. BEGIN
  397. if (swap(dosversion)>=$070A) AND LFNSupport then
  398. begin
  399. DosError:=0;
  400. S:='C:\'#0;
  401. if Drive=0 then
  402. begin
  403. GetDir(Drive,S);
  404. Setlength(S,4);
  405. S[4]:=#0;
  406. end
  407. else
  408. S[1]:=chr(Drive+64);
  409. Rec.Strucversion:=0;
  410. dosmemput(tb_segment,tb_offset,Rec,SIZEOF(ExtendedFat32FreeSpaceRec));
  411. dosmemput(tb_segment,tb_offset+Sizeof(ExtendedFat32FreeSpaceRec)+1,S[1],4);
  412. regs.dx:=tb_offset+Sizeof(ExtendedFat32FreeSpaceRec)+1;
  413. regs.ds:=tb_segment;
  414. regs.di:=tb_offset;
  415. regs.es:=tb_segment;
  416. regs.cx:=Sizeof(ExtendedFat32FreeSpaceRec);
  417. regs.ax:=$7303;
  418. msdos(regs);
  419. if regs.ax<>$ffff then
  420. begin
  421. copyfromdos(rec,Sizeof(ExtendedFat32FreeSpaceRec));
  422. if Free then
  423. Do_DiskData:=int64(rec.AvailAllocUnits)*rec.SecPerClus*rec.BytePerSec
  424. else
  425. Do_DiskData:=int64(rec.TotalAllocUnits)*rec.SecPerClus*rec.BytePerSec;
  426. end
  427. else
  428. Do_DiskData:=-1;
  429. end
  430. else
  431. begin
  432. DosError:=0;
  433. regs.dl:=drive;
  434. regs.ah:=$36;
  435. msdos(regs);
  436. if regs.ax<>$FFFF then
  437. begin
  438. if Free then
  439. Do_DiskData:=int64(regs.ax)*regs.bx*regs.cx
  440. else
  441. Do_DiskData:=int64(regs.ax)*regs.cx*regs.dx;
  442. end
  443. else
  444. do_diskdata:=-1;
  445. end;
  446. end;
  447. function diskfree(drive : byte) : int64;
  448. begin
  449. diskfree:=Do_DiskData(drive,TRUE);
  450. end;
  451. function disksize(drive : byte) : int64;
  452. begin
  453. disksize:=Do_DiskData(drive,false);
  454. end;
  455. Function GetCurrentDir : String;
  456. begin
  457. GetDir(0, result);
  458. end;
  459. Function SetCurrentDir (Const NewDir : String) : Boolean;
  460. begin
  461. {$I-}
  462. ChDir(NewDir);
  463. {$I+}
  464. result := (IOResult = 0);
  465. end;
  466. Function CreateDir (Const NewDir : String) : Boolean;
  467. begin
  468. {$I-}
  469. MkDir(NewDir);
  470. {$I+}
  471. result := (IOResult = 0);
  472. end;
  473. Function RemoveDir (Const Dir : String) : Boolean;
  474. begin
  475. {$I-}
  476. RmDir(Dir);
  477. {$I+}
  478. result := (IOResult = 0);
  479. end;
  480. {****************************************************************************
  481. Time Functions
  482. ****************************************************************************}
  483. Procedure GetLocalTime(var SystemTime: TSystemTime);
  484. var
  485. Regs: Registers;
  486. begin
  487. Regs.ah := $2C;
  488. RealIntr($21, Regs);
  489. SystemTime.Hour := Regs.Ch;
  490. SystemTime.Minute := Regs.Cl;
  491. SystemTime.Second := Regs.Dh;
  492. SystemTime.MilliSecond := Regs.Dl;
  493. Regs.ah := $2A;
  494. RealIntr($21, Regs);
  495. SystemTime.Year := Regs.Cx;
  496. SystemTime.Month := Regs.Dh;
  497. SystemTime.Day := Regs.Dl;
  498. end ;
  499. {****************************************************************************
  500. Misc Functions
  501. ****************************************************************************}
  502. procedure Beep;
  503. begin
  504. end;
  505. {****************************************************************************
  506. Locale Functions
  507. ****************************************************************************}
  508. { Codepage constants }
  509. const
  510. CP_US = 437;
  511. CP_MultiLingual = 850;
  512. CP_SlavicLatin2 = 852;
  513. CP_Turkish = 857;
  514. CP_Portugal = 860;
  515. CP_IceLand = 861;
  516. CP_Canada = 863;
  517. CP_NorwayDenmark = 865;
  518. { CountryInfo }
  519. type
  520. TCountryInfo = packed record
  521. InfoId: byte;
  522. case integer of
  523. 1: ( Size: word;
  524. CountryId: word;
  525. CodePage: word;
  526. CountryInfo: array[0..33] of byte );
  527. 2: ( UpperCaseTable: longint );
  528. 4: ( FilenameUpperCaseTable: longint );
  529. 5: ( FilecharacterTable: longint );
  530. 6: ( CollatingTable: longint );
  531. 7: ( DBCSLeadByteTable: longint );
  532. end ;
  533. procedure GetExtendedCountryInfo(InfoId: integer; CodePage, CountryId: word; var CountryInfo: TCountryInfo);
  534. Var Regs: Registers;
  535. begin
  536. Regs.AH := $65;
  537. Regs.AL := InfoId;
  538. Regs.BX := CodePage;
  539. Regs.DX := CountryId;
  540. Regs.ES := transfer_buffer div 16;
  541. Regs.DI := transfer_buffer and 15;
  542. Regs.CX := SizeOf(TCountryInfo);
  543. RealIntr($21, Regs);
  544. DosMemGet(transfer_buffer div 16,
  545. transfer_buffer and 15,
  546. CountryInfo, Regs.CX );
  547. end;
  548. procedure InitAnsi;
  549. var
  550. CountryInfo: TCountryInfo; i: integer;
  551. begin
  552. { Fill table entries 0 to 127 }
  553. for i := 0 to 96 do
  554. UpperCaseTable[i] := chr(i);
  555. for i := 97 to 122 do
  556. UpperCaseTable[i] := chr(i - 32);
  557. for i := 123 to 127 do
  558. UpperCaseTable[i] := chr(i);
  559. for i := 0 to 64 do
  560. LowerCaseTable[i] := chr(i);
  561. for i := 65 to 90 do
  562. LowerCaseTable[i] := chr(i + 32);
  563. for i := 91 to 255 do
  564. LowerCaseTable[i] := chr(i);
  565. { Get country and codepage info }
  566. GetExtendedCountryInfo(1, $FFFF, $FFFF, CountryInfo);
  567. if CountryInfo.CodePage = 850 then
  568. begin
  569. { Special, known case }
  570. Move(CP850UCT, UpperCaseTable[128], 128);
  571. Move(CP850LCT, LowerCaseTable[128], 128);
  572. end
  573. else
  574. begin
  575. { this needs to be checked !!
  576. this is correct only if UpperCaseTable is
  577. and Offset:Segment word record (PM) }
  578. { get the uppercase table from dosmemory }
  579. GetExtendedCountryInfo(2, $FFFF, $FFFF, CountryInfo);
  580. DosMemGet(CountryInfo.UpperCaseTable shr 16, 2 + CountryInfo.UpperCaseTable and 65535, UpperCaseTable[128], 128);
  581. for i := 128 to 255 do
  582. begin
  583. if UpperCaseTable[i] <> chr(i) then
  584. LowerCaseTable[ord(UpperCaseTable[i])] := chr(i);
  585. end;
  586. end;
  587. end;
  588. Procedure InitInternational;
  589. begin
  590. InitAnsi;
  591. end;
  592. function SysErrorMessage(ErrorCode: Integer): String;
  593. begin
  594. Result:=Format(SUnknownErrorCode,[ErrorCode]);
  595. end;
  596. {****************************************************************************
  597. Os utils
  598. ****************************************************************************}
  599. Function GetEnvironmentVariable(Const EnvVar : String) : String;
  600. begin
  601. Result:=getenv(EnvVar);
  602. end;
  603. {****************************************************************************
  604. Initialization code
  605. ****************************************************************************}
  606. Initialization
  607. InitExceptions; { Initialize exceptions. OS independent }
  608. InitInternational; { Initialize internationalization settings }
  609. Finalization
  610. DoneExceptions;
  611. end.
  612. {
  613. $Log$
  614. Revision 1.7 2002-01-19 11:57:55 peter
  615. * merged fixes
  616. Revision 1.6 2001/10/25 21:23:49 peter
  617. * added 64bit fileseek
  618. Revision 1.5 2001/06/03 15:18:01 peter
  619. * eoutofmemory and einvalidpointer fix
  620. Revision 1.4 2001/02/20 22:14:19 peter
  621. * merged getenvironmentvariable
  622. Revision 1.3 2000/08/30 06:29:19 michael
  623. + Merged syserrormsg fix
  624. Revision 1.2 2000/08/20 15:46:46 peter
  625. * sysutils.pp moved to target and merged with disk.inc, filutil.inc
  626. Revision 1.1.2.2 2000/08/22 19:21:48 michael
  627. + Implemented syserrormessage. Made dummies for go32v2 and OS/2
  628. * Changed linux/errors.pp so it uses pchars for storage.
  629. Revision 1.1.2.1 2000/08/20 15:08:32 peter
  630. * forgot the add command :(
  631. }