maclab@reed.UUCP (S.Gillespie/Mac Dev. Lab) (11/16/85)
I am currently in the midst of writing an indexing program which
operates on MacWrite files. Needless to say, the first hurdle was
to figure out how to read the text portion of a MacWrite file:
the following source is an example of one way to do it.
Hope someone finds this helpful...(you should definitely get your
hands on the Mac Technical Note which describes the file format,
if you wish to do anything very involved).
Scott Gillespie
Reed College
{decvax, ucbvax, pur-ee, uw-beaver, masscomp, cbosg, aat,
mit-ems, psu-cs, uoregon, orstcs, ihnp4, uf-cgrl, ssc-vax}!tektronix
\
+--!reed!maclab
{teneron, ogcvax, muddcs, cadic, oresoft, grpwre, /
harvard, psu-cs, omen, isonvax, nsc-pdc}-------------+
-----------------------------------------------------------------
Program ReadMacWrite;
(*
Program to read the text portion of MacWrite Files,
and write those chars to the screen.
Written by
Scott Gillespie
Reed College
Portland, OR 97202
See Macintosh Technical Notes, and Supplement documents for a complete
description of the MacWrite file format.
The key procedures are:
ReadFile: Takes filename and volume ref, and outputs chars
to Procedure OutChar.
Decompress: Called by ReadFile for decompressed paragraphs.
This could certainly be made faster....
*)
Uses __OSTraps,
(*$U+*)
uMemtypes;
Link __Uniform, (* Uniform File I/O Calls *)
__IO, (* Built-in System Call interface *)
__SFNames, (* Turnkey Standard File library *)
__EasyMenus, (* Turnkey Menus library *)
__OSTraps :; (* Operating System Routines *)
Var
NeedNib, (* For Decompr.: True = 2nd Nibble needed for ascii *)
NextAsc: boolean; (* For Decompr.: True = Two Nibbles needed *)
LastNib: byte; (* For Decompr.: Holds last nibble *)
WrapCount: Integer; (* For screen wordwrapping *)
(* Decompress takes a nibble at a time of compressed text. If it
returns True, a character has been reconstructed, and is placed
in c^. If False, more nibbles are needed to complete next character *)
Function Decompress(b: byte; c: ^Integer): Boolean;
{
Decompress := False;
if neednib Then { (* Low half of ascii nibble is needed. *)
neednib := False;
c^ := (LastNib or b); (* Put the two halves together *)
Decompress := True; (* Character is ready *)
}
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 *)
c^ := ptrb(++b + " etnroaisdlhcfp")^;
Decompress := True;
};
};
(* ReadFile takes a name and volume reference number of a MacWrite
File, and outputs the text characters to 'OutChar' (further
below). *)
Procedure ReadFile(np: ptrb; vref: Integer);
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
amt: longint;
Buf: ^Byte[20];
press: Boolean;
off: Longint;
infohand: ^^Iarray[20];
f, count,i,c,j,d,k,len,ch,mods: integer;
DocVars: Record
IApos: Longint;
IAlength: Integer;
End;
{
Buf := NewPtr(0L); (* Will be used for reading in paragraphs *)
fopen(@f,np,0,vref); (* Open the file *)
fmoveto(f,252L); (* Move to the start of the main
document information.*)
fmove(f,12L); (* Move Offset of Information Array Position *)
ffread(f,DocVars,6L); (* Read in position and length of Info. Array *)
fmoveto(f,DocVars.IAPos); (* Move to start of information Array *)
(* Read in the information array (could be big...) *)
InfoHand := NewHandle(Longint(DocVars.IALength));
Hlock(InfoHand);
ffread(f,InfoHand^,Longint(DocVars.IALength));
Hunlock(InfoHand);
Count := DocVars.IALength/16; (* 16 is size of Paragraph Array element.
Length/16 = Number of paragraphs
*)
(* Now, loop through each paragraph. If it is not text, go on to
the next one. If it is not compressed, just write it out, otherwise
decompress *)
loop(count,i:=0,,++i=count) {
CheckKey(@ch,@mods); (* Hitting the <Enter> key aborts the listing *)
If (ch = 3) Then
Break;
if InfoHand^^[i].height <= 0 Then Continue; (* Not a text paragraph *)
(* If the text is compressed, press will be true *)
press := (InfoHand^^[i].stpos.st >> 3) and 1;
(* Off will contain the file offset to this paragraph's data *)
Off := InfoHand^^[i].stpos.pos and $00FFFFFF; (* clear status byte *)
fMoveTo(f,off); (* Move to start of paragraph data *)
fgetint(f,@len); (* First two bytes specify the data length *)
SetPtrSize(buf,longint(len)); (* Make the pointer big enough *)
(* Now, we read in the data. If this is compressed data, too
many characters will be read in, but that's o.k., because the
decompression loop just ignores the extras *)
ffread(f,buf,longint(len));
If !press Then
(* Paragraph is straight text: just output *)
loop(len,j:=0,,++j=len)
Outchar(Integer(buf^[j]))
Else (* Initialize decompression vars to default, then
send a nibble at a time to 'Decompress' procedure.
If Decompress is True, output the character (d) *)
loop(len,NextAsc:=False;NeedNib:=False;j:=0;k:=0,++k,) {
If Decompress(buf^[k] >> 4 , @d) then {
Outchar(d);
If ++j>=len then break; (* len specifies how many characters
should be read in, so when we
have that many, we're done *)
};
If Decompress(buf^[k] and Byte($0F) , @d) then {
Outchar(d);
If ++j>=len then break;
};
};
};
Disposptr(Buf);
Disposhandle(infohand);
fclose(f);
};
(* ffread is a lazy man's fread.... *)
Procedure ffread(f: integer; b: ptrb; amt: longint); { fread(f,b,@amt) };
(* Outchar outputs a character, in this case to the screen. Although
you can obviously modify this to output to another file, or to
memory. A simple word wrap scheme is implemented here: if there
are >60 characters on the line, do a wordwrap at the next space *)
Proc OutChar(c: integer);
{
Writechar(c);
If c=13 then {
Writechar(10);
WrapCount := 0;
};
If ++WrapCount > 60 Then
If c=32 Then {
Writeln();
WrapCount := 0;
};
};
(* NewFile() puts up a standard file dialog box, allowing user to
select a file *)
Proc NewFile();
Var
vref, good : Integer;
name: ptrb;
{
ngetfile(100,70,@name," WORD"+2,1,@vref,@good);
if good then
ReadFile(name,vref);
Writeln();
Writeln();
Writeln();
};
Proc _Init();
{
InitEasyMenus();
AddMenu(1000,"Read");
AddItem(1000,"Open...");
WrapCount := 0;
};
Proc _Halt();
{
HaltEasyMenus();
};
Proc _Menu(id,item: integer);
{
if id=1000 Then
if item=1 Then
NewFile();
};
(* End of ReadMacWrite.src *)