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 (arg_ofs<length(c)) and (c[arg_ofs] in [' ',#9]) do
  394. inc(arg_ofs);
  395. dosregs.ax:=$2901;
  396. dosregs.ds:=(la_c+arg_ofs) shr 4;
  397. dosregs.esi:=(la_c+arg_ofs) and 15;
  398. dosregs.es:=fcb1_la shr 4;
  399. dosregs.edi:=fcb1_la and 15;
  400. msdos(dosregs);
  401. { allocate second FCB see dosexec code }
  402. dosregs.ax:=$2901;
  403. dosregs.ds:=(la_c+arg_ofs) shr 4;
  404. dosregs.esi:=(la_c+arg_ofs) and 15;
  405. dosregs.es:=fcb2_la shr 4;
  406. dosregs.edi:=fcb2_la and 15;
  407. msdos(dosregs);
  408. with execblock do
  409. begin
  410. envseg:=la_env shr 4;
  411. comtail.seg:=la_c shr 4;
  412. comtail.ofs:=la_c and 15;
  413. firstFCB.seg:=fcb1_la shr 4;
  414. firstFCB.ofs:=fcb1_la and 15;
  415. secondFCB.seg:=fcb2_la shr 4;
  416. secondFCB.ofs:=fcb2_la and 15;
  417. end;
  418. seg_move(get_ds,longint(@execblock),dosmemselector,la_e,sizeof(texecblock));
  419. dosregs.edx:=la_p and 15+1;
  420. dosregs.ds:=la_p shr 4;
  421. dosregs.ebx:=la_p and 15+la_e-la_p;
  422. dosregs.es:=la_p shr 4;
  423. dosregs.ax:=$4b00;
  424. msdos(dosregs);
  425. LoadDosError;
  426. if DosError=0 then
  427. begin
  428. dosregs.ax:=$4d00;
  429. msdos(dosregs);
  430. LastDosExitCode:=DosRegs.al
  431. end
  432. else
  433. LastDosExitCode:=0;
  434. end;
  435. procedure exec(const path : pathstr;const comline : comstr);
  436. begin
  437. exec_ansistring(path, comline);
  438. end;
  439. procedure getcbreak(var breakvalue : boolean);
  440. begin
  441. dosregs.ax:=$3300;
  442. msdos(dosregs);
  443. breakvalue:=dosregs.dl<>0;
  444. end;
  445. procedure setcbreak(breakvalue : boolean);
  446. begin
  447. dosregs.ax:=$3301;
  448. dosregs.dl:=ord(breakvalue);
  449. msdos(dosregs);
  450. end;
  451. procedure getverify(var verify : boolean);
  452. begin
  453. dosregs.ah:=$54;
  454. msdos(dosregs);
  455. verify:=dosregs.al<>0;
  456. end;
  457. procedure setverify(verify : boolean);
  458. begin
  459. dosregs.ah:=$2e;
  460. dosregs.al:=ord(verify);
  461. msdos(dosregs);
  462. end;
  463. {******************************************************************************
  464. --- Disk ---
  465. ******************************************************************************}
  466. type
  467. ExtendedFat32FreeSpaceRec = packed record
  468. RetSize : word; { $00 }
  469. Strucversion : word; { $02 }
  470. SecPerClus, { $04 }
  471. BytePerSec, { $08 }
  472. AvailClusters, { $0C }
  473. TotalClusters, { $10 }
  474. AvailPhysSect, { $14 }
  475. TotalPhysSect, { $18 }
  476. AvailAllocUnits, { $1C }
  477. TotalAllocUnits : longword; { $20 }
  478. Dummy, { $24 }
  479. Dummy2 : longword; { $28 }
  480. end; { $2C }
  481. const
  482. IOCTL_INPUT = 3; //For request header command field
  483. CDFUNC_SECTSIZE = 7; //For cdrom control block func field
  484. CDFUNC_VOLSIZE = 8; //For cdrom control block func field
  485. type
  486. TRequestHeader = packed record
  487. length : byte; { $00 }
  488. subunit : byte; { $01 }
  489. command : byte; { $02 }
  490. status : word; { $03 }
  491. reserved1 : longword; { $05 }
  492. reserved2 : longword; { $09 }
  493. media_desc : byte; { $0D }
  494. transf_ofs : word; { $0E }
  495. transf_seg : word; { $10 }
  496. numbytes : word; { $12 }
  497. end; { $14 }
  498. TCDSectSizeReq = packed record
  499. func : byte; { $00 }
  500. mode : byte; { $01 }
  501. secsize : word; { $02 }
  502. end; { $04 }
  503. TCDVolSizeReq = packed record
  504. func : byte; { $00 }
  505. size : longword; { $01 }
  506. end; { $05 }
  507. function do_diskdata(drive : byte; Free : boolean) : Int64;
  508. var
  509. blocksize, freeblocks, totblocks : longword;
  510. { Get disk data via old int21/36 (GET FREE DISK SPACE). It's always supported
  511. even if it returns wrong values for volumes > 2GB and for cdrom drives when
  512. in pure DOS. Note that it's also the only way to get some data on WinNTs. }
  513. function DiskData_36 : boolean;
  514. begin
  515. DiskData_36:=false;
  516. dosregs.dl:=drive;
  517. dosregs.ah:=$36;
  518. msdos(dosregs);
  519. if dosregs.ax=$FFFF then exit;
  520. blocksize:=dosregs.ax*dosregs.cx;
  521. freeblocks:=dosregs.bx;
  522. totblocks:=dosregs.dx;
  523. Diskdata_36:=true;
  524. end;
  525. { Get disk data via int21/7303 (FAT32 - GET EXTENDED FREE SPACE ON DRIVE).
  526. It is supported by win9x even in pure DOS }
  527. function DiskData_7303 : boolean;
  528. var
  529. s : shortstring;
  530. rec : ExtendedFat32FreeSpaceRec;
  531. begin
  532. DiskData_7303:=false;
  533. s:=chr(drive+$40)+':\'+#0;
  534. rec.Strucversion:=0;
  535. rec.RetSize := 0;
  536. dosmemput(tb_segment,tb_offset,Rec,sizeof(ExtendedFat32FreeSpaceRec));
  537. dosmemput(tb_segment,tb_offset+Sizeof(ExtendedFat32FreeSpaceRec)+1,s[1],4);
  538. dosregs.dx:=tb_offset+Sizeof(ExtendedFat32FreeSpaceRec)+1;
  539. dosregs.ds:=tb_segment;
  540. dosregs.di:=tb_offset;
  541. dosregs.es:=tb_segment;
  542. dosregs.cx:=Sizeof(ExtendedFat32FreeSpaceRec);
  543. dosregs.ax:=$7303;
  544. msdos(dosregs);
  545. if (dosregs.flags and fcarry) <> 0 then
  546. exit;
  547. copyfromdos(rec,Sizeof(ExtendedFat32FreeSpaceRec));
  548. if Rec.RetSize = 0 then
  549. exit;
  550. blocksize:=rec.SecPerClus*rec.BytePerSec;
  551. freeblocks:=rec.AvailAllocUnits;
  552. totblocks:=rec.TotalAllocUnits;
  553. DiskData_7303:=true;
  554. end;
  555. { Get disk data asking to MSCDEX. Pure DOS returns wrong values with
  556. int21/7303 or int21/36 if the drive is a CDROM drive }
  557. function DiskData_CDROM : boolean;
  558. var req : TRequestHeader;
  559. sectreq : TCDSectSizeReq;
  560. sizereq : TCDVolSizeReq;
  561. i : integer;
  562. status,byteswritten : word;
  563. drnum : byte;
  564. begin
  565. DiskData_CDROM:=false;
  566. drnum:=drive-1; //for MSCDEX, 0 = a, 1 = b etc, unlike int21/36
  567. { Is this a CDROM drive? }
  568. dosregs.ax:=$150b;
  569. dosregs.cx:=drnum;
  570. realintr($2f,dosregs);
  571. if (dosregs.bx<>$ADAD) or (dosregs.ax=0) then
  572. exit; // no, it isn't
  573. { Prepare the request header to send to the cdrom driver }
  574. FillByte(req,sizeof(req),0);
  575. req.length:=sizeof(req);
  576. req.command:=IOCTL_INPUT;
  577. req.transf_ofs:=tb_offset+sizeof(req); //CDROM control block will follow
  578. req.transf_seg:=tb_segment; //the request header
  579. req.numbytes:=sizeof(sectreq);
  580. { We're asking the sector size }
  581. sectreq.func:=CDFUNC_SECTSIZE;
  582. sectreq.mode:=0; //cooked
  583. sectreq.secsize:=0;
  584. for i:=1 to 2 do
  585. begin
  586. { Send the request to the cdrom driver }
  587. dosmemput(tb_segment,tb_offset,req,sizeof(req));
  588. dosmemput(tb_segment,tb_offset+sizeof(req),sectreq,sizeof(sectreq));
  589. dosregs.ax:=$1510;
  590. dosregs.cx:=drnum;
  591. dosregs.es:=tb_segment;
  592. dosregs.bx:=tb_offset;
  593. realintr($2f,dosregs);
  594. dosmemget(tb_segment,tb_offset+3,status,2);
  595. { status = $800F means "disk changed". Try once more. }
  596. if (status and $800F) <> $800F then break;
  597. end;
  598. dosmemget(tb_segment,tb_offset+$12,byteswritten,2);
  599. if (status<>$0100) or (byteswritten<>sizeof(sectreq)) then
  600. exit; //An error occurred
  601. dosmemget(tb_segment,tb_offset+sizeof(req),sectreq,sizeof(sectreq));
  602. { Update the request header for the next request }
  603. req.numbytes:=sizeof(sizereq);
  604. { We're asking the volume size (in blocks) }
  605. sizereq.func:=CDFUNC_VOLSIZE;
  606. sizereq.size:=0;
  607. { Send the request to the cdrom driver }
  608. dosmemput(tb_segment,tb_offset,req,sizeof(req));
  609. dosmemput(tb_segment,tb_offset+sizeof(req),sizereq,sizeof(sizereq));
  610. dosregs.ax:=$1510;
  611. dosregs.cx:=drnum;
  612. dosregs.es:=tb_segment;
  613. dosregs.bx:=tb_offset;
  614. realintr($2f,dosregs);
  615. dosmemget(tb_segment,tb_offset,req,sizeof(req));
  616. if (req.status<>$0100) or (req.numbytes<>sizeof(sizereq)) then
  617. exit; //An error occurred
  618. dosmemget(tb_segment,tb_offset+sizeof(req)+1,sizereq.size,4);
  619. blocksize:=sectreq.secsize;
  620. freeblocks:=0; //always 0 for a cdrom
  621. totblocks:=sizereq.size;
  622. DiskData_CDROM:=true;
  623. end;
  624. begin
  625. if drive=0 then
  626. begin
  627. dosregs.ax:=$1900; //get current default drive
  628. msdos(dosregs);
  629. drive:=dosregs.al+1;
  630. end;
  631. if not DiskData_CDROM then
  632. if not DiskData_7303 then
  633. if not DiskData_36 then
  634. begin
  635. do_diskdata:=-1;
  636. exit;
  637. end;
  638. do_diskdata:=blocksize;
  639. if free then
  640. do_diskdata:=do_diskdata*freeblocks
  641. else
  642. do_diskdata:=do_diskdata*totblocks;
  643. end;
  644. function diskfree(drive : byte) : int64;
  645. begin
  646. diskfree:=Do_DiskData(drive,TRUE);
  647. end;
  648. function disksize(drive : byte) : int64;
  649. begin
  650. disksize:=Do_DiskData(drive,false);
  651. end;
  652. {******************************************************************************
  653. --- LFNFindfirst LFNFindNext ---
  654. ******************************************************************************}
  655. type
  656. LFNSearchRec=packed record
  657. attr,
  658. crtime,
  659. crtimehi,
  660. actime,
  661. actimehi,
  662. lmtime,
  663. lmtimehi,
  664. sizehi,
  665. size : longint;
  666. reserved : array[0..7] of byte;
  667. name : array[0..259] of byte;
  668. shortname : array[0..13] of byte;
  669. end;
  670. procedure LFNSearchRec2Dos(const w:LFNSearchRec;hdl:longint;var d:Searchrec;from_findfirst : boolean);
  671. var
  672. Len : longint;
  673. begin
  674. With w do
  675. begin
  676. FillChar(d,sizeof(SearchRec),0);
  677. if DosError=0 then
  678. len:=StrLen(@Name)
  679. else
  680. len:=0;
  681. d.Name[0]:=chr(len);
  682. Move(Name[0],d.Name[1],Len);
  683. d.Time:=lmTime;
  684. d.Size:=Size;
  685. d.Attr:=Attr and $FF;
  686. if (DosError<>0) and from_findfirst then
  687. hdl:=-1;
  688. Move(hdl,d.Fill,4);
  689. end;
  690. end;
  691. {$ifdef DEBUG_LFN}
  692. const
  693. LFNFileName : string = 'LFN.log';
  694. LFNOpenNb : longint = 0;
  695. LogLFN : boolean = false;
  696. var
  697. lfnfile : text;
  698. {$endif DEBUG_LFN}
  699. procedure LFNFindFirst(path:pchar;attr:longint;var s:searchrec);
  700. var
  701. i : longint;
  702. w : LFNSearchRec;
  703. begin
  704. { allow slash as backslash }
  705. DoDirSeparators(path);
  706. dosregs.si:=1; { use ms-dos time }
  707. { don't include the label if not asked for it, needed for network drives }
  708. if attr=$8 then
  709. dosregs.ecx:=8
  710. else
  711. dosregs.ecx:=attr and (not 8) and $FF; { no required attributes }
  712. dosregs.edx:=tb_offset+Sizeof(LFNSearchrec)+1;
  713. dosmemput(tb_segment,tb_offset+Sizeof(LFNSearchrec)+1,path^,strlen(path)+1);
  714. dosregs.ds:=tb_segment;
  715. dosregs.edi:=tb_offset;
  716. dosregs.es:=tb_segment;
  717. dosregs.ax:=$714e;
  718. msdos(dosregs);
  719. LoadDosError;
  720. if DosError=2 then
  721. DosError:=18;
  722. {$ifdef DEBUG_LFN}
  723. if (DosError=0) and LogLFN then
  724. begin
  725. Append(lfnfile);
  726. inc(LFNOpenNb);
  727. Writeln(lfnfile,LFNOpenNb,' LFNFindFirst called ',path);
  728. close(lfnfile);
  729. end;
  730. {$endif DEBUG_LFN}
  731. copyfromdos(w,sizeof(LFNSearchRec));
  732. LFNSearchRec2Dos(w,dosregs.ax,s,true);
  733. end;
  734. procedure LFNFindNext(var s:searchrec);
  735. var
  736. hdl : longint;
  737. w : LFNSearchRec;
  738. begin
  739. Move(s.Fill,hdl,4);
  740. dosregs.si:=1; { use ms-dos time }
  741. dosregs.edi:=tb_offset;
  742. dosregs.es:=tb_segment;
  743. dosregs.ebx:=hdl;
  744. dosregs.ax:=$714f;
  745. msdos(dosregs);
  746. LoadDosError;
  747. copyfromdos(w,sizeof(LFNSearchRec));
  748. LFNSearchRec2Dos(w,hdl,s,false);
  749. end;
  750. procedure LFNFindClose(var s:searchrec);
  751. var
  752. hdl : longint;
  753. begin
  754. Move(s.Fill,hdl,4);
  755. { Do not call MsDos if FindFirst returned with an error }
  756. if hdl=-1 then
  757. begin
  758. DosError:=0;
  759. exit;
  760. end;
  761. dosregs.ebx:=hdl;
  762. dosregs.ax:=$71a1;
  763. msdos(dosregs);
  764. LoadDosError;
  765. {$ifdef DEBUG_LFN}
  766. if (DosError=0) and LogLFN then
  767. begin
  768. Append(lfnfile);
  769. Writeln(lfnfile,LFNOpenNb,' LFNFindClose called ');
  770. close(lfnfile);
  771. if LFNOpenNb>0 then
  772. dec(LFNOpenNb);
  773. end;
  774. {$endif DEBUG_LFN}
  775. end;
  776. {******************************************************************************
  777. --- DosFindfirst DosFindNext ---
  778. ******************************************************************************}
  779. procedure dossearchrec2searchrec(var f : searchrec);
  780. var
  781. len : longint;
  782. begin
  783. { Check is necessary!! OS/2's VDM doesn't clear the name with #0 if the }
  784. { file doesn't exist! (JM) }
  785. if dosError = 0 then
  786. len:=StrLen(@f.Name)
  787. else len := 0;
  788. Move(f.Name[0],f.Name[1],Len);
  789. f.Name[0]:=chr(len);
  790. end;
  791. procedure DosFindfirst(path : pchar;attr : word;var f : searchrec);
  792. var
  793. i : longint;
  794. begin
  795. { allow slash as backslash }
  796. DoDirSeparators(path);
  797. copytodos(f,sizeof(searchrec));
  798. dosregs.edx:=tb_offset;
  799. dosregs.ds:=tb_segment;
  800. dosregs.ah:=$1a;
  801. msdos(dosregs);
  802. dosregs.ecx:=attr;
  803. dosregs.edx:=tb_offset+Sizeof(searchrec)+1;
  804. dosmemput(tb_segment,tb_offset+Sizeof(searchrec)+1,path^,strlen(path)+1);
  805. dosregs.ds:=tb_segment;
  806. dosregs.ah:=$4e;
  807. msdos(dosregs);
  808. copyfromdos(f,sizeof(searchrec));
  809. LoadDosError;
  810. dossearchrec2searchrec(f);
  811. end;
  812. procedure Dosfindnext(var f : searchrec);
  813. begin
  814. copytodos(f,sizeof(searchrec));
  815. dosregs.edx:=tb_offset;
  816. dosregs.ds:=tb_segment;
  817. dosregs.ah:=$1a;
  818. msdos(dosregs);
  819. dosregs.ah:=$4f;
  820. msdos(dosregs);
  821. copyfromdos(f,sizeof(searchrec));
  822. LoadDosError;
  823. dossearchrec2searchrec(f);
  824. end;
  825. {******************************************************************************
  826. --- Findfirst FindNext ---
  827. ******************************************************************************}
  828. procedure findfirst(const path : pathstr;attr : word;var f : searchRec);
  829. var
  830. path0 : array[0..255] of char;
  831. begin
  832. doserror:=0;
  833. strpcopy(path0,path);
  834. if LFNSupport then
  835. LFNFindFirst(path0,attr,f)
  836. else
  837. Dosfindfirst(path0,attr,f);
  838. end;
  839. procedure findnext(var f : searchRec);
  840. begin
  841. doserror:=0;
  842. if LFNSupport then
  843. LFNFindnext(f)
  844. else
  845. Dosfindnext(f);
  846. end;
  847. Procedure FindClose(Var f: SearchRec);
  848. begin
  849. DosError:=0;
  850. if LFNSupport then
  851. LFNFindClose(f);
  852. end;
  853. type swap_proc = procedure;
  854. var
  855. _swap_in : swap_proc;external name '_swap_in';
  856. _swap_out : swap_proc;external name '_swap_out';
  857. _exception_exit : pointer;external name '_exception_exit';
  858. _v2prt0_exceptions_on : longbool;external name '_v2prt0_exceptions_on';
  859. procedure swapvectors;
  860. begin
  861. if _exception_exit<>nil then
  862. if _v2prt0_exceptions_on then
  863. _swap_out()
  864. else
  865. _swap_in();
  866. end;
  867. {******************************************************************************
  868. --- File ---
  869. ******************************************************************************}
  870. Function FSearch(path: pathstr; dirlist: string): pathstr;
  871. var
  872. p1 : longint;
  873. s : searchrec;
  874. newdir : pathstr;
  875. begin
  876. { No wildcards allowed in these things }
  877. if (pos('?',path)<>0) or (pos('*',path)<>0) then
  878. begin
  879. fsearch:='';
  880. exit;
  881. end;
  882. { check if the file specified exists }
  883. findfirst(path,anyfile and not(directory),s);
  884. if doserror=0 then
  885. begin
  886. findclose(s);
  887. fsearch:=path;
  888. exit;
  889. end;
  890. findclose(s);
  891. { allow slash as backslash }
  892. DoDirSeparators(dirlist);
  893. repeat
  894. p1:=pos(';',dirlist);
  895. if p1<>0 then
  896. begin
  897. newdir:=copy(dirlist,1,p1-1);
  898. delete(dirlist,1,p1);
  899. end
  900. else
  901. begin
  902. newdir:=dirlist;
  903. dirlist:='';
  904. end;
  905. if (newdir<>'') and (not (newdir[length(newdir)] in [DirectorySeparator,DriveSeparator])) then
  906. newdir:=newdir+DirectorySeparator;
  907. findfirst(newdir+path,anyfile and not(directory),s);
  908. if doserror=0 then
  909. newdir:=newdir+path
  910. else
  911. newdir:='';
  912. findclose(s);
  913. until (dirlist='') or (newdir<>'');
  914. fsearch:=newdir;
  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.