home *** CD-ROM | disk | FTP | other *** search
/ For Beginners & Professional Hackers / cd.iso / docum / dos-ref.doc / examples / chap4.arj / PHANTOM.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1990-09-25  |  40.9 KB  |  1,245 lines

  1. {$A-,B-,D+,L+,E-,F-,I-,N-,O-,R-,S-,V-}
  2. {$M 2048,128,1000}
  3. program phantom_drive;
  4. uses
  5.     dos, crt;
  6.  
  7. type
  8.     sig_rec = record
  9.         signature : string[7];
  10.         psp : word;
  11.         drive_no : byte;
  12.     end;
  13.  
  14. const
  15.     cds_id_size = 10;
  16.     cds_id = 'Phantom. :\';
  17.     our : sig_rec =
  18.         (   signature : 'PHANTOM'; psp : 0; drive_no : 0);
  19.     vollab : string[13] = 'AN ILLUS.ION'#0; { Our Volume label }
  20.     maxfilesize = 2047;                     { for our 1 file }
  21.  
  22.     isr_code_max = 102;                     { offset of last byte }
  23.                                             { in our ISR macine code }
  24.  
  25. type
  26.     strptr = ^string;
  27.     cdsidarr = array[1..cds_id_size] of char;
  28.     cdsidptr = ^cdsidarr;
  29.  
  30. { FindFirst/Next data block - ALL DOS VERSIONS }
  31.     sdb_ptr = ^sdb_rec;
  32.     sdb_rec = record
  33.         drv_lett : byte;
  34.         srch_tmpl : array[0..10] of char;
  35.         srch_attr : byte;
  36.         dir_entry : word;
  37.         par_clstr : word;
  38.         f1 : array[1..4] of byte;
  39.     end;
  40.  
  41. { DOS System File Table entry - ALL DOS VERSIONS }
  42.     sft_ptr = ^sft_rec;
  43.     sft_rec = record
  44.         handle_cnt,
  45.         open_mode : word;
  46.         attr_byte : byte;
  47.         dev_info : word;
  48.         devdrv_ptr : pointer;
  49.         start_clstr,        { we don't need to touch this }
  50.         f_time,
  51.         f_date : word;
  52.         f_size,
  53.         f_pos : longint;
  54.         rel_lastclstr,      { we don't need to touch this }
  55.         abs_lastclstr,      { we don't need to touch this }
  56.         dir_sector : word;  { we don't need to touch this }
  57.         dir_entryno : byte; { we don't need to touch this }
  58.         fcb_fn : array[0..10] of char;
  59.     end;
  60.  
  61. { DOS Current directory structure - DOS VERSION 3.xx }
  62.     cds3_rec = record
  63.         curr_path : array[0..66] of char;
  64.         flags : word;
  65.         f1 : array[1..10] of byte;  { we don't need to touch this }
  66.         root_ofs : word;
  67.     end;
  68.  
  69. { DOS Current directory structure - DOS VERSION 4.xx }
  70.     cds4_rec = record
  71.         curr_path : array[0..66] of char;
  72.         flags : word;
  73.         f1 : array[1..10] of byte;  { we don't need to touch this }
  74.         root_ofs : word;
  75.         f2 : array[1..7] of byte;   { we don't need to touch this }
  76.     end;
  77.  
  78. { DOS Directory entry for 'found' file - ALL DOS VERSIONS }
  79.     dir_ptr = ^dir_rec;
  80.     dir_rec = record
  81.         fname : array[0..10] of char;
  82.         fattr : byte;
  83.         f1 : array[1..10] of byte;
  84.         time_lstupd,
  85.         date_lstupd,
  86.         start_clstr : word;         { we don't need to touch this }
  87.         fsiz : longint;
  88.     end;
  89.  
  90. { Swappable DOS Area - DOS VERSION 3.xx }
  91.     sda3_rec = record
  92.          f0 : array[1..12] of byte;
  93.          curr_dta : pointer;
  94.          f1 : array[1..30] of byte;
  95.          dd,
  96.          mm : byte;
  97.          yy_1980 : word;
  98.          f2 : array[1..96] of byte;
  99.          fn1,
  100.          fn2 : array[0..127] of char;
  101.          sdb : sdb_rec;
  102.          found_file : dir_rec;
  103.          drive_cdscopy : cds3_rec;
  104.          fcb_fn1 : array[0..10] of char;
  105.          f3 : byte;
  106.          fcb_fn2 : array[0..10] of char;
  107.          f4 : array[1..11] of byte;
  108.          srch_attr : byte;
  109.          open_mode : byte;
  110.          f5 : array[1..48] of byte;
  111.          drive_cdsptr : pointer;
  112.          f6 : array[1..12] of byte;
  113.          fn1_csofs,
  114.          fn2_csofs : word;
  115.          f7 : array[1..56] of byte;
  116.          ren_srcfile : sdb_rec;
  117.          ren_file : dir_rec;
  118.     end;
  119.  
  120. { Swappable DOS Area - DOS VERSION 4.xx }
  121.     sda4_ptr = ^sda4_rec;
  122.     sda4_rec = record
  123.          f0 : array[1..12] of byte;
  124.          curr_dta : pointer;
  125.          f1 : array[1..32] of byte;
  126.          dd,
  127.          mm : byte;
  128.          yy_1980 : word;
  129.          f2 : array[1..106] of byte;
  130.          fn1,
  131.          fn2 : array[0..127] of char;
  132.          sdb : sdb_rec;
  133.          found_file : dir_rec;
  134.          drive_cdscopy : cds4_rec;
  135.          fcb_fn1 : array[0..10] of char;
  136.          f3 : byte;
  137.          fcb_fn2 : array[0..10] of char;
  138.          f4 : array[1..11] of byte;
  139.          srch_attr : byte;
  140.          open_mode : byte;
  141.          f5 : array[1..51] of byte;
  142.          drive_cdsptr : pointer;
  143.          f6 : array[1..12] of byte;
  144.          fn1_csofs,
  145.          fn2_csofs : word;
  146.          f7 : array[1..71] of byte;
  147.          spop_act,
  148.          spop_attr,
  149.          spop_mode : word;
  150.          f8 : array[1..29] of byte;
  151.          ren_srcfile : sdb_rec;
  152.          ren_file : dir_rec;
  153.     end;
  154.  
  155. { DOS List of lists structure - DOS VERSIONS 3.1 thru 4 }
  156.     lol_rec = record
  157.         f1 : array[1..22] of byte;
  158.         cds : pointer;
  159.         f2 : array[1..7] of byte;
  160.         last_drive : byte;
  161.     end;
  162.  
  163. { This serves as a list of the function types that we support }
  164.     fxn_type = (_inquiry, _rd, _md, _cd, _close, _commit, _read,
  165.                 _write, _lock, _unlock, _space, _setattr, _getattr, 
  166.                 _rename, _delete, _open, _create, _ffirst, _fnext, 
  167.                 _seek, _specopen, _unsupported);
  168.  
  169. { A de rigeur structure for manipulators of pointers }
  170.     os = record o,s:word; end;
  171.  
  172.     fcbfnbuf = array[0..12] of char;
  173.     fcbfnptr = ^fcbfnbuf;
  174.  
  175.     ascbuf = array[0..127] of char;
  176.     ascptr = ^ascbuf;
  177.  
  178. { This defines a pointer to our primary Int 2Fh ISR structure }
  179.     isrptr = ^isr_rec;
  180.  
  181. { A structure to contain all register values. The TP DOS registers 
  182.     type is insufficient }
  183.     regset = record 
  184.         bp,es,ds,di,si,dx,cx,bx,ax,ss,sp,cs,ip,flags:word; end;
  185.  
  186. { Our Int 2F ISR structure }
  187.     isr_code_buffer = array[0..isr_code_max] of byte;
  188.     isr_rec = record
  189.         ic:isr_code_buffer;  { Contains our macine code ISR stub code }
  190.         save_ss,             { Stores SS on entry before stack switch }
  191.         save_sp,             { Stores SP on entry before stack switch }
  192.         real_fl,             { Stores flags as they were on entry }
  193.         save_fl,             { Stores flags from the stack }
  194.         save_cs,             { Stores return CS from the stack }
  195.         save_ip : word;      { Stores return IP from the stack }
  196.         our_drive : boolean; { For ISR to either chain on or return } 
  197.     end;
  198.  
  199.     strfn = string[12];
  200.  
  201. const
  202.  { all the calls we need to support are in the range 0..33 }
  203.     fxn_map_max = $2e;
  204.     fxn_map : array[0..fxn_map_max] of fxn_type =
  205.                 (_inquiry, _rd, _unsupported, _md, _unsupported,
  206.                 _cd, _close, _commit, _read, _write,
  207.                 _lock, _unlock, _space, _unsupported, _setattr, 
  208.                 _getattr, _unsupported, _rename, _unsupported,
  209.                 _delete, _unsupported, _unsupported, _open, _create, 
  210.                 _unsupported, _unsupported, _unsupported, _ffirst, _fnext,
  211.                 _unsupported, _unsupported, _unsupported, _unsupported,
  212.                 _seek, _unsupported, _unsupported, _unsupported, 
  213.                 _unsupported, _unsupported, _unsupported, _unsupported, 
  214.                 _unsupported, _unsupported, _unsupported, _unsupported, 
  215.                 _unsupported, _specopen
  216.                 );
  217.  
  218. { The following are offsets into the ISR stub code where run time 
  219.   values must be fixed in }
  220.     prev_hndlr  = 99;
  221.     redir_entry = 49;
  222.     our_sp_ofs  = 45;
  223.     our_ss_ofs  = 40;
  224.  
  225. { The following offsets are known at compile time and are directly 
  226.   referenced in the ISR stub code }
  227.     save_ss_ofs = isr_code_max+1;
  228.     save_sp_ofs = isr_code_max+3;
  229.     save_rf_ofs = isr_code_max+5;
  230.     save_fl_ofs = isr_code_max+7;
  231.     save_cs_ofs = isr_code_max+9;
  232.     save_ip_ofs = isr_code_max+11;
  233.     our_drv_ofs = isr_code_max+13;
  234.  
  235. { Our ISR stub code is defined as a constant array of bytes which 
  236.   actually contains machine code as commented on the right }
  237.     isr_code : isr_code_buffer = { entry: }
  238.     (       $90,                { nop OR int 3          ; for debugging }
  239.             $9c,                { pushf                 ; save flags    }
  240.         $80,$fc,$11,            { cmp   ah,11h          ; our fxn?      }
  241.         $75,$5a,                { jne   not_ours        ; bypass        }
  242.     $2e,$8f,$06, save_rf_ofs, 0,{ pop   cs:real_fl      ; store act flgs}
  243.     $2e,$8f,$06, save_ip_ofs, 0,{ pop   cs:save_ip      ; store cs:ip   }
  244.     $2e,$8f,$06, save_cs_ofs, 0,{ pop   cs:save_cs      ; and flags     }
  245.     $2e,$8f,$06, save_fl_ofs, 0,{ pop   cs:save_fl      ; from stack    }
  246.  
  247.     $2e,$89,$26, save_sp_ofs, 0,{ mov   cs:save_sp,sp   ; save stack    }
  248.         $8c,$d4,                { mov   sp,ss                           }
  249.     $2e,$89,$26, save_ss_ofs, 0,{ mov   cs:save_ss,sp                   }
  250.  
  251.         $bc,     0,0,           { mov   sp,SSEG         ; set our stack }
  252.         $8e,$d4,                { mov   ss,sp                           }
  253.         $bc,     0,0,           { mov   sp,SPTR                         }
  254.  
  255.         $9c,                    { pushf                 ; call our      }
  256.         $9a,     0,0,0,0,       { call  redir           ; intr proc.    }
  257.  
  258.     $2e,$8b,$26, save_ss_ofs, 0,{ mov   sp,cs:save_ss   ; put back      }
  259.         $8e,$d4,                { mov   ss,sp           ; caller's stack}
  260.     $2e,$8b,$26, save_sp_ofs, 0,{ mov   sp,cs:save_sp                   }
  261.  
  262.     $2e,$ff,$36, save_fl_ofs, 0,{ push  cs:save_fl      ; restore       }
  263.     $2e,$ff,$36, save_cs_ofs, 0,{ push  cs:save_cs      ; restore       }
  264.     $2e,$ff,$36, save_ip_ofs, 0,{ push  cs:save_ip      ; return addr.  }
  265.     $2e,$ff,$36, save_rf_ofs, 0,{ push  cs:real_fl      ; save act flgs }
  266.  
  267.     $2e,$80,$3e, our_drv_ofs,0,0,{ cmp cs:our_drive,0; not our drive?}
  268.         $74,$04,                { je    not_ours        ; no, jump      }
  269.         $9d,                    { popf                  ; yes, restore  }
  270.         $ca,$02,$00,            { retf  2               ; & return flags}
  271.                             { not_ours: }
  272.         $9d,                    { popf                  ; restore flags }
  273.         $ea,    0,0,0,0         { jmp   far prev_hndlr  ; pass the buck }
  274.         );
  275.  
  276. var
  277. { The instance of our Int 2F ISR }
  278.     isr : isrptr;
  279.  
  280. { variables relating to the one allowable file.. }
  281.     file_name : fcbfnbuf;
  282.     file_buffer : array[0..maxfilesize] of byte;
  283.     file_opens,
  284.     file_date,
  285.     file_time : word;
  286.     file_attr : byte;
  287.     file_size : longint;
  288.  
  289. { Our full directory structure }
  290.     max_path : ascbuf;
  291.  
  292. { Global stuff }
  293.     our_sp : word;          { SP to switch to on entry }
  294.     dos_major,              { Major DOS vers }
  295.     dos_minor,              { Minor DOS vers }
  296.     drive_no : byte;        { A: is 1, B: is 2, etc. }
  297.     strbuf : string;        { General purpose pascal string buffer }
  298.     a1,                     { Pointer to an ASCIIZ string }
  299.     a2 : ascptr;            { Pointer to an ASCIIZ string }
  300.     drive : string[3];      { Command line parameter area }
  301.     fxn : fxn_type;         { Record of function in progress }
  302.     r : regset;             { Global save area for all caller's regs }
  303.     temp_name : fcbfnbuf;   { General purpose ASCIIZ filename buffer }
  304.     iroot,                  { Index to root directory in max_path }
  305.     icur,                   { Index to current directory in max_path }
  306.     lmax,                   { Length of max_path }
  307.     ifile : byte;           { Index to directory in max_path with file }
  308.     ver : word;             { full DOS version }
  309.     sda : pointer;          { pointer to the Swappable Dos Area }
  310.     lol : pointer;          { pointer to the DOS list of lists struct }
  311.  
  312. const h:array[0..15] of char = '0123456789abcdef';
  313. type str4 = string[4];
  314. function hex(inp:word):str4;
  315. begin
  316.     hex[0]:=#4;
  317.     hex[1]:=h[inp shr 12];
  318.     hex[2]:=h[(inp shr 8) and $f];
  319.     hex[3]:=h[(inp shr 4) and $f];
  320.     hex[4]:=h[inp and $f];
  321. end;
  322.  
  323. { Fail PHANTOM, print message, exit to DOS }
  324. procedure failprog(msg:string);
  325. begin
  326.     writeln(msg);
  327.     Halt(1);
  328. end;
  329.  
  330. { Get DOS version, address of Swappable DOS Area, and address of 
  331.   DOS List of lists. We only run on versions of DOS >= 3.10, so
  332.   fail otherwise }
  333. procedure get_dos_vars;
  334. var r : registers;
  335. begin
  336.     ver:=dosversion;
  337.     dos_major:=lo(ver);
  338.     dos_minor:=hi(ver);
  339.     if (dos_major<3) or ((dos_major=3) and (dos_minor<10)) then
  340.         failprog('DOS Version must be 3.10 or greater');
  341.     with r do
  342.         begin
  343.             ax:=$5d06; msdos(r); sda:=ptr(ds,si);   { Get SDA pointer }
  344.             ax:=$5200; msdos(r); lol:=ptr(es,bx);   { Get LoL pointer }
  345.         end;
  346. end;
  347.  
  348. { Fail the current redirector call with the supplied error number, i.e.
  349.   set the carry flag in the returned flags, and set ax=error code }
  350. procedure fail(err:word);
  351. begin
  352.     r.flags:=r.flags or fcarry;
  353.     r.ax:=err;
  354. end;
  355.  
  356. { Convert an 11 byte fcb style filename to ASCIIZ name.ext format }
  357. procedure fnfmfcbnm(var ss; var p:ascptr);
  358. var i,j:byte; s:ascbuf absolute ss;
  359.     dot : boolean;
  360. begin
  361.     p:=@temp_name;
  362.     i:=0;
  363.     while (i<8) and (s[i]<>' ') do inc(i);
  364.     move(s,p^,i);
  365.     j:=8;
  366.     while (j<11) and (s[j]<>' ') do inc(j);
  367.     move(s,p^[succ(i)],j-8);
  368.     if j<>8 then begin p^[i]:='.'; p^[j]:=#0; end
  369.     else p^[i]:=#0;
  370. end;    
  371.  
  372. { The opposite of the above, convert an ASCIIZ name.ext filename 
  373.   into an 11 byte fcb style filename }
  374. procedure cnvt2fcb(var ss; var pp);
  375. var i,j:byte;
  376.     s:ascbuf absolute ss;
  377.     p:ascbuf absolute pp;
  378. begin
  379.     i:=0; j:=0;
  380.     fillchar(p,11,' ');
  381.     while s[i]<>#0 do
  382.         begin
  383.             if s[i]='.' then j:=7 else p[j]:=s[i];
  384.             inc(i);
  385.             inc(j);
  386.         end;
  387. end;    
  388.  
  389. { Get the length of an ASCIIZ string }
  390. function asclen(var a:ascbuf):word;
  391. var i:word;
  392. begin i:=0; while (i<65535) and (a[i]<>#0) do inc(i); asclen:=i; end;
  393.  
  394. { Translate a maximum of strlim bytes of an ASCIIZ string to a Pascal string }
  395. procedure ascii2string(src, dst : pointer; strlim : byte);
  396. var i:integer;
  397. begin
  398.     byte(dst^):=strlim;
  399.     move(src^,pointer(succ(longint(dst)))^,strlim);
  400.     i:=pos(#0,string(dst^));
  401.     if i<>0 then byte(dst^):=pred(i);
  402. end;
  403.  
  404. { Set up global a1 to point to the appropriate source for the file
  405.   or directory name parameter for this call }
  406. procedure set_fn1;
  407. begin
  408.     case fxn of
  409. { For these calls, a fully qualified file/directory name is given in the
  410.   SDA first filename field. This field, incidentally, can also be referenced
  411.   indirectly through the SDA first filename offset field into DOS's CS. }
  412.         _rd .. _cd, _setattr .. _create, _ffirst, _specopen :
  413.             if dos_major=3 then
  414.                 a1:=@sda3_rec(sda^).fn1
  415.             else
  416.                 a1:=@sda4_rec(sda^).fn1;
  417.  
  418. { These do not need a filename... }
  419.         _close .. _write, _seek : ;
  420.  
  421. { For findnext, an fcb style filename template is available within the
  422.   SDA search data block field }
  423.         _fnext :
  424.             if dos_major=3 then
  425.                 a1:=@sda3_rec(sda^).sdb.srch_tmpl
  426.             else
  427.                 a1:=@sda4_rec(sda^).sdb.srch_tmpl;
  428.     end;
  429. end;
  430.  
  431. { Back up a directory level, ie go back to the previous \ in a path string }
  432. function back_1(var path:ascbuf; var i:byte):boolean;
  433. begin
  434.     if i=iroot then begin back_1:=false; exit; end;
  435.     repeat dec(i) until (i=iroot) or (path[i]='\');
  436.     back_1:=true;
  437. end;
  438.  
  439. { Check that the qualified pathname that is in a1 matches our full
  440.   directory structure to length lsrc. If not, fail with 'Path not found' }
  441. function process_path(a1 : ascptr; lsrc : byte):boolean;
  442. var isrc : byte;
  443. begin
  444.     process_path:=false;
  445.     isrc:=0; 
  446.     for isrc:=0 to pred(lsrc) do
  447.         if (isrc>lmax) or
  448.             (a1^[isrc]<>max_path[isrc]) then
  449.                 begin fail(3); exit; end;
  450.     inc(isrc);
  451.     if max_path[isrc]<>'\' then fail(3)
  452.     else process_path:=true;
  453. end;
  454.  
  455. function the_time:word;
  456.     function ticks:longint; 
  457.         { mov ah,0   int 1ah   mov ax,dx   mov dx,cx }
  458.         inline($b4/$00/$cd/$1a/$8b/$c2/$8b/$d1); 
  459. var t:longint;
  460.     hh, mm, s2 : word;
  461. begin
  462.     t:=ticks;
  463.     hh:=t div (182*6*60);
  464.     dec(t,hh*(182*6*60));
  465.     mm:=t div (182*6);
  466.     dec(t,mm*(182*6));
  467.     s2:=(t*10) div 364;
  468.     the_time:=(hh shl 11) or (mm shl 5) or s2;
  469. end;
  470.     
  471. function the_date:word;
  472. begin   
  473.     if dos_major=3 then
  474.         with sda3_rec(sda^) do 
  475.             the_date:=(yy_1980 shl 9) or (mm shl 5) or dd
  476.     else
  477.         with sda4_rec(sda^) do 
  478.             the_date:=(yy_1980 shl 9) or (mm shl 5) or dd;
  479. end;
  480.  
  481. { Change Directory - subfunction 05h }
  482. procedure cd;
  483. var lsrc : byte;
  484. begin
  485.     lsrc:=asclen(a1^);
  486.     if lsrc=succ(iroot) then dec(lsrc); { Special case for root }
  487.     if not process_path(a1,lsrc) then exit;
  488.     if dos_major=3 then             { Copy in the new path into the CDS }
  489.         move(max_path,cds3_rec(sda3_rec(sda^).drive_cdsptr^).curr_path,lsrc)
  490.     else
  491.         move(max_path,cds4_rec(sda4_rec(sda^).drive_cdsptr^).curr_path,lsrc);
  492.     icur:=lsrc;
  493. end;
  494.  
  495. { Remove Directory - subfunction 01h }
  496. procedure rd;
  497. var lsrc : byte;
  498. begin
  499.     lsrc:=asclen(a1^);
  500.     if not process_path(a1,lsrc) then exit;
  501.     if lsrc=icur then begin fail(5); exit; end;
  502.     if lsrc=ifile then begin fail(5); exit; end;
  503.     if lsrc<>lmax then begin fail(5); exit; end;
  504.     if not back_1(max_path,lmax) then begin fail(3); exit; end;
  505.     max_path[succ(lmax)]:=#0;
  506. end;
  507.  
  508. { Make Directory - subfunction 03h }
  509. procedure md;
  510. var lsrc, isrc : byte;
  511. begin
  512.     lsrc:=asclen(a1^);
  513.     isrc:=lsrc;
  514.     if not back_1(a1^,isrc) then begin fail(5); exit; end;
  515.     if not process_path(a1,isrc) then exit;
  516.     if isrc<>lmax then begin fail(5); exit; end;
  517.     move(a1^,max_path,lsrc);
  518.     max_path[lsrc]:='\';
  519.     max_path[succ(lsrc)]:=#0;
  520.     lmax:=lsrc;
  521. end;
  522.  
  523. { Close File - subfunction 06h }
  524. procedure clsfil;
  525. begin
  526. { Clear down supplied SFT entry for file }
  527.     with sft_rec(ptr(r.es,r.di)^) do
  528.         begin
  529.             if file_opens=0 then begin fail(5); exit; end;
  530.             dec(file_opens);
  531.             if boolean(open_mode and 3) and
  532.                not boolean(dev_info and $40) then
  533.                 begin                { if new or updated file... }
  534.                     if f_date=0 then file_date:=the_date
  535.                     else file_date:=f_date;
  536.                     if f_time=0 then file_time:=the_time
  537.                     else file_time:=f_time;
  538.                 end;
  539.         end;
  540. end;
  541.  
  542. { Commit File - subfunction 07h }
  543. procedure cmmtfil;
  544. begin
  545. { We support this but don't do anything... }
  546.     if file_opens=0 then fail(5);
  547. end;
  548.  
  549. { Read from File - subfunction 08h }
  550. procedure readfil;
  551. begin
  552.     if file_opens=0 then begin fail(5); exit; end;
  553.  
  554. { Fill the user's buffer (the DTA) from our internal; file buffer, 
  555.   and update the suplied SFT for the file }
  556.     with sft_rec(ptr(r.es,r.di)^) do
  557.         begin
  558.             if (f_pos+r.cx)>f_size then r.cx:=f_size-f_pos;
  559.             if dos_major=3 then
  560.                 move(file_buffer[f_pos],sda3_rec(sda^).curr_dta^,r.cx)
  561.             else
  562.                 move(file_buffer[f_pos],sda4_rec(sda^).curr_dta^,r.cx);
  563.             inc(f_pos,r.cx);
  564.         end;
  565. end;
  566.  
  567. { Write to File - subfunction 09h }
  568. procedure writfil;
  569. begin
  570.     if file_opens=0 then begin fail(5); exit; end;
  571.  
  572. { Update our internal file buffer from the user buffer (the DTA) and 
  573.   update the supplied SFT entry for the file }
  574.     with sft_rec(ptr(r.es,r.di)^) do
  575.         begin
  576.             if boolean(file_attr and readonly) then
  577.                 begin fail(5); exit; end; 
  578.             if (f_pos+r.cx)>maxfilesize then r.cx:=maxfilesize-f_pos;
  579.             if dos_major=3 then
  580.                 move(sda3_rec(sda^).curr_dta^,file_buffer[f_pos],r.cx)
  581.             else
  582.                 move(sda4_rec(sda^).curr_dta^,file_buffer[f_pos],r.cx);
  583.             inc(f_pos,r.cx);
  584.             if f_pos>file_size then file_size:=f_pos;
  585.             f_size:=file_size;
  586.             dev_info:=dev_info and (not $40);
  587.         end;
  588. end;
  589.  
  590. { Get Disk Space - subfunction 0Ch }
  591. procedure dskspc;
  592. begin
  593. { Our 'disk' has 1 cluster containing 1 sector of 2048 bytes, and ... }
  594.     r.ax:=1; 
  595.     r.bx:=1;
  596.     r.cx:=succ(maxfilesize);
  597. { ... its either all available or none! }
  598.     r.dx:=ord(ifile=0);
  599. end;
  600.  
  601. { Set File Attributes - subfunction 0Eh }
  602. procedure setfatt;
  603. var lsrc, isrc : byte;
  604. begin
  605.     lsrc:=asclen(a1^);
  606.     isrc:=lsrc;
  607.     if not back_1(a1^,isrc) then begin fail(2); exit; end;
  608.     if not process_path(a1,isrc) then exit;
  609.     if isrc<>ifile then begin fail(2); exit; end;
  610.     inc(isrc);
  611.     fillchar(temp_name,13,#0);
  612.     move(a1^[isrc],temp_name,lsrc-isrc);
  613.     if temp_name<>file_name then begin fail(2); exit; end;
  614.     if file_opens>0 then fail(5)
  615.     else  file_attr:=byte(ptr(r.ss,r.sp)^);
  616. end;
  617.  
  618. { Get File Attributes - subfunction 0Fh }
  619. procedure getfatt;
  620. var lsrc, isrc : byte;
  621. begin
  622.     lsrc:=asclen(a1^);
  623.     isrc:=lsrc;
  624.     if not back_1(a1^,isrc) then begin fail(2); exit; end;
  625.     if not process_path(a1,isrc) then exit;
  626.     if isrc<>ifile then begin fail(2); exit; end;
  627.     inc(isrc);
  628.     fillchar(temp_name,13,#0);
  629.     move(a1^[isrc],temp_name,lsrc-isrc);
  630.     if temp_name<>file_name then begin fail(2); exit; end;
  631.     if file_opens>0 then begin fail(5); exit; end;
  632.     r.ax:=file_attr;
  633. end;
  634.  
  635. { Rename File - subfunction 11h }
  636. procedure renfil;
  637. var lsrc, isrc, isav, i : byte;
  638.     dot:boolean;
  639. begin
  640.     if dos_major=3 then
  641.         a2:=ptr(r.ss,sda3_rec(sda^).fn2_csofs)
  642.     else
  643.         a2:=ptr(r.ss,sda4_rec(sda^).fn2_csofs);
  644.     lsrc:=asclen(a1^);
  645.     isrc:=lsrc;
  646.     if not back_1(a1^,isrc) then begin fail(3); exit; end;
  647.     if not process_path(a1,isrc) then exit;
  648.     if isrc<>ifile then begin fail(2); exit; end;
  649.     inc(isrc);
  650.     fillchar(temp_name,13,#0);
  651.     move(a1^[isrc],temp_name,lsrc-isrc);
  652. { Check that the current filename matches ours }
  653.     if temp_name<>file_name then begin fail(2); exit; end;
  654.     if boolean(file_attr and $7) then begin fail(5); exit; end;
  655.     if file_opens>0 then begin fail(5); exit; end;
  656.     lsrc:=asclen(a2^);
  657.     isrc:=lsrc;
  658.     if not back_1(a2^,isrc) then begin fail(3); exit; end;
  659.     if not process_path(a2,isrc) then exit;
  660.     ifile:=isrc;
  661.     inc(isrc);
  662. { Put in the new file name }
  663.     fillchar(file_name,13,#0);
  664.     move(a2^[isrc],file_name,lsrc-isrc);
  665. end;
  666.  
  667. { This procedure does a wildcard match from the mask onto the target, and,
  668.   if a hit, updates the search data block and found file areas supplied } 
  669. function match(var m, t; var s : sdb_rec; var d : dir_rec;
  670.                 d_e, p_c : word; s_a : byte) : boolean;
  671. var i, j : byte;
  672.     mask : ascbuf absolute m;
  673.     tgt : ascbuf absolute t;
  674. begin
  675.     i:=0; j:=0;
  676.     if tgt[0] in ['\',#0] then begin match:=false; exit; end;
  677.     while i<11 do
  678.         case mask[i] of
  679.             '?' :   if tgt[j] in [#0,'\','.'] then
  680.                         if (i=8) and (tgt[j]='.') then inc(j) else inc(i)
  681.                     else
  682.                         begin inc(i); inc(j); end;
  683.             ' ' :   if tgt[j] in ['.','\',#0] then inc(i)
  684.                     else begin match:=false; exit; end;
  685.             else    if (i=8) and (tgt[j]='.') then inc(j)
  686.                     else
  687.                     if tgt[j]=mask[i] then begin inc(i); inc(j); end
  688.                     else begin match:=false; exit; end;
  689.         end;
  690.     if not (tgt[j] in ['\',#0]) then begin match:=false; exit; end;
  691.     with s do
  692.         begin
  693.             move(mask,srch_tmpl,11);
  694.             dir_entry:=d_e;
  695.             srch_attr:=s_a;
  696.             par_clstr:=p_c;
  697.             drv_lett:=drive_no or $80;
  698.         end;
  699.     with d do
  700.         begin
  701.             i:=0; j:=0;
  702.             fillchar(fname,11,' ');
  703.             while not (tgt[i] in [#0,'\']) do
  704.                 if tgt[i] = '.' then begin j:=8; inc(i); end
  705.                 else begin fname[j]:=tgt[i]; inc(i); inc(j); end;
  706.             case d_e of
  707.                 0 : fattr:=$08;
  708.                 1 : fattr:=$10;
  709.                 2 : fattr:=file_attr;
  710.             end;
  711.             time_lstupd:=file_time;
  712.             date_lstupd:=file_date;
  713.             case d_e of
  714.                 0, 1 : fsiz:=0;
  715.                 2 : fsiz:=file_size;
  716.             end;
  717.         end;
  718.     match:=true;
  719. end;
  720.  
  721. { Delete File - subfunction 13h }
  722. procedure delfil;
  723. var isrc, lsrc : byte;
  724.     sdb:sdb_rec;    { These are dummies for the match procedure to hit }
  725.     der:dir_rec;
  726. begin
  727.     lsrc:=asclen(a1^);
  728.     isrc:=lsrc;
  729.     if not back_1(a1^,isrc) then begin fail(3); exit; end;
  730.     if not process_path(a1,isrc) then exit;
  731.     if isrc<>ifile then begin fail(2); exit; end;
  732.  
  733.     inc(os(a1).o,succ(isrc));
  734.     cnvt2fcb(a1^,temp_name);
  735.     if ((file_attr and $1f)>0) then begin fail(5); exit; end;
  736.     if not match(temp_name,file_name,sdb,der,0,0,0) then
  737.         begin fail(2); exit; end;
  738.     if file_opens=0 then ifile:=0 else fail(5);
  739. end;
  740.  
  741. { Open Existing File - subfunction 16h }
  742. procedure opnfil;
  743. var isrc, lsrc : byte;
  744. begin
  745.     lsrc:=asclen(a1^);
  746.     isrc:=lsrc;
  747.     if not back_1(a1^,isrc) then begin fail(3); exit; end;
  748.     if not process_path(a1,isrc) then exit;
  749.     if isrc<>ifile then begin fail(2); exit; end;
  750.     inc(isrc);
  751.     fillchar(temp_name,13,#0);
  752.     move(a1^[isrc],temp_name,lsrc-isrc);
  753. { Check file names match }
  754.     if temp_name<>file_name then begin fail(2); exit; end;
  755.  
  756. { Initialize supplied SFT entry }
  757.     with sft_rec(ptr(r.es,r.di)^) do
  758.         begin
  759.             file_attr:=byte(ptr(r.ss,r.sp)^);
  760.             if dos_major=3 then
  761.                 open_mode:=sda3_rec(sda^).open_mode and $7f
  762.             else
  763.                 open_mode:=sda4_rec(sda^).open_mode and $7f;
  764.             cnvt2fcb(temp_name,fcb_fn);
  765.             inc(file_opens);
  766.             f_size:=file_size;
  767.             f_date:=file_date;
  768.             f_time:=file_time;
  769.             dev_info:=$8040 or drive_no; { Network drive, unwritten to }
  770.             dir_sector:=0;
  771.             dir_entryno:=0;
  772.             attr_byte:=file_attr;
  773.             f_pos:=0;
  774.             devdrv_ptr:=nil;
  775.         end;
  776. end;
  777.  
  778. { Truncate/Create File - subfunction 17h }
  779. procedure creatfil;
  780. var isrc, lsrc : byte;
  781. begin
  782.     lsrc:=asclen(a1^);
  783.     isrc:=lsrc;
  784.     if not back_1(a1^,isrc) then begin fail(3); exit; end;
  785.     if not process_path(a1,isrc) then exit;
  786.  
  787.     if ifile=0 then 
  788.         begin
  789. { Creating new file }
  790.             ifile:=isrc;
  791.             inc(isrc);
  792.             if isrc=lsrc then begin fail(13); ifile:=0; exit; end;
  793.             fillchar(file_name,13,#0);
  794.             move(a1^[isrc],file_name,lsrc-isrc);
  795.         end
  796.     else
  797.  
  798.     if ifile=isrc then
  799.         begin
  800. { Truncate existing file }
  801.             inc(isrc);
  802.             fillchar(temp_name,13,#0);
  803.             move(a1^[isrc],temp_name,lsrc-isrc);
  804.             if temp_name<>file_name then begin fail(2); exit; end;
  805.             if boolean(file_attr and $7) then begin fail(5); exit; end;
  806.             if file_opens>0 then begin fail(5); exit; end;
  807.         end
  808.     else fail(82);  { This provokes a 'ran out of dir entries' error }
  809.  
  810. { Initialize supplied SFT entry }
  811.     with sft_rec(ptr(r.es,r.di)^) do
  812.         begin
  813.             file_attr:=byte(ptr(r.ss,r.sp)^); { File attr is top of stack }
  814.             open_mode:=$01;     { assume an open mode, none is supplied.. }
  815.             cnvt2fcb(file_name,fcb_fn);
  816.             inc(file_opens);
  817.             f_size:=0;
  818.             f_pos:=0;
  819.             file_size:=0;
  820.             dev_info:=$8040 or drive_no; { Network drive, unwritten to }
  821.             dir_sector:=0;
  822.             dir_entryno:=0;
  823.             f_date:=0;
  824.             f_time:=0;
  825.             devdrv_ptr:=nil;
  826.             attr_byte:=file_attr;
  827.         end;
  828. end;
  829.  
  830. { Special Multi-Purpose Open File - subfunction 2Eh }
  831. procedure spopnfil;
  832. var isrc, lsrc : byte;
  833.     action, mode, result : word;
  834. begin
  835.     lsrc:=asclen(a1^);
  836.     isrc:=lsrc;
  837.     if not back_1(a1^,isrc) then begin fail(3); exit; end;
  838.     if not process_path(a1,isrc) then exit;
  839.     mode:=sda4_rec(sda^).spop_mode and $7f;
  840.     action:=sda4_rec(sda^).spop_act;
  841. { First, check if file must or must not exist }
  842.     if ((((action and $f)=0) and (isrc<>0)) or
  843.         (((action and $f0)=0) and (isrc=0))) then begin fail(5); exit; end;
  844.  
  845.     if ifile=0 then 
  846.         begin
  847. { Creating new file }
  848.             result:=2;
  849.             ifile:=isrc;
  850.             inc(isrc);
  851.             if isrc=lsrc then begin fail(13); ifile:=0; exit; end;
  852.             fillchar(file_name,13,#0);
  853.             move(a1^[isrc],file_name,lsrc-isrc);
  854.         end
  855.     else
  856.  
  857.     if ifile=isrc then
  858.         begin
  859. { Open/Truncate existing file }
  860.             inc(isrc);
  861.             fillchar(temp_name,13,#0);
  862.             move(a1^[isrc],temp_name,lsrc-isrc);
  863.             if temp_name<>file_name then begin fail(82); exit; end;
  864.             if boolean(action and 2) then
  865.                 result:=3           { File existed, was replaced }
  866.             else
  867.                 result:=1;          { File existed, was opened }
  868.             if boolean(file_attr and $1) and
  869.                 ((result=3) or ((mode and 3)>0)) then
  870.                 begin fail(5); exit; end;   { It's a read only file }
  871.             if (result=3) and (file_opens>0) then
  872.                 begin fail(5); exit; end;   { Truncating an open file }
  873.         end
  874.     else fail(5);
  875.  
  876. { Initialize the supplied SFT entry }
  877.     with sft_rec(ptr(r.es,r.di)^) do
  878.         begin
  879.             if result>1 then
  880.                 begin
  881.                     file_attr:=byte(ptr(r.ss,r.sp)^); { Attr is top of stack }
  882.                     f_size:=0;
  883.                     file_size:=0;
  884.                 end;
  885.             open_mode:=mode;
  886.             cnvt2fcb(file_name,fcb_fn);
  887.             inc(file_opens);
  888.             f_pos:=0;
  889.             f_date:=0;
  890.             f_time:=0;
  891.             dev_info:=$8040 or drive_no; { Network drive, unwritten to }
  892.             dir_sector:=0;
  893.             dir_entryno:=0;
  894.             devdrv_ptr:=nil;
  895.             attr_byte:=file_attr;
  896.         end;
  897. end;
  898.  
  899. { FindFirst - subfunction 1Bh }
  900. procedure ffirst;
  901. var isrc, lsrc : byte;
  902.     sdb : sdb_ptr;
  903.     der : dir_ptr;
  904.     sa, fa : byte;
  905. begin
  906.     lsrc:=asclen(a1^);
  907.     isrc:=lsrc;
  908.     if not back_1(a1^,isrc) then begin fail(3); exit; end;
  909.     if not process_path(a1,isrc) then exit;
  910.     a2:=@max_path;
  911.     if dos_major=3 then
  912.         begin
  913.             a1:=@sda3_rec(sda^).fcb_fn1;
  914.             sdb:=@sda3_rec(sda^).sdb;
  915.             der:=@sda3_rec(sda^).found_file;
  916.             sa:=sda3_rec(sda^).srch_attr;
  917.         end
  918.     else
  919.         begin
  920.             a1:=@sda4_rec(sda^).fcb_fn1;
  921.             sdb:=@sda4_rec(sda^).sdb;
  922.             der:=@sda4_rec(sda^).found_file;
  923.             sa:=sda4_rec(sda^).srch_attr;
  924.         end;
  925.     fa:=file_attr and $1e;
  926.     inc(os(a2).o,succ(isrc));
  927.  
  928. { First try and match volume label, if asked for }
  929.     if ((sa=$08) or (boolean(sa and $08) and (isrc=iroot))) and
  930.        match(a1^,vollab[1],sdb^,der^,0,isrc,sa) then exit;
  931.  
  932. { Then try the one possible subdirectory, if asked for and if it exists }
  933.     if boolean(sa and $10) and
  934.        match(a1^,a2^,sdb^,der^,1,isrc,sa) then exit;
  935.  
  936. { Finally try the one possible file, if asked for, if it exists, and if
  937.   in this subdirectory }
  938.     if (ifile=isrc) and 
  939.        ((fa=0) or boolean(sa and fa)) and
  940.        match(a1^,file_name,sdb^,der^,2,isrc,sa) then exit;
  941.  
  942. { Otherwise report no more files }
  943.     fail(18);
  944. end;
  945.  
  946. { FindFirst - subfunction 1Bh }
  947. procedure fnext;
  948. var fa : byte;
  949.     sdb : sdb_ptr; der : dir_ptr;
  950. begin
  951.     if dos_major=3 then
  952.         begin
  953.             sdb:=@sda3_rec(sda^).sdb;
  954.             der:=@sda3_rec(sda^).found_file;
  955.         end
  956.     else
  957.         begin
  958.             sdb:=@sda4_rec(sda^).sdb;
  959.             der:=@sda4_rec(sda^).found_file;
  960.         end;
  961.     fa:=file_attr and $1e;
  962.     inc(sdb^.dir_entry);
  963.     case sdb^.dir_entry of
  964.         1 : a2:=@max_path[succ(sdb^.par_clstr)];
  965.         2 : a2:=@file_name;
  966.         else begin fail(18); exit; end;
  967.     end;
  968.  
  969. { First try the one possible subdirectory, if it exists. FNext can never
  970.   match a volume label }
  971.     if (sdb^.dir_entry=1) and boolean(sdb^.srch_attr and $10) and
  972.         match(a1^,a2^,sdb^,der^,
  973.             sdb^.dir_entry,sdb^.par_clstr,sdb^.srch_attr) then exit;
  974.  
  975. { Then try the one possible file, if exists, and if in this subdirectory }
  976.     if sdb^.dir_entry=1 then
  977.         begin a2:=@file_name; sdb^.dir_entry:=2; end;
  978.     if (sdb^.dir_entry=2) and (ifile=sdb^.par_clstr) and
  979.         ((fa=0) or boolean(sdb^.srch_attr and fa)) and
  980.         match(a1^,a2^,sdb^,der^,
  981.             sdb^.dir_entry,sdb^.par_clstr,sdb^.srch_attr) then exit;
  982.  
  983. { Otherwise return no more files }
  984.     fail(18);
  985. end;
  986.  
  987. { Seek From End Of File - subfunction 21h }
  988. procedure skfmend;
  989. var skamnt : longint;
  990. begin
  991.     skamnt:=(longint(r.cx)*65536)+r.dx;
  992.     if file_opens=0 then begin fail(5); exit; end;
  993.  
  994. { Update supplied SFT entry for file }
  995.     with sft_rec(ptr(r.es,r.di)^) do
  996.         begin
  997.             f_pos:=f_size-skamnt;
  998.             r.dx:=f_pos shr 16;
  999.             r.ax:=f_pos and $ffff;
  1000.         end;
  1001. end;
  1002.  
  1003. function call_for_us(es,di:word):boolean;
  1004. var p:pointer;
  1005. begin
  1006.     if (fxn in [_close.._unlock,_seek]) then
  1007.         call_for_us:=(sft_rec(ptr(es,di)^).dev_info and $1f)=drive_no
  1008.     else
  1009.     if fxn=_inquiry then call_for_us:=true
  1010.     else
  1011.         begin
  1012.             if dos_major=3 then p:=sda3_rec(sda^).drive_cdsptr
  1013.             else p:=sda4_rec(sda^).drive_cdsptr;
  1014.             call_for_us:=cdsidptr(p)^=cdsidptr(@max_path)^;
  1015.         end;
  1016. end;
  1017.  
  1018. { This is the main entry point for the redirector. The procedure is actually
  1019.   invoked from the Int 2F ISR stub via a PUSHF and a CALL FAR IMMEDIATE
  1020.   instruction to simulate an interrupt.  That way we have many of the
  1021.   registers on the stack and DS set up for us by the TP interrupt keyword.
  1022.   This procedure saves the registers into the regset variable, assesses if
  1023.   the call is for our drive, and if so, calls the appropriate routine. On
  1024.   exit, it restores the (possibly modified) register values. }
  1025. procedure redirector(_flags,_cs,_ip,_ax,_bx,_cx,_dx,_si,_di,_ds,_es,_bp:word);
  1026.     interrupt;
  1027. begin
  1028.     with r do
  1029.         begin
  1030.             isr^.our_drive:=false;
  1031. { If we don't support the call, pretend we didn't see it...! }
  1032.             if lo(_ax)>fxn_map_max then exit
  1033.             else fxn:=fxn_map[lo(_ax)];
  1034.             if fxn=_unsupported then exit;
  1035. { If the call isn't for our drive, jump out here... }
  1036.             if not call_for_us(_es,_di) then exit;
  1037. { Set up our full copy of the registers }
  1038.             isr^.our_drive:=true;
  1039.             move(_bp,bp,18); ss:=isr^.save_ss; sp:=isr^.save_sp;
  1040.             cs:=isr^.save_cs; ip:=isr^.save_ip; flags:=isr^.real_fl;
  1041.             ax:=0; flags:=flags and not fcarry;
  1042.             set_fn1;
  1043.             case fxn of
  1044.                 _inquiry    : r.ax:=$00ff;
  1045.                 _rd         : rd;
  1046.                 _md         : md;
  1047.                 _cd         : cd;
  1048.                 _close      : clsfil;
  1049.                 _commit     : cmmtfil;
  1050.                 _read       : readfil;
  1051.                 _write      : writfil;
  1052.                 _space      : dskspc;
  1053.                 _setattr    : setfatt;
  1054.                 _lock, _unlock : ; 
  1055.                 _getattr    : getfatt;
  1056.                 _rename     : renfil;
  1057.                 _delete     : delfil;
  1058.                 _open       : opnfil;
  1059.                 _create     : creatfil;
  1060.                 _specopen   : spopnfil;
  1061.                 _ffirst     : ffirst;
  1062.                 _fnext      : fnext;
  1063.                 _seek       : skfmend;
  1064.             end;
  1065. { Restore the registers, including any that we have modified.. }
  1066.             move(bp,_bp,18); isr^.save_ss:=ss; isr^.save_sp:=sp;
  1067.             isr^.save_cs:=cs; isr^.save_ip:=ip; isr^.real_fl:=flags;
  1068.         end;
  1069. end;
  1070.  
  1071. { This procedure sets up our ISR stub as a structure on the heap. It
  1072.   also ensures that the structure is addressed from an offset of 0 so
  1073.   that the CS overridden offsets in the ISR code line up. Finally. it
  1074.   fixes in some values which are only available to us at run time,
  1075.   either because they are variable, or because of limitations of the
  1076.   language. }
  1077. procedure init_isr_code;
  1078. var p:pointer;
  1079.     i:pointer absolute isr;
  1080. begin
  1081.     getmem(isr,sizeof(isr_rec)+15);
  1082.     inc(os(isr).s,(os(isr).o+15) shr 4);
  1083.     isr^.ic:=isr_code;
  1084.     getintvec($2f,p);
  1085.     os(isr).o:=redir_entry; pointer(i^):=@redirector;
  1086.     os(isr).o:=our_ss_ofs; word(i^):=sseg;
  1087.     os(isr).o:=our_sp_ofs; word(i^):=our_sp;
  1088.     os(isr).o:=prev_hndlr; pointer(i^):=p;
  1089.     os(isr).o:=0;
  1090. end;
  1091.  
  1092. { Do our initializations }
  1093. procedure init_vars;
  1094.     function installed_2f:byte;
  1095.         { mov ax,1100h   int 2fh }
  1096.         inline($b8/$00/$11/$cd/$2f);
  1097. begin
  1098.     if installed_2f=1 then
  1099.         failprog('Not OK to install a redirector...'); 
  1100.     drive_no:=byte(drive[1])-byte('@');
  1101.     our_sp:=sptr+$100;
  1102.     file_opens:=0;
  1103. { Note that the assumption is that we lost 100h bytes of stack
  1104.   on entry to main }
  1105. { Initialise and fix-up the master copy of the ISR code }
  1106.     init_isr_code;
  1107.     ifile:=0;
  1108. end;
  1109.  
  1110. { This is where we do the initializations of the DOS structures
  1111.   that we need in order to fit the mould }
  1112. procedure set_path_entry;
  1113. var our_cds:pointer;
  1114. begin
  1115.     our_cds:=lol_rec(lol^).cds;
  1116.     if dos_major=3 then
  1117.         inc(os(our_cds).o,sizeof(cds3_rec)*pred(drive_no))
  1118.     else
  1119.         inc(os(our_cds).o,sizeof(cds4_rec)*pred(drive_no));
  1120.     if drive_no>lol_rec(lol^).last_drive then
  1121.         failprog('Drive letter higher than last drive...'); 
  1122.  
  1123. { Edit the Current Directory Structure for our drive }
  1124.     with cds3_rec(our_cds^) do
  1125.         begin
  1126.             ascii2string(@curr_path,@strbuf,255);
  1127.             writeln('Curr path is ',strbuf);
  1128.             if (flags and $c000)<>0 then
  1129.                 failprog('Drive already assigned.');
  1130.             flags:=flags or $c000;  { Network+Physical bits on ... }
  1131.             strbuf:=cds_id;
  1132.             strbuf[length(strbuf)-2]:=char(byte('@')+drive_no);
  1133.             move(strbuf[1],curr_path,byte(strbuf[0]));
  1134.             move(curr_path,max_path,byte(strbuf[0]));
  1135.             curr_path[byte(strbuf[0])]:=#0;
  1136.             max_path[byte(strbuf[0])]:=#0;
  1137.             root_ofs:=pred(length(strbuf));
  1138.             iroot:=root_ofs;
  1139.             lmax:=iroot;
  1140.         end;
  1141. end;
  1142.  
  1143. { Use in place of Turbo's 'keep' procedure. It frees the environment
  1144.   and keeps the size of the TSR in memory smaller than 'keep' does }
  1145. procedure tsr;
  1146. var r:registers;
  1147. begin
  1148.     swapvectors;
  1149.     r.ax:=$4900;
  1150.     r.es:=memw[prefixseg:$2c];
  1151.     msdos(r);
  1152.     r.ax:=$3100;
  1153.     r.dx:=os(heapptr).s-prefixseg+1;
  1154.     msdos(r);
  1155. end;
  1156.  
  1157. procedure settle_down;
  1158. var p:pointer;
  1159.     i:integer;
  1160.     w:word;
  1161. begin
  1162. { Plug ourselves into Int 2F }
  1163.     setintvec($2f,isr);
  1164.     writeln('Phantom drive installed as ',drive[1],':');
  1165. { Find ourselves a free interrupt to call our own. Without it, future
  1166.   invocations of Phantom will not be able to unload us. }
  1167.     i:=$60;
  1168.     while (i<=$67) and (pointer(ptr(0,i shl 2)^)<>nil) do inc(i);
  1169.     if i=$68 then 
  1170.         begin
  1171.             writeln('No user intrs available. PHANTOM not unloadable..');
  1172.             tsr;
  1173.         end;
  1174. { Have our new found interrupt point at the command line area of 
  1175.   our PSP. Complete our signature record, put it into the command line, 
  1176.   and go to sleep. }
  1177.     w:=$80;
  1178.     setintvec(i,ptr(prefixseg,w));
  1179.     our.psp:=prefixseg;
  1180.     our.drive_no:=drive_no;
  1181.     sig_rec(ptr(prefixseg,w)^):=our;
  1182.     tsr;
  1183. end;
  1184.  
  1185. { Find the latest Phantom installed, unplug it from the Int 2F chain if
  1186.   possible, undo the dpb chain, make the CDS reflect an invalid drive,
  1187.   and free its memory.. }
  1188. procedure do_unload;
  1189. var i:integer; p, cds:pointer; w:word; r:registers;
  1190. begin
  1191.     i:=$67;
  1192.     while (i>=$60) and
  1193.       (sig_rec(pointer(ptr(0,i shl 2)^)^).signature<>our.signature) do
  1194.         dec(i);
  1195.     if i=$5f then 
  1196.         begin writeln(our.signature,' not found...'); halt; end;
  1197.     getintvec($2f,p);
  1198.     if os(p).o<>0 then 
  1199.         failprog('2F superceded...'); 
  1200.     os(p).o:=prev_hndlr;
  1201.     setintvec($2f,pointer(p^));
  1202.     getintvec(i,p);
  1203.     drive_no:=sig_rec(p^).drive_no;
  1204.     with r do
  1205.         begin
  1206.             ax:=$4900; es:=sig_rec(p^).psp;
  1207.             msdos(r);
  1208.             if boolean(flags and fcarry) then
  1209.                 writeln('Could not free main memory...');
  1210.         end;
  1211.     setintvec(i,nil);
  1212.     cds:=lol_rec(lol^).cds;
  1213.     if dos_major=3 then
  1214.         inc(os(cds).o,sizeof(cds3_rec)*pred(drive_no))
  1215.     else
  1216.         inc(os(cds).o,sizeof(cds4_rec)*pred(drive_no));
  1217.     with cds3_rec(cds^) do flags:=flags and $3fff;
  1218.     writeln('Drive ',char(byte('@')+drive_no),': is now invalid.');
  1219. end;
  1220.  
  1221. begin { MAIN }
  1222. { Check parameter count }
  1223.     if (paramcount<>1) then
  1224.         failprog('Usage is: PHANTOM drive-letter:'); 
  1225.     drive:=paramstr(1);
  1226.     drive[1]:=upcase(drive[1]);
  1227. { If this is an unload request, go to it }
  1228.     if (drive='-u') or (drive='-U') then
  1229.         begin
  1230.             get_dos_vars;
  1231.             do_unload;
  1232.             halt;
  1233.         end;
  1234. { Otherwise, check that it's a valid drive letter }
  1235.     if  (length(drive)>2) or
  1236.         not (drive[1] in ['A'..'Z']) or
  1237.         ((length(drive)=2) and (drive[2]<>':'))
  1238.             then failprog('Usage is: PHANTOM drive-letter:'); 
  1239. { ... and set up shop }
  1240.     init_vars;
  1241.     get_dos_vars;
  1242.     set_path_entry;
  1243.     settle_down;
  1244. end.
  1245.