dos.pp 25 KB

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