[comp.lang.pascal] How to do file sharing under TP.

mke@redsun.uucp (Mike Miller) (08/30/90)

I downloaded the following off a local BBS.  There is no copyright on it,
and the author places no restrictions on its distribution, so I thought
I'd post it here.  We've been using Turbo Pascal for 3 years, and didn't
know about this stuff, so I assume its not all that well known.  It's 
around 23K long.  It covers Turbo 5.0 and 3.0.

_________________________________________________________________________




                        Methods for Dealing with Shared Files
                             in TURBO Pascal Version 5.0
                                          by
                                    John W. Wulff
                               Wulff Enterprises, Inc.
                                 260 Terranova Drive
                              Warrenton, VA  22186-9227
                                    (703) 349-8805

                                 Revised May 15, 1989
           
          This paper was first written January 19, 1987 as a means of
          demonstrating how to modify Turbo Pascal 3.x to handle the
          opening of READ-ONLY files and to make it able to deal with files
          in a Shared, network environment.  It provided a means of finding
          an undocumented byte known as the "Open Mode Byte".  Since then,
          Turbo Pascal 5.0 has been released and this byte has been
          globally declared as the FILEMODE variable.  Even so, Turbo
          Pascal 5.0 ignores the variable when opening TEXT files.  While
          there is a small explanation in the Turbo Pascal Reference Guide
          as to its use, the information in this paper is still valid today
          and has been updated to reflect its relevance to Turbo Pascal
          5.0.  For those wishing to continue to work in Turbo Pascal 3.0,
          I have included the locations for this byte in appendix A.

          There have been many innovations in the development of PCBoard
          software for BBS's.  One of which is the network environment that
          the authors have included in the code.  While this is wonderful
          for the multi-node system, it does pose certain problems and
          restrictions on application programs and utilities that are being
          written for it, especially those written in Borland's Turbo
          Pascal.

          One problem that plagues most authors writing application
          programs that eventually become used in network environments is
          coping with the Share utility that DOS uses to protect files. 
          Any file opened for reading under Turbo Pascal will cause a
          Sharing violation if running under a DOS 3.x and 4.x networking
          mode.  This is because Turbo Pascal opens all files in what is
          known as "Inherited by Child Processes, Compatibility Mode,
          Read/Write access". 

          I have written a utility for PCBoard systems, PCBFile, and since
          it is extremely file intensive, I've had to do some research on
          the technical aspects of DOS in the network mode.  Because of
          this research, I've been able to get Turbo Pascal to cooperate
          and have written this paper to help other authors who are
          struggling with the same problems.

          As documented in the Turbo Pascal instructions, the FILEMODE
          variable has a value of 2 when opening files for RESET or REWRITE
          which allows both reading and writing.  The instructions suggest
          that you should assign a value of 0 to the variable to RESET or
          REWRITE read-only files.  While this works for READ-ONLY files,












          it is not the only value to consider when running an application
          in a network environment, especially one with PCBoard software
          upon which I will focus my attention for the remainder of the
          document.
           
          One thing to consider, if using other languages, especially
          assembly language, is that this FILEMODE variable, corresponds to
          the AL register.  All references to that byte and its decimal
          number can be applied to assembly in this way: 

                 AH contains 3DH - the function call 
                 DS:DX points to an ASCIIZ path name 
                 AL will be loaded with the 8 bit number that the          
                 FILEMODE variable contains. 

          When the function returns, AX will contain error codes or a 16
          bit file handle if successful.  (See DOS manual for details.)  I
          don't profess to be anywhere close to fluent in assembly so I
          will leave this information with those who are best suited to
          take advantage of it. 

          This variable is GLOBALLY pre-declared in Turbo Pascal, so all
          you have to do is refer to it as FILEMODE.  Then a simple check
          is necessary to see if the variable contains a 2 and if so, load
          it with a 0: 
           
                         begin 
                           if FILEMODE = 2 then 
                             FILEMODE := 0; 
                         end;

          The completed routine would look like this:  

          Procedure OpenMode(Var OpenFile : text; 
                               InFileName : string
                             Var GoAhead  : boolean); 

          {Remember, FILEMODE is a GLOBALLY declared variable of type
           byte.  No other declaration is necessary on your part}

          begin 
            if FILEMODE = 2 then 
              FILEMODE := 0; (* allows reading of READ-ONLY files *) 
            assign(OpenFile,InFileName); 
            {$I-} reset(Openfile) {$I+}; 
            GoAhead := (ioresult = 0);   
            if GoAhead then 
              writeln(InFileName,' opened!') 
            else 
              writeln(InFileName,' failed to open!'); 
          end; { of Procedure OpenMode } 


          Shared Files in Turbo Pascal 5.0                                2












          Now we need to determine just what is going on with PCBoard and
          how it opens files using DOS's SHARE.   The DIR files, or the
          files that contain the filenames of the available files for the
          user to download, are opened in READ SHARED mode which in
          QuickBASIC would be: 

              OPEN "<filename>" FOR INPUT ACCESS READ SHARED AS #1.   

          Upload DIR files are opened for APPEND in a LOCKED WRITE mode.
          This keeps other nodes from writing at that particular moment but
          allows other nodes to read this file.

          In order to accomplish these same modes in Turbo Pascal we must
          look into the DOS Technical Reference Manual (groan!).  The
          following is reprinted from that manual with some additions by
          me.

          The OPEN mode consists of 4 bit-oriented fields:     
               * INHERITANCE FLAG
               * SHARING MODE FIELD 
               * RESERVED FIELD 
               * ACCESS FIELD   
          The INHERITANCE FLAG determines if the file will ever be
          inherited by another process, which in the case of a network is
          usually the desired effect.  The SHARING FIELD defines what
          operations may be performed on the file by other nodes.  The
          ACCESS FIELD defines what operations THIS node may perform on the
          file.  

          The bit fields are mapped as follows: 
                              <I> < S > <R> < A > 
             Open Mode bits    7  6 5 4  3  2 1 0 
          I  INHERITANCE FLAG 
             If I = 0; File is inherited by child processes 
             If I = 1; File is private to the current process 

          S  SHARING MODE 
             The file is opened like this:
             if S = 000;  Compatibility mode - The default open mode - it
                          denies ALL OTHER processes access to the file. 
                          Since this is the mode that Turbo Pascal uses to
                          open a file, what do you think will happen on the
                          BBS side if you have a file open on your end and
                          the BBS tries to open it? 
             if S = 001;  Deny Read/Write mode (Exclusive).  This would
                          actually be the same as setting the I flag to 1. 
             if S = 010;  Deny Write mode - you should open a file in this
                          mode if you wish to protect it.  It will allow
                          other processes to read it but not write.
             if S = 011;  Deny Read mode 
             if S = 100;  Deny None mode - Who cares what happens!


          Shared Files in Turbo Pascal 5.0                                3












          It is important to specify what operations you want to perform
          (access mode).  The default access mode is Read/Write and causes
          the open request to fail if another process has the file opened
          with any sharing mode other than Deny None.  File sharing
          requires cooperation of both sharing processes.  This is
          communicated through the sharing and access modes.

          R  RESERVED (set third bit field to 0)

          A  ACCESS - The file access is assigned as follows:

             If A = 000; Read Access 
             if A = 001; Write Access 
             if A = 010; Read/Write access

          If all this seems a bit involved, I'm sorry.  I don't know of any
          way to give you the background for all this hocus-pocus except
          with the above info.  I also recommend picking up a Tech Ref
          manual for more detailed study of the 3DH function call.

          OK!  With all these numbers in hand, let's see how to get Turbo
          Pascal to duplicate these modes.  Earlier I said that other gurus
          had stated that Turbo Pascal opens files in COMPATIBILITY MODE
          with READ/WRITE ACCESS and INHERITANCE BY CHILD PROCESSES and the
          magic value for the FILEMODE variable is 2.  Let's look at how
          that was done:

               Compatibility mode:   000 {S} 
               Read/Write ACCESS :   010 {A} 
               Inherited by child:     0 {I} 
               Reserved as ALWAYS:     0 {R} 
           
          Remember the bit fields are: 
                              <I> < S > <R> < A > 
             Open Mode bits    7  6 5 4  3  2 1 0  Let's plug in 
                               0  0 0 0  0  0 1 0  the numbers. 
          Using binary arithmetic: 
                              <I> <  S   > <R> < A >
                              128 64 32 16  8  4 2 1
                               0   0  0  0  0  0 1 0 = 00000010 = 2 
            
          By using a FILEMODE value of 0 we change the ACCESS field to 000,
          READ ACCESS, which allows us to read a READ-ONLY file.
           
          PCBoard is opening its DIR files as READ ACCESS SHARED and
          actually opening the file with a SHARING MODE of Deny/Write which
          would be a SHARE <S> field of 010.  The value for the FILEMODE
          variable then becomes: 
                              <I> <  S   > <R> < A > 
                              128 64 32 16  8  4 2 1 
                               0   0  1  0  0  0 0 0 = 00100000 = 32


          Shared Files in Turbo Pascal 5.0                                4












          This is how I open ALL my files for reading or for general
          ASSIGNING for RESET in Turbo Pascal for my program PCBFile.  I
          have some procedures written for TEXT files, and files of type
          BYTE.  I have reproduced the code below:  
          (* This procedure, KeepOn, is used to determine if the file has 
          been locked out.  I try a file for 10 times as determined by 
          OpenAtt variable before I give up  *) 
           
          Procedure KeepOn(OpenAtt : byte; var GA : boolean); 
          begin 
            if OpenAtt <= 10 then 
              GA := TRUE      (* GoAhead Flag - if within 10 go for it *) 
            else GA := FALSE;    (* forget it! *) 
          end; {of Procedure KeepOn}
           
          Procedure SetResetMode(Var OpenFile : text;  (* OPEN MODE FOR
                                 InFileName   : string;    TEXT FILES *) 
                                  Var GoAhead : boolean); 
          var
            OpenAttempts  : byte; 

          begin 
            OpenAttempts := 1; 
            FILEMODE := 32;         (* this is Deny Write                  
                                       Mode/Read Access *) 
            assign(OpenFile,InFileName); 
            repeat 
              {$I-} reset(Openfile) {$I+}; 
              GoAhead := (ioresult = 0); 
              if not GoAhead then 
                OpenAttempts := OpenAttempts + 1; 
            until (GoAhead) OR (OpenAttempts > 10); (* keep trying *) 
            KeepOn(OpenAttempts,GoAhead); 
          end; {of Procedure SetResetMode} 
           
          Procedure SetResetModeFile(Var OpenFile : file of byte 
                                     InFileName   : string;                
                                      var GoAhead : boolean);             
          var 
            OpenAttempts  : byte; 
          begin 
            OpenAttempts := 1; 
            if FILEMODE := 32;   (* this is Deny Write                     
                                  Mode/Read Access *)    
          assign(OpenFile,InFileName); 
            repeat 
              {$I-} reset(Openfile) {$I+}; 
              GoAhead := (ioresult = 0); 
              if not GoAhead then OpenAttempts := OpenAttempts + 1; 
            until GoAhead OR (OpenAttempts > 10); 
            KeepOn(OpenAttempts,GoAhead); 
          end; {of Procedure SetResetModeFile}

          Shared Files in Turbo Pascal 5.0                                5













          Now here comes a little zinger to change things up.  I want to
          create a file that I don't want the other nodes to damage.  I
          elect to open the file for READ/WRITE ACCESS for myself and give
          the other nodes READ capability and deny them the ability to
          write to my file.  This would be Deny/Write Mode under the <S> or
          SHARING FIELD and would be coded 010.  For READ/WRITE ACCESS the
          <A> or ACCESS FIELD is coded 010 also.  This is the same mode
          that PCBoard uses for writing to the Upload directory.  Using our
          binary formulae, the FILEMODE value then becomes: 
                              <I> <  S   > <R> < A > 
                              128 64 32 16  8  4 2 1 
                               0   0  1  0  0  0 1 0 = 00100010 = 34 

          With the magic number of 34 the SetFileLock procedure was born.
           
          Procedure SetFileLock(Var   OpenFile : text;
                                    InFileName : string; 
                                   var GoAhead : boolean); 
          var 
            OpenAttempts  : byte;  

          begin 
            OpenAttempts := 1; 
            FILEMODE := 34;      (* Deny Write Mode/Read-Write Access *) 
            assign(OpenFile,InFileName); 
            repeat 
              {$I-} rewrite(Openfile) {$I+}; 
              GoAhead := (ioresult = 0); 
              if not GoAhead then 
                OpenAttempts := OpenAttempts + 1; 
            until GoAhead or (OpenAttempts > 10); 
            KeepOn(OpenAttempts,GoAhead); 
          end; {of Procedure SetFileLock}
           
          Finally, a little walk around the park to insure that the
          FILEMODE variable is returned to Borland's normal mode.

          Procedure ReleaseOpenMode; 

          begin 
            FILEMODE := 2; 
          end; 

          So it's really simple to change the Turbo Pascal Open mode to
          exactly what you want, you just have to know what results you
          desire from the program.  Just remember these definitions of the
          fields that make up the magic number for DOS Function call 3DH. 
           
          * INHERITANCE FLAG       I = 0;   Inherited (usually the case) 
                                   I = 1;   Private


          Shared Files in Turbo Pascal 5.0                                6












          * SHARING MODE FIELD     (Other node or process)
                                   S = 000; Compatibility mode 
                                   S = 001; Deny Read/Write mode           
                                   (Exclusive) 
                                   S = 010; Deny Write mode 
                                   S = 011; Deny Read mode 
                                   S = 100; Deny None mode 

          * RESERVED FIELD         R = 0; Always 

          * ACCESS FIELD           (Your node or process) 
                                   A = 000; Read Access 
                                   A = 001; Write Access 
                                   A = 010; Read/Write Access
          The bit fields: 
                                   <I> < S > <R> < A >
                  Open Mode bits    7  6 5 4  3  2 1 0 
           
          Even though a DOS Technical Reference Manual gives a more
          thorough discussion of Function Call 3DH, I will attempt to
          create a matrix with the number for the FILEMODE variable based
          on the SHARE and ACCESS fields. 
           
            ACCESS   |SHARE-> Compat  Deny/RW  Deny/W  Deny/R  Deny/N 
             |                 000      001     010     011     100 
             v              ------------------------------------------- 
            Read  000       |   0    |   16  |   32  |   48  |   64   | 
                            |--------|-------|-------|-------|--------| 
            Write 001       |   1    |   17  |   33  |   49  |   65   | 
                            |--------|-------|-------|-------|--------| 
            Read/ 010       |   2    |   18  |   34  |   50  |   66   | 
            Write           ------------------------------------------- 
           
          I know that this is probably more than a human can bear to
          assimilate at any one time but I hope that you will be able to
          see the logic behind my system and be able to use Turbo Pascal to
          its full potential.  
           
          PCBoard (c) Clark Development Company, Inc., Murray, UT 
          Turbo Pascal (c) Borland International, Scotts Valley, CA 
          PCBFile (c) John W. Wulff 
          Compuserve (c) Compuserve, Inc., Columbus, OH 











          Shared Files in Turbo Pascal 5.0                                7













                                       Appendix

          For Turbo Pascal 3.x Die-Hards:

          Bela Lubkin published a text article, ACMODE.DOC, which is on
          Compuserve in the Borland Sig that gives the locations of a
          little gem known as the "Open Mode Byte".  This byte is at an
          absolute address for the various editions of Turbo Pascal and
          communicates to DOS, via Function call 3DH, how the file is to be
          accessed and what access to give other processes.  It also
          becomes very handy for us in trying to use Turbo Pascal in a
          network environment. These locations are:
               Open mode byte for Reset & Rewrite for Turbo 3.00x (PC-DOS) 
                    TURBO.COM      CSEG:$248D 
                    TURBO-87.COM   CSEG:$1F3C 
                    TURBOBCD.COM   CSEG:$2393 
              Open mode byte for Reset & Rewrite for Turbo 3.00x (MS-DOS) 
                    TURBO.COM      CSEG:$2182 
                    TURBO-87.COM   CSEG:$1C31 
                    TURBOBCD.COM   CSEG:$2088 
              Open mode byte for Reset & Rewrite for Turbo 3.01x (PC-DOS) 
                    TURBO.COM      CSEG:$24FC 
                    TURBO-87.COM   CSEG:$1FAB 
                    TURBOBCD.COM   CSEG:$2402 
              Open mode byte for Reset & Rewrite for Turbo 3.01x (MS-DOS) 
                    TURBO.COM      CSEG:$21D4 
                    TURBO-87.COM   CSEG:$1C83 
                    TURBOBCD.COM   CSEG:$20DA 
              Open mode byte for Reset & Rewrite for Turbo 3.02x (PC-DOS) 
                    TURBO.COM      CSEG:$24C6 
                    TURBO-87.COM   CSEG:$1F75 
                    TURBOBCD.COM   CSEG:$23CE 
           
          Another valuable document is Robert K. Blaine's RES_MODE.INC,
          also available on CSERV.  It details the procedure for finding
          out the location of this byte, using DEBUG.
















          Shared Files in Turbo Pascal 5.0                                8





-- 
*	Insert your favorite funny/witty saying here, but make it small.       *
* mke@cs.pdx.edu or psueea.ee.pdx.edu!qiclab!redsun!mke  Stand. disc. applies. *
*            (REM,BUGGLES,ASIA,YES,PYLON,THE SUNDAYS,AMIGA3000) 	       *
*   Please Reply if the content of this message implies that one is expected.  *