dos.pp 26 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 1999-2000 by the Free Pascal development team.
  4. Dos unit for BP7 compatible RTL
  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. {$inline on}
  12. unit dos;
  13. interface
  14. Type
  15. searchrec = packed record
  16. fill : array[1..21] of byte;
  17. attr : byte;
  18. time : longint;
  19. { reserved : word; not in DJGPP V2 }
  20. size : longint;
  21. name : string[255]; { LFN Name, DJGPP uses only [12] but more can't hurt (PFV) }
  22. end;
  23. {$DEFINE HAS_REGISTERS}
  24. {$I registers.inc}
  25. {$i dosh.inc}
  26. {$IfDef SYSTEM_DEBUG_STARTUP}
  27. {$DEFINE FORCE_PROXY}
  28. {$endif SYSTEM_DEBUG_STARTUP}
  29. Const
  30. { This variable can be set to true
  31. to force use of !proxy command lines even for short
  32. strings, for debugging purposes mainly, as
  33. this might have negative impact if trying to
  34. call non-go32v2 programs }
  35. force_go32v2_proxy : boolean =
  36. {$ifdef FORCE_PROXY}
  37. true;
  38. {$DEFINE DEBUG_PROXY}
  39. {$else not FORCE_PROXY}
  40. false;
  41. {$endif not FORCE_PROXY}
  42. { This variable allows to use !proxy if command line is
  43. longer than 126 characters.
  44. This will only work if the called program knows how to handle
  45. those command lines.
  46. Luckily this is the case for Free Pascal compiled
  47. programs (even old versions)
  48. and go32v2 DJGPP programs.
  49. You can set this to false to get a warning to stderr
  50. if command line is too long. }
  51. Use_go32v2_proxy : boolean = true;
  52. { Added to interface so that there is no need to implement it
  53. both in dos and sysutils units }
  54. //procedure exec_ansistring(path : string;comline : ansistring);
  55. procedure Intr(IntNo: Byte; var Regs: Registers);
  56. procedure MsDos(var Regs: Registers); external name 'FPC_MSDOS';
  57. implementation
  58. uses
  59. strings, winprocs, wintypes;
  60. type
  61. PFarByte = ^Byte;far;
  62. PFarChar = ^Char;far;
  63. PFarWord = ^Word;far;
  64. {$DEFINE HAS_GETMSCOUNT}
  65. {$DEFINE HAS_INTR}
  66. {$DEFINE HAS_SETCBREAK}
  67. {$DEFINE HAS_GETCBREAK}
  68. {$DEFINE HAS_SETVERIFY}
  69. {$DEFINE HAS_GETVERIFY}
  70. {//$DEFINE HAS_SWAPVECTORS}
  71. {$DEFINE HAS_GETINTVEC}
  72. {$DEFINE HAS_SETINTVEC}
  73. {//$DEFINE HAS_KEEP}
  74. {$DEFINE HAS_GETSHORTNAME}
  75. {$DEFINE HAS_GETLONGNAME}
  76. {$DEFINE FPC_FEXPAND_UNC} (* UNC paths are supported *)
  77. {$DEFINE FPC_FEXPAND_DRIVES} (* Full paths begin with drive specification *)
  78. {$I dos.inc}
  79. {******************************************************************************
  80. --- Dos Interrupt ---
  81. ******************************************************************************}
  82. var
  83. dosregs : registers;
  84. procedure Intr(IntNo: Byte; var Regs: Registers);assembler;
  85. asm
  86. // todo
  87. end;
  88. { in protected mode, loading invalid values into segment registers causes an
  89. exception, so we use this function to initialize our Registers structure }
  90. procedure ZeroSegRegs(var regs: Registers); inline;
  91. begin
  92. regs.DS:=0;
  93. regs.ES:=0;
  94. end;
  95. procedure LoadDosError;
  96. var
  97. r : registers;
  98. SimpleDosError : word;
  99. begin
  100. if (dosregs.flags and fcarry) <> 0 then
  101. begin
  102. { I got a extended error = 0
  103. while CarryFlag was set from Exec function }
  104. SimpleDosError:=dosregs.ax;
  105. ZeroSegRegs(r);
  106. r.ax:=$5900;
  107. r.bx:=$0;
  108. msdos(r);
  109. { conversion from word to integer !!
  110. gave a Bound check error if ax is $FFFF !! PM }
  111. doserror:=integer(r.ax);
  112. case doserror of
  113. 0 : DosError:=integer(SimpleDosError);
  114. 19 : DosError:=150;
  115. 21 : DosError:=152;
  116. end;
  117. end
  118. else
  119. doserror:=0;
  120. end;
  121. {******************************************************************************
  122. --- Info / Date / Time ---
  123. ******************************************************************************}
  124. function dosversion : word;
  125. begin
  126. ZeroSegRegs(dosregs);
  127. dosregs.ax:=$3000;
  128. msdos(dosregs);
  129. dosversion:=dosregs.ax;
  130. end;
  131. procedure getdate(var year,month,mday,wday : word);
  132. begin
  133. ZeroSegRegs(dosregs);
  134. dosregs.ax:=$2a00;
  135. msdos(dosregs);
  136. wday:=dosregs.al;
  137. year:=dosregs.cx;
  138. month:=dosregs.dh;
  139. mday:=dosregs.dl;
  140. end;
  141. procedure setdate(year,month,day : word);
  142. begin
  143. ZeroSegRegs(dosregs);
  144. dosregs.cx:=year;
  145. dosregs.dh:=month;
  146. dosregs.dl:=day;
  147. dosregs.ah:=$2b;
  148. msdos(dosregs);
  149. end;
  150. procedure gettime(var hour,minute,second,sec100 : word);
  151. begin
  152. ZeroSegRegs(dosregs);
  153. dosregs.ah:=$2c;
  154. msdos(dosregs);
  155. hour:=dosregs.ch;
  156. minute:=dosregs.cl;
  157. second:=dosregs.dh;
  158. sec100:=dosregs.dl;
  159. end;
  160. procedure settime(hour,minute,second,sec100 : word);
  161. begin
  162. ZeroSegRegs(dosregs);
  163. dosregs.ch:=hour;
  164. dosregs.cl:=minute;
  165. dosregs.dh:=second;
  166. dosregs.dl:=sec100;
  167. dosregs.ah:=$2d;
  168. msdos(dosregs);
  169. end;
  170. function GetMsCount: int64;
  171. begin
  172. GetMsCount:=GetTickCount;
  173. end;
  174. {******************************************************************************
  175. --- Exec ---
  176. ******************************************************************************}
  177. const
  178. DOS_MAX_COMMAND_LINE_LENGTH = 126;
  179. (*procedure exec_ansistring(path : string;comline : ansistring);
  180. type
  181. realptr = packed record
  182. ofs,seg : word;
  183. end;
  184. texecblock = packed record
  185. envseg : word;
  186. comtail : realptr;
  187. firstFCB : realptr;
  188. secondFCB : realptr;
  189. iniStack : realptr;
  190. iniCSIP : realptr;
  191. end;
  192. var
  193. execblock : texecblock;
  194. c : ansistring;
  195. p : string;
  196. arg_ofs : integer;
  197. fcb1 : array [0..15] of byte;
  198. fcb2 : array [0..15] of byte;
  199. begin
  200. { create command line }
  201. c:=comline;
  202. if length(c)>DOS_MAX_COMMAND_LINE_LENGTH then
  203. begin
  204. writeln(stderr,'Dos.exec command line truncated to ',
  205. DOS_MAX_COMMAND_LINE_LENGTH,' chars');
  206. writeln(stderr,'Before: "',c,'"');
  207. setlength(c, DOS_MAX_COMMAND_LINE_LENGTH);
  208. writeln(stderr,'After: "',c,'"');
  209. end;
  210. p:=path;
  211. { allow slash as backslash }
  212. DoDirSeparators(p);
  213. if LFNSupport then
  214. GetShortName(p);
  215. { allocate FCB see dosexec code }
  216. arg_ofs:=1;
  217. while (arg_ofs<length(c)) and (c[arg_ofs] in [' ',#9]) do
  218. inc(arg_ofs);
  219. dosregs.ax:=$2901;
  220. dosregs.ds:=Seg(c[arg_ofs]);
  221. dosregs.si:=Ofs(c[arg_ofs]);
  222. dosregs.es:=Seg(fcb1);
  223. dosregs.di:=Ofs(fcb1);
  224. msdos(dosregs);
  225. { allocate second FCB see dosexec code }
  226. dosregs.ax:=$2901;
  227. dosregs.ds:=Seg(c[arg_ofs]);
  228. dosregs.si:=Ofs(c[arg_ofs]);
  229. dosregs.es:=Seg(fcb2);
  230. dosregs.di:=Ofs(fcb2);
  231. msdos(dosregs);
  232. c := Chr(Length(c)) + c + #13 + #0;
  233. with execblock do
  234. begin
  235. envseg:={la_env shr 4}0;
  236. comtail.seg:=Seg(c[1]);
  237. comtail.ofs:=Ofs(c[1]);
  238. firstFCB.seg:=Seg(fcb1);
  239. firstFCB.ofs:=Ofs(fcb1);
  240. secondFCB.seg:=Seg(fcb2);
  241. secondFCB.ofs:=Ofs(fcb2);
  242. end;
  243. p := p + #0;
  244. dosregs.dx:=Ofs(p[1]);
  245. dosregs.ds:=Seg(p[1]);
  246. dosregs.bx:=Ofs(execblock);
  247. dosregs.es:=Seg(execblock);
  248. dosregs.ax:=$4b00;
  249. msdos(dosregs);
  250. LoadDosError;
  251. if DosError=0 then
  252. begin
  253. dosregs.ax:=$4d00;
  254. msdos(dosregs);
  255. LastDosExitCode:=DosRegs.al
  256. end
  257. else
  258. LastDosExitCode:=0;
  259. end;*)
  260. procedure exec(const path : pathstr;const comline : comstr);
  261. begin
  262. // exec_ansistring(path, comline);
  263. end;
  264. procedure getcbreak(var breakvalue : boolean);
  265. begin
  266. ZeroSegRegs(dosregs);
  267. dosregs.ax:=$3300;
  268. msdos(dosregs);
  269. breakvalue:=dosregs.dl<>0;
  270. end;
  271. procedure setcbreak(breakvalue : boolean);
  272. begin
  273. ZeroSegRegs(dosregs);
  274. dosregs.ax:=$3301;
  275. dosregs.dl:=ord(breakvalue);
  276. msdos(dosregs);
  277. end;
  278. procedure getverify(var verify : boolean);
  279. begin
  280. ZeroSegRegs(dosregs);
  281. dosregs.ah:=$54;
  282. msdos(dosregs);
  283. verify:=dosregs.al<>0;
  284. end;
  285. procedure setverify(verify : boolean);
  286. begin
  287. ZeroSegRegs(dosregs);
  288. dosregs.ah:=$2e;
  289. dosregs.al:=ord(verify);
  290. msdos(dosregs);
  291. end;
  292. {******************************************************************************
  293. --- Disk ---
  294. ******************************************************************************}
  295. type
  296. ExtendedFat32FreeSpaceRec = packed record
  297. RetSize : word; { $00 }
  298. Strucversion : word; { $02 }
  299. SecPerClus, { $04 }
  300. BytePerSec, { $08 }
  301. AvailClusters, { $0C }
  302. TotalClusters, { $10 }
  303. AvailPhysSect, { $14 }
  304. TotalPhysSect, { $18 }
  305. AvailAllocUnits, { $1C }
  306. TotalAllocUnits : longword; { $20 }
  307. Dummy, { $24 }
  308. Dummy2 : longword; { $28 }
  309. end; { $2C }
  310. const
  311. IOCTL_INPUT = 3; //For request header command field
  312. CDFUNC_SECTSIZE = 7; //For cdrom control block func field
  313. CDFUNC_VOLSIZE = 8; //For cdrom control block func field
  314. type
  315. TRequestHeader = packed record
  316. length : byte; { $00 }
  317. subunit : byte; { $01 }
  318. command : byte; { $02 }
  319. status : word; { $03 }
  320. reserved1 : longword; { $05 }
  321. reserved2 : longword; { $09 }
  322. media_desc : byte; { $0D }
  323. transf_ofs : word; { $0E }
  324. transf_seg : word; { $10 }
  325. numbytes : word; { $12 }
  326. end; { $14 }
  327. TCDSectSizeReq = packed record
  328. func : byte; { $00 }
  329. mode : byte; { $01 }
  330. secsize : word; { $02 }
  331. end; { $04 }
  332. TCDVolSizeReq = packed record
  333. func : byte; { $00 }
  334. size : longword; { $01 }
  335. end; { $05 }
  336. function do_diskdata(drive : byte; Free : boolean) : Int64;
  337. var
  338. blocksize, freeblocks, totblocks : longword;
  339. { Get disk data via old int21/36 (GET FREE DISK SPACE). It's always supported
  340. even if it returns wrong values for volumes > 2GB and for cdrom drives when
  341. in pure DOS. Note that it's also the only way to get some data on WinNTs. }
  342. function DiskData_36 : boolean;
  343. begin
  344. DiskData_36:=false;
  345. ZeroSegRegs(dosregs);
  346. dosregs.dl:=drive;
  347. dosregs.ah:=$36;
  348. msdos(dosregs);
  349. if dosregs.ax=$FFFF then exit;
  350. blocksize:=dosregs.ax*dosregs.cx;
  351. freeblocks:=dosregs.bx;
  352. totblocks:=dosregs.dx;
  353. Diskdata_36:=true;
  354. end;
  355. { Get disk data via int21/7303 (FAT32 - GET EXTENDED FREE SPACE ON DRIVE).
  356. It is supported by win9x even in pure DOS }
  357. function DiskData_7303 : boolean;
  358. var
  359. s : shortstring;
  360. rec : ExtendedFat32FreeSpaceRec;
  361. begin
  362. DiskData_7303:=false;
  363. s:=chr(drive+$40)+':\'+#0;
  364. rec.Strucversion:=0;
  365. rec.RetSize := 0;
  366. { no need to call ZeroSegRegs(dosregs), because es and ds are initialized below }
  367. dosregs.dx:=Ofs(s[1]);
  368. dosregs.ds:=Seg(s[1]);
  369. dosregs.di:=Ofs(Rec);
  370. dosregs.es:=Seg(Rec);
  371. dosregs.cx:=Sizeof(ExtendedFat32FreeSpaceRec);
  372. dosregs.ax:=$7303;
  373. msdos(dosregs);
  374. if (dosregs.flags and fcarry) <> 0 then
  375. exit;
  376. if Rec.RetSize = 0 then
  377. exit;
  378. blocksize:=rec.SecPerClus*rec.BytePerSec;
  379. freeblocks:=rec.AvailAllocUnits;
  380. totblocks:=rec.TotalAllocUnits;
  381. DiskData_7303:=true;
  382. end;
  383. { Get disk data asking to MSCDEX. Pure DOS returns wrong values with
  384. int21/7303 or int21/36 if the drive is a CDROM drive }
  385. function DiskData_CDROM : boolean;
  386. var req : TRequestHeader;
  387. sectreq : TCDSectSizeReq;
  388. sizereq : TCDVolSizeReq;
  389. i : integer;
  390. drnum : byte;
  391. begin
  392. DiskData_CDROM:=false;
  393. exit;
  394. (* drnum:=drive-1; //for MSCDEX, 0 = a, 1 = b etc, unlike int21/36
  395. { Is this a CDROM drive? }
  396. dosregs.ax:=$150b;
  397. dosregs.cx:=drnum;
  398. intr($2f,dosregs);
  399. if (dosregs.bx<>$ADAD) or (dosregs.ax=0) then
  400. exit; // no, it isn't
  401. { Prepare the request header to send to the cdrom driver }
  402. FillByte(req,sizeof(req),0);
  403. req.length:=sizeof(req);
  404. req.command:=IOCTL_INPUT;
  405. req.transf_ofs:=Ofs(sectreq);
  406. req.transf_seg:=Seg(sectreq);
  407. req.numbytes:=sizeof(sectreq);
  408. { We're asking the sector size }
  409. sectreq.func:=CDFUNC_SECTSIZE;
  410. sectreq.mode:=0; //cooked
  411. sectreq.secsize:=0;
  412. for i:=1 to 2 do
  413. begin
  414. { Send the request to the cdrom driver }
  415. dosregs.ax:=$1510;
  416. dosregs.cx:=drnum;
  417. dosregs.es:=Seg(req);
  418. dosregs.bx:=Ofs(req);
  419. intr($2f,dosregs);
  420. { status = $800F means "disk changed". Try once more. }
  421. if (req.status and $800F) <> $800F then break;
  422. end;
  423. if (req.status<>$0100) or (req.numbytes<>sizeof(sectreq)) then
  424. exit; //An error occurred
  425. { Update the request header for the next request }
  426. FillByte(req,sizeof(req),0);
  427. req.length:=sizeof(req);
  428. req.command:=IOCTL_INPUT;
  429. req.transf_ofs:=Ofs(sizereq);
  430. req.transf_seg:=Seg(sizereq);
  431. req.numbytes:=sizeof(sizereq);
  432. { We're asking the volume size (in blocks) }
  433. sizereq.func:=CDFUNC_VOLSIZE;
  434. sizereq.size:=0;
  435. { Send the request to the cdrom driver }
  436. dosregs.ax:=$1510;
  437. dosregs.cx:=drnum;
  438. dosregs.es:=Seg(req);
  439. dosregs.bx:=Ofs(req);
  440. intr($2f,dosregs);
  441. if (req.status<>$0100) or (req.numbytes<>sizeof(sizereq)) then
  442. exit; //An error occurred
  443. blocksize:=sectreq.secsize;
  444. freeblocks:=0; //always 0 for a cdrom
  445. totblocks:=sizereq.size;
  446. DiskData_CDROM:=true;*)
  447. end;
  448. begin
  449. if drive=0 then
  450. begin
  451. ZeroSegRegs(dosregs);
  452. dosregs.ax:=$1900; //get current default drive
  453. msdos(dosregs);
  454. drive:=dosregs.al+1;
  455. end;
  456. if not DiskData_CDROM then
  457. if not DiskData_7303 then
  458. if not DiskData_36 then
  459. begin
  460. do_diskdata:=-1;
  461. exit;
  462. end;
  463. do_diskdata:=blocksize;
  464. if free then
  465. do_diskdata:=do_diskdata*freeblocks
  466. else
  467. do_diskdata:=do_diskdata*totblocks;
  468. end;
  469. function diskfree(drive : byte) : int64;
  470. begin
  471. diskfree:=Do_DiskData(drive,TRUE);
  472. end;
  473. function disksize(drive : byte) : int64;
  474. begin
  475. disksize:=Do_DiskData(drive,false);
  476. end;
  477. {******************************************************************************
  478. --- LFNFindfirst LFNFindNext ---
  479. ******************************************************************************}
  480. type
  481. LFNSearchRec=packed record
  482. attr,
  483. crtime,
  484. crtimehi,
  485. actime,
  486. actimehi,
  487. lmtime,
  488. lmtimehi,
  489. sizehi,
  490. size : longint;
  491. reserved : array[0..7] of byte;
  492. name : array[0..259] of byte;
  493. shortname : array[0..13] of byte;
  494. end;
  495. procedure LFNSearchRec2Dos(const w:LFNSearchRec;hdl:longint;var d:Searchrec;from_findfirst : boolean);
  496. var
  497. Len : integer;
  498. begin
  499. With w do
  500. begin
  501. FillChar(d,sizeof(SearchRec),0);
  502. if DosError=0 then
  503. len:=StrLen(@Name)
  504. else
  505. len:=0;
  506. d.Name[0]:=chr(len);
  507. Move(Name[0],d.Name[1],Len);
  508. d.Time:=lmTime;
  509. d.Size:=Size;
  510. d.Attr:=Attr and $FF;
  511. if (DosError<>0) and from_findfirst then
  512. hdl:=-1;
  513. Move(hdl,d.Fill,4);
  514. end;
  515. end;
  516. {$ifdef DEBUG_LFN}
  517. const
  518. LFNFileName : string = 'LFN.log';
  519. LFNOpenNb : longint = 0;
  520. LogLFN : boolean = false;
  521. var
  522. lfnfile : text;
  523. {$endif DEBUG_LFN}
  524. procedure LFNFindFirst(path:pchar;attr:longint;var s:searchrec);
  525. var
  526. w : LFNSearchRec;
  527. begin
  528. { allow slash as backslash }
  529. DoDirSeparators(path);
  530. { no need to call ZeroSegRegs(dosregs), because both ds and es are initialized below }
  531. dosregs.si:=1; { use ms-dos time }
  532. { don't include the label if not asked for it, needed for network drives }
  533. if attr=$8 then
  534. dosregs.cx:=8
  535. else
  536. dosregs.cx:=attr and (not 8);
  537. dosregs.dx:=Ofs(path^);
  538. dosregs.ds:=Seg(path^);
  539. dosregs.di:=Ofs(w);
  540. dosregs.es:=Seg(w);
  541. dosregs.ax:=$714e;
  542. msdos(dosregs);
  543. LoadDosError;
  544. if DosError=2 then
  545. DosError:=18;
  546. {$ifdef DEBUG_LFN}
  547. if (DosError=0) and LogLFN then
  548. begin
  549. Append(lfnfile);
  550. inc(LFNOpenNb);
  551. Writeln(lfnfile,LFNOpenNb,' LFNFindFirst called ',path);
  552. close(lfnfile);
  553. end;
  554. {$endif DEBUG_LFN}
  555. LFNSearchRec2Dos(w,dosregs.ax,s,true);
  556. end;
  557. procedure LFNFindNext(var s:searchrec);
  558. var
  559. hdl : longint;
  560. w : LFNSearchRec;
  561. begin
  562. Move(s.Fill,hdl,4);
  563. { no need to call ZeroSegRegs(dosregs), because both ds and es are initialized below }
  564. dosregs.si:=1; { use ms-dos time }
  565. dosregs.di:=Ofs(w);
  566. dosregs.es:=Seg(w);
  567. dosregs.ds:=0; { avoid invalid selector values in protected mode }
  568. dosregs.bx:=hdl;
  569. dosregs.ax:=$714f;
  570. msdos(dosregs);
  571. LoadDosError;
  572. LFNSearchRec2Dos(w,hdl,s,false);
  573. end;
  574. procedure LFNFindClose(var s:searchrec);
  575. var
  576. hdl : longint;
  577. begin
  578. Move(s.Fill,hdl,4);
  579. { Do not call MsDos if FindFirst returned with an error }
  580. if hdl=-1 then
  581. begin
  582. DosError:=0;
  583. exit;
  584. end;
  585. ZeroSegRegs(dosregs);
  586. dosregs.bx:=hdl;
  587. dosregs.ax:=$71a1;
  588. msdos(dosregs);
  589. LoadDosError;
  590. {$ifdef DEBUG_LFN}
  591. if (DosError=0) and LogLFN then
  592. begin
  593. Append(lfnfile);
  594. Writeln(lfnfile,LFNOpenNb,' LFNFindClose called ');
  595. close(lfnfile);
  596. if LFNOpenNb>0 then
  597. dec(LFNOpenNb);
  598. end;
  599. {$endif DEBUG_LFN}
  600. end;
  601. {******************************************************************************
  602. --- DosFindfirst DosFindNext ---
  603. ******************************************************************************}
  604. procedure dossearchrec2searchrec(var f : searchrec);
  605. var
  606. len : integer;
  607. begin
  608. { Check is necessary!! OS/2's VDM doesn't clear the name with #0 if the }
  609. { file doesn't exist! (JM) }
  610. if dosError = 0 then
  611. len:=StrLen(@f.Name)
  612. else len := 0;
  613. Move(f.Name[0],f.Name[1],Len);
  614. f.Name[0]:=chr(len);
  615. end;
  616. procedure DosFindfirst(path : pchar;attr : word;var f : searchrec);
  617. begin
  618. { allow slash as backslash }
  619. DoDirSeparators(path);
  620. dosregs.dx:=Ofs(f);
  621. dosregs.ds:=Seg(f);
  622. dosregs.es:=0; { avoid invalid selector values in protected mode }
  623. dosregs.ah:=$1a;
  624. msdos(dosregs);
  625. dosregs.cx:=attr;
  626. dosregs.dx:=Ofs(path^);
  627. dosregs.ds:=Seg(path^);
  628. dosregs.es:=0; { avoid invalid selector values in protected mode }
  629. dosregs.ah:=$4e;
  630. msdos(dosregs);
  631. LoadDosError;
  632. dossearchrec2searchrec(f);
  633. end;
  634. procedure Dosfindnext(var f : searchrec);
  635. begin
  636. dosregs.dx:=Ofs(f);
  637. dosregs.ds:=Seg(f);
  638. dosregs.es:=0; { avoid invalid selector values in protected mode }
  639. dosregs.ah:=$1a;
  640. msdos(dosregs);
  641. ZeroSegRegs(dosregs);
  642. dosregs.ah:=$4f;
  643. msdos(dosregs);
  644. LoadDosError;
  645. dossearchrec2searchrec(f);
  646. end;
  647. {******************************************************************************
  648. --- Findfirst FindNext ---
  649. ******************************************************************************}
  650. procedure findfirst(const path : pathstr;attr : word;var f : searchRec);
  651. var
  652. path0 : array[0..255] of char;
  653. begin
  654. doserror:=0;
  655. strpcopy(path0,path);
  656. if LFNSupport then
  657. LFNFindFirst(path0,attr,f)
  658. else
  659. Dosfindfirst(path0,attr,f);
  660. end;
  661. procedure findnext(var f : searchRec);
  662. begin
  663. doserror:=0;
  664. if LFNSupport then
  665. LFNFindnext(f)
  666. else
  667. Dosfindnext(f);
  668. end;
  669. Procedure FindClose(Var f: SearchRec);
  670. begin
  671. DosError:=0;
  672. if LFNSupport then
  673. LFNFindClose(f);
  674. end;
  675. {procedure SwapIntVec(IntNo: Byte; var Vector: FarPointer);
  676. var
  677. tmpvec: FarPointer;
  678. begin
  679. GetIntVec(IntNo, tmpvec);
  680. SetIntVec(IntNo, Vector);
  681. Vector := tmpvec;
  682. end;
  683. procedure SwapVectors;
  684. begin
  685. SwapIntVec(0, SaveInt00);
  686. end;}
  687. {******************************************************************************
  688. --- File ---
  689. ******************************************************************************}
  690. Function FSearch(path: pathstr; dirlist: string): pathstr;
  691. var
  692. p1 : longint;
  693. s : searchrec;
  694. newdir : pathstr;
  695. begin
  696. { No wildcards allowed in these things }
  697. if (pos('?',path)<>0) or (pos('*',path)<>0) then
  698. begin
  699. fsearch:='';
  700. exit;
  701. end;
  702. { check if the file specified exists }
  703. findfirst(path,anyfile and not(directory),s);
  704. if doserror=0 then
  705. begin
  706. findclose(s);
  707. fsearch:=path;
  708. exit;
  709. end;
  710. findclose(s);
  711. { allow slash as backslash }
  712. DoDirSeparators(dirlist);
  713. repeat
  714. p1:=pos(';',dirlist);
  715. if p1<>0 then
  716. begin
  717. newdir:=copy(dirlist,1,p1-1);
  718. delete(dirlist,1,p1);
  719. end
  720. else
  721. begin
  722. newdir:=dirlist;
  723. dirlist:='';
  724. end;
  725. if (newdir<>'') and (not (newdir[length(newdir)] in [DirectorySeparator,DriveSeparator])) then
  726. newdir:=newdir+DirectorySeparator;
  727. findfirst(newdir+path,anyfile and not(directory),s);
  728. if doserror=0 then
  729. newdir:=newdir+path
  730. else
  731. newdir:='';
  732. findclose(s);
  733. until (dirlist='') or (newdir<>'');
  734. fsearch:=newdir;
  735. end;
  736. { change to short filename if successful DOS call PM }
  737. function GetShortName(var p : String) : boolean;
  738. var
  739. c : array[0..255] of char;
  740. begin
  741. move(p[1],c[0],length(p));
  742. c[length(p)]:=#0;
  743. { no need to call ZeroSegRegs(dosregs), because es and ds are initialized below }
  744. dosregs.ax:=$7160;
  745. dosregs.cx:=1;
  746. dosregs.ds:=Seg(c);
  747. dosregs.si:=Ofs(c);
  748. dosregs.es:=Seg(c);
  749. dosregs.di:=Ofs(c);
  750. msdos(dosregs);
  751. LoadDosError;
  752. if DosError=0 then
  753. begin
  754. move(c[0],p[1],strlen(c));
  755. p[0]:=char(strlen(c));
  756. GetShortName:=true;
  757. end
  758. else
  759. GetShortName:=false;
  760. end;
  761. { change to long filename if successful DOS call PM }
  762. function GetLongName(var p : String) : boolean;
  763. var
  764. c : array[0..260] of char;
  765. begin
  766. move(p[1],c[0],length(p));
  767. c[length(p)]:=#0;
  768. { no need to call ZeroSegRegs(dosregs), because es and ds are initialized below }
  769. dosregs.ax:=$7160;
  770. dosregs.cx:=2;
  771. dosregs.ds:=Seg(c);
  772. dosregs.si:=Ofs(c);
  773. dosregs.es:=Seg(c);
  774. dosregs.di:=Ofs(c);
  775. msdos(dosregs);
  776. LoadDosError;
  777. if DosError=0 then
  778. begin
  779. c[255]:=#0;
  780. move(c[0],p[1],strlen(c));
  781. p[0]:=char(strlen(c));
  782. GetLongName:=true;
  783. end
  784. else
  785. GetLongName:=false;
  786. end;
  787. {******************************************************************************
  788. --- Get/Set File Time,Attr ---
  789. ******************************************************************************}
  790. procedure getftime(var f;var time : longint);
  791. begin
  792. ZeroSegRegs(dosregs);
  793. dosregs.bx:=textrec(f).handle;
  794. dosregs.ax:=$5700;
  795. msdos(dosregs);
  796. loaddoserror;
  797. time:=(longint(dosregs.dx) shl 16)+dosregs.cx;
  798. end;
  799. procedure setftime(var f;time : longint);
  800. begin
  801. ZeroSegRegs(dosregs);
  802. dosregs.bx:=textrec(f).handle;
  803. dosregs.cx:=time and $ffff;
  804. dosregs.dx:=time shr 16;
  805. dosregs.ax:=$5701;
  806. msdos(dosregs);
  807. loaddoserror;
  808. end;
  809. procedure getfattr(var f;var attr : word);
  810. var
  811. path: pchar;
  812. {$ifndef FPC_ANSI_TEXTFILEREC}
  813. r: rawbytestring;
  814. {$endif not FPC_ANSI_TEXTFILEREC}
  815. begin
  816. {$ifdef FPC_ANSI_TEXTFILEREC}
  817. path:=@filerec(f).Name;
  818. {$else}
  819. r:=ToSingleByteFileSystemEncodedFileName(filerec(f).Name);
  820. path:=pchar(r);
  821. {$endif}
  822. dosregs.dx:=Ofs(path^);
  823. dosregs.ds:=Seg(path^);
  824. dosregs.es:=0; { avoid invalid selector values in protected mode }
  825. if LFNSupport then
  826. begin
  827. dosregs.ax:=$7143;
  828. dosregs.bx:=0;
  829. end
  830. else
  831. dosregs.ax:=$4300;
  832. msdos(dosregs);
  833. LoadDosError;
  834. Attr:=dosregs.cx;
  835. end;
  836. procedure setfattr(var f;attr : word);
  837. var
  838. path: pchar;
  839. {$ifndef FPC_ANSI_TEXTFILEREC}
  840. r: rawbytestring;
  841. {$endif not FPC_ANSI_TEXTFILEREC}
  842. begin
  843. { Fail for setting VolumeId. }
  844. if ((attr and VolumeID)<>0) then
  845. begin
  846. doserror:=5;
  847. exit;
  848. end;
  849. {$ifdef FPC_ANSI_TEXTFILEREC}
  850. path:=@filerec(f).Name;
  851. {$else}
  852. r:=ToSingleByteFileSystemEncodedFileName(filerec(f).Name);
  853. path:=pchar(r);
  854. {$endif}
  855. dosregs.dx:=Ofs(path);
  856. dosregs.ds:=Seg(path);
  857. dosregs.es:=0; { avoid invalid selector values in protected mode }
  858. if LFNSupport then
  859. begin
  860. dosregs.ax:=$7143;
  861. dosregs.bx:=1;
  862. end
  863. else
  864. dosregs.ax:=$4301;
  865. dosregs.cx:=attr;
  866. msdos(dosregs);
  867. LoadDosError;
  868. end;
  869. {******************************************************************************
  870. --- Environment ---
  871. ******************************************************************************}
  872. function GetEnvStr(EnvNo: Integer; var OutEnvStr: string): integer;
  873. var
  874. dos_env_ptr: LPSTR;
  875. Ch: Char;
  876. begin
  877. dos_env_ptr := GetDOSEnvironment;
  878. GetEnvStr := 1;
  879. OutEnvStr := '';
  880. repeat
  881. Ch := dos_env_ptr^;
  882. if (Ch = #0) and ((dos_env_ptr+1)^ = #0) then
  883. exit;
  884. if Ch = #0 then
  885. Inc(GetEnvStr);
  886. if (Ch <> #0) and (GetEnvStr = EnvNo) then
  887. OutEnvStr := OutEnvStr + Ch;
  888. Inc(dos_env_ptr);
  889. if Ofs(dos_env_ptr^) = 0 then
  890. exit;
  891. until false;
  892. end;
  893. function envcount : longint;
  894. var
  895. tmpstr: string;
  896. begin
  897. envcount := GetEnvStr(-1, tmpstr);
  898. end;
  899. function envstr (Index: longint): string;
  900. begin
  901. GetEnvStr(Index, envstr);
  902. end;
  903. Function GetEnv(envvar: string): string;
  904. var
  905. hs : string;
  906. eqpos : integer;
  907. I : integer;
  908. begin
  909. envvar:=upcase(envvar);
  910. getenv:='';
  911. for I := 1 to envcount do
  912. begin
  913. hs:=envstr(I);
  914. eqpos:=pos('=',hs);
  915. if upcase(copy(hs,1,eqpos-1))=envvar then
  916. begin
  917. getenv:=copy(hs,eqpos+1,length(hs)-eqpos);
  918. break;
  919. end;
  920. end;
  921. end;
  922. {******************************************************************************
  923. --- Get/SetIntVec ---
  924. ******************************************************************************}
  925. procedure GetIntVec(intno: Byte; var vector: farpointer); assembler;
  926. asm
  927. mov al, intno
  928. mov ah, 35h
  929. int 21h
  930. xchg ax, bx
  931. {$if defined(FPC_MM_TINY) or defined(FPC_MM_SMALL) or defined(FPC_MM_MEDIUM)}
  932. mov bx, vector
  933. mov [bx], ax
  934. mov ax, es
  935. mov [bx + 2], ax
  936. {$else}
  937. {$ifdef FPC_ENABLED_CLD}
  938. cld
  939. {$endif FPC_ENABLED_CLD}
  940. push es
  941. pop bx
  942. les di, vector
  943. stosw
  944. xchg ax, bx
  945. stosw
  946. {$endif}
  947. end;
  948. procedure SetIntVec(intno: Byte; vector: farpointer); assembler;
  949. asm
  950. push ds
  951. mov al, intno
  952. mov ah, 25h
  953. lds dx, word [vector]
  954. int 21h
  955. pop ds
  956. end;
  957. {******************************************************************************
  958. --- Keep ---
  959. ******************************************************************************}
  960. {Procedure Keep(exitcode: word); assembler;
  961. asm
  962. mov bx, PrefixSeg
  963. dec bx
  964. mov es, bx
  965. mov dx, es:[3]
  966. mov ax, exitcode
  967. mov ah, 31h
  968. int 21h
  969. end;}
  970. {$ifdef DEBUG_LFN}
  971. begin
  972. LogLFN:=(GetEnv('LOGLFN')<>'');
  973. assign(lfnfile,LFNFileName);
  974. {$I-}
  975. Reset(lfnfile);
  976. if IOResult<>0 then
  977. begin
  978. Rewrite(lfnfile);
  979. Writeln(lfnfile,'New lfn.log');
  980. end;
  981. close(lfnfile);
  982. {$endif DEBUG_LFN}
  983. end.