dos.pp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 1999-2004 by the Free Pascal development team.
  4. Dos unit for BP7 compatible RTL (novell netware libc)
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {$IFNDEF FPC_DOTTEDUNITS}
  12. unit dos;
  13. {$ENDIF FPC_DOTTEDUNITS}
  14. interface
  15. {$IFDEF FPC_DOTTEDUNITS}
  16. uses NetwareLibCApi.libc;
  17. {$ELSE FPC_DOTTEDUNITS}
  18. uses libc;
  19. {$ENDIF FPC_DOTTEDUNITS}
  20. Type
  21. searchrec = packed record
  22. DirP : POINTER; { used for opendir }
  23. EntryP: POINTER; { and readdir }
  24. Magic : WORD;
  25. fill : array[1..11] of byte;
  26. attr : byte;
  27. time : longint;
  28. size : longint;
  29. name : string[255];
  30. { Internals used by netware port only: }
  31. _mask : string[255];
  32. _dir : string[255];
  33. _attr : word;
  34. end;
  35. {$i dosh.inc}
  36. {Extra Utils}
  37. function weekday(y,m,d : longint) : longint;
  38. implementation
  39. {$IFDEF FPC_DOTTEDUNITS}
  40. uses
  41. System.Strings;
  42. {$ELSE FPC_DOTTEDUNITS}
  43. uses
  44. strings;
  45. {$ENDIF FPC_DOTTEDUNITS}
  46. {$DEFINE HAS_GETMSCOUNT}
  47. {$DEFINE HAS_KEEP}
  48. {$DEFINE FPC_FEXPAND_DRIVES}
  49. {$DEFINE FPC_FEXPAND_VOLUMES}
  50. {$DEFINE FPC_FEXPAND_NO_DEFAULT_PATHS}
  51. {$i dos.inc}
  52. {$ASMMODE ATT}
  53. {*****************************************************************************
  54. --- Info / Date / Time ---
  55. ******************************************************************************}
  56. {$PACKRECORDS 4}
  57. function dosversion : word;
  58. var i : Tutsname;
  59. begin
  60. if Fpuname (i) >= 0 then
  61. dosversion := WORD (i.netware_minor) SHL 8 + i.netware_major
  62. else dosversion := $0005;
  63. end;
  64. function WeekDay (y,m,d:longint):longint;
  65. {
  66. Calculates th day of the week. returns -1 on error
  67. }
  68. var
  69. u,v : longint;
  70. begin
  71. if (m<1) or (m>12) or (y<1600) or (y>4000) or
  72. (d<1) or (d>30+((m+ord(m>7)) and 1)-ord(m=2)) or
  73. ((m*d=58) and (((y mod 4>0) or (y mod 100=0)) and (y mod 400>0))) then
  74. WeekDay:=-1
  75. else
  76. begin
  77. u:=m;
  78. v:=y;
  79. if m<3 then
  80. begin
  81. inc(u,12);
  82. dec(v);
  83. end;
  84. WeekDay:=(d+2*u+((3*(u+1)) div 5)+v+(v div 4)-(v div 100)+(v div 400)+1) mod 7;
  85. end;
  86. end;
  87. procedure getdate(var year,month,mday,wday : word);
  88. var
  89. t : TTime_t;
  90. tm : Ttm;
  91. begin
  92. time(t); localtime_r(t,tm);
  93. with tm do
  94. begin
  95. year := tm_year+1900;
  96. month := tm_mon+1;
  97. mday := tm_mday;
  98. wday := tm_wday;
  99. end;
  100. end;
  101. procedure setdate(year,month,day : word);
  102. begin
  103. end;
  104. procedure gettime(var hour,minute,second,sec100 : word);
  105. var
  106. t : TTime_t;
  107. tm : Ttm;
  108. begin
  109. time(t); localtime_r(t,tm);
  110. with tm do
  111. begin
  112. hour := tm_hour;
  113. minute := tm_min;
  114. second := tm_sec;
  115. sec100 := 0;
  116. end;
  117. end;
  118. procedure settime(hour,minute,second,sec100 : word);
  119. begin
  120. end;
  121. function GetMsCount: int64;
  122. var
  123. tv : TimeVal;
  124. tz : TimeZone;
  125. begin
  126. FPGetTimeOfDay (tv, tz);
  127. GetMsCount := int64 (tv.tv_Sec) * 1000 + tv.tv_uSec div 1000;
  128. end;
  129. {******************************************************************************
  130. --- Exec ---
  131. ******************************************************************************}
  132. const maxargs=256;
  133. procedure exec(const path : pathstr;const comline : comstr);
  134. var c : comstr;
  135. i : integer;
  136. args : array[0..maxargs] of PAnsiChar;
  137. arg0 : pathstr;
  138. numargs,wstat : integer;
  139. Wiring : TWiring;
  140. begin
  141. if pos ('.',path) = 0 then
  142. arg0 := fexpand(path+'.nlm'#0) else
  143. arg0 := fexpand (path)+#0;
  144. //writeln (stderr,'dos.exec (',path,',',comline,') arg0:"',copy(arg0,1,length(arg0)-1),'"');
  145. args[0] := @arg0[1];
  146. numargs := 0;
  147. c:=comline;
  148. i:=1;
  149. while i<=length(c) do
  150. begin
  151. if c[i]<>' ' then
  152. begin
  153. {Commandline argument found. append #0 and set pointer in args }
  154. inc(numargs);
  155. args[numargs]:=@c[i];
  156. while (i<=length(c)) and (c[i]<>' ') do
  157. inc(i);
  158. c[i] := #0;
  159. end;
  160. inc(i);
  161. end;
  162. args[numargs+1] := nil;
  163. // i := spawnvp (P_WAIT,args[0],@args);
  164. Wiring.infd := StdInputHandle; //textrec(Stdin).Handle;
  165. Wiring.outfd:= textrec(stdout).Handle;
  166. Wiring.errfd:= textrec(stderr).Handle;
  167. //writeln (stderr,'calling procve');
  168. i := procve(args[0],
  169. PROC_CURRENT_SPACE+PROC_INHERIT_CWD,
  170. envP, // const AnsiChar * env[] If passed as NULL, the child process inherits the parent.s environment at the time of the call.
  171. @Wiring, // wiring_t *wiring, Pass NULL to inherit system defaults for wiring.
  172. nil, // struct fd_set *fds, Not currently implemented. Pass in NULL.
  173. nil, // void *appdata, Not currently implemented. Pass in NULL.
  174. 0, // size_t appdata_size, Not currently implemented. Pass in 0
  175. nil, // void *reserved, Reserved. Pass NULL.
  176. @args); // const AnsiChar *argv[]
  177. //writeln (stderr,'Ok');
  178. if i <> -1 then
  179. begin
  180. Fpwaitpid(i,@wstat,0);
  181. doserror := 0;
  182. lastdosexitcode := wstat;
  183. end else
  184. begin
  185. doserror := 8; // for now, what about errno ?
  186. end;
  187. end;
  188. {******************************************************************************
  189. --- Disk ---
  190. ******************************************************************************}
  191. function getvolnum (drive : byte) : longint;
  192. {var dir : STRING[255];
  193. P,PS,
  194. V : LONGINT;}
  195. begin
  196. {if drive = 0 then
  197. begin // get volume name from current directory (i.e. SERVER-NAME/VOL2:TEST)
  198. getdir (0,dir);
  199. p := pos (':', dir);
  200. if p = 0 then
  201. begin
  202. getvolnum := -1;
  203. exit;
  204. end;
  205. byte (dir[0]) := p-1;
  206. dir[p] := #0;
  207. PS := pos ('/', dir);
  208. INC (PS);
  209. if _GetVolumeNumber (@dir[PS], V) <> 0 then
  210. getvolnum := -1
  211. else
  212. getvolnum := V;
  213. end else
  214. getvolnum := drive-1;}
  215. getvolnum := -1;
  216. end;
  217. function diskfree(drive : byte) : int64;
  218. {VAR Buf : ARRAY [0..255] OF AnsiChar;
  219. TotalBlocks : WORD;
  220. SectorsPerBlock : WORD;
  221. availableBlocks : WORD;
  222. totalDirectorySlots : WORD;
  223. availableDirSlots : WORD;
  224. volumeisRemovable : WORD;
  225. volumeNumber : LONGINT;}
  226. begin
  227. // volumeNumber := getvolnum (drive);
  228. (*
  229. if volumeNumber >= 0 then
  230. begin
  231. {i think thats not the right function but for others i need a connection handle}
  232. if _GetVolumeInfoWithNumber (byte(volumeNumber),@Buf,
  233. TotalBlocks,
  234. SectorsPerBlock,
  235. availableBlocks,
  236. totalDirectorySlots,
  237. availableDirSlots,
  238. volumeisRemovable) = 0 THEN
  239. begin
  240. diskfree := int64 (availableBlocks) * int64 (SectorsPerBlock) * 512;
  241. end else
  242. diskfree := 0;
  243. end else*)
  244. diskfree := 0;
  245. end;
  246. function disksize(drive : byte) : int64;
  247. {VAR Buf : ARRAY [0..255] OF AnsiChar;
  248. TotalBlocks : WORD;
  249. SectorsPerBlock : WORD;
  250. availableBlocks : WORD;
  251. totalDirectorySlots : WORD;
  252. availableDirSlots : WORD;
  253. volumeisRemovable : WORD;
  254. volumeNumber : LONGINT;}
  255. begin
  256. (*
  257. volumeNumber := getvolnum (drive);
  258. if volumeNumber >= 0 then
  259. begin
  260. {i think thats not the right function but for others i need a connection handle}
  261. if _GetVolumeInfoWithNumber (byte(volumeNumber),@Buf,
  262. TotalBlocks,
  263. SectorsPerBlock,
  264. availableBlocks,
  265. totalDirectorySlots,
  266. availableDirSlots,
  267. volumeisRemovable) = 0 THEN
  268. begin
  269. disksize := int64 (TotalBlocks) * int64 (SectorsPerBlock) * 512;
  270. end else
  271. disksize := 0;
  272. end else*)
  273. disksize := 0;
  274. end;
  275. {******************************************************************************
  276. --- Utils ---
  277. ******************************************************************************}
  278. procedure timet2dostime (timet:longint; var dostime : longint);
  279. var tm : Ttm;
  280. begin
  281. localtime_r(timet,tm);
  282. dostime:=(tm.tm_sec shr 1)+(tm.tm_min shl 5)+(tm.tm_hour shl 11)+(tm.tm_mday shl 16)+((tm.tm_mon+1) shl 21)+((tm.tm_year+1900-1980) shl 25);
  283. end;
  284. function nwattr2dosattr (nwattr : longint) : word;
  285. begin
  286. nwattr2dosattr := 0;
  287. if nwattr and M_A_RDONLY > 0 then nwattr2dosattr := nwattr2dosattr + readonly;
  288. if nwattr and M_A_HIDDEN > 0 then nwattr2dosattr := nwattr2dosattr + hidden;
  289. if nwattr and M_A_SYSTEM > 0 then nwattr2dosattr := nwattr2dosattr + sysfile;
  290. if nwattr and M_A_SUBDIR > 0 then nwattr2dosattr := nwattr2dosattr + directory;
  291. if nwattr and M_A_ARCH > 0 then nwattr2dosattr := nwattr2dosattr + archive;
  292. end;
  293. {******************************************************************************
  294. --- Findfirst FindNext ---
  295. ******************************************************************************}
  296. {returns true if attributes match}
  297. function find_setfields (var f : searchRec) : boolean;
  298. var
  299. StatBuf : TStat;
  300. fname : string[255];
  301. begin
  302. find_setfields := false;
  303. with F do
  304. begin
  305. if Magic = $AD01 then
  306. begin
  307. attr := nwattr2dosattr (Pdirent(EntryP)^.d_mode);
  308. size := Pdirent(EntryP)^.d_size;
  309. name := strpas (Pdirent(EntryP)^.d_name);
  310. doserror := 0;
  311. fname := f._dir + f.name;
  312. if length (fname) = 255 then dec (byte(fname[0]));
  313. fname := fname + #0;
  314. if Fpstat (@fname[1],StatBuf) = 0 then
  315. timet2dostime (StatBuf.st_mtim.tv_sec, time)
  316. else
  317. time := 0;
  318. if (f._attr and hidden) = 0 then
  319. if attr and hidden > 0 then exit;
  320. if (f._attr and Directory) = 0 then
  321. if attr and Directory > 0 then exit;
  322. if (f._attr and SysFile) = 0 then
  323. if attr and SysFile > 0 then exit;
  324. find_setfields := true;
  325. end else
  326. begin
  327. FillChar (f,sizeof(f),0);
  328. doserror := 18;
  329. end;
  330. end;
  331. end;
  332. procedure findfirst(const path : pathstr;attr : word;var f : searchRec);
  333. var
  334. path0 : array[0..256] of AnsiChar;
  335. p : longint;
  336. begin
  337. IF path = '' then
  338. begin
  339. doserror := 18;
  340. exit;
  341. end;
  342. f._attr := attr;
  343. p := length (path);
  344. while (p > 0) and (not (path[p] in AllowDirectorySeparators)) do
  345. dec (p);
  346. if p > 0 then
  347. begin
  348. f._mask := copy (path,p+1,255);
  349. f._dir := copy (path,1,p);
  350. strpcopy(path0,f._dir);
  351. end else
  352. begin
  353. f._mask := path;
  354. getdir (0,f._dir);
  355. if (f._dir[length(f._dir)] <> '/') and
  356. (f._dir[length(f._dir)] <> '\') then
  357. f._dir := f._dir + '/';
  358. strpcopy(path0,f._dir);
  359. end;
  360. if f._mask = '*' then f._mask := '';
  361. if f._mask = '*.*' then f._mask := '';
  362. //writeln (stderr,'mask: "',f._mask,'" dir:"',path0,'"');
  363. f._mask := f._mask + #0;
  364. Pdirent(f.DirP) := opendir (path0);
  365. if f.DirP = nil then
  366. doserror := 18
  367. else begin
  368. F.Magic := $AD01;
  369. findnext (f);
  370. end;
  371. end;
  372. procedure findnext(var f : searchRec);
  373. begin
  374. if F.Magic <> $AD01 then
  375. begin
  376. doserror := 18;
  377. exit;
  378. end;
  379. doserror:=0;
  380. repeat
  381. Pdirent(f.EntryP) := readdir (Pdirent(f.DirP));
  382. if F.EntryP = nil then
  383. doserror := 18
  384. else
  385. if find_setfields (f) then
  386. begin
  387. if f._mask = #0 then exit;
  388. if fnmatch(@f._mask[1],Pdirent(f.EntryP)^.d_name,FNM_CASEFOLD) = 0 then
  389. exit;
  390. end;
  391. until doserror <> 0;
  392. end;
  393. Procedure FindClose(Var f: SearchRec);
  394. begin
  395. if F.Magic <> $AD01 then
  396. begin
  397. doserror := 18;
  398. EXIT;
  399. end;
  400. doserror:=0;
  401. closedir (Pdirent(f.DirP));
  402. f.Magic := 0;
  403. f.DirP := NIL;
  404. f.EntryP := NIL;
  405. end;
  406. {******************************************************************************
  407. --- File ---
  408. ******************************************************************************}
  409. Function FSearch(path: pathstr; dirlist: string): pathstr;
  410. var
  411. p1 : longint;
  412. s : searchrec;
  413. newdir : pathstr;
  414. begin
  415. { No wildcards allowed in these things }
  416. if (pos('?',path)<>0) or (pos('*',path)<>0) then
  417. begin
  418. fsearch:='';
  419. exit;
  420. end;
  421. { check if the file specified exists }
  422. findfirst(path,anyfile and not(directory),s);
  423. if doserror=0 then
  424. begin
  425. findclose(s);
  426. fsearch:=path;
  427. exit;
  428. end;
  429. findclose(s);
  430. { allow backslash as slash }
  431. DoDirSeparators(dirlist);
  432. repeat
  433. p1:=pos(';',dirlist);
  434. if p1<>0 then
  435. begin
  436. newdir:=copy(dirlist,1,p1-1);
  437. delete(dirlist,1,p1);
  438. end
  439. else
  440. begin
  441. newdir:=dirlist;
  442. dirlist:='';
  443. end;
  444. if (newdir<>'') and (not (newdir[length(newdir)] in [DirectorySeparator,DriveSeparator])) then
  445. newdir:=newdir+DirectorySeparator;
  446. findfirst(newdir+path,anyfile and not(directory),s);
  447. if doserror=0 then
  448. newdir:=newdir+path
  449. else
  450. newdir:='';
  451. findclose(s);
  452. until (dirlist='') or (newdir<>'');
  453. fsearch:=newdir;
  454. end;
  455. {******************************************************************************
  456. --- Get/Set File Time,Attr ---
  457. ******************************************************************************}
  458. procedure getftime(var f;var time : longint);
  459. var
  460. StatBuf : TStat;
  461. begin
  462. doserror := 0;
  463. if Fpfstat (filerec (f).handle, StatBuf) = 0 then
  464. timet2dostime (StatBuf.st_mtim.tv_sec,time)
  465. else begin
  466. time := 0;
  467. doserror := ___errno^;
  468. end;
  469. end;
  470. procedure setftime(var f;time : longint);
  471. Var
  472. utim: utimbuf;
  473. DT: DateTime;
  474. path: pathstr;
  475. tm : TTm;
  476. Begin
  477. doserror:=0;
  478. with utim do
  479. begin
  480. actime:=libc.time(nil); // getepochtime;
  481. UnPackTime(Time,DT);
  482. with tm do
  483. begin
  484. tm_sec := DT.Sec; // seconds after the minute [0..59]
  485. tm_min := DT.Min; // minutes after the hour [0..59]
  486. tm_hour := DT.hour; // hours since midnight [0..23]
  487. tm_mday := DT.Day; // days of the month [1..31]
  488. tm_mon := DT.month-1; // months since January [0..11]
  489. tm_year := DT.year-1900;
  490. tm_wday := -1;
  491. tm_yday := -1;
  492. tm_isdst := -1;
  493. end;
  494. modtime:=mktime(tm);
  495. end;
  496. if utime(@filerec(f).name,utim)<0 then
  497. begin
  498. Time:=0;
  499. doserror:=3;
  500. end;
  501. end;
  502. procedure getfattr(var f;var attr : word);
  503. var
  504. StatBuf : TStat;
  505. {$ifndef FPC_ANSI_TEXTFILEREC}
  506. r: rawbytestring;
  507. {$endif not FPC_ANSI_TEXTFILEREC}
  508. p: PAnsiChar;
  509. begin
  510. doserror := 0;
  511. {$ifdef FPC_ANSI_TEXTFILEREC}
  512. p := @filerec(f).name;
  513. {$else FPC_ANSI_TEXTFILEREC}
  514. r := ToSingleByteFileSystemEncodedFileName(filerec(f).name);
  515. p := PAnsiChar(r);
  516. {$endif FPC_ANSI_TEXTFILEREC}
  517. if Fpstat (p, StatBuf) = 0 then
  518. attr := nwattr2dosattr (StatBuf.st_mode)
  519. else
  520. begin
  521. attr := 0;
  522. doserror := ___errno^;
  523. end;
  524. end;
  525. procedure setfattr(var f;attr : word);
  526. var
  527. StatBuf : TStat;
  528. newMode : longint;
  529. {$ifndef FPC_ANSI_TEXTFILEREC}
  530. r: rawbytestring;
  531. {$endif not FPC_ANSI_TEXTFILEREC}
  532. p: PAnsiChar;
  533. begin
  534. {$ifdef FPC_ANSI_TEXTFILEREC}
  535. p := @filerec(f).name;
  536. {$else FPC_ANSI_TEXTFILEREC}
  537. r := ToSingleByteFileSystemEncodedFileName(filerec(f).name);
  538. p := PAnsiChar(r);
  539. {$endif FPC_ANSI_TEXTFILEREC}
  540. if Fpstat (p,StatBuf) = 0 then
  541. begin
  542. newmode := StatBuf.st_mode and ($FFFF0000 - M_A_RDONLY-M_A_HIDDEN-M_A_SYSTEM-M_A_ARCH); {only this can be set by dos unit}
  543. newmode := newmode or M_A_BITS_SIGNIFICANT; {set netware attributes}
  544. if attr and readonly > 0 then
  545. newmode := newmode or M_A_RDONLY;
  546. if attr and hidden > 0 then
  547. newmode := newmode or M_A_HIDDEN;
  548. if attr and sysfile > 0 then
  549. newmode := newmode or M_A_SYSTEM;
  550. if attr and archive > 0 then
  551. newmode := newmode or M_A_ARCH;
  552. if Fpchmod (@textrec(f).name,newMode) < 0 then
  553. doserror := ___errno^ else
  554. doserror := 0;
  555. end else
  556. doserror := ___errno^;
  557. end;
  558. {******************************************************************************
  559. --- Environment ---
  560. ******************************************************************************}
  561. Function EnvCount: Longint;
  562. var
  563. envcnt : longint;
  564. p : PPAnsiChar;
  565. Begin
  566. envcnt:=0;
  567. p:=envp; {defined in system}
  568. while (p^<>nil) do
  569. begin
  570. inc(envcnt);
  571. inc(p);
  572. end;
  573. EnvCount := envcnt
  574. End;
  575. Function EnvStr (Index: longint): String;
  576. Var
  577. i : longint;
  578. p : PPAnsiChar;
  579. Begin
  580. if Index <= 0 then
  581. envstr:=''
  582. else
  583. begin
  584. p:=envp; {defined in system}
  585. i:=1;
  586. while (i<Index) and (p^<>nil) do
  587. begin
  588. inc(i);
  589. inc(p);
  590. end;
  591. if p=nil then
  592. envstr:=''
  593. else
  594. envstr:=strpas(p^)
  595. end;
  596. end;
  597. { works fine (at least with netware 6.5) }
  598. Function GetEnv(envvar: string): string;
  599. var envvar0 : array[0..512] of AnsiChar;
  600. p : PAnsiChar;
  601. SearchElement : string[255];
  602. i,isDosPath,res : longint;
  603. begin
  604. if upcase(envvar) = 'PATH' then
  605. begin // netware does not have search paths in the environment var PATH
  606. // return it here (needed for the compiler)
  607. GetEnv := '';
  608. i := 1;
  609. res := GetSearchPathElement (i, isdosPath, @SearchElement[0]);
  610. while res = 0 do
  611. begin
  612. if isDosPath = 0 then
  613. begin
  614. if GetEnv <> '' then GetEnv := GetEnv + ';';
  615. GetEnv := GetEnv + SearchElement;
  616. end;
  617. inc (i);
  618. res := GetSearchPathElement (i, isdosPath, @SearchElement[0]);
  619. end;
  620. DoDirSeparators(getenv);
  621. end else
  622. begin
  623. strpcopy(envvar0,envvar);
  624. p := libc.getenv (envvar0);
  625. if p = NIL then
  626. GetEnv := ''
  627. else
  628. GetEnv := strpas (p);
  629. end;
  630. end;
  631. {******************************************************************************
  632. --- Not Supported ---
  633. ******************************************************************************}
  634. Procedure keep(exitcode : word);
  635. Begin
  636. { simply wait until nlm will be unloaded }
  637. while true do delay (60000);
  638. End;
  639. end.