dubois@uwmacc.UUCP (Paul DuBois) (03/17/86)
Program __StreamLib;
(*
__StreamLib - set of routines for treating TEXT or WORD files as a
stream of characters or lines. To use:
Call InitStream() first. Call OpenStream() to open a stream. If it
returns noErr, a stream was opened ok. To read characters, call
StreamGetC(). This returns the next character or -1 on end of stream
(and closes the stream). To read lines, call StreamGetS(buf). This
fills up the buffer passed to it and returns a pointer to it, or nil
on end of stream (and closes the stream). To close a stream early,
call CloseStream().
There are also corresponding routines for reading TEXT files only, or
WORD files only. See below. These are useful if you want to end up
with less code linked into your program.
To compile this library:
Select Batch... from the Options Menu
Select Compile Same (or Compile...) for Step 1).
Select Combine Same for Step 2).
Click OK.
Version 1.0 11 March 1986
Paul DuBois
Wisconsin Regional Primate Research Center
1220 Capitol Court
University of Wisconsin-Madison
Madison, WI 53706
UUCP: {allegra, ihnp4, seismo}!uwvax!uwmacc!dubois
*)
Uses
__OSTraps
(*$U+*)
uOSIntf
uPackIntf
;
Link
__OSTraps
:;
Const
bufSiz = 1024;
wordStream = 1; (* stream is of 'WORD' file *)
textStream = 2; (* stream is of 'TEXT' file *)
paraLen = 16; (* paragraph information 16 bytes long *)
Type
IArray = Record (* Information array element *)
height: integer;
pagepos: integer;
parahand: ^^longint;
StPos: Union
St: byte; (* first byte is status *)
Pos: longint;
End;
DataLength: integer;
formats: Integer;
End;
Var
theReply: SFReply; (* SFGetFile reply record *)
f: Integer; (* input file reference number *)
streamOpen: Boolean; (* whether stream currently open *)
streamType: Integer; (* wordStream or textStream *)
(* vars needed for TEXT stream only *)
filBuf: Byte[bufSiz]; (* file buffer *)
fChars: LongInt; (* number of chars gotten on last read *)
fIndex: Integer; (* current position in filBuf *)
(* vars needed for WORD stream only *)
paraBuf: ^^Byte[1];
infoHand: ^^Iarray[1];
compressed: Boolean;
inPara: Boolean;
nParas: Integer; (* number of paragraphs *)
paraNum: Integer; (* current paragraph number *)
pIndex: Integer; (* index into paragraph *)
pChars: Integer; (* number of chars extracted from paragraph *)
firstHalf: Boolean; (* which half of current index char *)
pLen: Integer; (* number of chars in paragraph *)
needNib, (* For Decompr.: true = 2nd Nibble needed for ascii *)
nextAsc: boolean; (* For Decompr.: true = Two Nibbles needed *)
lastNib: byte; (* For Decompr.: Holds last nibble *)
lineLen: Integer; (* for line wrapping *)
(* -------------------------------- *)
(* miscellaneous utility routines *)
(* -------------------------------- *)
Proc _ffRead (f: Integer; b: PtrB; amount: LongInt);
{
amount := FSRead(f, @amount, b);
};
Proc _fMoveTo (f: Integer; amt: LongInt);
Var
result: Integer;
{
result := SetFPos (f, fsFromStart, amt);
};
(* --------------------------------- *)
(* stream init/open/close routines *)
(* --------------------------------- *)
(*
InitStream - must be called before you do anything else.
_FSOpen - like FSOpen (in __OSTraps), but has open mode parameter.
_OpenStream - Open a file for stream I/O. numTypes is either 1 or 2,
and typeList is either 'TEXT', 'WORD', or 'TEXTWORD'.
Return noErr if file opened OK, or fnOpnErr if not.
if OK, set streamType to wordStream or textStream,
according to the type of the opened file, and set
fileOpen true.
OpenTextSream - open 'TEXT' file stream. Returns:
noErr - file open OK
fnOpnErr - file not open
OpenWordSream - open 'WORD' file stream. Returns:
noErr - file open OK
fnOpnErr - file not open
mFulErr - couldn't get memory to read paragraph info into memory.
file not open.
OpenSream - open 'TEXT' or 'WORD' stream. Return values same as for
OpenWordS. Returns stream type in function argument sType.
_WordStreamInit does special handling necessary for 'WORD' stream:
Open the file, advance 252 + 12 bytes (to start of main document info
+ offset of Information Array. Then read position and length of Info
Array, move to it, read it in, and calculate number of paragraphs.
(16 bytes info per paragraph.) _TextStreamInit does TEXT stream
specific initialization.
*)
Proc InitStream ();
{
streamOpen := false;
lineLen := 65;
paraBuf := nil;
infoHand := nil;
};
Proc CloseStream ();
Var
result : OSErr;
{
if streamOpen then
{
result := FSClose (f);
streamOpen := false;
if streamType = wordStream then
{
if paraBuf <> nil then DisposHandle (paraBuf);
if infoHand <> nil then DisposHandle (infoHand);
};
};
};
Func _FSOpen (fName: PtrB; vRefNum: Integer;
refNum: ^Integer; mode: Integer): OSErr;
Var
p: ParamBlockRec;
{
p.ioNamePtr := fName;
p.ioVRefNum := vRefNum;
p.ioPermssn := mode;
p.ioVersNum := 0;
p.ioMisc := 0;
_FSOpen := PBOpen (p, false);
refnum^ := p.ioRefNum;
};
Func _OpenStream (numTypes: Integer; typeList: OSType): OSErr;
{
CloseStream (); (* close any currently open stream *)
_OpenStream := fnOpnErr;
streamOpen := false;
ToolBox ($A9EA, 100, 70, "", nil, numTypes, typeList, nil, @theReply, 2);
if theReply.good then
{
if _FSOpen(theReply.fName, theReply.vRefNum, @f, fsRdPerm) = noErr then
{
streamType := wordStream;
if theReply.fType = PtrL (" TEXT"+2)^ then
streamType := textStream;
streamOpen := true;
_OpenStream := noErr;
};
};
};
Proc _TextStreamInit ();
{
fChars := 0; (* set these to trigger a read on the first *)
fIndex := 0; (* call to TextStreamGetC() *)
};
Func _WordStreamInit (): OSErr;
Type
DocVars = Record
IApos: Longint;
IAlength: Integer;
End;
Var
docVars: DocVars;
{
paraBuf := NewHandle (0L); (* Will be used for reading in paragraphs *)
_fmoveto (f, 264L); (* 252 + 12 *)
_ffRead (f, docVars, LongInt (SizeOf (DocVars)));
_fMoveTo (f, docVars.IAPos);
infoHand := NewHandle (Longint (docVars.IALength));
if infoHand = nil then
{
CloseStream ();
_WordStreamInit := mFulErr;
}
else
{
HLock (InfoHand);
_ffRead (f, infoHand^, Longint (docVars.IALength));
HUnlock (InfoHand);
nParas := docVars.IALength/paraLen;
paraNum := -1;
inPara := false; (* not in any paragraph yet *)
_WordStreamInit := noErr;
};
};
Func OpenTextStream (): OSErr;
{
OpenTextStream := _OpenStream (1, " TEXT"+2);
_TextStreamInit ();
};
Func OpenWordStream (): OSErr;
Var
result: OSErr;
{
OpenWordStream := fnOpnErr;
if _OpenStream (1, " WORD"+2) = noErr then
OpenWordStream := _WordStreamInit ();
};
Func OpenStream (): OSErr;
{
OpenStream := fnOpnErr;
if _OpenStream (2, " TEXTWORD"+2) = noErr then
{
OpenStream := noErr;
case streamType of
wordStream: OpenStream := _WordStreamInit ();
textStream: _TextStreamInit ();
end;
};
};
Proc GetStreamInfo (reply: SFReply);
{
reply := theReply;
};
(*
'Get a character' routines
TextStreamGetC - get character from 'TEXT' stream.
WordStreamGetC - get character from 'WORD' stream.
StreamGetC - get character from stream.
*)
Func TextStreamGetC (): Integer;
Var
err: OSErr;
{
TextStreamGetC := -1;
if streamOpen = false then return;
if fIndex >= fChars then (* need to read in a new block *)
{
fChars := bufSiz;
err := FSRead (f, @fChars, filBuf);
if fChars = 0 then
{
CloseStream ();
return;
};
fIndex := 0;
};
TextStreamGetC := filBuf[fIndex];
++fIndex;
};
(*
_Decompress takes a nibble at a time of compressed text. If more
nibbles are needed to complete the next character, return -1, else
returns the character.
*)
Func _Decompress (b: Byte): Integer;
{
_Decompress := -1;
if needNib then (* Low half of ascii nibble is needed. *)
{
needNib := false;
_Decompress := (lastNib or b); (* Put the two halves together *)
}
else if nextasc then (* Two nibbles are needed *)
{
nextAsc := false;
lastNib := b << 4; (* Save this one as the high nibble *)
needNib := true; (* Need one more nibble *)
}
else if b = 15 then (* Nibble of 15 means the next char is ascii *)
nextAsc := true
else (* Add the nibble value to the English decompression *)
(* key (saved as Resource Type "STR " 700 in file) *)
(* to get the proper character *)
{
_Decompress := PtrB (++b + " etnroaisdlhcfp")^;
};
};
Func WordStreamGetC (): Integer; (* return -1 on EOF *)
Var
c: Integer;
offset: LongInt;
{
WordStreamGetC := -1;
if streamOpen = false then return;
if inPara = false then (* must read in next paragraph *)
{
loop (,,,)
{
if ++paraNum >= nparas then
{
CloseStream ();
return;
};
if infoHand^^[paranum].height <= 0 then continue;
(*
offset will contain the file offset to this paragraph's data. must
mask the high byte. Move to the paragraph, get its length, make
the pointer big enough, and read it in. (skip to next para if this
one is empty, though.)
compressed will be set true if the paragraph is compressed.
*)
offset := infoHand^^[paranum].stpos.pos and $00FFFFFF;
_fMoveTo (f, offset);
_ffRead (f, @plen, LongInt (SizeOf (Integer))); (* get length *)
if plen = 0 then continue;
SetHandleSize (paraBuf, LongInt (plen)); (* make big enough *)
if MemError () <> noErr then
{
paranum := nparas; (* force close and exit of loop *)
continue;
};
_ffRead (f, paraBuf^, LongInt (plen));
compressed := (infoHand^^[paranum].stpos.st >> 3) and 1;
inPara := true;
nextAsc := false;
needNib := false;
pIndex := 0; (* index into current paragraph *)
pChars := 0; (* chars extracted from current paragraph *)
firstHalf := true; (* use first half of current index char *)
break;
};
};
(*
At this point, know eitherthat we have a new non-empty paragraph, or
are still in the previous one.
*)
if !compressed then (* uncompressed *)
{
c := paraBuf^^[pchars];
}
else
{
loop (,,, c <> -1)
{
c := paraBuf^^[pIndex];
if firstHalf then
{
c := _Decompress (Byte (c >> 4));
}
else
{
c := _Decompress (Byte (c and $0f));
++pIndex; (* go to next char at next index *)
};
firstHalf := !firstHalf;
};
};
if ++pChars >= pLen then (* see if need new paragraph next time *)
inPara := false;
WordStreamGetC := c;
};
Func StreamGetC (): Integer;
{
case streamType of
wordStream: StreamGetC := WordStreamGetC ();
textStream: StreamGetC := TextStreamGetC ();
end;
};
(*
'Get a string' routines
TextStreamGetS - get string from 'TEXT' stream.
WordStreamGetS - get string from 'WORD' stream.
StreamGetS - get string from stream.
All return nil if no string obtained, otherwise a pointer to the argument.
*)
Func TextStreamGetS (s: StringPtr): StringPtr;
Var
c: Integer;
{
TextStreamGetS := nil;
if !streamOpen then return;
s[0] := 0; (* clear string *)
loop (,,,)
{
c := TextStreamGetC ();
if c = -1 then
break;
TextStreamGetS := s; (* got something, so NextLine succeeds *)
if c = 13 then break;
s[++s[0]] := c; (* add char to end *)
};
};
Func WordStreamGetS (s: StringPtr): StringPtr;
Var
c: Integer;
{
WordStreamGetS := nil;
if !streamOpen then return;
s[0] := 0; (* clear string *)
loop (,,,)
{
c := WordStreamGetC ();
if c = -1 then
break;
WordStreamGetS := s; (* got something, so NextLine succeeds *)
if (c = 13) or ((s[0] > lineLen) and (c = ' ')) then
break;
s[++s[0]] := c; (* add char to end *)
};
};
Func StreamGetS (s: StringPtr): StringPtr;
{
case streamType of
wordStream: StreamGetS := WordStreamGetS (s);
textStream: StreamGetS := TextStreamGetS (s);
end;
};
(*
Set wrap length (number of chars after which point the line will be
broken at the next space if a carriage return is not seen first). This
only affects TextStreamGetS (or StreamGetS when the current stream is
a WORD stream).
*)
Proc SetLineLen (len: Integer);
{
lineLen := len;
};
--
|
Paul DuBois {allegra,ihnp4,seismo}!uwvax!uwmacc!dubois --+--
|
|