THE TRUTH ABOUT DISC FILES
                       by Eugene Volokh, VESOFT
       Presented at 1982 HPIUG Conference, San Antonio, TX, USA
             Published by SUPERGROUP Magazine, Feb 1983.
 Published in "Thoughts & Discourses on HP3000 Software", 1st-4th ed.


ABSTRACT

The disc file is probably the most important part of MPE; however, due
to  the large number of  different options and considerations inherent
in  disc  files,  these  objects are often  'under-understood' -- this
paper  will  try  to present the truth and  nothing but the truth (the
whole  truth  will  not be printed owing to  lack of paper) about disc
files, which will hopefully remedy this situation.

SECTION I -- FILE STRUCTURE

WHERE IT'S AT

Before  discussing  disc  files  themselves, we must  take a moment to
point  out  some  terms, probably already known  to you, regarding the
physical  medium  on  which disc files reside  -- the disc.  This disc
consists of a lot of 128-word SECTORS, and is assumed to be configured
on the system as one logical device.

Some  considerations  to be judged when  referring to these discs are:
one, space -- each disc has an ever-so-finite amount of sectors on it,
the number of which varies from disc to disc, but is, by Murphy's Law,
never  enough  -- and two, speed of  access, which is typically on the
order of 30 disc accesses per second.

The  discs typically used with the  HP are ones that constantly rotate
in  order for all parts of the disc to be accessible by the unrotating
DISC HEAD.  It is this rotation of the disc that is the culprit in the
slowness of disc accesses.

Similar  considerations  can  be applied to  the two other significant
types  of  hardware:  memory (which is very,  very fast yet lamentably
limited  --  up  to  4 Megabytes on a Series  44) and tapes (which are
virtually infinite yet quite slow).

The  above  hardware  considerations,  though  elementary, will  be of
paramount importance in further discussion.

THE EXTENT QUESTION

Let  us  start  at the beginning -- the  creation of the file. We will
examine what the MPE operating system has to do to create a file.  For
example,  let us say that you ask MPE  to build you a data file, which
is  to  have room for at most 100,000  records of 128 words each (note
that  128  words  is  the size of a sector,  and thus a good value for
simplicity).   This would be done, perhaps,  by an MPE command akin to
':BUILD  ING;DISC=100000' (MPE will automatically  assume 128 words as
the record size).  Now, what does MPE do?

Well,  of course, MPE must allocate some  disc space for that file. In
this  particular  case,  MPE must allocate  a whopping 100,001 sectors
(the  1  extra  sector is for the file  label, a place where MPE holds
internal  file  information like the lockword,  etc.) all at one time.
But,  wait  a minute!  There may be  100,001 sectors out there on your
disc  (or  discs),  but it's possible that there  is no one single gap
that  large out there.  Moreover, maybe you don't really need all that
space -- quite probably, you'll never use more than 10% of it!  So, we
are  faced with a dilemma: if MPE  were to allocate the space for that
file  nicely  and  simply, in one big chunk,  it might not have enough
space  on  disc; or, if it does, most  of that space would probably be
wasted, as (for a time, at least) you would not use all of that space.

Let  us look at the other  'extreme' solution.  Why don't we, perhaps,
allocate  only one sector of space at  a time -- one in the beginning,
for  the file label, and one every  time the user needs one. That way,
even  if  the disc is hopelessly fragmented  (i.e. there are very many
one-sector  pieces of free space out there, but no large ones), we can
probably  fit a sector -- if we can't, it is time to buy another disc;
moreover,  we do not allocate any disc  space until we really need it.
This  was, perhaps, a decent solution in the 'good old days' when disc
space was very expensive. But, now, the operating system would have to
maintain  100,001 pointers to enable access  to that file, which makes
the above method unworkable.

Enter  the EXTENT!  The extent is  a reasonable compromise between the
two  extreme  methods outlined above.  A  file can consist of anywhere
from  1  to 32 extents (the default number  is 8).  Now, when we build
the  above file  (with 8 extents), we will have to allocate only about
12,500 sectors in the beginning (a savings of disc space) and allocate
new  extents  only every 12,500 records  (a savings of disc accesses).
We  could, however, allocate the file  with only 1 extent, thus losing
out  on  disc space but gaining on  disc accesses (but, of course, the
savings  on  disc accesses is rather  small compared to the incredible
wastage  of disc space), or with 32 extents, thus saving disc space at
the expense of a few extra disc accesses.

Two  other  considerations  come  into  play,  however:  one  is  that
accessing  files  with  a  lot  of  extents  FRAGMENTS THE  DISC (i.e.
increases  the  number of small holes at  the expense of large holes),
making new files harder to allocate in the future, and another is that
it  is better to run out of disc  space when building a disc file than
when  allocating  a new extent in the  middle of the program (precious
time  and internal data consistency may be lost this way).  The former
can  be  handled  best  by  decreasing  the number of  extents (at the
expense  of,  of  course, disc space) and  the latter by allocating at
:BUILD-time all of the specified extents (but only if you are sure you
will  use all of the space).  Note that the number of extents (maximum
and  initially allocated) may be specified in the :BUILD command's DEV
keyword,  whose  format is  'DEV=device[,maxexts] [,initalloc]', where
maxexts defaults to 8 and initalloc to 1.

FOR THOSE WITH MULTIPLE DISCS

If  you  are  the  proud owner of several  disc drives, another factor
comes  into play.  For example, let us  say that you build a file with
the  command ':BUILD ING;DISC=100000' (note that the maximum number of
extents  defaults  to  8, 1 initially allocated),  and start to wonder
about  which  disc  your file resides on.   Well, MPE, has adopted the
so-called  'eeny,  meeny,  miney,  moe'  algorithm.   That is,  if you
succeed  in filling all 8 extents of your file, you may well find that
that  file does not reside on just one disc; rather, it resides on the
discs  of the DEVICE CLASS 'DISC' (which are special sets of different
devices,  not  necessarily  discs, configured at  system set-up time).
Each  extent, of course, resides wholly  on one disc; but, the extents
may  reside on different discs -- thus, a file with 8 extents may well
find  itself with 4 extents on disc #1, 2  on disc #2, 1 on disc #3, 1
on  disc  #4,  and  0  on  disc  #5.  If you want  that file to reside
exclusively  on  disc  #4,  however,  'no  sweat'  (as is  said in the
vernacular)!   Merely :BUILD the file  with the 'DEV=4' parameter. Or,
if  you set up another device class called PRODDISC which will contain
discs  #3, #4, #5, building the  file on DEV=PRODDISC will ensure that
all  extents  of  that  file will be located  on one of those devices.
What, you may ask, is the importance of this?  Well, the word that has
leaked  down from HP is: SPREAD OUT YOUR FILES -- for instance, if you
have  two heavily accessed files, it might  be wise to put them on two
different discs.

This is done for the following reason: let us assume that you have two
disc  drives,  each  one  able  to  perform approximately  30 I/Os per
second,  and  you  spread out your files in  such a way that each disc
gets  about  30  I/O  requests  per  second -- those  requests will be
executed  within  one  second.  But, if there  are 20 I/O requests per
second  to one disc and 40 to the second disc, the first disc will not
perform up to capacity, and 10 of the requests to the second disc will
have to wait for a second or more, thus degrading system performance.

(NOTE:  The  entire question of head locality  and what it buys you is
the  topic  of  much  argument  in the HP  community, and is generally
unresolved.   The  above statements are an  expression of the author's
understanding of this issue and may be fallacious in some respects.)

Another  promising idea is to configure all of your devices except the
system  disc as device class 'DISC', thus keeping files off the system
disc, and thus reducing the amount of access to the system disc, which
already  has  the  operating  system  and  the  virtual memory  on it.
However,  with  MPE  IV,  in  which you are  allowed to spread virtual
memory over several devices, this may not be as important.

Note  that for easy file disc location handling, MPEX/3000's %LISTF ,4
and  %ALTFILE  commands  and  ADAGER's DBCREATE  and SETMOVE functions
should be used.

THE LOGICAL FILE STRUCTURE

Besides  the  physical  file  structure  described  above  -- extents,
sectors,  etc.  -- MPE files also  have an internal logical structure,
not  enforced  in most ways by the  actual file contents but rather by
certain  logical  file descriptors like the  record size, the blocking
factor, the block size, the file type, and the like.  First of all, we
will  discuss the simplest sort of MPE file -- the fixed-record length
file.

THE FIXED RECORD LENGTH FILE

A  file is more than just a collection of data placed out on disc.  It
usually  has certain logical relationships within it.  One of the most
frequent  and  fundamental  relationships  is  one  in  which  data is
organized  into  chunks  (called  RECORDS)  of  a  fixed  length;  for
instance,  if you have a data  file which contains [for each customer]
the  customer code (6 characters),  customer name (30 characters), and
the  amount owed you by the customer (8 zoned decimal characters), you
have  a 44-character entry for each  customer.  Therefore, it would be
logical,  for  the  sake  of  ease of access, to  build that file with
44-byte  (or 22-word) records, having one record per customer.  So, to
build   that  file,  you  would  perform  a  BUILD  command  with  the
REC=-44,,F,ASCII  parameter  ("-"  stands for bytes  and "F" for fixed
record length).

THE BLOCK

A  familiar  example of a fixed record  length disc file is your usual
EDITOR /KEEP-NUMBERED file, a file with a record size of 80 bytes = 40
words.  However, do you know that in your EDITOR /KEEP files more than
6%  of all disc space they occupy  is wasted?  This may not sound like
much, but if you are running short on disc space, this can be a lot --
what's  more, that disc space can be saved (for large files) by merely
specifying  a  certain :FILE equation for the  file to be kept.  What,
you may ask, is the reason for this wastage?  Well, the answer lies in
the secrets of the BLOCK.

The  fundamental unit of disc I/O (as  far as MPE is concerned) is the
SECTOR  (128 words).  Practically all disc I/O ends up as multiples of
128  words.   Forty,  of course, is not a  multiple of 128. So, if MPE
decided  to place 40 words per sector,  it would waste not 6%, but 69%
of  each sector!  So, you ask, why not pack three 40-word records into
one  128-word sector?  Well, that's exactly what MPE does; but because
128  is  not  a multiple of 40 either, 6%  of the file's disc space is
still  wasted. (Although 6% may not  sound like much, for some unlucky
files which have different record lengths, it can be worse, with up to
50%  wasted space!)  But, there is light at the end of the tunnel!  We
can  very  snugly fit 16 40-word records  into 5 128-word sectors -- a
perfect fit.

From  the above labyrinth come the  notions of the BLOCKING FACTOR and
the BLOCK.  The BLOCKING FACTOR is, very simply, the number of records
that  we  choose  to fit into a multiple of  128 words -- in the above
'snug  fit'  scenario,  this is 16; in the  6% wastage method that MPE
uses,  the blocking factor is 3 (3 records to 1 sector); in the (ugh!)
69%  wastage  at  1 record to 1 sector,  the blocking factor is 1. The
BLOCK  therefore, is BLOCKING FACTOR records -- i.e. when the blocking
factor is 16, the block is 16*40 = 640 words = 5 sectors.

In  general, MPE chooses the blocking factor as follows: if the record
size  of  a  file  is  less than one sector  (128 words), the blocking
factor = 128/recordsize = the number of records that will fit into one
sector;  if  the record size of a file  is greater than 128 words, the
blocking  factor is always 1.  A  good example of the possible wastage
is  when a record is 65 words  long; then, 128/65 = blocking factor of
1,  wasting 63 words for every 65 words  used -- a wastage of 49%!  If
that  record  was,  however,  64 words long,  then the blocking factor
would be 2, with NO wastage.

By  the way, it happens that the blocking factor for a new file can be
defined  in  a  :BUILD  or  :FILE  command  --  always  as  the second
subparameter  (between  the  record  size  and  the F, V,  or U record
format)  of  the REC= keyword.  Thus, if  you want to eliminate the 6%
waste  due  to  the  blocking  factor of 3 on  EDITOR keep files, just
execute  an equation of the form ':FILE filename;REC=,16' right before
keeping the file as 'filename', and presto! -- out comes a file with a
blocking  factor of 16.  For already existing files, some disgustingly
complicated  tricks can be used -- or,  if you are blessed with a copy
of MPEX/3000, just use the BLKFACT= keyword of the %ALTFILE command.

Now,  you  may  wonder,  what causes MPE to  choose a default blocking
factor  calculation  system  that  leads  to  considerable  wastage in
perhaps one of the most common form of files?  Well, for one, it would
be  unfair  not to remark at this  point that the 'NO wastage' schemes
described  above really DO waste some space (although not a lot).  The
reason  for this is that a file (in  fact, each extent of a file) must
be  an  integral  number  of  blocks.   If  it isn't, a  full block is
allocated  for less than 'BLOCKING FACTOR' records.  Thus, if you have
a  file containing 50 80-byte records with a blocking factor of 16, it
would  use  up 4 blocks, the last one  having only 2 actual records --
this  file  will  thus use 21 sectors; however,  if this file is built
with  a blocking factor of 3, it would  use up 17 blocks (the last one
also  having  only  2 records), and would thus  use only 18 sectors of
disc  space.  However, this consideration is less important for larger
files.   Another reason for MPE's  default blocking factor strategy is
that  the  block  and  the blocking factor govern  more than just disc
space  usage -- they also control  certain parameters of buffered file
access  (see  the  chapter  on FILE ACCESS).   However, for most files
(especially  large ones!) it is beneficial to select your own blocking
factors (with the use of the contributed BLOCK program, for instance).

THE VARIABLE RECORD LENGTH FILE

Let  us take a hypothetical EDITOR COBOL-format file. At the beginning
of  each line there is a 6-digit  line number; the other 74 characters
contain   the   line,   blank-padded.   Now,  those  trailing  blanks,
especially  in large source files, convey absolutely no information to
anybody, and (since the average length of a line could be estimated at
half  of  74 characters) will cause a  wastage of APPROXIMATELY 50% OF
THE  DISC SPACE USED BY THOSE FILES!   But, you reply, if EDITOR built
the  file with a record length of, say, 40 characters, all of my lines
that  are longer than 40 characters  will get truncated.  Well, you're
right  -- but that is not what is  to be done!  Wouldn't it be nice if
EDITOR  and/or  the  file system allowed you to  have files with not a
FIXED  record length, but with a  VARIABLE record length -- i.e. lines
that  are 74 characters long will use 74 characters and lines that are
10 characters long will use 10 characters?  Well, it does!

In  fact,  if  you  type in the little-known  /SET VARIABLE command in
EDITOR,  it  will  instruct EDITOR to keep  the workfile as a variable
length  record file (WARNING:  USE THIS ONLY FOR COBOL AND DATA FILES,
NEVER  FOR NUMBERED FORTRAN OR SPL  SOURCES, OR THOSE SOURCES WON'T BE
COMPILER  READABLE!!!), thus letting it  ignore those trailing blanks,
but  still keeping the file format  transparent to other programs that
read these files -- for example, compilers.  In your own programs (not
just  in  EDITOR),  you can read variable  record length files without
changing your programs at all -- COBOL's or FORTRAN's READ command can
read  variable  record  length  files. You can  write them without any
changes either -- if you write a 10-character record to a fixed record
length file of 80 characters, the record will be padded with 70 blanks
or  nulls; if you write that record  to a variable record length file,
the  record  will  not  be  padded by anything,  thus saving the space
required  for  the  padding.  To build a  variable record length file,
specify  the third subparameter of the  REC= parameter of the :FILE or
:BUILD command as 'V' (e.g. REC=-80,,V).  The record size specified is
now  no  longer  the actual record size of  each record but rather the
maximum;  whether  the  file  is  ASCII  or BINARY  now really doesn't
matter.   Also,  do not call in the  National Guard (or PICS) when you
see on a :LISTF that the END OF FILE for that file is GREATER THAN ITS
FILE LIMIT -- it can happen with variable record length files.

Therefore,  with  COBOL source files  (especially) and unnumbered data
files,  variable-length  records  are  usually  the way  to go; again,
however,  we  must warn you that  numbered default-format (e.g. SPL or
FORTRAN  source)  files  SHOULD  NEVER BE KEPT  WITH THE /SET VARIABLE
OPTION SET or else they will not be readable by the compiler.

However,  as  the old proverb says, 'EVERY  SILVER LINING COMES WITH A
CLOUD  ATTACHED  TO  IT.'   Variable  record  length  files  have some
drawbacks;  for  one, they cannot be  accessed directly (for instance,
with  the  FREADDIR,  FWRITEDIR,  or  FPOINT intrinsics,  or FORTRAN's
READ/WRITE (fnum @ record) construct); i.e. you can read their records
sequentially, but you cannot ask to get, for instance, the 17th record
of  the file.  Moreover, they cannot  be accessed by many file copiers
using  the fast MR NOBUF file access  method (see under FILE ACCESS in
this  paper),  such  as HP's own DSCOPY,  MPEX's %FCOPY ,,FAST, MPEX's
%ALTFILE,  SUPRTOOL/ROBELLE, etc.  Also, before  MPE IV, append access
to  variable  record  length files was not  supported; it is supported
starting with MPE IV.

Another  important  consideration to keep in  mind when using variable
record  length  files  is  that  when you build  a new variable record
length  file with record size RECSIZE and blocking factor BLKFACT, the
resultant  block  size of the file will  not be RECSIZE*BLKFACT (as in
fixed   record   length   files),   but   rather   RECSIZE*BLKFACT   +
(BLKFACT+1)*(2  bytes).   Thus, if you build  a variable record length
file  of  record  size  80 bytes and blocking  factor 3, the file will
actually  have a block size of  (80*3+4*2)=248 bytes.  However, if the
same  file is built with a blocking  factor of 16, the block size will
end  up being (80*16+17*2)=1314 bytes, not 1,280 bytes! The end result
is  that AN OPTIMAL BLOCKING FACTOR FOR A FIXED RECORD LENGTH FILE MAY
BE FAR FROM OPTIMAL FOR VARIABLE RECORD LENGTH FILES!

Incidentally,  MPE IV's new INTER-PROCESS COMMUNICATION features (i.e.
Message and Circular files) rely EXCLUSIVELY on variable record length
files (q.v. COMMUNICATOR issue 26 -- the C MIT).

UNDEFINED RECORD LENGTH FILES

There  exists another type of disc file -- the undefined record length
file.  These are rather bizarre specimens which are not intended to be
and  should  not be used as disc files,  but rather are supposed to be
utilized  as tape files and terminal files, which are beyond the scope
of this paper.

ASCII VS. BINARY FILES

When  using  fixed record length files, it  often happens that you may
write  a  30-character record into a file  with a record length of 80.
Then,  what  happens to the other 50  characters of the record?  Well,
for  some  files (for instance source  files) that contain simple text
data, you would typically want to initialize them to spaces because of
the  nature  of  the file.  If that is  what you want, you would build
that  file  (EDITOR will build it that way  for you) as an ASCII file.
This parameter can be specified as the fourth subparameter of the REC=
parameter  of the :FILE or :BUILD command, e.g. REC=,,,ASCII. However,
for some data files, you may want to pad the records with binary zeros
(nulls).   Files built in such a way  are called BINARY files, and can
be built by specifying the BINARY parameter as the fourth subparameter
of  the  REC=  parameter  of the :FILE or  :BUILD command, for example
REC=,,,BINARY.   Note that this is usually  not necessary as BINARY is
the default file mode.  Also note that since no record padding is done
in  variable record length files, the  ASCII vs. BINARY distinction is
usually irrelevant to them.

THE FILE CODE

If  you  do  a :LISTF mode 1 or 2 on  a group of files, you may notice
that  some  files  have a file code of  0 (blanks), some of PROG, USL,
EDTCT,  KSAM, PRIV, and assorted  numeric codes.  These filecodes, for
the  most part, are merely for the sake of file identification -- they
have  no  physical  influence on the actual  contents of files. If you
change  the filecode of a file  (for example with MPEX/3000's %ALTFILE
filename;CODE=  command), the contents of  the file will not magically
change.   However, the filecode is  useful for identification purposes
--  for instance, the MPE loader knows that files of filecode PROG are
:RUNable  program files, the EDITOR knows that files of code EDTCT are
/SET  FORMAT=COBOL  files, and QEDIT/ROBELLE knows  that files of code
111   are  its  files.   In  fact,  you  can  set  up  your  own  file
identification  system for source or data files -- you can build files
with a certain file code (via the CODE= parameter of a :FILE or :BUILD
command), alter the file code (with MPEX/3000 or by copying the file),
and   examine   the   file   code   (via   the   :LISTF   command  or,
programmatically,  with  the FGETINFO intrinsic).   Certain tools like
MPEX  also allow you to LISTF files  by file code.  An example of this
kind  of file identification system (recently implemented by us) is to
set  the  file  code to be the Julian date  of the day on which it was
created, or some other important date.

Note  that  the  file code of each file is  in reality a number -- for
example,  program files (PROG) have a file  code of 1029, but they are
listed  in a :LISTF output as PROG.   Also, KSAM files do not actually
have  a numeric file code that identifies  them as such -- they can in
reality  have any numeric file code.  However, KSAM files which have a
file  code of 0 (which usually shows up as blanks on a :LISTF listing)
will  be  printed  as  having  code = KSAM.  Files  that are listed as
having  file code = PRIV are in  reality files that have NEGATIVE file
codes  (like  IMAGE  files).   Unlike  usual  files, they  can only be
accessed  by programs running in PRIVILEGED  MODE.  This is handy, for
instance,  for  IMAGE  files,  to ensure that  an ordinary user cannot
physically  change  an  IMAGE file without  going through the existing
IMAGE utilities/intrinsics.

USER LABELS

It  is often desirable or necessary to  store information in a file in
such  a  way  that  it  can  later  be  retrieved, but  is nonetheless
transparent  when you read it in  an ordinary fashion.  The concept of
USER  LABELS provides this capability.  With it, you can write special
label  records (the maximum number of which is specified at open time,
defaults  to 0, and can be up  to 254) with the FWRITELABEL intrinsic,
read  them with the FREADLABEL intrinsic, but have them be transparent
to  any user who reads or writes ordinary records to that file.  These
labels  are  used  by  IMAGE, KSAM, and the  message system file (e.g.
CATALOG  and CICAT).  Another advantage of user labels is that you can
write  user  labels when you open the  file for read access, read user
labels  when you open the file for write access, and open the file for
OUT access (see access modes below) which will erase all of the file's
records but not its user labels.

CARRIAGE  CONTROL  FILES, RELATIVE I/O  FILES, MESSAGE FILES, CIRCULAR
FILES, KSAM FILES, IMAGE FILES, AND OTHER MONSTERS THAT INHABIT THE HP
3000

This  paper will not talk about the  above types of files (for want of
time,  will, and disc space).  However, maybe sooner or later you will
hear the truth about them, too!

SECTION II -- FILE ACCESS

Once a file is built, it really isn't much good if you can't access it
--  read  it,  write  it, append to it, etc.   In this chapter we will
discuss the different methods of accessing files that MPE provides for
you.

BUFFERED FILE ACCESS

A  while back we referred to the concept of the BLOCK.  Well, it turns
out that the block is more than a convenient way of storing records on
disc.   In fact, it plays a very important role in the default mode of
file  access called BUFFERED FILE ACCESS.   Let us assume that you are
reading  a 10,000-record disc file which has a record size of 40 words
(80  bytes),  a  blocking  factor of 16, and thus  a block size of 640
words.   Let us assume that you had to do one disc I/O for each record
-- this would come up to a total of 10,000 disc I/Os, quite a lot!

So,  MPE  implemented  a rather ingenious  idea called file buffering.
Each  file  opened  as a buffered file has  allocated for it a certain
amount (default 2, changeable at open time with the FOPEN intrinsic or
the  BUF=  parameter  of a :FILE equation)  of buffers, each of length
equal to the file's block size (in this case 640 words). These buffers
are placed in an Extra Data Segment (because extra data segment access
is  faster than disc access) and accessed there. They are read from or
written  to  disc  only  when  a  record that is not  in the buffer is
requested.  Thus,  for  the file described  above, only 10,000/16 disc
I/O's  =  625 disc I/O's are necessary  -- a considerable savings! The
advantage  of  having more than 1 buffer  is that then you can access,
for  instance,  records 17-32 (in one buffer)  and 49-64 (in the other
buffer) without necessitating a disc I/O each time you switch from one
record  range to the other.  However, if  you then read in record 100,
the contents of buffer 1 will be flushed out to disc and buffer 1 will
then contain records 97-112.  In general, with buffering, one disc I/O
is  required for every (BLOCKING FACTOR)  records -- in this case, one
disc I/O is needed for each 16 records.

In  the discussion above, we advised you to set up blocking factors so
that  BLOCKING  FACTOR  *  RECORD  SIZE  be  an even  multiple of 128;
therefore,  for  instance, 16 was chosen for  files with records of 40
words  in  length. But, there is more than  one way to skin a blocking
factor! In fact, since 16 * 40 is a multiple of 128, 32 * 40 certainly
is  too!   Very  little  disc  wastage  will result  from changing the
blocking  factor  from  16 to 32, but each  buffer will now be not 640
words long, but rather 1,280 words long, and now only 313 (=10,000/32)
disc I/Os will be necessary to read the file!  A blocking factor of 64
will  require  less  than  160  I/O's,  and  so  on.   This  will  not
necessarily  halve  the  time  used  by  the read, but  it will surely
decrease  it.  Of  course,  the same thing can  be said for writing to
files. We must, however, point out that memory space will be used much
more  heavily  by  files that have large  blocking factors.  Also, the
total  size  of the buffers must be less  than or equal to 8,192 words
(or  14,000 words starting with the D  MIT version of MPE).  Since the
default  number  of  buffers  is 2, this puts  an upper limit of 4,096
words  (or 7,000 words starting with the D MIT) on the block size of a
file.   However,  you  can  increase  that maximum to  8,192 words (or
14,000  words  starting  with  the  D MIT) by opening  the file with 1
buffer (by specifying BUF=1 on a file equation).

MULTIPLE RECORD NON-BUFFERED ACCESS (MR NOBUF)

The  buffering method described above is  rather good, but it is still
not  optimal; first of all, access to  the extra data segment in which
the  buffers  are located is faster  than disc access, but nonetheless
not as fast as access to your own stack.  Moreover, as was pointed out
above,  certain  memory  usage considerations forbid  the buffers from
being  more than 5K to 10K words, which is also not optimal.  Wouldn't
it  be truly wonderful if one could  read not just single records, not
just  blocks of 16 or so records,  but 4,000 words at one shot?  Well,
one can, through the magic of MR NOBUF, probably the MOST POWERFUL AND
FASTEST  FILE  ACCESS  METHOD  NOW  AVAILABLE! MR  stands for Multiple
Record I/O (do not confuse this with the Multiple RIN capability, also
abbreviated MR), and NOBUF stands for No Buffering (this is a bit of a
misnomer  --  it  means that it is you,  not MPE, who will provide the
buffer space needed).  Note that MR must be used with NOBUF!

Certain  factors  to beware of when using  MR NOBUF are, for one, that
this  method is rather hard to  use with variable record length files.
Also,  the  efficiency  of  this method is best  with a buffer size of
4,096  words.   Another  factor is that when  the block size (BLOCKING
FACTOR  * RECORD SIZE) is not a multiple of 128 words, MR NOBUF is not
that  much more efficient than ordinary  file access, and simple NOBUF
access should be used instead.

By  far, the best application of MR NOBUF is with file copying. FCOPY,
which  uses ordinary buffering methods, is often 10 to 20 times slower
than  MR NOBUF  copiers  like  HP's  own  DSCOPY,  MPEX/3000's  %FCOPY
,,FAST/DSLINE  or  %ALTFILE  commands, SUPRTOOL/ROBELLE,  and numerous
other programs.  However, you can do MR NOBUF reading and writing from
your  own  programs  by  specifying  the MR NOBUF  access options when
accessing  the  file (or specifying the MR  or NOBUF parameters on the
:FILE  equation  --  however,  a  bug present in  some versions of MPE
forces  you to specify MR in the FOPEN because it ignores the MR :FILE
equation  parameter;  see 'ANOTHER MPE  FEATURE (BUG)' in SCRUGletter,
Jan  1981 Vol 4 #1).  This will allow you to read more than one record
(always  at least one block, however) at  a time, and also lets you do
direct  I/O  (e.g.  with FREADDIR and/or FWRITEDIR)  on a block number
rather than record number basis.

However, reading files MR NOBUF in your own programs is rather hard to
do  because of many concerns that  have to do with deblocking records.
Because  of  this,  it  is  suggested that you either  do most of your
record  selection outside of your program (with SUPRTOOL/ ROBELLE, for
instance),  or  develop  your  own  MR NOBUF I/O  routines that can be
easily called from your applications programs.

MR  NOBUF I/O can therefore really cut down the execution time and CPU
time  demands of your disc I/O-heavy programs.  The only problems with
MR  NOBUF are that it is hard to apply to variable record length files
and  KSAM files and that it may (because of the large in-stack buffers
necessary) use up a lot of stack space and much memory space.

THE ACCESS TYPES FOR DISC FILES

In  the access options parameter of the FOPEN intrinsic or in the ACC=
parameter  of  the :FILE command you  can specify the so-called access
type  which defines whether a program will read the file, write to the
file,  do  both,  or  append  records to the file.   There are 7 legal
access types, which can be very useful if used properly.

The default access type is IN access.  This is read-only access -- all
attempts  at writing records to files  opened with IN access will fail
with  File System Error 40 -- OPERATION INCONSISTENT WITH ACCESS TYPE.
If you only want to read the file, you should open it with this access
type;  this  will prevent your program  from accidentally writing over
the  file;  it will work even if somebody  else has the file opened in
Share or Exclusive Allow Read mode (see the SHARING FILES chapter) and
it  will  also  work  if  the file's security  prevents you from doing
anything but reading that file.

Another  type  of  access is OUT access.   OPENING OLD FILES WITH THIS
ACCESS  TYPE WILL ERASE THEM!  If you  do not want that to happen, you
should  open  the  file with OUTKEEP access.   However, if you want to
erase  the file, or the file is new,  or you do not care about its old
contents  anyway,  this  is the access type  that should be specified.
Note that you need WRITE access to the file to open it in this mode.

OUTKEEP  access is useful for opening  files to write to them, WITHOUT
DESTROYING  THEIR  OLD  CONTENTS  (as OUT access  would do).  You need
WRITE access to the file to open it with this access type.

Often  you do not need to write over the old contents of a file -- you
merely  need to add new records to it.  In that case, APPEND access is
for you -- it forces the record pointer to be positioned at the end of
the  file and permits you only to append records to the file.  Another
advantage  is  that it requires that you  have only APPEND access (not
WRITE  access) to the file to open it  thus. So, if you wish to permit
users  to  only  append  and not overwrite data  in a given file, they
should  be  allowed  only APPEND, and not  WRITE, access to this file.
For  instance,  VESOFT's  SECURITY/3000  permits APPEND  access to its
security  violation  log file, but not  WRITE access (so user's cannot
obliterate the record of their violations).

The  above access types permit you either  to READ ONLY or WRITE ONLY,
but  never  both.   INOUT  access lets you both  READ and WRITE to the
file.   All intrinsics (except FUPDATE) can  be used against that file
in this mode.  Note that you need READ and WRITE access to open a file
in this way.

There  is  also a special form of  access called UPDATE access that is
PRECISELY the same as INOUT access except that it permits the usage of
the  FUPDATE  intrinsic.   Since this is  apparently no less expensive
than  INOUT  access,  and requires no extra access  to the file, it is
suggested  that  this option be used instead  of the INOUT access type
because it is more powerful and no more dangerous.

Another  access,  permissible only to programs  that run in Privileged
Mode  (Ohmigod!),  is EXECUTE access; its  advantages are twofold: for
one,  it  requires  only  EXECUTE  access, not READ  access to a file;
moreover,  it allows you to write to loaded program or SL files.  This
is  listed  only  for the sake of completeness,  and all you nice non-
privileged  users  out  there don't even need to  know about it. For a
discussion  of  privileged  mode, see PRIVILEGED  MODE: USE VS. ABUSE,
SCRUGletter July 1981, Vol 4 #4.

POSTING END OF FILE TO DISC

As  was  mentioned before, each file has  a special record called 'the
file  label' (which contains all sorts  of information about the file,
such  as  its  type,  name, and, among other  things, its end of file,
which is the number of records which the file contains). Now, if every
time  that you wrote a record to the file, MPE would have updated that
file's  file  label, your programs would run  quite slow -- after all,
that  would mean extra disc I/Os to handle.  For this reason, MPE does
not  post the end of file to disc  until a record write would cause it
to  allocate  a new extent (in which case  it would have to change the
file label anyway), thus saving the extra I/Os.

This  is  all  fine  and dandy, provided that  MPE will actually get a
chance  to  post  the end of file to  disc sometime.  But, what if the
system  crashes  after you wrote the record  but before MPE posted the
end  of  file?  Then, even though the  record (or records, as the case
may  be), are already out on disc,  MPE does not know about it because
the  end of file pointer does not  reflect this.  So, you've just lost
all those records that were written before the system crashed.

You  can, however, minimize your losses through a little-known feature
of  the  file  system  by  calling the FCONTROL  intrinsic (see System
Intrinsics  Manual) with a parameter 6  (WRITE END OF FILE) which lets
you post the end of file to disc.  If you do this after you write each
record,  the  most  records  that  you will ever lose  due to a system
failure  is one!  Of course, this will  triple the number of disc I/Os
that  you'll have to do, so this  is not advised for large batch runs;
however,  if  you are updating a disc  file interactively, the time it
takes  to input all of the data from the screen will dwarf the time it
will  take to do the extra I/O to  such a degree that that the posting
of  the  end of file will be virtually  free in terms of time, and may
save you hours of re-entering vital data.

WHEN YOU ARE NOT ALONE

When  you  use  any of the access types  listed above except read only
(IN)  access, the file specified will  be opened EXCLUSIVELY; that is,
you  cannot open it if anybody else  has it opened, but, once you have
it  opened,  NOBODY  ELSE CAN USE IT UNTIL  YOU CLOSE IT.  This is, of
course,  somewhat of a problem if that file is intended to be read and
written by many different users.  There are several ways to get around
this dilemma.

EXCLUSIVE ALLOW READ ACCESS

One  of these ways is Exclusive Allow Read (EAR) access.  This permits
you  to  forbid all other users from  writing to a file, while letting
them  read  that  file.  Also, this access  (unlike EXCLUSIVE) will be
granted  to  you  even if the file is  already being accessed for read
access  (but  not  for  write  access)  by someone else.   This can be
specified by setting the appropriate bits in the access options of the
FOPEN call, or issuing a FILE equation with the EAR keyword.

TRUE SHARED ACCESS

But,  sometimes you do not want to have  only one writer to a file, or
one  writer and several readers, but MORE THAN ONE PERSON WRITING TO A
GIVEN  FILE.   This  can  be accomplished with  SHARED ACCESS (to use,
specify  the  appropriate  bits  in  the FOPEN call  or append the SHR
keyword to the file equation for that file), which is the default mode
for  read only access, but has  to be explicitly specified and handled
when  writing to a file.  Shared access  is a very complicated form of
access, one at which we will look closer in the next chapter.

SECTION III -- SHARED FILES

SHARING FILES WITH INPUT/OUTPUT ACCESS

Merely  specifying SHR access when opening the file will get you where
you are going -- it will allow you and anybody else who opens the file
with  SHR access to read and write  to this file.  But, let us suppose
the following situation: two processes have opened one file for IN/OUT
access in SHR mode, and the following happens:

   PROCESS A                        PROCESS B
   Reads a record
                                    Reads the same record
   Changes the record
                                    Changes the record
   Writes the record back
                                    Writes the record back

In  the  above  scenario, process A reads  the record before process B
reads  the same record but writes it back out after process B reads it
in!   That way, process A's changes WILL  NOT BE REFLECTED IN THE FILE
because  of the interference of process B.  In fact, what is needed is
a method of 'LOCKING OUT' all other writers of the file while the file
is  being  updated!  Well, MPE's FLOCK  and FUNLOCK intrinsics provide
this method.

DYNAMIC LOCKING AND UNLOCKING FOR SHARED FILES

In  order to use dynamic locking, the process that opens the file must
open  it with dynamic locking enabled (the LOCK parameter on the :FILE
equation  --  together  with the IN/OUT SHR  access, the file equation
would   now   look   like  :FILE  file;LOCK;SHR;ACC=INOUT  --  or  the
appropriate  bit  in the access options  of the FOPEN intrinsic call).
Then, before each 'logical transaction' (a period in time in which the
data  in the file is not consistent -- in the above example, while the
record  is  being  changed,  the  current  state of the  file does not
reflect  the  true intended state; therefore,  the file must be locked
before the read and unlocked after the write) the file must be locked,
and  then unlocked after the end of the transaction (note that opening
a  file with dynamic locking enabled does not actually lock the file).
This  will  ensure that there will be  no inconsistencies like the one
shown  above.   Note that THIS WILL WORK  ONLY IF ALL WRITERS LOCK THE
FILE  IN APPROPRIATE CASES -- this  locking arrangement works only for
programs that honor it.

SHARING FILES WITH APPEND ACCESS

In some cases, however, locking does not really help.  For example, if
two  writers  are  just  writing  to  a  file (no  reading, etc.), the
'logical  transactions' like the ones  described above are composed of
merely one write.  For these transactions, it does no good to lock the
file.   One of the most common examples of this type of file access is
shared append access to a file by two or more writers.

In  fact, if the file has a blocking  factor of 1, there is no need to
do  anything  but  the write.  However, look  at what happens when the
file  has  a  blocking  factor  other than 1,  for example 3; consider
process A and process B, both writing to the same file:

   PROCESS A                PROCESS B
   Record 1 written; kept in buffer
                            Record 1 written; kept in buffer
   Record 2 written; kept in buffer
                            Record 2 written; kept in buffer
   Record 3 written; buffer flushed
                            Record 3 written; buffer flushed

Note  that, by the principles of buffering, the actual disc I/O is not
done  until the third record is written  and the buffer is flushed out
to  disc.   But  because of that, when it  is flushed out to disc, the
buffers  from  process A and process B  interfere with each other, and
data can be lost.

Therefore,  the  rule  for  locking when appending  (or performing any
other  such  operation  in  which  each transaction  contains only one
operation) is: LOCK WHEN THE BLOCKING FACTOR IS GREATER THAN 1; IF THE
BLOCKING FACTOR IS 1, LOCKING IS UNNECESSARY.

MULTIPLE FILE ACCESS

Another  way to ensure that no data is lost while writing to a file is
with  a  useful  tool (which is even more  useful under MPE IV) called
MULTIPLE  FILE ACCESS.  With multiple file  access in shared mode, the
internal  file control information and the  I/O buffers are shared, as
is  the  file  itself, thus avoiding many  problems of ordinary shared
access.

So, if process A and process B (IN THE SAME JOB/SESSION) access a file
SHARED,  APPEND, and MULTI, then their internal end-of-file and buffer
pointers are shared; thus, the risk of one's file I/O interfering with
the   other's   is  eliminated.   To  specify  MULTI-access,  set  the
appropriate  bit in the FOPEN parameters or specify the ;MULTI keyword
on a :FILE command for the file in question.

So,  very  many  of  the  problems and  complicated locking strategies
discussed  above can be avoided if  MULTI-access to that file is used.
However,  there  are two things that you  must keep in mind when using
MULTI-access;  for  one, ordinary sequential reads  and writes to that
file  will  not  behave  as expected.  Why?   Well, the current record
pointer  is  among  those values that is  shared with MULTI-access and
thus  if  process  A  reads  a record sequentially  and then process B
requests  to  read a record sequentially, process  B will get the next
record because the record pointer was already incremented by A's read.
Thus,   if   the   two  processes  read  the  file  sequentially  with
MULTI-access,  each one will read  approximately half the file instead
of the full file!

MULTI-ACCESS  WAS  PERMISSIBLE  WITHIN ONE JOB/SESSION  ONLY UNDER MPE
III.  However,  under  MPE  IV,  you can use  the GMULTI (Global MULTI
access),  which  can be specified in the  FOPEN parameters or with the
GMULTI  keyword  of  the  :FILE equation, to  have MULTI-ACCESS ACROSS
JOBS/SESSIONS, with which you can avoid most of the problems of shared
file access very easily.

MORE ABOUT LOCKING

There are two methods of locking files: UNCONDITIONAL, which means 'if
the  file is already locked by somebody  else, wait for them to unlock
it,  and then establish the lock' and CONDITIONAL, which means 'if the
file is already locked by somebody else, return to me immediately with
an  error  condition'.   The UNCONDITIONAL method  is usually the most
useful,  although the CONDITIONAL option is handy when you do not want
to  take  the risk of waiting a long  time (if the program that has it
locked won't unlock it for a while).  Needless to say, the file should
not  be  locked  for  a long time, and SHOULD  NEVER BE LOCKED WHILE A
TERMINAL  READ IS GOING ON unless you do not mind the fact that if the
terminal   operator  goes  to  lunch,  everybody  else  who  tries  to
unconditionally lock the file will hang.

LOCKING MULTIPLE FILES, OR THE SECRETS OF MULTIPLE RINs (MR)

Let  us  consider  another hypothetical circumstance:  Process A locks
File  1;  meanwhile Process B locks File  2.  Then, Process A tries to
unconditionally  lock  File  2  and  is  then impeded  until Process B
unlocks  File  2.  Meanwhile, Process B  tries to unconditionally lock
File  1  and  is  then impeded until Process  A unlocks File 1.  Thus,
Process  A  is  waiting  for  Process  B and Process  B is waiting for
Process  A.   Result:  Deadlock.   Both  processes are  hung until the
system is re-started.

The  sages of Cupertino thought of  this when designing the system; in
fact, their solution (which may not sound like much of a solution, but
which  is better than nothing) is TO  FORBID PROGRAMS TO LOCK MORE THA
ONE  FILE AT A TIME.  But, one may object, what if I have to lock more
than one file at a time?  Well, the answer to that problem is that you
can  get around (but at your own risk) that restriction if the program
that does the locking has Multiple RINs (MR -- not to be confused with
Multiple Record access) capability (i.e. was :PREPed with it).  By the
way,  RIN  stands  for Resource Identification  Number. These programs
can,  IF THEY REALLY HAVE TO, lock two or more files at a single time.
Needless  to  say,  this  capability  should  not  be freely  given to
everybody  and his brother, but only to  people who really need it and
are smart enough to use it without causing deadlocks.

That  brings  us  to the question: How do  you get around the deadlock
problem?   Well,  you  may have already noted  that the reason why the
programs  got into a deadlock was that one locked File 1 before File 2
and  the  other locked File 2 before File  1.  If they had only kept a
consistent  locking  arrangement  (e.g.  File 1 must  ALWAYS be locked
before  File  2),  they  would  not  have  had the problem  -- this is
probably  the best way to avoid the deadlocks.  Another way is to lock
the  files CONDITIONALLY, and if the lock fails, do something else (or
even  go into a loop, which can at  least be broken out of by aborting
the job or doing a break/:ABORT, rather than re-starting the system).

SUMMARY OF LOCKING AND LOCKING STRATEGY

The following are the 10 Commandments of Locking:

1.  THOU  SHALT LOCK AROUND LOGICAL  TRANSACTIONS WHICH INVOLVE TWO OR
    MORE  OPERATIONS.  For example, this kind of a logical transaction
    would  be  a  read of a record followed  by a modification of that
    record  followed by a write.  If you  do not lock around this, you
    stand the risk of losing data consistency.

2.  THOU  SHALT ALSO LOCK AROUND ALL LOGICAL TRANSACTIONS THAT INVOLVE
    A  FILE  WHICH YOU SHARE WITH  SOMEBODY WHO HAS TRANSACTIONS WHICH
    INVOLVE  TWO  OR  MORE  OPERATIONS.   That  means  if  process A's
    transactions  are just single writes  and process B's transactions
    are  reads  followed by writes, both process  A AND process B must
    lock around their transactions.

3.  THOU  NEED NOT LOCK A SHARED FILE IF ALL ITS WRITERS' TRANSACTIONS
    INVOLVE JUST ONE OPERATION AND ITS BLOCKING FACTOR IS 1.  Thus, if
    process  A  and process B are writing  to a shared file, and their
    transactions  are merely single writes (e.g. they are appending to
    a file), neither one has to lock the file.

4.  THOU  SHALT USE GMULTI ACCESS UNDER MPE IV WHEN THOU ARE APPENDING
    TO A SHARED FILE.  This can save you time, worry, and your neck.

5.  HONOR  THY  LOCKING ARRANGEMENTS.  This means  that if it has been
    decided  that  a  shared file is to be  locked by its writers, all
    writers  must lock it.  If so much as one writer fails to lock the
    file, all of the locking arrangements will be useless.

6.  THOU  SHALT  NOT  KEEP  A FILE LOCKED WHILE  A TERMINAL READ IS IN
    PROGRESS.  If you do, the file will be locked down until something
    is  entered, which could mean an indefinite waiting period for any
    other program that wants to lock the file.

7.  THOU  SHALT NOT LOCK MORE THAN ONE  FILES AT THE SAME TIME WITHOUT
    MR CAPABILITY.  The second file lock will fail unless your program
    was :PREPed with MR capability.

8.  THOU  SHALT PROTECT THYSELF FROM DEADLOCKS BY ESTABLISHING A FIXED
    FILE  LOCKING  SEQUENCE  IF  THOU  USES  MR CAPABILITY.   Thus, if
    process A locks file 1 and then file 2, process B must lock in the
    same order, (i.e. file 1 and then file 2, not file 2 and then file
    1!).

9.  THOU  SHALT NOT GIVE MR CAPABILITY TO JUST ANYBODY.  MR capability
    can cause big trouble, and thus should be passed out sparingly.

10. THOU SHALT USE IMAGE/3000 IF THY FILE LOCKING ARRANGEMENTS GET TOO
    COMPLICATED.    IMAGE/3000   has  file  locking  capabilities  far
    superior  to  MPE's file locking features.   If you find that your
    locking  arrangements are getting too  complicated or programs are
    waiting  for  inordinate amounts of time to  get at a shared file,
    think about converting it to an IMAGE file -- it may be worth your
    while.

SECTION IV -- FILE DOMAINS AND EQUATIONS

PERMANENT AND TEMPORARY FILES

Most  of  the files that we discussed  in previous sections were usual
PERMANENT  files  --  files  that,  once  built, exist  until they are
:PURGEd  or somehow deleted.  There is, however, another type of file,
one  that  is  also  often  quite  useful.   This  is  the JOB/SESSION
TEMPORARY FILE.  These files, once built (by placing the ;TEMP keyword
in  the  :BUILD  or  :FILE command), exist until  they are :PURGEd (by
performing  a  ':PURGE filename,TEMP') OR UNTIL  THE JOB OR SESSION IN
WHICH  THEY  WERE  CREATED  LOGS OFF.  Why  are these files desirable?
Imagine, for instance, that you want to create a certain file that you
want  to  stream.   After  the  file  is streamed (in  the same job or
session  that it was built in), you no longer need it.  If you were to
create  that  file as a permanent file and  then purge it, it is quite
possible  that somebody else may have built a file with the same name;
for instance, if the same program is being run on another terminal and
that file is created there.

However,  if  you  create  the  file  as a temporary  file, you can be
certain  that  creating  it will not interfere  with anybody else; the
nature  of job/session temporary files is such that two different jobs
or  sessions can create within them temporary files with the same name
which do not interfere with each other.

Most  MPE commands either attempt to open  the file given to them as a
temporary  file and then (if the  temporary open fails) as a permanent
file  (e.g. :STREAM,:COBOL,:RUN, etc.), thus being able to accept both
temporary  and permanent files, or have special keywords that instruct
them  to  open  the  file as a temporary  file (e.g. PURGE file,TEMP).
Programs  that  open files as permanent can  be instructed to open the
file  as job/session temporary by issuing  a file equation of the form
':FILE  filename,OLDTEMP'.   Note  that  some commands  and subsystems
(e.g. :BASICOMP, :PREP, :SEGMENTER's -BUILDUSL command) build files as
temporary  files; others can be instructed to build files as temporary
by using a file equation like ':FILE filename;TEMP'.

If you need to keep a temporary file as a permanent file with the same
name,  you  can  do a ':SAVE filename'; if you  want to keep it as one
with  a  different  name,  do  a ':RENAME  oldfile,newfile,TEMP' and a
':SAVE  newfile'. The names of your temporary files can be listed with
LISTEQ2  or (in a more  complete, :LISTF-like format, with MPEX/3000's
%LISTF fileset: TEMP command).

$NEWPASS AND $OLDPASS

Two other useful critters are the system-defined files called $NEWPASS
and  $OLDPASS.   Consider, for instance, the  :COBOL command. When the
USL file is omitted in this command, it is usually followed by a :PREP
command that is to prepare the resultant USL file into a program file.
But  what  intermediate  USL file should be used?   Well, if you use a
permanent  or  temporary  file you run the risk  of having a file with
that  name already in existence.  This  is where $NEWPASS and $OLDPASS
come  in.   $NEWPASS  is a peculiar file  that, when closed, magically
turns  into  $OLDPASS.   So, once you open  $NEWPASS, write to it, and
close it, you can then open $OLDPASS, and read it.

So, in the case of the :COBOL and :PREP, the USL file parameter of the
:COBOL  command  defaults  to  $NEWPASS.   The USL file  is closed and
presto!,  it becomes $OLDPASS.  Now, you  can execute a command of the
form  ':PREP $OLDPASS,progfile', and that USL will be :PREPed into the
specified  program file.  If you really want to be fancy and you don't
need  the program file to be a temporary or permanent file, you can do
a ':PREP $OLDPASS,$NEWPASS', and, after this is done, the program file
(which  was specified as $NEWPASS) becomes $OLDPASS. Now, you can just
':RUN  $OLDPASS.'.   Note  that  $OLDPASS  contains the  USL file from
:COBOLPREP  (or  :FORTPREP, :SPLPREP, etc.) and  the program file from
:COBOLGO (or :FORTGO, :SPLGO, etc.).

If  you  decide  that  you want to save the  contents of $OLDPASS in a
permanent  file,  just  do  a  ':SAVE  $OLDPASS,filename'.   A  rather
bizarre,  undocumented feature is that to save $OLDPASS as a TEMPORARY
file,  you can do a  ':RENAME $OLDPASS,filename'!  Of course, $OLDPASS
vanishes as soon as you :BYE off.

THE CARE AND FEEDING OF :FILE EQUATIONS

Perhaps one of the single most important and least understood tools in
handling files is the :FILE equation.  The file equation allows one to
re-define  certain open parameters of old and new files.  For example,
let  us  say that you are keeping a  file with EDITOR, and you want to
keep  it with a blocking factor of  16 and 32 extents.  Then you would
issue  the  file equation ':FILE  filename;REC=,16;DEV=,32'. Note that
THIS  DOES  NOT  BUILD THE FILE!  However,  when you execute the /KEEP
command (and EDITOR therefore opens the file) or when you open it from
your  own or any other program as a new file, it will be opened with a
blocking factor 16 and 32 extents.

If,  however,  the  specified  file already exists  and has a blocking
factor  of 3 and 8 extents and you issue the file equation in the hope
that  the  equation  will  magically  transform  it,  you're in  for a
letdown.   This is because if that  file already has a blocking factor
of  3,  it will always have a blocking factor  of 3 even if you say on
the  :FILE  equation  or when opening the file  that it has a blocking
factor  of  16.  Its blocking factor is  3, and merely opening it with
another blocking factor changes nothing.  To truly change the blocking
factor,  record size, number of extents, file limit, or any one of the
other  file parameters, you need to either rebuild the file (remember,
these  parameters  can be redefined when you  are building a new file)
and  copy the old contents of the  file into it, or use utilities such
as MPEX/3000.

However,  some options can be redefined  for OLD files.  These are not
the  file options (like CCTL or REC) but the access options (like ACC,
BUF,  MR, etc.), which are not inherent  parts of the file, but rather
attributes of access, which are defined when the file is opened. These
can therefore be redefined for OLD or NEW files.

Another class of :FILE equation parameters governs actions that are to
be  performed  not  at OPEN time, but rather  at CLOSE time.  The only
parameters  in this class are disposition parameters.  The SAVE option
instructs  the program to close the file as a permanent file; the TEMP
option  tells  it  to  close the file as  a job/session temporary file
(q.v.  TEMPORARY VS. PERMANENT FILES); and, the DEL option will delete
the file referenced when it is closed.

Note  that  although all :FILE equation  parameters correspond to some
FOPEN  or FCLOSE parameter, not all FOPEN and FCLOSE parameters can be
redefined  with  a  :FILE  equation; for instance,  the number of user
labels (on open), or the flag that indicates whether space between end
of  file  and  file  limit  is  to  be  released (on  close) cannot be
redefined with :FILE equations.

If  you do not want the user to  be able to redefine the open or close
parameters  of a file, you should open the file with the Disallow File
Equations bit in the FOPTIONS parameter of the FOPEN intrinsic set.

A GLOSSARRY OF COMMON DISC FILE HANDLING TERMS

ACCESS-MODES   The  file's  ACCESS  MODE  (IN,  OUT,  OUTKEEP, APPEND,
              INOUT, UPDATE, or EXECUTE) which is defined at file open
              time  and  which  restricts  the  actions  that  can  be
              performed  on the file.  This  can be redefined with the
              ACC=  parameter  of  the  :FILE  equation.   See  APPEND
              ACCESS,  IN  ACCESS,  INOUT ACCESS,  OUT ACCESS, OUTKEEP
              ACCESS, UPDATE ACCESS.

ACCESS-OPTION  The   ACCESS  OPTIONS  is  a  parameter  to  the  FOPEN
               intrinsic (q.v.) which defines the access mode, sharing
               status, dynamic locking flags, etc.  See FOPEN.

ASCII          ASCII  files are fixed/undefined-length files which are
               padded  or initialized to blanks instead of zeros. That
               is,  writing  a record that is  shorter than the record
               size  causes the result to  be blank-padded. To create,
               use ASCII as the 4th subparameter of the REC= parameter
               of the :FILE/:BUILD command.  See BINARY.

BINARY         BINARY files are fixed/undefined-length files which are
               padded or initialized to zeros (nulls).  To create, use
               BINARY as the 4th subparameter of the REC= parameter of
               the :FILE/:BUILD command.  See ASCII.

BLOCK          A  BLOCK  is  the  unit  in  which data  is transferred
               between  I/O  devices  and  file buffers  on disc.  One
               BLOCK  =  BLOCKING  FACTOR records.   Each block always
               starts  on a sector boundary,  and thus, for disc space
               usage  efficiency should be equal to an integral number
               of  sectors  whenever  possible.  See  BLOCKING FACTOR,
               BLOCK SIZE.

BLOCKING-FACT  The BLOCKING FACTOR is the number of records per block.
               To  optimize disc space usage,  set the blocking factor
               such  that BLOCKING FACTOR *  RECORD SIZE is a multiple
               of  128 words.  To optimize  file access speed, set the
               blocking  factor  as  large  as possible.   To minimize
               memory  usage,  set  the  blocking  factor as  small as
               possible.   To  set the BLOCKING  FACTOR for new files,
               specify   it  as  the  2nd  subparameter  of  the  REC=
               parameter  of the :FILE or  :BUILD command.  See BLOCK,
               BLOCK SIZE.

BLOCK-SIZE     For  fixed  record  length  files,  BLOCK SIZE  = BLOCK
               FACTOR  *  RECORD  SIZE.   For  variable  record length
               files, BLOCK SIZE = BLOCK FACTOR * RECORD SIZE + (BLOCK
               FACTOR + 1) * (2 bytes).  The most efficient disc space
               usage  occurs when the block size of a file is equal to
               an integral number of SECTORS. See BLOCK, BLOCK FACTOR.

BUFFERING      The default mode of file access is BUFFERED FILE ACCESS
               --  in this mode records  are not immediately read from
               or  written  to  disc, but are rather  kept in an extra
               data segment which contains (BUFFERS) buffers of length
               (BLOCK SIZE) words each.  See BUFFERS, NOBUF ACCESS.

BUFFERS        When  a  file  is accessed in  buffered mode, a certain
               number  of  BUFFERS  is  allocated, each  one of length
               (BLOCK  SIZE)  words,  in one extra  data segment.  The
               default  number  of buffers is 2,  and can be redefined
               with  the  BUF=  parameter  of  a  file  equation.  See
               BUFFERING.

DEADLOCK       A  situation in which two  processes are hung, each one
               waiting for the other to do something.  This can happen
               when  several  files  are  locked by  processes with MR
               capability.  See LOCKING FILES, MR CAPABILITY.

DEVICE         The DEVICE on which a disc file resides can be a single
               disc  (specified  by  placing its device  number in the
               FOPEN  call  or  as  the  1st subparameter  of the DEV=
               keyword  of  the  :FILE equation) or  a device class, a
               collection of disc devices grouped under a generic name
               (specified  in  the  same place as  the device number).
               All  of  the  extents  of  the file are  placed on this
               device or device class.

DOMAIN         The  DOMAIN  of  a file can  be PERMANENT or TEMPORARY.
               This  can  be  specified  in  a  :BUILD  command (;TEMP
               indicates TEMPORARY, omission of it means PERMANENT) or
               a  :FILE  command  (for  old files,  :FILE filename,OLD
               means   PERMANENT   and  :FILE  filename,OLDTEMP  means
               TEMPORARY;  for  new  files, :FILE  filename;TEMP means
               TEMPORARY  and ;SAVE means  permanent).  See PERMANENT,
               TEMPORARY.

EAR            EAR  (short for Exclusive Allow Read) is an access mode
               which  permits a user to open  a file for write access,
               while  still  allowing  other users read  access to the
               file. See EXCLUSIVE ACCESS, SHARED ACCESS.

END-OF-FILE    The  END OF FILE is usually  the number of records that
               have  been written to a given file.  It is usually less
               than the file limit (q.v.), which is the maximum number
               of  records in a file, but  could be greater than it in
               variable record length files (q.v.).

EXCLUSIVE      EXCLUSIVE  ACCESS to a file is  an access mode in which
               the accessor forbids everybody else to access that file
               while  he  is  accessing it.  This  mode is the default
               mode  for all non-read access.   It can be specified in
               the  access  options  of  an  FOPEN call or  in a :FILE
               equation  with  the  EXC  parameter.   See  EAR ACCESS,
               LOCKING, SHARED ACCESS.

EXTENT         An  EXTENT  is  a  collection  of blocks  that occupies
               contiguous  space on a given disc.   There can be up to
               32 such extents in a file; the default is 8. See EXTENT
               SIZE, MAXIMUM EXTENTS, NUMBER OF EXTENTS.

EXTENT-SIZE    All  extents  of  any file must be  of equal length (in
               sectors)  except the last one,  which may be of smaller
               length.  For formulae for these lengths, see APPENDIX B
               -- DETERMINING DISC SPACE USAGE.  See EXTENT.

FILE-CODE      The  FILE CODE of a file  is an integer which describes
               the  type  of this file; some  of the more common codes
               have special mnemonics corresponding to them (e.g. PROG
               =  1029 = file code  of program files). These mnemonics
               show  up  on  :LISTFs  of  that  file, and  can also be
               specified  on  a  :BUILD  or  :FILE command.  The code,
               whether mnemonic or numeric, can be placed on the CODE=
               parameter of a :BUILD or :FILE command.

FILE-EQUATION  A  file equation is a useful tool that allows a user to
               redefine  certain open or close  parameters of the file
               (e.g.  the file code (CODE), the access type (ACC), the
               close   disposition   (SAVE/TEMP),  etc.).  It  can  be
               specified through the MPE :FILE command.

FILE-LABEL     The  FILE  LABEL  of a file  contains information about
               that  file  (e.g.  file  name,  creator id,  file code,
               record  size, extent information,  etc.) needed by MPE.
               Ordinary users need not worry about this entity.

FILE-LIMIT     The  maximum  number  of  records permitted  in a file,
               necessary  for knowing how much disc space to allocate,
               specified  at file creation time.  Note that the END OF
               FILE  can  actually exceed the  FILE LIMIT for variable
               record  length files.  The file  limit can be specified
               in  a :BUILD or :FILE command as the first subparameter
               of the DISC= keyword.

FIXED-LENGTH   FIXED  RECORD LENGTH files are files whose records have
               a  fixed  length  --  if a record  of smaller length is
               written  to the file, the record is padded on the right
               with  an appropriate number of  blanks (ASCII files) or
               nulls  (BINARY files).  An example of this kind of file
               is the usual EDITOR file which has a fixed length of 80
               bytes.   To build fixed record  length files, specify F
               as  the  third subparameter of the  REC= parameter on a
               :BUILD   or   :FILE  command  (e.g.  REC=-80,,F).   See
               VARIABLE RECORD LENGTH, UNDEFINED RECORD LENGTH.

FOPEN          FOPEN is a system intrinsic which permits its caller to
               open a file.  BASIC, COBOL, FORTRAN, and RPG users need
               not  be  concerned  about this  intrinsic because their
               languages provide file access features already (this is
               therefore  mostly  used  by SPL  programmers); however,
               this  intrinsic  is  alluded  to  often  in  this paper
               because   all  file  open  commands  in  all  languages
               eventually translate out to this intrinsic.

GMULTI-ACCESS  GMULTI  access  is  an  extended  form of  MULTI access
               (q.v.)  available only in MPE IV.  Its usage (which can
               be  specified  by  appending the GMULTI  keyword to the
               :FILE   equation)  together  with  SHR  and  ACC=APPEND
               provides  a painless way of  appending to shared files.
               See MULTI, SHARED ACCESS.

LOCKING        MPE's DYNAMIC FILE LOCKING mechanism (available through
               the  FLOCK and FUNLOCK intrinsics) gives users a way to
               have  more  than  one  program write to  a file without
               jeopardizing  data consistency.  In order to call FLOCK
               and  FUNLOCK, the file must have been previously opened
               with  the dynamic locking access  option set (which can
               be  done in the FOPEN call  or using the LOCK parameter
               of  the :FILE command).   See DEADLOCKS, MULTIPLE RINs,
               SHARED ACCESS.

MAX-EXTENTS    The  MAXIMUM  NUMBER  OF EXTENTS defines  the number of
               extents  (q.v.) into which a file  is to be split. Note
               that  (usually) not all of  these extents are allocated
               at  the  time  a  file  is  built  -- the  default is 1
               (although more can be allocated initially by specifying
               their  number  as  the  3rd  subparameter of  the DISC=
               keyword  of the :FILE command).   The maximum number of
               extents can be specified as the 2nd subparameter of the
               DISC=  keyword of the :FILE command, and defaults to 8.
               See EXTENTS, NUM EXTENTS.

MULTI-ACCESS   MULTI  access  is a form of  access that is very useful
               for  sharing  files.  It permits you  to share not just
               the  files  but also internal  file control information
               and  file buffers.  It can  be specified by placing the
               MULTI  keyword on a :FILE  command.  See GMULTI ACCESS,
               SHARED ACCESS.

MULTIPLE-RINs  The  MR (MULTIPLE RINs) capability is an account, file,
               group,  and  user  capability that  governs a program's
               ability to have more than one file locked at at a time.
               In  order for a program to  be permitted to do this, it
               must  have  been  :PREPed with MR  capability by a user
               with  MR capability, and it must reside in a group that
               has MR capability.  See DEADLOCKS, LOCKING.

MULTI-RECORD   MULTI-RECORD ACCESS (abbreviated MR) is a mode in which
               a  file  accessor  can  read more than  one record at a
               time,  thus  greatly  speeding  up  file  access.  This
               option must be used together with the NOBUF option (see
               NOBUF ACCESS).  It can be specified on a :FILE equation
               as the MR parameter.  See NOBUF ACCESS.

$NEWPASS       $NEWPASS  is  a  special system-defined  temporary file
               that,  when  closed, turns into  $OLDPASS (q.v.).  This
               file  (and  $OLDPASS)  disappears (along  with all job/
               session temporary files) at logoff time.  See $OLDPASS,
               TEMPORARY FILES.

NUM-BUFFERS    The  NUMBER  OF  BUFFERS  is the number  of I/O buffers
               allocated for buffered file access (q.v.).  This number
               can  be  specified  with the BUF=  parameter of a :FILE
               equation.  See BUFFERING.

NUM-EXTENTS    The NUMBER OF EXTENTS is the number of extents that are
               currently allocated in the file; this starts out as the
               initially allocated number of extents (see EXTENTS) and
               is  increased by 1 whenever a record which will not fit
               into  the  currently  allocated  number  of  extents is
               written to the file.  See EXTENTS, MAXIMUM EXTENTS.

$OLDPASS       $OLDPASS  is  a  special system-defined  temporary file
               that  is  the  last $NEWPASS (q.v.)  file closed.  This
               file  disappears at logoff time,  but can be saved with
               the MPE :SAVE command.  See $NEWPASS, TEMPORARY FILES.

PERMANENT-FILE  A permanent file is a disc file which is accessible by
               all users in the system (that have the proper access to
               it,  of  course)  and  remains until it  is :PURGEd, as
               opposed  to  a  temporary  file  (q.v.)  which  can  be
               accessed  only  by the session in  which it was created
               and  is  automatically  deleted when  that session logs
               off.   To  specify that a file is  to be accessed as an
               OLD  permanent file, execute file  equation of the form
               ':FILE  filename,OLD'; to specify that  a file is to be
               saved  as  a  NEW  permanent file, just  place the SAVE
               keyword  in  a  :FILE  equation  for  that  file.   See
               TEMPORARY FILES.

RECORD-LENGTH  The  RECORD  LENGTH  of  a  file is the  length of each
               record  in  that  file  if  it is a  fixed or undefined
               record  length  file,  and  the maximum  length of each
               record  in that file if it  is a variable record length
               file.   This  parameter  can  be  specified as  the 1st
               subparameter  of the REC= parameter in a file equation.
               See  FIXED  RECORD  LENGTH,  UNDEFINED  RECORD  LENGTH,
               VARIABLE RECORD LENGTH.

SECTOR         A SECTOR is 128 words of disc space.

SHARED-ACCESS  Files open in SHARED ACCESS mode can be written by more
               than  one program at the same time.  This option can be
               specified  in a :FILE equation  with the SHR parameter.
               It  is imperative for data consistency that the dynamic
               locking  (q.v.)  facility be used  by all programs that
               write to a file shared by two or more writing programs.
               See EAR ACCESS, EXCLUSIVE ACCESS.

TEMPORARY-FILE  A  temporary file is a disc  file that can be accessed
               only   in   the   session   that  created  it,  and  is
               automatically  purged when that  session terminates. It
               can,  however, be saved as a permanent file (q.v.) with
               the  MPE  :SAVE command, and  purged before the session
               logs  off  with  the  PURGE  filename,TEMP  command. To
               specify  that  a  file is an OLD  temporary file, use a
               file  equation like ':FILE  filename, OLDTEMP; the fact
               that  it is to be saved as  a NEW temporary file can be
               specified  by  appending  the  TEMP keyword  to a :FILE
               equation for that file. See PERMANENT FILES.

UNDEFINED-LEN  Undefined  record  length files are  not intended to be
               used  as  disc  files.  Instead, use   fixed / variable
               record length files.  See FIXED RECORD LENGTH, VARIABLE
               RECORD LENGTH.

USER-LABELS    User  labels  are records which,  although parts of the
               file,  are  transparent  to  the normal  reader of that
               file, and which can only be accessed via via FREADLABEL
               and FWRITELABEL intrinsics.

VARIABLE-LEN   Variable  record  length  files are files  in which all
               records are not required to have the same length.  When
               records of a length less than the record length (which,
               incidentally,  is  the maximum length  of any record in
               that  file) are written to the file, no padding is done
               (which means that the ASCII / BINARY distinction has no
               meaning  here), but rather the size of the record to be
               written  becomes  the record length  of that particular
               record.   A  result of this is  that no space is wasted
               due  to  padding,  which  makes  these files  much more
               efficient  users of disc space than fixed record length
               files  (q.v.).   See  FIXED  RECORD  LENGTH,  UNDEFINED
               RECORD LENGTH.

DETERMINING DISC SPACE USED BY FILES GIVEN FILE PARAMETERS

Perhaps  because  there are so many  different file parameters (record
size,  blocking  factor,  end  of  file,  file  limit, etc.)  that are
involved  in determining the disc space used up by a certain file, the
formula  for  this  calculation  is  hard  to  come  by  and  is quite
complicated.   However,  we will attempt to  list it together with all
its  interesting ramifications below.  Note that this method will work
only  for  FIXED  RECORD  LENGTH  FILES  that  are to be  WRITTEN IN A
SEQUENTIAL  FASHION (i.e. no directed  writes).  The parameters needed
for  this  algorithm are the RECORD  SIZE (in words), BLOCKING FACTOR,
END  OF FILE, FILE LIMIT, NUMBER OF USER LABELS, and MAXIMUM NUMBER OF
EXTENTS  REQUESTED.  This method will yield the NUMBER OF SECTORS USED
BY  THE  FILE,THE  EXTENT SIZE OF ALL BUT  THE LAST EXTENT, THE EXTENT
SIZE  OF  THE  LAST EXTENT OF THE FILE,  THE MAXIMUM NUMBER OF EXTENTS
GRANTED,  THE  NUMBER OF EXTENTS ACTUALLY USED,  and THE BLOCK SIZE OF
THE FILE.

BLOCKING CONSIDERATIONS

The  first  parameter that must be  determined for this calculation is
the  BLOCK  SIZE,  in SECTORS, which we  will denote by the 'variable'
name  BLKSIZE.   Using  standard  SPL  notation,  the names  BLKFACT =
blocking  factor  and RECSIZE = record size,  and keeping in mind that
ALL  DIVIDES  PERFORMED  BY US FROM NOW  ON WILL BE 'CEILING' DIVIDES,
I.E.  DIVIDES  IN  WHICH  THE  RESULT IS THE  SMALLEST INTEGER THAT IS
LARGER  THAN  OR  EQUAL  TO THE FRACTIONAL  DIVIDE RESULT (E.G. 5/2=3,
20/4=5), we get the following formula:
   BLKSIZE:=(RECSIZE*BLKFACT)/128;  <<Record size IN WORDS>>

Next,  we must find out the number of blocks (not records, but blocks)
that  are used up by the data portion  of the file and the label (user
label  and  file  label)  portion of the file.   The formulae for this
(note  FLIMIT  =  file limit, ULAB =  number of user labels allocated)
are:
   DATABLKS:=FLIMIT/BLKFACT;
   LABBLKS:=(ULAB+1)/BLKSIZE;<<the 1 is for the file label>>
   TOTALBLKS:=DATABLKS+LABBLKS;  << blocks used by both >>

EXTENT CONSIDERATIONS

At  this  point,  we  can  determine the extent size  (in blocks or in
sectors)  of  each file extent.  The formula  is (given MAXEXTS is the
maximum  number  of extents requested by  the file creator at creation
time) as follows:
   EXTSIZE'BLOCKS:=TOTALBLKS/MAXEXTS;   << in blocks >>, or
   EXTSIZE'SECTORS:=EXTSIZE'BLOCKS*BLKSIZE; <<in sectors>>
For  our  purposes, we will  use the EXTSIZE'BLOCKS-in-blocks formula.
Now, let us digress for a moment.

As  we have said before, the maximum number of extents of a given file
can be specified in a :BUILD or :FILE command, and defaults to 8. But,
the maximum number of extents actually granted (this is NOT the number
of  extents actually used!) may be  smaller than the maximum number of
extents  requested  in  this way!  In order  to explain the reason for
this, we must first recall a fact that will be of paramount importance
to  us in this entire discussion: ALL EXTENTS OF A FILE MUST BE OF THE
SAME LENGTH, EXCEPT THE LAST ONE, WHICH MAY BE OF SMALLER LENGTH.

Let  us suppose that you try to :BUILD a file of 100 blocks with 16 as
the  maximum number of extents using,  for example an MPE command like
:BUILD  MYFILE;DISC=100,16).   The  file  system  must fit  an integer
number  of blocks into one extent.  Now,  how many blocks can fit into
one  extent?   Well, the number is 7  (the ceiling of 100/16). But, by
the  rule stated above, all extents of a file except the last one must
be  of the same length.  Thus, each extent except the last one must be
7  blocks  long.   But, only 14 such extents  can fit into 100 blocks,
leaving one 2-block extent!  So, the file system cannot possibly grant
you  a maximum number of extents larger than 15, even though you asked
for 16!

In  short,  the 'real' number of maximum  extents granted turns out to
be:
   REALMAXEXTS:=TOTALBLKS/EXTSIZE'BLOCKS;
where TOTALBLKS and EXTSIZE'BLOCKS were defined above.

Now,  the above statements have yet to  use the END OF FILE parameter.
Nevertheless,  this  parameter is a vital  one to our calculation.  It
permits  us to determine another crucial factor, the number of extents
currently used (USEDEXTS), through the following formula:
   USEDEXTS:=(LABBLKS+EOF/BLKFACT)/EXTSIZE'BLOCKS;
The  above  takes  the number of blocks actually  used by the file and
divides  it by the number of blocks per extent, determining the number
of extents actually used.

Now,  we  have  the answer: if the number  of extents used is the real
maximum  number  of  extents,  i.e.  all  of  the  file's  extents are
allocated, the number of sectors used can be found by the following:
   SECTORS:=TOTALBLKS*BLKSIZE;
If,  however,  some of the extents of  the file remain unallocated, we
can find the number of sectors used with this formula:
   SECTORS:=IF USEDEXTS=REALMAXEXTS THEN
              TOTALBLKS*BLKSIZE
                        << if all extents are allocated >>
            ELSE
              USEDEXTS*EXTSIZE'BLOCKS*BLKSIZE;

THE FACTS IN A NUTSHELL

In  short,  the above rantings and ravings  boil down to the following
algorithm:
   (variables:
   MAXEXTS  = maximum number of extents requested
   RECSIZE  = record size (in words) of the file
   BLKFACT  = blocking factor of the file
   FLIMIT   = the file's file limit
   EOF      = the file's end of file
   ULAB     = the number of user labels allocated in file
   BLKSIZE:=(RECSIZE*BLKFACT)/128;
   DATABLKS:=FLIMIT/BLKFACT;
   LABBLKS:=(ULAB+1)/BLKSIZE;
   TOTALBLKS:=DATABLKS+LABBLKS;
   EXTSIZE'BLOCKS:=TOTALBLKS/MAXEXTS;
   REALMAXEXTS:=TOTALBLKS/EXTSIZE'BLOCKS;
   USEDEXTS:=(LABBLKS+EOF/BLKFACT)/EXTSIZE'BLOCKS;
   SECTORS:=IF USEDEXTS=REALMAXEXTS THEN
              TOTALBLKS*BLKSIZE
            ELSE
              USEDEXTS*EXTSIZE'BLOCKS*BLKSIZE;

Let us analyze an example case (you can verify it yourself!):
   MAXEXTS = 8 extents
   RECSIZE = 40 words
   BLKFACT = 3 records per block
   FLIMIT  = file limit of 10000
   EOF     = 4600 records
   ULAB    = 0 user labels
   BLKSIZE            := (40*3)/128 = 1 sector;
   DATABLKS           := 10000/3 = 3334 blocks;
   LABBLKS            := 1/1 = 1 block;
   TOTALBLKS          := 3334+1 = 3335 blocks;
   EXTSIZE'BLOCKS     := 3335/8 = 417 blocks;
   REALMAXEXTS        := 3335/417 = 8 extents;
   USEDEXTS           := (1+4600/3)/417 = 4 extents;
   SECTORS            := since USEDEXTS(4)<>REALMAXEXTS(8),
                         then 4*417*1 = 1668 sectors;

A SUMMARY OF METHODS TO SAVE DISC SPACE

The  following  is  a summary of some of  the possible methods to save
disc  space without deleting files  or making them unreadable (methods
are arranged in order of descending average percentage savings):
:% savings:  method                                        :
:---------:------------------------------------------------:
: 25%-75% : Convert source files to QEDIT format; this     :
:         : format is very efficient in terms of disc space:
:         : usage yet is still readable by compilers.      :
: 25%-50% : Convert data/COBOL files to variable record    :
:         : length.  This can be accomplished with EDITOR's:
:         : /SET VARIABLE command.                         :
: 0%-50%  : Improve blocking factor of files;              :
:         : a file's block size should be a multiple of 128:
:         : words or disc space will be wasted.            :
: 0%-25%  : Set file limit of files to end of file;        :
:         : if the file limit of a file is not its EOF     :
:         : disc space is probably being lost.  Note that  :
:         : for data files, the file limit should be       :
:         : greater than the EOF to allow for expansion.   :
:         : If you don't want to do this, set the file's   :
:         : maximum number of extents to 32.               :
:---------:------------------------------------------------:
These operations can be performed on files one by one, or en
masse using MPEX/3000.

A SUMMARY OF METHODS TO SPEED UP FILE ACCESS

The  following  is  a summary of some  possible methods of speeding up
disc  file access, arranged in  order of descending average percentage
savings of file access time:

:% savings: method                                         :
:---------:------------------------------------------------:
: 50%-95% : Use MR NOBUF access for file reading/writing;  :
: 5%-10%  : Increase the block factor of files;            :
:         : this will increase the block size, and thus the:
:         : buffer size of files accessed with buffering   :
:         : and thus decrease the number of disc I/Os      :
:         : needed to access them.  Make the block size as :
:         : large as possible but no more than 8,000.      :
:         : Set BUF=1 (only 1 buffer) to avoid getting     :
:         : file system error 57 (OUT OF VIRTUAL MEMORY).  :
:---------:------------------------------------------------:

RELATED PAPERS / USEFUL PROGRAMS

As we could not (and never intended to) say everything there is to say
about  disc files, we would like to  refer you to the following useful
reference documents and utility programs:

PAPERS

ANOTHER MPE FEATURE (BUG)
A discussion of a bug in Multi-Record file access
by Vladimir Volokh
VESOFT, Inc.
SCRUGletter Volume 4 Issue 1 for January 1981

HOW TO AVOID PROBLEMS WITH MPE CARRIAGE CONTROL (CCTL)
All there is to know (well, almost) about Carriage Control
Robert M. Green
ROBELLE Consulting Ltd.
8648 Armstrong Road, R.R. #6
Langley, B.C. V3A 4P9  CANADA
(604) 888-3666

HP3000 Computer Systems MPE Commands Reference Manual
Section VI -- MANAGING FILES

HP3000 Computer Systems MPE Intrinsics Reference Manual
Section III -- ACCESSING AND ALTERING FILES

HP3000 Computer Systems MPE IV Intrinsics Reference Manual
Section III -- INTERPROCESS COMMUNICATION AND CIRCULAR FILES
Section X -- ACCESSING AND ALTERING FILES

PRIVILEGED MODE -- USE AND ABUSE
What is privileged mode and how to use it safely
by Eugene Volokh
VESOFT, Inc.
SCRUGletter Volume 4 Issue 4 for June 1981

SOFTWARE

MPEX/3000
Many useful extensions to MPE
available from VESOFT, Inc.
7174 Melrose Ave.
Los Angeles, CA 90046 USA
(213) 937-6620

QEDIT/ROBELLE
A superior editor, with disc space-saving features
available from ROBELLE Consulting Ltd.
8648 Armstrong Road, R.R. #6
Langley, B.C. V3A 4P9  CANADA
(604) 888-3666
CRYPTIC FILE SYSTEM ERROR MESSAGE DE-CRYPTED

In  addition to its other failings,  the System Intrinsics Manual does
not  explain  the  exact  reason  for and/or workaround  for most file
system errors.  In fact, most file system error messages are very hard
to understand.  The following is an attempt at an adequate explanation
of  the  causes,  effects,  and workarounds for  different file system
errors that pertain to disc files:

0    END  OF FILE (FSERR 0): This  error is encountered when a program
     attempts  to read beyond the end of file or write beyond the file
     limit.  WORKAROUND: Change the program or the file.

1    ILLEGAL  DB  REGISTER  SETTING (FSERR 1):  Should never occur for
     non-privileged mode programs.  For privileged mode programs, this
     means that the programmer attempted to do an FFILEINFO, FGETINFO,
     FOPEN,  or FRENAME in split-stack mode  (i.e. after a call to the
     EXCHANGEDB  or SWITCHDB procedures).   WORKAROUND: Do not perform
     the function in split-stack mode.

2    ILLEGAL CAPABILITY (FSERR 2): A function that requires privileged
     mode capability (e.g. open a file for NOWAIT I/O, open a file for
     EXECUTE  access,  etc.)  was  attempted  without  privileged mode
     capability.   WORKAROUND: Enter privileged  mode before executing
     the function or do not attempt to execute it at all.

8    ILLEGAL  PARAMETER  VALUE (FSERR 8):  Parameters specified in the
     FOPEN  call are mutually contradictory;  for instance, an attempt
     to  open  a  file  NOWAIT  on a serial disc  was detected, or the
     program  tried  to  open  a new KSAM  file without specifying the
     FORMALDESIGNATOR   or   KSAMPARAM   parameters   on   the  FOPEN.
     WORKAROUND: Correct the parameter.

9    INVALID  FILE TYPE SPECIFIED IN FOPTIONS (FSERR 9): The file type
     field  of the FOPEN file options is  not one of 0 (STD = standard
     file), 1 (KSAM file), 2 (RIO file), 4 (CIR = circular file), or 6
     (MSG = message file).  WORKAROUND: Correct the file type field.

10   INVALID  RECORD  SIZE  SPECIFICATION (FSERR 10):  The record size
     requested  was  more  than  32,767 bytes.   WORKAROUND: Specify a
     smaller record size.

11   INVALID  RESULTANT  BLOCK  SIZE (FSERR 11):  If the user requests
     were  honored, the block size (BLOCK FACTOR * RECORD SIZE) of the
     resultant  file  would be greater  than 32,767 bytes. WORKAROUND:
     Specify a smaller record size or blocking factor.

12   RECORD NUMBER OUT OF RANGE (FSERR 12): The user passed a negative
     record  number to the FPOINT, FREADDIR, or FWRITEDIR intrinsic --
     this is illegal.  WORKAROUND: Correct your program.

22   SOFTWARE  TIME-OUT  (FSERR  22): The user tried  to read an empty
     message  file  or  write to a full  message file, an action which
     would  cause the user to be impeded  until the file was no longer
     empty  or  full,  respectively  (see  MPE IV  INTRINSICS MANUAL).
     However,  a time out was set with the FCONTROL intrinsic (mode 4)
     and the request timed out before it could be honored. WORKAROUND:
     Do  not  set  the  time  out,  or ensure that  the request can be
     serviced before it times out.

26   TRANSMISSION  ERROR  (FSERR  26): Hardware  failure.  WORKAROUND:
     Call your CE.

30   UNIT FAILURE (FSERR 30): Hardware failure.  WORKAROUND: Call your
     CE.

40   OPERATION  INCONSISTENT  WITH ACCESS TYPE  (FSERR 40): The access
     type  specified at FOPEN time does not permit this operation; for
     instance,  an FWRITE is not permitted  when a file is opened with
     ACC=IN.   WORKAROUND: Specify an access  type at FOPEN time which
     permits this operation, or do not perform the operation at all.

41   OPERATION INCONSISTENT WITH RECORD TYPE (FSERR 41): It seems that
     this  error should never show up and  is merely a leftover from a
     previous version of MPE.

42   OPERATION  INCONSISTENT WITH DEVICE TYPE  (FSERR 42): The program
     tried  to  execute  an  operation  that is  incompatible with the
     device  that  it is trying to perform  it on; for instance, it is
     trying to read the line printer or change the baud rate of a disc
     drive.  WORKAROUND: Do not execute the operation.

43   WRITE  EXCEEDS  RECORD  SIZE  (FSERR 43): An  attempt was made to
     write  a record that would not  fit in the destination file, e.g.
     trying  to  write  a  100-byte  record into a  file with a record
     length  of 80 bytes.  WORKAROUND:  Change the file's record size,
     change  the length of the record to  be written, or open the file
     with the Multi-Record (MR) access option.

44   UPDATE AT RECORD ZERO (FSERR 44): The FUPDATE intrinsic (which is
     equivalent  to  the COBOL REWRITE statement)  was called with the
     record  pointer  at record 0, which  indicates that no record has
     been  read  and therefore no record  can be updated.  WORKAROUND:
     Call FPOINT or FREAD before the FUPDATE call.

45   PRIVILEGED FILE VIOLATION (FSERR 45): A program attempted to open
     a  privileged file (one with a  negative file code; e.g. an IMAGE
     file)  while  specifying  a  filecode  not  equal  to  the file's
     filecode  or  while  not  in privileged  mode.  WORKAROUND: Enter
     privileged mode before the call or specify the correct filecode.

46   OUT OF DISC SPACE (FSERR 46): The device class on which this file
     resides  (if  this error occurs at  extent allocation time) or is
     requested  to reside (if this error occurs at file creation time)
     does  not  have enough contiguous disc  space to accommodate this
     file;  i.e.  if NUMEXTS is the number  of extents to be allocated
     and  EXTSIZE is the size (in  sectors) of one extent, this device
     class  does not have NUMEXTS contiguous chunks of EXTSIZE sectors
     each.   WORKAROUND:  Move the file to  another, less full, device
     class,  decrease the requested file  size, or decrease the extent
     size by increasing the number of extents in the file.

47   I/O  ERROR  ON FILE LABEL (FSERR 47):  The internal file label of
     this  file cannot be accessed.  Most  likely, the file is totally
     clobbered and will return INVALID FILE LABEL (FSERR 108) when it
     is subsequently accessed.  WORKAROUND: None.

48   OPERATION  INVALID DUE TO MULTIPLE FILE ACCESS (FSERR 48): One of
     the  following  conditions  is true: 1) The  program is trying to
     purge  (i.e. close with disposition DEL) a file that is currently
     loaded  or  being  stored/restored,  2) The program  is trying to
     rename  (with the FRENAME intrinsic) a file that it does not have
     exclusive  access  to,  or 3) The program  is trying to open with
     LOCK  access  a  file  that  someone else has  opened with NOLOCK
     access  or  vice  versa.  WORKAROUND: 1) Don't  purge the file or
     wait  for the file to become purgeable again, 2) Don't rename the
     file  or open the file with EXC  access, or 3) Open the file with
     LOCK  or NOLOCK access (whichever the  other program has the file
     open with).

49   UNIMPLEMENTED  FUNCTION  (FSERR  49):  The  program  specified an
     invalid  parameter value in a file  system intrinsic call; e.g. a
     disposition of 5, 6, or 7 at FCLOSE time or a file type of RIO on
     pre-Athena  systems  (ones  which  do  not  support  RIO  files).
     WORKAROUND:  Correct your program.

50   NONEXISTENT  ACCOUNT  (FSERR  50): An attempt was  made to open a
     file  in  an  account  which  was  not configured  in the system.
     WORKAROUND:  Correct the filename or build the account.

51   NONEXISTENT  GROUP (FSERR 51): An attempt was made to open a file
     in  a group which was not  configured in the system.  WORKAROUND:
     Correct the filename or build the group.

52   NONEXISTENT  PERMANENT  FILE  (FSERR 52): An  attempt was made to
     open  a  file  which  does  not  exist.  WORKAROUND:  Correct the
     program or build the file.

53   NONEXISTENT  TEMPORARY FILE (FSERR 53): The program tried to open
     a  temporary file which does  not exist.  WORKAROUND: Correct the
     program or build the file.

54   INVALID  FILE  REFERENCE (FSERR 54): The  program tried to open a
     file  whose filename was invalid;  for instance, the file, group,
     or  account  name  was longer than 8  characters long, an invalid
     system-defined  file  was  specified  (e.g.  $XYZZY), or  no file
     equation was found for a back-referenced file (e.g. *MANSION with
     no  file  equation  for  file MANSION).   WORKAROUND: Correct the
     filename specified.

55   DEVICE  UNAVAILABLE  (FSERR  55):  The  program  tried to  open a
     message  file  that  was  already  opened for  MULTI (not GMULTI)
     access  by  another job/session.  WORKAROUND:  Wait for the other
     job/session  to finish, or rewrite the  other program to open the
     file NOMULTI or GMULTI.

56   INVALID  DEVICE  SPECIFICATION  (FSERR 56): The  device number or
     device class on which the file was to be opened is not configured
     on the system.  WORKAROUND: Correct the program.

57   OUT  OF  VIRTUAL  MEMORY  (FSERR 57): The  buffer size (NUMBER OF
     BUFFERS * RECORD SIZE * BLOCKING FACTOR) of the file to be opened
     exceeds  8,192  words  (or  14,000 words starting  with the D MIT
     version  of MPE).  WORKAROUND: Decrease the number of buffers (by
     specifying BUF=1 on a :FILE equation, for instance), decrease the
     record  size of the file, or  decrease the blocking factor of the
     file.

58   NO  PASSED  FILE  (FSERR  58):  The  program  attempted  to  open
     $OLDPASS,  but no $OLDPASS file  exists.  WORKAROUND: Correct the
     program or build a $OLDPASS file.

60   GLOBAL  RIN UNAVAILABLE (FSERR 60): The program requested dynamic
     locking  at file open time,  but the RIN (Resource Identification
     Number)  necessary  for  dynamic  locking could  not be obtained.
     WORKAROUND:  Free  some global RINs  (with the :FREERIN command),
     file  RINs  (by closing files opened  with LOCK access), open the
     file with NOLOCK access, or enlarge the RIN table.

61   OUT OF GROUP DISC SPACE (FSERR 61): The program tried to allocate
     more  disc space than is allowed for a given group; e.g. it tried
     to build a 10,000-sector file in a group which already had 95,000
     sectors  and was limited to 100,000 sectors. WORKAROUND: Decrease
     the  amount of disc space used by files in that group (by purging
     or  squeezing  files) or ask the  account manager to increase the
     group disc space limit.

62   OUT  OF  ACCOUNT  DISC  SPACE  (FSERR  62): The  program tried to
     allocate  more  disc  space than is permitted  for the account in
     which  it tried to allocate  it.  WORKAROUND: Decrease the amount
     of  disc  space  used  by  files  in that account  (by purging or
     squeezing  files)  or  ask  the  system  manager to  increase the
     account disc space limit.

64   USER  LACKS MULTI-RIN CAPABILITY (FSERR  64): The program was not
     :PREPed  with MR (Multi-Rin) capability, yet tried to lock a file
     when  another  file (or RIN) was  already locked by that program.
     WORKAROUND: :PREP the program with MR capability or do not try to
     lock a file when you have already locked another one.

71   TOO  MANY FILES OPEN (FSERR 71):  The program attempted to open a
     file,  but there was not enough room in the system area (PCBX) of
     the   program's   stack   for  the  information  for  that  file.
     WORKAROUND: Close some files which are no longer necessary before
     trying the open, or run the program with the ;NOCB keyword in the
     :RUN command.

72   INVALID  FILE  NUMBER  (FSERR 72): An attempt  was made to access
     (e.g. read or write) a file that had not been opened or that is a
     privileged  file; for instance, a read was requested against file
     number  10,  but  no  file  file number 10  was open. WORKAROUND:
     Correct  your  program or enter privileged  mode before trying to
     access the file (if the file is privileged).

73   BOUNDS  VIOLATION (FSERR 73): You are attempting to read or write
     more  data  than  could  fit  into your I/O  buffer (e.g. you are
     trying  to  read  100 words into  an 80-word array).  WORKAROUND:
     Decrease  the length of the data you  are trying to read or write
     or enlarge your program's I/O buffer.

74   NO  ROOM LEFT IN STACK SEGMENT FOR ANOTHER FILE ENTRY (FSERR 74):
     When  trying to do an FOPEN, the file system tried to expand your
     stack  data segment to have room  for the information on the file
     being opened; this caused an error because it would make the data
     segment too large. WORKAROUND: Often, this does not mean that you
     were using too much stack space AT THE TIME OF THE FOPEN. Rather,
     you  might have used a lot of stack space earlier, and that space
     was  not  deallocated  (since  your  stack data  segment is never
     automatically  shrunk by the system). If you call "ZSIZE (0)" (to
     shrink the stack data segment to only the amount you're currently
     using)  immediately  before the FOPEN call,  the problem may very
     well  go away. If it doesn't, you might consider using less stack
     space or running your program with ;NOCB.

90   EXCLUSIVE  VIOLATION:  FILE BEING ACCESSED  (FSERR 90): Exclusive
     access  was requested to a file  which is already being accessed;
     thus,  exclusive  access cannot be  granted.  WORKAROUND: Specify
     SHR  (shared) or EAR (exclusive - allow read) access when opening
     the file or wait for the accessor to close the file.

91   EXCLUSIVE  VIOLATION: FILE BEING ACCESSED EXCLUSIVELY (FSERR 91):
     Access   was   requested  to  a  file  which  is  being  accessed
     exclusively  by  some  other  user.   WORKAROUND:  Wait  for  the
     accessor to close the file.

92   LOCKWORD  VIOLATION (FSERR 92): An invalid lockword was specified
     at file open time or when the file system prompted the user for a
     lockword.   WORKAROUND:  Specify a correct  lockword or remove or
     change the lockword on the disc file.

93   SECURITY VIOLATION (FSERR 93): Permitting the user to access this
     file  in  the  specified  access  mode would be  a breach of file
     security.   WORKAROUND:  Change the access  mode specified in the
     program  to  one which is permitted or  ask the file's creator to
     :RELEASE or :ALTSEC the file.

94   USER IS NOT CREATOR (FSERR 94): An attempt was made to :RENAME or
     FRENAME  a  file  by  someone  other  than  the  file's  creator.
     WORKAROUND:  Do  not  perform  the  :RENAME  or FRENAME,  ask the
     creator  of the file to do the  :RENAME, or (if you have read and
     write  access to the file and are a user of MPEX/3000) use MPEX's
     %RENAME command.

96   DISC I/O ERROR (FSERR 96): Hardware failure.  Call your CE.

100  DUPLICATE  PERMANENT FILE NAME (FSERR  100): The program tried to
     save  (close with SAVE disposition) a  new or temporary file as a
     permanent  file,  but  a  permanent  file with  that name already
     exists.  WORKAROUND: Purge the other file with that name.

101  DUPLICATE  TEMPORARY FILE NAME (FSERR  101): The program tried to
     save  as  a  temporary  file (close with  TEMP disposition) a new
     file,  but  a  temporary  file  with  that  name  already exists.
     WORKAROUND: Purge the other temporary file with that name.

102  DIRECTORY I/O ERROR (FSERR 102): The directory (or part of it) is
     clobbered.  You're in big trouble.  WORKAROUND: None.

103  PERMANENT  FILE DIRECTORY OVERFLOW (FSERR  103): There is no more
     room  in the system file directory for this file (the system file
     directory  typically allows approximately 1,200 files per group).
     WORKAROUND:  Purge  some  of the files in  the group in which you
     wish to build the file.

104  TEMPORARY  FILE DIRECTORY OVERFLOW (FSERR  104): There is no more
     room  in  your  job  / session temporary  file directory for this
     file.   WORKAROUND:  Purge  some  temporary files  or :RESET some
     :FILE equations or :CRESET some :CLINE equations.

105  BAD  VARIABLE  BLOCK  STRUCTURE (FSERR 105):  The variable record
     length  file  being  accessed  has an  inconsistent structure, or
     would  have  an inconsistent structure if  this access were to go
     through  (if  you  are  writing  NOBUF).  WORKAROUND:  If you are
     writing NOBUF, correct your program; otherwise, none.

106  EXTENT SIZE EXCEEDS MAXIMUM (FSERR 106): The program attempted to
     build a file which would have extents larger than 65,534 sectors,
     the  maximum  permitted.   WORKAROUND:  Increase  the  number  of
     extents in the file or decrease the extent size by decreasing the
     record size or file limit of the file.

107  INSUFFICIENT  SPACE  FOR  USER  LABELS  (FSERR 107):  The maximum
     number  of  user labels for a  file is 254.  WORKAROUND: Decrease
     the number of user labels requested by the program.

108  INVALID  FILE LABEL (FSERR 108): The file is inaccessible because
     the  file  is  invalid (probably destroyed  - and unrecoverable).
     WORKAROUND: None.

109  INVALID  CARRIAGE CONTROL (FSERR 109): The  program tried to do a
     write  with  a  CCTL code of 1 (embedded  CCTL) but with a buffer
     length  of  0;  or,  the  program  attempted  an FCONTROL  mode 1
     (transfer  CCTL code) with a parameter of 1.  WORKAROUND: Correct
     the program.

110  ATTEMPT  TO  SAVE  PERMANENT  FILE  AS TEMPORARY  (FSERR 110): An
     attempt  was made to close a permanent file with temporary (TEMP)
     disposition: this is illegal.  WORKAROUND: Correct the program.

148  INACTIVE  RIO RECORD (FSERR 148):  An FPOINT, FREADDIR, or FSPACE
     positioned  the  record  pointer at an inactive  record in an RIO
     (Relative I/O) file.  WORKAROUND: None necessary.

149  MISSING  ITEM  NUMBER  OR  RETURN-VARIABLE  (FSERR 148):  An item
     number  was  specified  without a corresponding  variable or vice
     versa  in  an FFILEINFO intrinsic  call.  WORKAROUND: Correct the
     program.

150  INVALID  ITEM NUMBER (FSERR 150): An  item number specified in an
     FFILEINFO  intrinsic  call  is invalid.   WORKAROUND: Correct the
     program.

ACKNOWLEDGEMENTS

Praises and kudos GOTO:

Robert  Saunders  (of  the  HP  lab)  for much  important information;
Vladimir   Volokh   (of   VESOFT,  Inc.),  Robert  Green  (of  ROBELLE
Consulting),  Joe  Berry (of the Los  Angeles Airport Area SE Office),
and  many others for comments, questions, criticisms, suggestions, and
overall moral support.

Go to Adager's index of technical papers