dos.pp 25 KB

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