Contributor: GAYLE DAVIS              

{$V-,S-,I-}
{$M 16384,0,355360}   { leave some memory for PKZIP !!! }


{ By POPULAR Request .................
  this SIMPLE program let's you read SWAG files and CONVERT them to QWK
  format readable by many of the popular MAIL readers out there.  I
  tested it with OLX by MUSTANG.  It should would with the others as well.

  WARNING ...  Many QWK mail readers are limited in the amount of text
  that can be contained in one message.  SEVERAL of the SWAG files exceed
  what can be read !!  Therefore, you will NOT be able to read all of these.
  Your mail reader program will truncate them.  This was an interesting
  exercise anyway, and shows how QWK mail packets can be created.

  Gayle Davis
  November, 1993  }

USES
  Dos, Crt;

CONST
     ControlHdr : ARRAY [1..11] OF STRING [30] = (

 {1} 'SOURCEWARE ARCHIVAL GROUP',
 {2} 'Goshen',
 {3} '875-8133',
 {4} 'Gayle Davis',
 {5} '99999,SWAG',
 {6} '11-03-1993,04:41:37',
 {7} 'SWAG Genius',
 {8} '',     { QMAIL Menu name ???                 }
 {9} '0',    { allways ZERO ???                    }
{10} '0',    { total number of messages in package }
{11} '56');  { number of conferences-1 here        }
             { next is 0 , then first conference   }

TYPE

  BlockArray   = ARRAY [1..128] OF CHAR;
  CharArray    = ARRAY [1..6] OF CHAR;  { to read in chunks }
  ControlArray = ARRAY [1..200] OF STRING [20];
  bsingle      = array [0..4] of byte;

  MSGDATHdr = RECORD  { ALSO the format for SWAG files !!! }
    Status   : CHAR;
    MSGNum   : ARRAY [1..7] OF CHAR;
    Date     : ARRAY [1..8] OF CHAR;
    Time     : ARRAY [1..5] OF CHAR;
    UpTO     : ARRAY [1..25] OF CHAR;
    UpFROM   : ARRAY [1..25] OF CHAR;
    Subject  : ARRAY [1..25] OF CHAR;
    PassWord : ARRAY [1..12] OF CHAR;
    ReferNum : ARRAY [1..8] OF CHAR;
    NumChunk : CharArray;
    Alive    : BYTE;
    LeastSig : BYTE;
    MostSig  : BYTE;
    Reserved : ARRAY [1..3] OF CHAR;
  END;

CONST

     PKZIP   : PathStr = 'PKZIP.EXE';

VAR

  SWAGF,
  QWKF        : FILE;
  ControlF    : TEXT;

  SavePath,
  SwagPath,
  SWAGFn,
  MsgFName    : PATHSTR;

  TR          : SearchRec;

  ConfNum,
  Number      : WORD;

  MSGHdr      : MSGDatHdr;
  ch          : CHAR;
  count       : INTEGER;
  chunks      : INTEGER;
  ControlVal  : ControlArray;
  ControlIdx  : BYTE;
  WStr        : STRING;

FUNCTION TrimL (InpStr : STRING) : STRING; ASSEMBLER;
ASM
      PUSH   DS
      LDS    SI, InpStr
      XOR    AX, AX
      LODSB
      XCHG   AX, CX
      LES    DI, @Result
      INC    DI
      JCXZ   @@2

      MOV    BL, ' '
      CLD
@@1 :  LODSB
      CMP    AL, BL
      LOOPE  @@1
      DEC    SI
      INC    CX
      REP    MOVSB

@@2 :  XCHG   AX, DI
      MOV    DI, WORD PTR @Result
      SUB    AX, DI
      DEC    AX
      STOSB
      POP    DS
END;

FUNCTION TrimR (InpStr : STRING) : STRING;

VAR i : INTEGER;

BEGIN
   i := LENGTH (InpStr);
   WHILE (i >= 1) AND (InpStr [i] = ' ') DO
      i := i - 1;
   TrimR := COPY (InpStr, 1, i)
END;

FUNCTION TrimB (InpStr : STRING) : STRING;

BEGIN
 TrimB := TrimL (TrimR (InpStr) );
END;

FUNCTION IntStr (Num : LONGINT; Width : BYTE; Zeros : BOOLEAN) : STRING;
{ Return a string value (width 'w')for the input integer ('n') }
  VAR
    Stg : STRING;
  BEGIN
    STR (Num : Width, Stg);
    IF Zeros THEN BEGIN
    FOR Num := 1 TO Width DO IF Stg [Num] = #32 THEN Stg [Num] := '0';
    END ELSE Stg := TrimL (Stg);
    IntStr := Stg;
  END;

FUNCTION NameOnly (FileName : PathStr) : PathStr;
{ Strip any path information from a file specification }
VAR
   Dir  : DirStr;
   Name : NameStr;
   Ext  : ExtStr;
BEGIN
   FSplit (FileName, Dir, Name, Ext);
   NameOnly := Name;
END {NameOnly};

FUNCTION EraseFile ( S : PathStr ) : BOOLEAN ;
VAR F : FILE;
BEGIN
EraseFile := FALSE;
ASSIGN (F, S);
RESET (F);
IF IORESULT <> 0 THEN EXIT;
  CLOSE (F);
  ERASE (F);
  EraseFile := (IORESULT = 0);
END;

PROCEDURE FindSwagPath (VAR P : PathStr);
VAR
  S : PathStr;
BEGIN
  IF SwagPath <> '' THEN S := SwagPath + '\DRIVES.SWG' ELSE
     S := 'DRIVES.SWG';
  S := FSearch (S, GetEnv ('PATH') );
  IF S = '' THEN
     BEGIN
     WriteLn(#7,'You GOTTA have the SWAG files somewhere on your PATH to do this !!');
     WriteLn(#7,'OR, you can enter the path on the command line !!');
     HALT(1);
     END;
S := FExpand (S);
P := FExpand (COPY(S,1,POS('DRIVES',S)-1));
END;

PROCEDURE FindPKZip;
VAR
  S : PathStr;
BEGIN
  S := FSearch ('PKZIP.EXE', GetEnv ('PATH') );
  IF S = '' THEN
     BEGIN
     WriteLn(#7,'You GOTTA have PKZIP somewhere on your PATH to do this !!');
     HALT(1);
     END;
     PKZIP := FExpand (S);
END;

PROCEDURE CleanUp;
{ clean up after ourselves }
BEGIN
  FINDFIRST ('*.NDX', $21, TR);
  WHILE DosError = 0 DO
        BEGIN
        EraseFile(TR.NAME);
        FINDNEXT (TR);
        END;
  EraseFile('MESSAGES.DAT');
  EraseFile('CONTROL.DAT');
END;

PROCEDURE CreateControlDat;
VAR
    I : BYTE;
BEGIN
     ControlHdr [11] := IntStr (PRED (ConfNum), 3, FALSE);
     ASSIGN (ControlF, 'CONTROL.DAT');
     REWRITE (ControlF);
     FOR I := 1 TO 11 DO
         WRITELN (ControlF, ControlHdr [i]);
     FOR I := 1 TO ControlIdx DO
         WRITELN (ControlF, ControlVal [i]);
     CLOSE (ControlF);
END;

PROCEDURE CreateMessageDat;
VAR
    I    : BYTE;
    Buff : BlockArray;
BEGIN
  FILLCHAR (ControlVal, SIZEOF (ControlVal), #0);
  FILLCHAR (Buff, SIZEOF (Buff), #32);
  FILLCHAR (MsgHdr, SIZEOF (MsgHdr), #32);
  ConfNum    := 0;
  ControlIdx := 0;
  Number     := 0;
  ASSIGN (QWKF, 'MESSAGES.DAT');
  REWRITE (QWKF, SIZEOF (MsgHdr) );
  WStr := 'SWAG TO QWK (c) 1993 GDSOFT';
  FOR I := 1 TO LENGTH (WStr) DO Buff [i] := WSTR [i];
  BLOCKWRITE (QwkF, Buff, 1);
END;

FUNCTION ArrayTOInteger (B : CharArray; Len : BYTE) : LONGINT;

VAR I : BYTE;
    S : STRING;
    E  : INTEGER;
    T  : INTEGER;

BEGIN
    S := '';
    FOR I := 1 TO PRED (Len) DO IF B [i] <> #32 THEN S := S + B [i];
    VAL (S, T, E);
    IF E = 0 THEN ArrayToInteger := T;
END;

PROCEDURE ReadMessage (HDR : MSGDatHdr; RelNum : LONGINT; VAR Chunks : INTEGER);
VAR
  Buff : BlockArray;
  J    : INTEGER;
  I    : BYTE;
  NS   : STRING;

BEGIN

  { read the header block }
  SEEK (SwagF, RelNum - 1);
  BLOCKREAD  (SwagF, Hdr, 1);

  { Correct the record number }
  INC(Number);
  NS := IntStr(Number,7,FALSE);
  WHILE Length(NS) < 7 DO NS := NS + #32;
  MOVE (NS, Hdr.MsgNum, 7);
  Hdr.LeastSig := ConfNum;
  Hdr.MostSig  := Number;

  { write the header to our QWK file }
  BLOCKWRITE (QwkF,  Hdr, 1);

  { process the rest of the blocks }
  Chunks := ArrayToInteger (HDR.NumChunk, 6);
  FOR J := 1 TO PRED (Chunks) DO
  BEGIN
    BLOCKREAD  (SwagF, Buff, 1);
    BLOCKWRITE (QwkF,  Buff, 1);
  END;

END;

PROCEDURE ProcessSwag (FN : PathStr);
VAR
    ndxF : File;
    b    : bSingle;
    r    : REAL;
    n    : LONGINT;

    { converts TP real to Microsoft 4 bytes single .. GOOFY !!!! }
    procedure real_to_msb (preal : real; var b : bsingle);
    var
         r : array [0 .. 5] of byte absolute preal;
    begin
         b [3] := r [0];
         move (r [3], b [0], 3);
    end; { procedure real_to_msb }


BEGIN

  WriteLn('Process .. ',FN);
  { create the NDX file }
  ASSIGN  (ndxF,IntStr(ConfNum,3,TRUE)+'.NDX');
  REWRITE (ndxF,1);

  ASSIGN (SwagF, FN);
  RESET (SwagF, SIZEOF (MsgHdr) );
  Count  := 2;  { start at RECORD #2 }

  WHILE (Count < FILESIZE (SwagF) ) DO
        BEGIN

        n := SUCC(FilePos(QwkF));      { ndx wants the RELATIVE position }
        r := N;                        { make a REAL                     }
        REAL_TO_MSB(r,b);              { convert to MSB format           }
        BLOCKWRITE(ndxF,B,SizeOf(B));  { store it                        }

        ReadMessage (MSGHdr, Count, Chunks);
        INC (Count, Chunks);
        END;

  CLOSE (SwagF);
  CLOSE (NdxF);

  { update the CONTROL file array }
  INC (ControlIdx);
  ControlVal [ControlIdx] := IntStr (ConfNum, 3, TRUE);
  INC (ControlIdx);
  ControlVal [ControlIdx] := NameOnly (FN);
  INC (ConfNum);

END;


BEGIN

  ClrScr;

  IF ParamCount > 0 THEN SwagPath := FExpand(ParamStr(1));

  EraseFile('SWAG.QWK');  { make sure we don't have one yet }

  FindSwagPath (SwagPath);

  FindPkZip;

  CreateMessageDat;

  IF SwagPath [LENGTH (SwagPath) ] <> '\' THEN SwagPath := SwagPath + '\';

  FINDFIRST (SwagPath + '*.SWG', $21, TR);
  WHILE DosError = 0 DO
        BEGIN
        ProcessSwag (SwagPath + TR.Name);
        FINDNEXT (TR);
        END;

  CLOSE (QwkF);

  CreateControlDat;

  SwapVectors;
  Exec(PKZIP,' -ex SWAG.QWK *.NDX MESSAGES.DAT CONTROL.DAT');
  SwapVectors;

  CleanUp;

END.