home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 10 / 10.iso / l / l440 / 2.ddi / CHAP4 / PHANTOM.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1991-05-14  |  40.9 KB  |  1,232 lines

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