2
0

dos.pp 31 KB

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