MMCC MASTHEAD
Mid-Michigan Computer Consultants - Bay City, Michigan
 


CONTENTS       (old style)
Mid-Michigan Computer Consultants
509 Center
Bay City, Michigan

Sales (989) 892-9242
Support (989) 686-8860

Plb-0310.cfm v1.0


plb-t010.cfm
 

ANSI Standard PL/B Language and Visual PL/B

File Handling Notes
PLB-0310.cfm Rev 07/29/2020 18:00


This section discusses various file handling techniques and tools. Topics on this page include:


DEFINING FILES:

Use the FILE, IFILE, or AFILE to define a file. These are well defined in the Sunbelt manuals.

MMCC Typically uses IFILE (indexed) and FILE (sequential).

Sequential FILE are typically defined as {name} FILE BUFFER=nnnn. The understanding is that larger buffer sizes help performance. With modern Windows systems having huge amounts of RAM, we'll frequently just make this size 8000. It's hard to prove if it really helps.

IFILE index files are typically defined as {name} IFILE BUFFER=nnn,NODUP. In this case the BUFFER size is the defined record size desired for the file. It is not clear that the buffer size is still needed, but MMCC does it as a rule.

OPENING FILES:

Files may be OPENed or PREPed.
    OPEN is for a pre-existing file,
    PREP is used to create a new file.

There are quite a few variations on OPEN. But OPEN us usually as simple as:
    OPEN {filename}
        or
    PREP {filename}.

There are quite a few variations on OPEN. But OPEN us usually as simple as:
    OPEN {filename}
        or
    PREP {filename}.

OPEN MODE:
  • The most common variation to the OPEN is to add a sharing mode qualifier such as:
        OPEN {filename},READ

  • The most common sharing mode choices are READ, SHARE, and EXCLUSIVE,

  • The default mode is SHARE.

  • There are other opening modes documented in the SUNBELT manuals.

  • NOTE: Indexed files can have multiple indexes. If you're doing that then you must open all of the related files in SHARE mode if you plan to update any of the indexes.


There are quite a few variations on OPEN. But OPEN us usually as simple as:
    OPEN {filename}
        or
    PREP {filename}.

OPENING BINARY FILES:

As a general rule PL/B makes no distinction concerning binary files. Declare the file using the standard sequential FILE statement. The trick comes when reading the file since it may contain standard end of record characters. Most people would read it using the "absolute" (*ABSON / *ABSOFF) list controls. This allows you to read a block or a byte without regard to it being a control character.

The following note was from Sunbelt's Steve White in March 2009 in response to a question about why a very large binary file took a long time to open.
You are correct in your suspicion that the binary file without record delimiters is causing the problem. Since Sunbelt supports multiple data formats transparently we must know what format the file is in. So we start at the end of file and work backwards looking for CR/LF or LF sequences. If the file does not have either anywhere in it, then we will eventually get to the beginning of file and give up. So a gigabyte size file will take a while before getting to the beginning.

We have had a method for the programmer telling the compiler what type the file is and avoiding this searching. It is the FORMAT option on the FILE statement. If you set FORMAT=BINARY, then NO searching is done. This has been a documented option, but unfortunately in the wrong place. The current documentation says it is for RMSRAS from Datapoint only. This is not correct and the documentation will be corrected for the next release.

USEFUL TOOLS and TECHNIQUES


OPEN/PREP {nul file name}
When you use a file name that's
NUL (empty), the open instruction will present the standard Windows open dialog window. The user can browse to the file of their choice.

To do this, the file name string must be completely nul. You can NOT include a partial file name like "*.JPG". If that's what you want to do, look at the next item: OPENDEFAULT.

OPENDEFAULT and PREPDEFAULT
These two don't actually perform any opening actions. Instead they set the default PROMPT and FILE MASK that will be used in other open methods. For example you could say
     OPENDEFAULT "Select a picture file","*.jpg"
The next OPEN which gets Windows' open dialog will have that prompt and will only allow files with extension ".jpg".

You can also include a PATH in this opening method:
     OPENDEFAULT "Select a picture file","\pictures\condos\*.jpg"

GETFILE
This will just retreive information about an open file. If you open a file using a NUL name, which means that the user browses to the file of choice, you can use the GETFILE to find the name of the file.

This is a simple instruction and it's well documented in the Sunbelt manuals. We've added more information under: GETFILE GETFILE     Get information about a file after it's open.

This is really handy when you let the user enter the file name and they enter a blank which causes Windows to let them broswe for the file. You need some way to know which file they chose. Use GETFILE after the file is open, then you can use
FINDFILE to get even more information about the file.

[label] GETFILE {file},{keyword}={value}[,{keyword=value}...]
    If the file is NOT OPEN, the ZERO flag is cleared and the variables remain unchanged.
    If the file is OPEN, the ZERO flag is set.
    The EOS flag is set if any variable overflows.
   ...........................
   .   Example
   .
   MYFILE     FILE
   FILENAME   DIM   250
       OPEN        MYFILE, "ZZDATA"
       GETFILE     MYFILE, TXTNAME=FILENAME
       IF ZERO
           DISPLAY "File name is ",*HON,*LL,FILENAME,*PL,*HOFF
         ELSE
           DISPLAY "File is NOT OPEN"
       ENDIF
Selected Keywords from the Sunbelt Visual PL/B manual. (There are lots more)
   ISINAME={svar} qualified (with path) ISI file name.
   MODE={nva} opened: 0=Not Defined, 1=EXCLUSIVE, 2=SHARE, 3=READ, 4=SHARENF.
   PRTNAME={svar} name stored for a PFILE.
   TXTNAME={svar} qualified (with path) text file name.
Check the manual for information relating to COMFILEs, PFILEs, etc.

Fillow this with a FINDFILE to get even more information about the file


VERSION 9.x:     GETFILE FILEFORMAT={nvar}

Version 9 added a FILEFORMAT flag to the GETFILE. This gives some useful information about an ISI file. The basic usage would be something like:
    OPEN    ISIFILE,"NAME"
    GETFILE FILEFORMAT=NWK03
The language ref says the FILEFORMAT value contains bits with the following meaning
0x1  0000 0001  IFILE
0x2  0000 0010  AFILE
0x10 0001 0000  version 9.0x format
0x20 0010 0000  version 8.7x format
0x40 0100 0000  the ISI file supports file larger than 4BG.
The GETFILE FILEFORMAT should be given a 3 digit FORM field for the return value. That's because you're going to get a decimal number which could range from 0 to 255 (8 bit binary value).

Move that number to an INTEGER 1 to get the binary value in a single byte.

Move the INTEGER 1 to a DIM 1 so that you can do a logical AND against it.

Now test with the AND. If the ZERO flag is not set then the bit position you're testing has a 1. (1 AND 1 = 1 . . . 1 AND 0 = 0)

If you test multiple flags, be sure to move the integer to the DIM 1 again because the AND is destructive.

 
TEST_ISI_FILE  IFILE
NWK03          FORM     3
INT1           INTEGER 1
WORK01         DIM        1
.
    OPEN      TEST_ISI_FILE,"TESTFILE"
    GETFILE   TEST_ISI_FILE, FILEFORMAT=NWK03
    MOVE      NWK03, INT1
.
    MOVE      INT1,    WORK01
    AND       0x1,      WORK01
    IF NOT ZERO
        ..... I file
    ENDIF
.
    MOVE      INT1,    WORK01
    AND       0x2,      WORK01
    IF NOT ZERO
        ..... A file
    ENDIF
.
    MOVE      INT1,     WORK01
    AND       0x10,      WORK01
    IF NOT ZERO
        ..... 9.x ISI
    ENDIF
.
    MOVE      INT1,     WORK01
    AND       0x20,      WORK01
    IF NOT ZERO
        ..... 8.7 ISI
    ENDIF
.
    MOVE      INT1,     WORK01
    AND       0x40,      WORK01
    IF NOT ZERO
        ..... allows 4GB files
    ENDIF

  • GETFNAME      THIS ONE IS POWERFUL

    This instruction does everything that an OPEN or a PREP would do except to actually open or prep the files. Basically it will present the Windows OPEN dialog to let the user select the file they want. On exit it returns the selected filename and path. You can then use that name in a subsequent OPEN or PREP.

    This is very powerful, but the syntax in the manual can be a bit difficult to follow.
    Here's what we've learned:

    The basic format for the instruction is:
           GETFNAME    TYPE=OPEN_MODE:
                       PROMPT_STRING:
                       FILE_NAME:
                       FILE_PATH:
                       FILTER_STRING
    
    • The TYPE parameter defines the "mode" and the "dialog type". That is, it tells Windows how to handle the interaction with the user. We'll defened that below.

    • The PROMPT_STRING will appear in the title of the Windows open dialog box. You can use a literal or a string variable for this one.

    • The FILE_NAME is just that. If you pre-load the name, even with a mask like *.TXT, then only files matching that name or mask will be shown. On return you get the file name that the user clicked.

    • The FILE_PATH is just that. You can enter any path that you want and that's where the open dialog starts. The user can still navigate to other paths as you'd expect. On return you get the final path to the file that the user selected.

    • FILTER_STRING is a special case and only goes with certain open dialog types. We'll talk about those next.

    RETURNS
    • If user CANCELs, OVER is set.

    • If NOT OVER:
      • File name is stored in the name variable
      • The path is stored in the path variable.
        (Path ends with a backslash).

    GETFNAME: The TYPE parameter:

    The TYPE parameter defines the "mode" and the "dialog type". That is, it tells Windows how to handle the interaction with the user.

    TYPE allows two basic MODES: Open and Prep. There are several variations of each.

    All that this particular MODE does is to tell windows it it should ask "do you want to overwrite" if the file already exits. That is:
    • In OPEN mode the user selects a file and control immediately returns to your program.
    • In PREP mode Windows will see if the selected filename already exists and, if so, it will ask the "overwrite" question. If the user answers NO to "overwrite" then control stays in the open dialog so the user can try again.

    You can actually code the GETFNAME using the word OPEN or PREP in that first parameter. But there are other, more useful modes which are identified numerically using by saying TYPE=nn. The nn is a number representing the mode.

    Both the plain OPEN and PREP are included in the numbered type list so you can really just always use the TYPE=nn. For example the following two opens are equivalent to each other:
         GETFNAME TYPE=OPEN
         GETFNAME TYPE=1


    That numeric TYPE can be a variable as well. So you can easily set it from your program and use a single GETFNAME to do several different things.
        OPEN_TYPE  FORM        2
                   MOVE        "1" to OPEN_TYPE
                   GETFNAME    TYPE=OPEN_TYPE
    
    The GETFNAME open TYPE table is as follows:
    MODE DIALOG TYPE
    OPEN Standard open dialog
    PREP Standard prep dialog
    TYPE=1 Standard open dialog
    TYPE=2 Standard prep dialog
    TYPE=17 Open dialog with user filter
    TYPE=18 Prep dialog with user filter.
    TYPE=33 Open dialog with multi-select feature.
    TYPE=34 Prep dialog with multi-select feature.
    TYPE=49 Open dialog with filter and multi-select.
    TYPE=50 Prep dialog with filter and multi-select.

    Here's what the Sunbelt manual says about TYPE and FILTERS:
    3. When {mode} identifies employment of a user filter, the string format requires {info} and {filter} pairs to follow the extension which are then passed to the Open or Prep dialogs. The {type} string format is as follows:
          {ext}[,{info1},{filter1}[,{info2},{filter2}]...]
    Where:
    • {ext} Identifies the default extension used when no name filtering is provided.
    • {info1} First information/comment string presented and associated with the file filter which follows it.
    • {filter1} First file filter which can be one or more files separated by the semicolon character. The file names can be specific or partial names and include wildcard characters. When a user selects the {info1} identifier, only those files which conform to the filter appear in the dialog presentation window.
    Here's what that translates to in real-world usage:

    TYPE:

    • You can use a variable or a literlal numeric parameter for the GETFNAME. For type 1 you can use the literal OPEN and for type 2 you can use PREP. Example code might include:
      OPEN_TYPE  FORM   2
                 MOVE    "1",OPEN_TYPE
                 GETFNAME  TYPE=OPEN_TYPE ....
                    or
                 GETFNAME  TYPE=1 ....
                    or
                 GETFNAME  TYPE=OPEN ....
      

    • The numeric types are grouped in pairs for OPEN and PREP dialogs:
           1 = open      2 = prep
          17 = open     18 = prep
          33 = open     34 = prep
          49 = open     50 = prep
      
    • The numeric types are grouped broadly by MULTI-SELECT capability.

      What multi-select does is to allow the user to click more than one file, drag a box around a group of files, use CTRL-click and SHIFT-click. When the GETFNAME operation returns, multi selected files are returned as a comma-delimited list of files (without path) in FILE_NAME.
           1,  2, 17 and 18   do NOT allow multi-select
          33, 34, 49 and 50   DO allow multi-select
      
    • Two groups of the numeric types allow FILTERS to be used. (Filters are described below)
          17  Open with filter
          18  Prep with filter
          49  Open with filter and multi-select
          50  Prep with filter and multi-select
      
    FILTERS:

    Here's how I describe the "filter" string.
    First, the general form is:
          {ext} [,{info1},{filter1}] [,{info2},{filter2}] ...
    I call this entire thing a FILTER STRING although the official definition refers to just those elements within the string as "filters".

    Note:
    • This entire filter string can be a literal or can be a string variable.
    • The values are comma delimited and don't need any quotes within the string.
    • There can be multiple "filters" elements in the string but they're delimited by semi-colons.
    The breakdown of the filter string is this:

    • {ext} Identifies the default extension used when no name filtering is provided. That means that if you don't give any other information in the filter string, this is the file extent ion that will be displayed. If you are going to use filter pairs, you can just ignore this parameter and use a "," as a place holder. For example:
            ,pictures,*.jpg;*.tif

    • {info1},{filter} work in pairs. The info string is just text describing what the filter represents. For example the pair could be
            picture types allowed,*.jpg;*.tif;*.bmp
      In that example, the description will be the entire string picture types allowed. Note that there are no quotes.

      The actual filter is *.jpg;*.tif;*.bmp. Again, these are not in quotes... they're just a string. Note that the three types are separated by semi-colons.

      The actual filters may be wild card masks such as CWV*.TXT.

    • SPECIAL NOTE:
      To make the filter work, you MUST provide a default value in the FILENAME field of the GETFNAME. That doesn't sound reasonable, but if you don't the filter will be ignored and you'll get everything. Interestingly, the value can be a SPACE. What it can NOT be is NUL.

      If you include an actual name in the FILENAME field, that will be shown as the default filename in the dialog box... even if it is a non-existant file!
    Now here's how the filter string works. For this example we're using filter elements so we do not need the opening {ext} parameter. Our example GETFNAME is as follows. (We'll show a screen shot of the results a bit farther down)
           PACK        FILTER_STRING WITH "":
                          ",Picture Files,*.tif;*.jpg;*.bmp":
                          ",BGT Text File,*.txt":
                          ",Source code,*.pls;*.plf":
                          ",Programs,*.plc;*.exe"
    					  
           MOVE        " ",FILE_NAME             ;Insure it's not NUL
    	  
           GETFNAME    TYPE=17:                  ;open dialog, with filter
                       "Select a file":          ;prompt
                       FILE_NAME:                ;file name return string
                       "C:\MMCC-WWW\BARGAINT":   ;starting path to look in          
                       FILTER_STRING
    
    The effect of the filter strings is to give the user a collection of views to the folders. Each of your filter strings will be added to a combo box at the bottom of the OPEN dialog screen. Look at the example below. You'll see the Files of type combo is expanded and it shows the description of each of our filters.

    The combo box does not show the actual filter elements. You could put that into your filter definition if you want; just don't use any commas. For example, this would work in our example PACK above:
         ",Picture Files: *.tif *.jpg *.bmp,*.tif;*.jpg;*.bmp"
    There are no commas within the actual "info" string.




    Is a file OPEN? When asked this question on the Web board (Nov 2011), Bud replied:
       GETFILE    file
       IF ZERO
          DISPLAY "The file is open"
       ENDIF
       
    ...............
    
    ckv_open
    ......open the file here
       return 
    
    Where the Data Manager is involved, Stuart Elliot wrote:
          getfile if1ckv,ismanaged=#nManaged,dmisconnected=#nConnected
          if ( not equal or ( #nManaged and #nConnected != 1 ) )
                call ckv_open
          endif 
    


    Datalist.DIR method
    {datalist object}.DIR [GIVING {return}] USING [*FileSpec=]{filespec},[*Flags=]{flags}
        (Works for both DataList and ComboBox)

    This is a great method for filling a datalist with the files in a given path.

    Here are a few notes from experimentation:

    The *FILESPEC seems to require the full path. To get the path that you're running from we usually start with:
           PATH  CURRENT,CURR_PATH
           PACK  WORK_PATH,CURR_PATH,"\*.txt"
           MY_DataList.DIR USING *FileSpec=WORK_PATH:
                                 *Flags=DDL_READWRITE
    

    The method requires the SHORT filename in the path. If you're starting with a long path, i.e. My_Documents, you can use a FINDFILE to get the ALTPATH which is the short filename MYDOCU~1.

    The method also RETURNS SHORT filenames. Our practice is to do the .DIR, then come back and pull each line out of the datalist and use FINDFILE to get the full details. We then put those back into the list.

    The *FLAGs shown below are defined in the Sunbelt supplied PLBMETH.INC source file.

    The *FLAGs defined by Sunbelt are INTEGER 4. It looks like they could also be INTEGER 2, but that's not confirmed.

    The *FLAGs seem to be a 16 wide bitmask. According to the manual you can add flags together to get multiple properties. We've not tried that.

    The PLBWIN manual can give the basics. Here's an extract which we'll add to later:

    1. {filespec} is the file specification string that may contain wildcard characters. {filespec} may include a standard or a UNC path.

    2. {flags} is a numeric variable, literal or keyword as defined in PLBMETH.INC indicating the following actions. These bit values may be added together as needed:

    Found in PLBMETH.INC
    Value Keyword Includes...
    0x0 DDL_READWRITE Files that can be read or written.
    0x1 DDL_READONLY Files that can be read but not written.
    0x2 DDL_HIDDEN Hidden files.
    0x4 DDL_SYSTEM System files.
    0x10 DDL_DIRECTORY Directories.
    0x20 DDL_ARCHIVE File has been archived.
    0x4000 DDL_DRIVES Include all drives.
    0x8000 DDL_EXCLUSIVE Exclusive flag. If the exclusive flag is set, only files of the specified type are listed. Otherwise, files of the specified type are listed in addition to normal files.

    3. Upon completion, {return} will contain the zero-based index of the last filename in the list or -1 if the method fails.
    4. If the value returned is zero, the ZERO (or EQUAL) Condition Flag is set (TRUE).
    5. If {return} is too small to contain the string index, the OVER Condition Flag is set (TRUE).
    6. The EOS Condition Flag is always cleared (FALSE).



    IMAGE FILE TYPES

    This was from the PL/B web board.
    Subject: check file type     11/24/2015
    Hiep asked "is there a way to check for file type (.pdf, .tif, .gif, .bmp, .jpg, etc...) after getfname without rely on its file extension? "

    R.Liedy's reply said:
    If you read the first few bytes of the file-in-question you can determine what type of image file it is. The cut-paste-below should get you started.

    Regarding PDF files, they all start with "%PDF". Of course there is a chance at false-positives with all of these, for example a simple text document telling you "%PDF means its a PDF". would ironically look like a PDF file.

    Code:
    thePngChars       init   0x89,0x50,0x4E,0x47     // 137, 80, 78, 71
    theJpegChars      init   0xff,0xd8               // 255, 216
    theGifChars       init   0x47,0x49,0x46          // G, I, F
    theBmpChars       init   0x42,0x4d               // B, M           
    theTif2Chars      init   0x4d,0x4d,0x2a          // 77, 77, 42
    theTifChars       init   0x49,0x49,0x2a          // 73, 73, 42
    

    Hiep asked later about other file types. Check back to see if they found those.


    OTHER USEFUL ROUTINES
    MMCC Staff : Don't forget that we have several routines in the UTIL-PLB folder that deal with directories. These use Windows API calls to access the directory structure.
    • WWW-ANLZ was started as a file chaser to find all HTML files in a given directory structure.
    • GET-TREE is an external routine to chase all folders within a given path.
    • GET-DIR is an external routine which gets the list of files within a directory.



  • v1.10

    Write to MMCC Technical Support at:               Send
    e-mail to MMCC.
    MMCC - Technical Support
    600 W. Midland
    Bay City, MI 48708
    (989) 686-8860
    © 1997 - 2024 MMCC - All Rights Reserved