dos.pp 30 KB

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