From: g_dotzel@tron.gun.de (Guenter Dotzel)
Newsgroups: comp.lang.modula2
Subject: Re: Of Mice & Modula-2/Random Files
Organization: TRON Public Mailbox, Neurath, Germany
Date: Mon, 31 Aug 92 09:37:00 +0200

MWALLS%NMSU.EDU@USENET.ZER wrote on Sat, 08-29-1992 at 21:01 h
under the subject 'Of Mice & Modula-2':

>       In BASIC you can open a Random (or Indexed) file by using the
> following: ...
>       where record size is the number of bytes set aside per record.
> 
>       Then, at any time while it is open, one can get or put information
> into a specific point in the file by using: ...
>       (where rec is the record to be accessed)
> 
> Is there a way to do this in Modula-2.  This is probably a stupid
> question, but I really don't know.
> 

Yes, you *can* do that quite easily and portable in Modula-2 (see example
below), provided you have the ISO M2 Std Lib available.

***** Note,
***** We port this library to any popular platform on request, so
***** if anybody needs this library for his/her favorite compiler/platform,
***** please send email

----cut here------>
  To Guenter Dotzel, email/internet: g_dotzel@ame.nbg.sub.org
  
  We are using the _______ V___ Modula-2 compiler under ______ V____
  We are using the _______ V___ Oberon-2 compiler under ______ V____
  and would be happy to see an implementation of the ISO M2 Std Lib
  for that compiler/system.
  Best wishes, ....
<----cut here------

I'll post a summary/statistic for the requests received in Oct-92.
Also I'll provide a portation schedule for the top-most popular platforms.

Random File Access:
I've already posted the following random file access example and test programs
some months ago, but maybe its worth to post 200 lines of code again:

MODULE TestPos;
(* ISO Modula-2 I/O-Library test program for random file access.
   This program runs under VAX/VMS using ModulaWare's compiler kit MVR V3.20.
   This program also runs on the Mac with using p1 compiler/ISO M2 Std Lib.
   An Oberon-2 version of this program is contained in a follow up message.
   
   (c) 1992 by ModulaWare GmbH, written by Elmar Baumgart 10-Mar-1992,
     27-Mar-1992 (skip multiple blank lines)

  - If at the start of the program the hash table file DATA does not
    exist, a new file is created and initialized.
    Otherwise, the existing file called DATA is used.
  - The program reads two files called INSERT and SEARCH. Both files
    may have any text file format.
  - inserts token strings from file INSERT into a hash-table file DATA
  - searches token strings from file SEARCH in file DATA
*)
FROM ProgramArgs IMPORT IsArgPresent, ArgChan;
IMPORT STextIO, SWholeIO;
FROM IOResult IMPORT ReadResult, ReadResults;
FROM TextIO IMPORT WriteString, ReadToken, SkipLine, ReadString;
IMPORT SeqFile;
FROM RndFile IMPORT OpenClean, OpenOld, read, write, text, binary, 
  ChanId, OpenResults, Close, FilePos, SetPos, NewPos, StartPos;
FROM RawIO IMPORT Write, Read;

CONST
  ITEMLENGTH = 40;
  ITEMS = 3877;  (*prime*)   (*creates ITEMS * ITEMLENGTH hash-table on disk*)
  DATA = 0; INSERT = 1; SEARCH = 2;

TYPE
  WCproc = PROCEDURE(CARDINAL, CARDINAL);
  WSproc = PROCEDURE(ARRAY OF CHAR);
  ItemType = ARRAY[0..ITEMLENGTH-1] OF CHAR;
  fnam = ARRAY[0..255] OF CHAR;
  Args = ARRAY[DATA..SEARCH] OF fnam;

VAR
  WC: WCproc; WS: WSproc; NL: PROC;
  data, ins, sea: ChanId;
  free: INTEGER;
  file: Args;
  startPos: FilePos;

PROCEDURE CalcPos(Key: CARDINAL): FilePos;
BEGIN
  RETURN NewPos(data, Key, ITEMLENGTH, startPos);
END CalcPos;

PROCEDURE CalcKey(item: ARRAY OF CHAR): CARDINAL;
VAR len, i, key: CARDINAL;
BEGIN
  len:= LENGTH(item);
  key:= 1;
  IF len > 0 THEN
    FOR i:= 0 TO len-1 DO
      key:= (key * (ORD(item[i]) MOD 26 + 1)) MOD ITEMS;
    END;
  END;
  RETURN key;
END CalcKey;

PROCEDURE EQUAL(old, new: ARRAY OF CHAR): BOOLEAN;
VAR i: INTEGER; eq: BOOLEAN; len: INTEGER;
BEGIN
  i:= 0; eq:= TRUE; len:= LENGTH(new);
  WHILE (i < len) AND eq DO
    eq:= old[i] = new[i]; INC(i);
  END;
  RETURN eq;
END EQUAL;

PROCEDURE Insert(item: ARRAY OF CHAR);
VAR Key, offset: CARDINAL; atPos: ItemType; eq: BOOLEAN;
BEGIN
  IF free > 0 THEN
    Key:= CalcKey(item);
    SetPos(data, CalcPos(Key));
    Read(data, atPos); offset:= 0; 
    eq:= EQUAL(atPos, item);
    WHILE NOT eq AND (atPos[0] <> 0C) AND (offset < ITEMS) DO
      IF ReadResult(data) = endOfInput THEN SetPos(data, startPos);
      END;
      Read(data, atPos);
      eq:= EQUAL(atPos, item); INC(offset);
    END;
    IF eq THEN WS('already inserted');
    ELSE
      SetPos(data, CalcPos((Key + offset) MOD ITEMS));
      Write(data, item);
      WS('        inserted');
      DEC(free);
    END;
    WC(offset, 4); WS(' "'); WS(item); WS('"'); NL;
  ELSE WS('-table full-'); NL;
  END;
END Insert;

PROCEDURE Search(item: ARRAY OF CHAR);
VAR Key, offset: CARDINAL; atPos: ItemType; eq: BOOLEAN;
BEGIN
  Key:= CalcKey(item);
  SetPos(data, CalcPos(Key));
  Read(data, atPos); offset:= 0; 
  eq:= EQUAL(atPos, item);
  WHILE NOT eq AND (atPos[0] <> 0C) AND (offset < ITEMS) DO 
    IF ReadResult(data) = endOfInput THEN SetPos(data, startPos);
    END;
    Read(data, atPos);
    eq:= EQUAL(atPos, item); INC(offset);
  END;
  IF offset >= ITEMS THEN eq:= FALSE;
  END;
  IF eq THEN WS('    found');
  ELSE WS('not found');
  END;
  WC(offset, 4); WS(' "'); WS(item); WS('"'); NL;
END Search;

PROCEDURE InsertIt;
VAR item: ItemType;
BEGIN
  SeqFile.OpenRead(ins, file[INSERT], text, ores);
  IF ores <> opened THEN WS('failed to open '); WS(file[INSERT]); NL;
  ELSE
    WS('Inserting token strings from file '); WS(file[INSERT]); NL;
    ReadToken(ins, item);
    WHILE ReadResult(ins) <> endOfInput DO
      Insert(item);
      ReadToken(ins, item);
      WHILE ReadResult(ins) = endOfLine DO 
        SkipLine(ins);
        ReadToken(ins, item);
      END;
    END;
    SeqFile.Close(ins);
  END;
END InsertIt;

PROCEDURE SearchIt;
VAR item: ItemType;
BEGIN
  SeqFile.OpenRead(sea, file[SEARCH], text, ores);
  IF ores <> opened THEN WS('failed to open '); WS(file[SEARCH]); NL;
  ELSE
    WS('Searching token strings from file '); WS(file[SEARCH]); NL;
    ReadToken(sea, item);
    WHILE ReadResult(sea) <> endOfInput DO
      Search(item);
      ReadToken(sea, item);
      WHILE ReadResult(sea) = endOfLine DO 
        SkipLine(sea);
        ReadToken(sea, item);
      END;
    END;
    SeqFile.Close(sea);
  END;
END SearchIt;

PROCEDURE TestIt;
BEGIN
  InsertIt; NL;
  SearchIt;
END TestIt;

VAR 
  ores: OpenResults;
  empty: ItemType;
  i: INTEGER; wrongArgs: BOOLEAN;

BEGIN
  free:= ITEMS;
  FOR i:= 0 TO ITEMLENGTH-1 DO empty[i]:= 0C;
  END;
  WC:= SWholeIO.WriteCard; WS:= STextIO.WriteString; NL:= STextIO.WriteLn;
  i:= 0;
  WHILE (ReadResult(ArgChan()) <> endOfInput) AND (i <= SEARCH) DO
    ReadToken(ArgChan(), file[i]); INC(i);
  END;

  IF (ReadResult(ArgChan()) <> endOfInput) AND (i > SEARCH) THEN
    OpenOld(data, file[DATA], read + write + binary, ores);
    IF ores = noSuchFile THEN
      OpenClean(data, file[DATA], read + write + binary, ores); 
      IF ores = opened THEN
        FOR i:= 1 TO ITEMS DO Write(data, empty);
        END;
        startPos:= StartPos(data);
        TestIt;
        Close(data);
      ELSE WS('data file not present, create failed'); NL;
      END;
    ELSIF ores = opened THEN
      startPos:= StartPos(data);
      i:= 0; Read(data, empty);
      WHILE ReadResult(data) <> endOfInput DO INC(i); Read(data, empty);
        IF empty[0] <> 0C THEN DEC(free);
        END;
      END;
      IF i <> ITEMS THEN WS(file[DATA]); WS(' is of illegal size'); NL;
      ELSE TestIt; 
      END;
      Close(data);
    ELSE WS('data file present, access failed'); NL;
    END;
    WC(free, 1); WS(' entries free in '); WS(file[DATA]); NL;
  ELSE WS('  TESTPOS hash-file insert-file search-file '); NL;
  END;
END TestPos.

Guenter Dotzel, ModulaWare GmbH, Wilhelmstr. 17A, D-W 8520 Erlangen/F.R.Germany
Modula-2 & Oberon-2 Compiler Manufactur (VAX/VMS, OS/2 and Unix-Platforms)
Tel. +49 (9131) 208395, Fax +49 (9131) 28205.
E-mail/Internet: 100023.2527@compuserve.com
                 g_dotzel@ame.nbg.sub.org

Newsgroups: comp.lang.modula2