563 lines
15 KiB
ObjectPascal
563 lines
15 KiB
ObjectPascal
// ====================================================================
|
|
// Mystic BBS Software Copyright 1997-2012 By James Coyle
|
|
// ====================================================================
|
|
//
|
|
// This file is part of Mystic BBS.
|
|
//
|
|
// Mystic BBS is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Mystic BBS is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Mystic BBS. If not, see <http://www.gnu.org/licenses/>.
|
|
//
|
|
// ====================================================================
|
|
|
|
Program MystPack;
|
|
|
|
{ when DELETEing a message, the pointers are one less than they should be }
|
|
{ should be fixed, but may cause other problems. commented out the last }
|
|
{ read updating in the msgkill part of the program }
|
|
|
|
{ also when a user is reading a base, it could cause MP to crash with an }
|
|
{ RTE 005: access violation error }
|
|
|
|
{$I M_OPS.PAS}
|
|
|
|
Uses
|
|
m_FileIO,
|
|
m_Strings,
|
|
m_DateTime,
|
|
CRT,
|
|
DOS;
|
|
|
|
{$I RECORDS.PAS}
|
|
|
|
Const
|
|
PackVer = '1.2';
|
|
Jam_Deleted = $80000000;
|
|
JamSubBufSize = 4096;
|
|
|
|
Type
|
|
JamSubBuffer = Array[1..JamSubBufSize] of Char;
|
|
|
|
JamHdrType = Record
|
|
Signature : Array[1..4] of Char;
|
|
Created : LongInt;
|
|
ModCounter : LongInt;
|
|
ActiveMsgs : LongInt;
|
|
PwdCRC : LongInt;
|
|
BaseMsgNum : LongInt;
|
|
HighWaterMark : Longint;
|
|
Extra : Array[1..996] of Char;
|
|
End;
|
|
|
|
JamMsgHdrType = Record
|
|
Signature : Array[1..4] of Char;
|
|
Rev : Word;
|
|
Resvd : Word;
|
|
SubFieldLen : LongInt;
|
|
TimesRead : LongInt;
|
|
MsgIdCrc : LongInt;
|
|
ReplyCrc : LongInt;
|
|
ReplyTo : LongInt;
|
|
ReplyFirst : LongInt;
|
|
ReplyNext : LongInt;
|
|
DateWritten : LongInt;
|
|
DateRcvd : LongInt;
|
|
DateArrived : LongInt;
|
|
MsgNumber : LongInt;
|
|
Attr1 : LongInt;
|
|
Attr2 : LongInt;
|
|
TextOfs : LongInt;
|
|
TextLen : LongInt;
|
|
PwdCrc : LongInt;
|
|
Cost : LongInt;
|
|
End;
|
|
|
|
JamIdxType = Record
|
|
MsgToCrc : LongInt;
|
|
HdrLoc : LongInt;
|
|
End;
|
|
|
|
JamLastType = Record
|
|
NameCrc : LongInt;
|
|
UserNum : LongInt;
|
|
LastRead : LongInt;
|
|
HighRead : LongInt;
|
|
End;
|
|
|
|
SubFieldType = Record
|
|
LoId : Word;
|
|
HiId : Word;
|
|
DataLen : LongInt;
|
|
Data : Array[1..1000] of Char;
|
|
End;
|
|
|
|
TxtType = Array[1..65000] of Char;
|
|
|
|
JamType = Record
|
|
Hdr : JamHdrType;
|
|
MsgHdr : JamMsgHdrType;
|
|
HdrFile : File;
|
|
Idx : JamIdxType;
|
|
IdxFile : File of JamIdxType;
|
|
Last : JamLastType;
|
|
LastFile : File of JamLastType;
|
|
TxtFile : File;
|
|
SubField : SubFieldType;
|
|
End;
|
|
|
|
Const
|
|
SpinStr : String[8] = ('\|/-\|/-');
|
|
SpinPos : Byte = 1;
|
|
SkipFirst : Boolean = False;
|
|
PackMsgs : Boolean = False;
|
|
|
|
Var
|
|
ConfigFile : File of RecConfig;
|
|
MBaseFile : File of RecMessageBase;
|
|
Config : RecConfig;
|
|
MBase : RecMessageBase;
|
|
|
|
Const
|
|
DATEC1970 = 2440588;
|
|
DATED0 = 1461;
|
|
DATED1 = 146097;
|
|
DATED2 = 1721119;
|
|
|
|
Procedure UnixToDT(SecsPast: LongInt; Var Dt: DateTime);
|
|
Var
|
|
DateNum : LongInt;
|
|
N1 : Word;
|
|
Begin
|
|
Datenum := (SecsPast Div 86400) + DATEc1970;
|
|
|
|
DateJ2G(DateNum, SmallInt(N1), SmallInt(DT.Month), SmallInt(DT.day));
|
|
DT.Year := N1;
|
|
|
|
SecsPast := SecsPast Mod 86400;
|
|
DT.Hour := SecsPast Div 3600;
|
|
SecsPast := SecsPast Mod 3600;
|
|
DT.Min := SecsPast Div 60;
|
|
DT.Sec := SecsPast Mod 60;
|
|
End;
|
|
|
|
Procedure PWrite (Str : String);
|
|
Var
|
|
A : Byte;
|
|
Code : String[2];
|
|
Begin
|
|
A := 1;
|
|
While A <= Length(Str) Do Begin
|
|
If (Str[A] = '|') and (A < Length(Str) - 1) Then Begin
|
|
Code := Copy(Str, A + 1, 2);
|
|
|
|
If (Code = '00') or (strS2I(Code) > 0) Then Begin
|
|
If strS2I(Code) < 16 Then
|
|
TextColor(strS2I(Code))
|
|
Else
|
|
TextBackground(strS2I(Code) - 16);
|
|
End Else
|
|
Write(Str[A] + Code);
|
|
|
|
Inc (A, 2);
|
|
End Else
|
|
Write(Str[A]);
|
|
|
|
Inc(A);
|
|
End;
|
|
End;
|
|
|
|
Procedure PWriteLN (Str : String);
|
|
Begin
|
|
PWrite (Str + #13#10);
|
|
End;
|
|
|
|
Procedure UpdateSpin;
|
|
Begin
|
|
Write (#8 + SpinStr[SpinPos]);
|
|
Inc (SpinPos);
|
|
|
|
If SpinPos > 8 Then SpinPos := 1;
|
|
End;
|
|
|
|
Procedure PackJAMBase (Var TotalKilled : LongInt; Var SavedBytes : LongInt);
|
|
Var
|
|
BasePath : String;
|
|
OldHdrFile : File;
|
|
OldTxtFile : File;
|
|
OldIdxFile : File of JamIdxType;
|
|
NewHdrFile : File;
|
|
NewTxtFile : File;
|
|
NewIdxFile : File of JamIdxType;
|
|
TmpHdrFile : File;
|
|
LastFile : File of JamLastType;
|
|
Last : JamLastType;
|
|
SigHdr : JamHdrType;
|
|
MsgHdr : JamMsgHdrType;
|
|
TmpSigHdr : JamHdrType;
|
|
TmpMsgHdr : JamMsgHdrType;
|
|
MsgIdx : JamIdxType;
|
|
TxtBuf : ^TxtType;
|
|
SubField : SubFieldType;
|
|
Count : LongInt;
|
|
Killed : Boolean;
|
|
KillOffset : LongInt;
|
|
LimitKill : Boolean;
|
|
TotalMsgs : LongInt;
|
|
MsgDateTime : DateTime;
|
|
Temp : LongInt;
|
|
HaveHdr : Boolean;
|
|
Begin
|
|
PWrite ('|07Processing |08-> |07' + strPadR(MBase.Name, 35, ' ') + '|08 -> |07');
|
|
|
|
BasePath := MBase.Path + MBase.FileName;
|
|
|
|
Assign (OldHdrFile, BasePath + '.jhr');
|
|
Assign (OldTxtFile, BasePath + '.jdt');
|
|
Assign (OldIdxFile, BasePath + '.jdx');
|
|
|
|
{$I-} Reset (OldHdrFile, 1); {$I+}
|
|
If IOResult <> 0 Then Exit;
|
|
|
|
{$I-} Reset (OldTxtFile, 1); {$I+}
|
|
If IOResult <> 0 Then Begin
|
|
Close (OldHdrFile);
|
|
Exit;
|
|
End;
|
|
|
|
{$I-} Reset (OldIdxFile); {$I+}
|
|
If IoResult <> 0 Then Begin
|
|
Close (OldHdrFile);
|
|
Close (OldTxtFile);
|
|
Exit;
|
|
End;
|
|
|
|
Assign (LastFile, BasePath + '.jlr');
|
|
{$I-} Reset (LastFile); {$I+}
|
|
If IoResult <> 0 Then ReWrite (LastFile);
|
|
Close (LastFile);
|
|
|
|
Assign (NewHdrFile, BasePath + '._hr');
|
|
ReWrite (NewHdrFile, 1);
|
|
Assign (NewTxtFile, BasePath + '._dt');
|
|
ReWrite (NewTxtFile, 1);
|
|
Assign (NewIdxFile, BasePath + '._dx');
|
|
ReWrite (NewIdxFile);
|
|
|
|
BlockRead (OldHdrFile, SigHdr, SizeOf(SigHdr));
|
|
|
|
Inc (SigHdr.ModCounter);
|
|
|
|
BlockWrite (NewHdrFile, SigHdr, SizeOf(SigHdr));
|
|
|
|
If SigHdr.ActiveMsgs > MBase.MaxMsgs Then
|
|
KillOffset := SigHdr.ActiveMsgs - MBase.MaxMsgs
|
|
Else
|
|
KillOffset := 0;
|
|
|
|
TotalMsgs := 0;
|
|
TotalKilled := 0;
|
|
|
|
New (TxtBuf);
|
|
|
|
While Not Eof(OldIdxFile) Do Begin
|
|
UpdateSpin;
|
|
|
|
Read (OldIdxFile, MsgIdx);
|
|
|
|
If MsgIdx.HdrLoc = -1 Then Begin
|
|
Killed := True;
|
|
LimitKill := False;
|
|
HaveHdr := False;
|
|
End Else Begin
|
|
Seek (OldHdrFile, MsgIdx.HdrLoc);
|
|
|
|
BlockRead (OldHdrFile, MsgHdr, SizeOf(MsgHdr));
|
|
|
|
LimitKill := False;
|
|
Killed := MsgHdr.Attr1 and Jam_Deleted <> 0;
|
|
HaveHdr := True;
|
|
|
|
If MBase.MaxAge > 0 Then Begin
|
|
UnixToDT (MsgHdr.DateWritten, MsgDateTime);
|
|
PackTime (MsgDateTime, Temp);
|
|
|
|
LimitKill := DaysAgo(Temp, 2) > MBase.MaxAge;
|
|
Killed := Killed or LimitKill;
|
|
End;
|
|
|
|
If MBase.MaxMsgs > 0 Then
|
|
If KillOffset > 0 Then Begin
|
|
Dec (KillOffset);
|
|
LimitKill := True;
|
|
Killed := True;
|
|
End;
|
|
|
|
If SkipFirst and (MBase.NetType = 0) and (TotalMsgs = 0) and (MsgHdr.Attr1 and Jam_Deleted = 0) Then
|
|
Killed := False;
|
|
End;
|
|
|
|
If Killed Then Begin
|
|
Inc (TotalKilled);
|
|
|
|
(*
|
|
Reset (LastFile);
|
|
While Not Eof(LastFile) Do Begin
|
|
Read (LastFile, Last);
|
|
If (Last.LastRead > TotalMsgs) And Not LimitKill Then Begin
|
|
Dec (Last.LastRead);
|
|
Seek (LastFile, FilePos(LastFile) - 1);
|
|
Write (LastFile, Last);
|
|
End;
|
|
End;
|
|
Close (LastFile);
|
|
*)
|
|
If HaveHdr And (MsgHdr.ReplyFirst <> 0) Then Begin
|
|
Assign (TmpHdrFile, BasePath + '.jhr');
|
|
Reset (TmpHdrFile, 1);
|
|
|
|
BlockRead (TmpHdrFile, TmpSigHdr, SizeOf(TmpSigHdr));
|
|
|
|
While Not Eof(TmpHdrFile) Do Begin
|
|
BlockRead (TmpHdrFile, TmpMsgHdr, SizeOf(TmpMsgHdr));
|
|
|
|
If TmpMsgHdr.ReplyTo = MsgHdr.MsgNumber Then Begin
|
|
TmpMsgHdr.ReplyTo := 0;
|
|
Seek (TmpHdrFile, FilePos(TmpHdrFile) - SizeOf(TmpMsgHdr));
|
|
BlockWrite (TmpHdrFile, TmpMsgHdr, SizeOf(TmpMsgHdr));
|
|
End;
|
|
|
|
Seek (TmpHdrFile, FilePos(TmpHdrFile) + TmpMsgHdr.SubFieldLen);
|
|
End;
|
|
Close (TmpHdrFile);
|
|
End;
|
|
|
|
End Else Begin
|
|
Inc (TotalMsgs);
|
|
|
|
If TotalKilled > 0 Then Begin
|
|
Reset (LastFile);
|
|
While Not Eof(LastFile) Do Begin
|
|
Read (LastFile, Last);
|
|
If Last.LastRead = MsgHdr.MsgNumber Then Begin
|
|
Last.LastRead := TotalMsgs;
|
|
Seek (LastFile, FilePos(LastFile) - 1);
|
|
Write (LastFile, Last);
|
|
End;
|
|
End;
|
|
Close (LastFile);
|
|
End;
|
|
|
|
If (TotalKilled > 0) and (MsgHdr.ReplyFirst <> 0) Then Begin
|
|
Assign (TmpHdrFile, BasePath + '.jhr');
|
|
Reset (TmpHdrFile, 1);
|
|
|
|
BlockRead (TmpHdrFile, TmpSigHdr, SizeOf(TmpSigHdr));
|
|
|
|
While Not Eof(TmpHdrFile) Do Begin
|
|
BlockRead (TmpHdrFile, TmpMsgHdr, SizeOf(TmpMsgHdr));
|
|
|
|
If TmpMsgHdr.ReplyTo = MsgHdr.MsgNumber Then Begin
|
|
TmpMsgHdr.ReplyTo := TotalMsgs;
|
|
Seek (TmpHdrFile, FilePos(TmpHdrFile) - SizeOf(TmpMsgHdr));
|
|
BlockWrite (TmpHdrFile, TmpMsgHdr, SizeOf(TmpMsgHdr));
|
|
End;
|
|
|
|
Seek (TmpHdrFile, FilePos(TmpHdrFile) + TmpMsgHdr.SubFieldLen);
|
|
End;
|
|
Close (TmpHdrFile);
|
|
End;
|
|
|
|
If (TotalKilled > 0) and (MsgHdr.ReplyTo <> 0) Then Begin
|
|
Assign (TmpHdrFile, BasePath + '._hr');
|
|
Reset (TmpHdrFile, 1);
|
|
|
|
BlockRead (TmpHdrFile, TmpSigHdr, SizeOf(TmpSigHdr));
|
|
|
|
While Not Eof(TmpHdrFile) Do Begin
|
|
BlockRead (TmpHdrFile, TmpMsgHdr, SizeOf(TmpMsgHdr));
|
|
|
|
If TmpMsgHdr.ReplyFirst = MsgHdr.MsgNumber Then Begin
|
|
TmpMsgHdr.ReplyFirst := TotalMsgs;
|
|
Seek (TmpHdrFile, FilePos(TmpHdrFile) - SizeOf(TmpMsgHdr));
|
|
BlockWrite (TmpHdrFile, TmpMsgHdr, SizeOf(TmpMsgHdr));
|
|
End;
|
|
|
|
Seek (TmpHdrFile, FilePos(TmpHdrFile) + TmpMsgHdr.SubFieldLen);
|
|
End;
|
|
Close (TmpHdrFile);
|
|
End;
|
|
|
|
MsgHdr.MsgNumber := TotalMsgs;
|
|
MsgIdx.HdrLoc := FilePos(NewHdrFile);
|
|
|
|
(* write text from old file to new file *)
|
|
|
|
If MsgHdr.TextLen > 65000 Then MsgHdr.TextLen := 65000;
|
|
// Why did I put this limitation here? Prob should be removed
|
|
// need to be tested.
|
|
|
|
Seek (OldTxtFile, MsgHdr.TextOfs);
|
|
BlockRead (OldTxtFile, TxtBuf^, MsgHdr.TextLen);
|
|
|
|
MsgHdr.TextOfs := FileSize(NewTxtFile);
|
|
|
|
BlockWrite (NewTxtFile, TxtBuf^, MsgHdr.TextLen);
|
|
|
|
(* write header from old to new file *)
|
|
|
|
BlockWrite (NewHdrFile, MsgHdr, SizeOf(MsgHdr));
|
|
|
|
(* write subfield data if it exists *)
|
|
|
|
If MsgHdr.SubFieldLen > 0 Then Begin
|
|
Count := 1;
|
|
|
|
While (Count <= MsgHdr.SubFieldLen) Do Begin
|
|
BlockRead (OldHdrFile, SubField, 8);
|
|
BlockRead (OldHdrFile, SubField.Data, SubField.DataLen);
|
|
BlockWrite (NewHdrFile, SubField, 8);
|
|
BlockWrite (NewHdrFile, SubField.Data, SubField.DataLen);
|
|
|
|
Inc (Count, 8 + SubField.DataLen);
|
|
End;
|
|
End;
|
|
|
|
(* write new index to index file *)
|
|
|
|
Write (NewIdxFile, MsgIdx);
|
|
End;
|
|
End;
|
|
|
|
Dispose (TxtBuf);
|
|
|
|
SigHdr.ActiveMsgs := TotalMsgs;
|
|
SigHdr.BaseMsgNum := 1;
|
|
|
|
Reset (NewHdrFile, 1);
|
|
BlockWrite (NewHdrFile, SigHdr, SizeOf(SigHdr));
|
|
|
|
SavedBytes := (FileSize(OldHdrFile) - FileSize(NewHdrFile)) +
|
|
(FileSize(OldTxtFile) - FileSize(NewTxtFile)) +
|
|
((FileSize(OldIdxFile) - FileSize(NewIdxFile)) * SizeOf(MsgIdx));
|
|
|
|
Close (OldHdrFile);
|
|
Close (OldTxtFile);
|
|
Close (OldIdxFile);
|
|
Close (NewHdrFile);
|
|
Close (NewTxtFile);
|
|
Close (NewIdxFile);
|
|
|
|
Erase (OldHdrFile);
|
|
Erase (OldTxtFile);
|
|
Erase (OldIdxFile);
|
|
|
|
ReName (NewHdrFile, BasePath + '.jhr');
|
|
ReName (NewTxtFile, BasePath + '.jdt');
|
|
ReName (NewIdxFile, BasePath + '.jdx');
|
|
|
|
If TotalKilled > 0 Then Begin
|
|
Reset (LastFile);
|
|
While Not Eof(LastFile) Do Begin
|
|
Read (LastFile, Last);
|
|
If Last.LastRead > TotalMsgs Then Last.LastRead := TotalMsgs;
|
|
If Last.HighRead > Last.LastRead Then Last.HighRead := Last.LastRead;
|
|
Seek (LastFile, FilePos(LastFile) - 1);
|
|
Write (LastFile, Last);
|
|
End;
|
|
Close (LastFile);
|
|
End;
|
|
End;
|
|
|
|
Procedure ShowHelp;
|
|
Begin
|
|
WriteLn ('Invalid command line options');
|
|
WriteLn;
|
|
WriteLn ('-PACK : Pack all jam message bases');
|
|
WriteLn ('-SKIPFIRST : Skips the first message of each local message base');
|
|
WriteLn;
|
|
PWriteLn ('|12NOTE: This program can sometimes crash if users are online.|07');
|
|
Halt(1);
|
|
End;
|
|
|
|
Var
|
|
TotalMsgs : LongInt;
|
|
TotalBytes : LongInt;
|
|
Msgs : LongInt;
|
|
Bytes : LongInt;
|
|
Count : Byte;
|
|
Str : String;
|
|
Begin
|
|
FileMode := 66;
|
|
|
|
ClrScr;
|
|
PWriteLn ('|08-> |15MYSTPACK ' + PackVer + ' : JAM message base packer');
|
|
PWriteLn ('|08-> |07Compatible with Mystic BBS software v' + mysVersion);
|
|
PWriteLn ('|08ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ|07');
|
|
WriteLn;
|
|
|
|
Window (1, 5, 80, 24);
|
|
|
|
If ParamCount = 0 Then ShowHelp;
|
|
|
|
For Count := 1 to ParamCount Do Begin
|
|
Str := strUpper(ParamStr(Count));
|
|
|
|
If Str = '-PACK' Then
|
|
PackMsgs := True
|
|
Else
|
|
If Str = '-SKIPFIRST' Then
|
|
SkipFirst := True
|
|
Else
|
|
ShowHelp;
|
|
End;
|
|
|
|
Assign (ConfigFile, 'mystic.dat');
|
|
{$I-} Reset (ConfigFile); {$I+}
|
|
If IoResult <> 0 Then Begin
|
|
WriteLn ('ERROR: Unable to read MYSTIC.DAT. Run from root Mystic directory');
|
|
Halt(1);
|
|
End;
|
|
Read (ConfigFile, Config);
|
|
Close (ConfigFile);
|
|
|
|
If Config.DataChanged <> mysDataChanged Then Begin
|
|
WriteLn('ERROR: Data files are not current and must be upgraded.');
|
|
Halt(1);
|
|
End;
|
|
|
|
Assign (MBaseFile, Config.DataPath + 'mbases.dat');
|
|
{$I-} Reset(MBaseFile); {$I+}
|
|
If IoResult <> 0 Then Begin
|
|
WriteLn ('ERROR: Unable to read message area data');
|
|
Halt(1);
|
|
End;
|
|
|
|
While Not Eof(MBaseFile) Do Begin
|
|
Read (MBaseFile, MBase);
|
|
|
|
If MBase.BaseType = 0 Then Begin
|
|
PackJAMBase(Msgs, Bytes);
|
|
WriteLn (#8 + 'Killed ', Msgs, '; ', Bytes, ' bytes');
|
|
|
|
Inc (TotalMsgs, Msgs);
|
|
Inc (TotalBytes, Bytes);
|
|
End;
|
|
End;
|
|
|
|
Close (MBaseFile);
|
|
|
|
WriteLn;
|
|
PWriteLn ('|08[|07-|08] |07Killed |15' + strI2S(TotalMsgs) + '|07 Msgs; Removed |15' + strI2S(TotalBytes) + '|07 bytes');
|
|
|
|
Window (1, 1, 80, 25);
|
|
End.
|