home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-12-25 | 63.9 KB | 2,098 lines |
- (***************************************************************************
- :Program. XStat
- :Author. Jⁿrgen Weinelt
- :Address. Zur Kanzel 1, D-97762 Hammelburg, Germany
- :Address. EMail: jow@rz.uni-wuerzburg.de (preferred)
- :Address. EMail: jow@hcast.franken.de
- :Address. EMail: jow@hcast.adsp.sub.org (avoid if possible)
- :Version. $VER: XStat_Source 1.14 (25-DEC-93)
- :Copyright. Freeware
- :Language. Modula-2
- :Translator. M2Amiga V4.2
- :History. 1.00 initial release
- :History. 1.01 bug fix: used to guru when xfer = 0 bytes
- :History. 1.02 added peak cps rating
- :History. 1.03 added monthly statistics
- :History. 1.04 switched to DosL.ReadArgs(), fixed nasty current date
- :History. bug with WB2.1 (problems with locale and "TODAY")
- :History. 1.05 added per host statistics
- :History. 1.06 integrated "per host" statistics into "HOSTNAME" option
- :History. (pattern matching using dos.library)
- :History. 1.07 bug fix: linked list of Statistics-records was not
- :History. sorted correctly
- :History. 1.08 added STDERR option; replaced all "°"'s with "av."
- :History. 1.09 added "LOCAL" and "IGNORE" to XStat.data
- :History. 1.10 added multiple names feature to XStat.data N-lines
- :History. fixed a small bug in the NI/NO feature
- :History. 1.11 multiple XStat.data N-lines implemented
- :History. XStat.data N-line entry ".default." implemented
- :History. Xferstat "EOF" bug fixed
- :History. string length bumped to 256
- :History. 1.12 added ".illegal." for missing host names in XFerStat
- :History. XStat now skips the 1st 10 chars of the 2nd XferStat line
- :History. 1.13 output now shows the first and last date that was used
- :History. added "PERIOD" and "LAST" options
- :History. 1.14 added XSTATFORM, XSTATVERBFORM, "flat rate" and
- :History. "negotiation time offset"; quiet turns off ⌐-notice
- :Contents. Extracts statistics from UUCiCo "Xferstat" logfile. Requires
- :Contents. one of the "Xferstat" UUCICOs (tested with the SWB UUCiCo
- :Contents. and the wUUCP UUCiCo - thanks Charly & wusel!)
- **************************************************************************)
-
-
-
- MODULE XStat;
- (*$ StackChk:=FALSE *)
- (*$ CaseChk:=FALSE *)
- (*$ Volatile:=FALSE *)
-
-
-
- (*$ DEFINE BETA:=FALSE *)
-
-
-
- IMPORT Arts;
- IMPORT ASCII;
- IMPORT Break;
- IMPORT Conversions;
- IMPORT DosD;
- IMPORT DosL;
- IMPORT ExecD;
- IMPORT ExecL;
- IMPORT InOut;
- IMPORT R;
- IMPORT RealConversions;
- IMPORT RealInOut;
- IMPORT String;
-
- IMPORT SYSTEM;
-
-
-
- CONST
- strlen=255; (* was 100 before 1.11 *)
-
- formlen=8191; (* buffer for the output forms *)
-
- defverbform="&cd &8HN &FD &IC &LC &3OT &2UN &6CO &7GR &7GS &7NR &7NS &4GC &4NC&LF";
-
- defstatform="&LF"+
- "&LF"+
- "&HN: Connection statistics for &CD calls.&LF"+
- "&LF"+
- "Transfer statistics from &FD to &TD&LF"+
- "&LF"+
- "number of connects &08AC&LF"+
- "local connects &08LC&LF"+
- "ignored connects &08IC&LF"+
- "&LF"+
- "total online time &08OT seconds&LF"+
- "total phone units &08UN units&LF"+
- "total phone cost &12CO &CS&LF"+
- "average online time &08ot seconds/connect&LF"+
- "average phone units &12un units/connect&LF"+
- "average phone cost &12co &CS/connect&LF"+
- "&LF"+
- "total gross read &08GR bytes&LF"+
- "total gross send &08GS bytes&LF"+
- "total net read &08NR bytes&LF"+
- "total net send &08NS bytes&LF"+
- "average gross read &08gr bytes/connect&LF"+
- "average gross send &08gs bytes/connect&LF"+
- "average net read &08nr bytes/connect&LF"+
- "average net send &08ns bytes/connect&LF"+
- "&LF"+
- "average speed &08GC CPS (gross data transfer)&LF"+
- "fastest connect &08gp CPS (gross data transfer)&LF"+
- "average speed &08NC CPS (net data transfer)&LF"+
- "fastest connect &08np CPS (net data transfer)&LF"+
- "&LF"+
- "average cost &12CG &CS/MB (gross data transfer)&LF"+
- "average cost &12CN &CS/MB (net data transfer)&LF"+
- "&LF";
-
-
-
- smult=1; (* seconds per second :-) *)
- mmult=60*smult; (* seconds per minute *)
- hmult=60*mmult; (* seconds per hour *)
- dmult=24*hmult; (* seconds per day *)
-
- dsun=0*dmult; (* sunday offset into week (in seconds) *)
- dmon=1*dmult; (* monday offset into week (in seconds) *)
- dtue=2*dmult; (* tuesday offset into week (in seconds) *)
- dwed=3*dmult; (* wednesday offset into week (in seconds) *)
- dthu=4*dmult; (* thursday offset into week (in seconds) *)
- dfri=5*dmult; (* friday offset into week (in seconds) *)
- dsat=6*dmult; (* saturday offset into week (in seconds) *)
-
- defxstatdata="UULIB:XStat.data";
- defxferstat="UUSPOOL:XferStat";
-
- vers="XStat" (*$ IF BETA *) +"_BETA" (*$ ENDIF *) +" 1.14";
- version="$VER: "+vers+" (25-DEC-93)";
- argTemplate="FILE/K,DATA/K,V=VERBOSE/S,M=MONTH/K,NI=NOINCOM/S,NO=NOOUTGO/S,"+
- "Q=QUIET/S,H=HOSTNAME/K,FD=FROMDATE/K,TD=TODATE/K,SE=STDERR/S,"+
- "P=PERIOD/N,L=LAST/N";
-
- extendedhelp="\nXStat "+argTemplate+"\n"+
- "\n DATA XStat.data file\t\t def="+defxstatdata+
- "\n FILE Xferstat file\t\t def="+defxferstat+
- "\n VERBOSE verbose mode switch \t def=off"+
- "\n MONTH monthly, override fd/td\t def=off (format MMM-YY)"+
- "\n NOINCOM don't evaluate incoming calls def=off"+
- "\n NOOUTGO don't evaluate outgoing calls def=off"+
- "\n QUIET suppress non-fatal errors\t def=off"+
- "\n HOSTNAME evaluate calls to host\t def=all"+
- "\n FROMDATE ignore calls before date\t def=01-JAN-78"+
- "\n TODATE ignore calls after date\t def=current date/time"+
- "\n STDERR send error messages to StdErr def=off"+
- "\n PERIOD n days from fd, override td\t def=0"+
- "\n LAST n days before td, override fd def=0\n\n";
-
-
-
- TYPE
- StringT=ARRAY[0..strlen] OF CHAR;
- StringTPtr=POINTER TO StringT;
-
- StrPtr=StringTPtr; (* just a dummy declaration *)
-
- IntPtr=POINTER TO LONGINT; (* only needed because of the *)
- (* brain dead /N-handling in *)
- (* DosL.ReadArgs() *)
-
- FormBuffer=ARRAY[0..formlen] OF CHAR; (* buffer for the output forms *)
-
-
-
- DosArgs=
- RECORD
- file: StrPtr;
- data: StrPtr;
- verb: LONGINT;
- month: StrPtr;
- noin: LONGINT;
- noout: LONGINT;
- quiet: LONGINT;
- host: StrPtr;
- fdate: StrPtr;
- tdate: StrPtr;
- stderr: LONGINT;
- period: IntPtr;
- last: IntPtr;
- END;
-
-
-
- File=
- RECORD
- fh: DosD.FileHandlePtr;
- eof: BOOLEAN;
- END;
-
-
-
- CostPtr=POINTER TO Cost;
- Cost=
- RECORD
- next: CostPtr;
- start: LONGINT; (* start time for mpu and unit *)
- stop: LONGINT; (* end time for mpu and unit *)
- mpu: REAL; (* price per unit *)
- unit: REAL; (* unit duration *)
- END;
-
-
-
- XSDataPtr=POINTER TO XSData;
- XSData=
- RECORD
- next: XSDataPtr;
- host: StringT; (* host name *)
- local: BOOLEAN; (* count transmissions, no cost, no units *)
- ignore: BOOLEAN; (* ignore this host entirely *)
- double: BOOLEAN; (* if true, don't free the attached costlist *)
- offset: LONGINT; (* connection negotiation offset (in seconds) *)
- costlist: CostPtr; (* list of Cost records *)
- END;
-
-
-
- Connect=
- RECORD
- host: StringT; (* host name *)
- callout: BOOLEAN; (* TRUE: outgoing call *)
- start: LONGINT; (* session start time *)
- stop: LONGINT; (* session end time *)
- sdate: DosD.Date; (* session start time in dos format *)
- inbb: LONGINT; (* gross bytes in *)
- outbb: LONGINT; (* gross bytes out *)
- inbn: LONGINT; (* net bytes in *)
- outbn: LONGINT; (* net bytes out *)
- cost: REAL; (* connection phone costs *)
- units: LONGINT; (* phone units consumed *)
- seconds: LONGINT; (* online time in seconds *)
- local: BOOLEAN; (* was local -> no cost, no units *)
- ignore: BOOLEAN; (* do not count this at all *)
- END;
-
-
-
- StatisticsPtr=POINTER TO Statistics;
- Statistics=
- RECORD
- next: StatisticsPtr;
- name: StringT;
- in: BOOLEAN;
- connects: LONGINT;
- inbb: LONGINT;
- outbb: LONGINT;
- inbn: LONGINT;
- outbn: LONGINT;
- cost: REAL;
- units: LONGINT;
- seconds: LONGINT;
- bpeak: LONGINT;
- npeak: LONGINT;
- localconnects: LONGINT;
- ignoreconnects: LONGINT;
- END;
-
-
-
- Args=
- RECORD
- xstatdata: StringT; (* xstat.data name and path; *)
- xferstat: StringT; (* xferstat name and path; *)
- verbose: BOOLEAN; (* lotsa output; ; off *)
- from: DosD.Date; (* from date; ; zero *)
- to: DosD.Date; (* to date; ; TODAY *)
- host: ARRAY[0..400] OF CHAR; (* host name pattern; ; all *)
- in: BOOLEAN; (* process incoming calls; ; on *)
- out: BOOLEAN; (* process outgoing calls; ; on *)
- quiet: BOOLEAN; (* suppress non-fatal errors; ; off *)
- monthly: BOOLEAN; (* monthly mode; ; off *)
- perhost: BOOLEAN; (* perhost mode; ; off *)
- stderr: BOOLEAN; (* stderr mode; ; off *)
- END;
-
-
-
- VAR
- inf: File;
- ln1,ln2,temp: StringT;
- log: Connect;
- totali,totalo: Statistics;
- xsroot: XSDataPtr;
- args: Args;
- currency: StringT;
- rdargsPtr: DosD.RDArgsPtr;
- statroot: StatisticsPtr;
- index: INTEGER;
- stdErr: DosD.FileHandlePtr;
- statform,verbform: FormBuffer;
-
-
-
- (*---------------------------------------------------------------------------
- FreeCostChain: FreeMem() a linked list of "Cost" records
- ---------------------------------------------------------------------------*)
- PROCEDURE FreeCostChain(d:CostPtr);
- VAR
- n: CostPtr;
- BEGIN
- REPEAT
- n:=d^.next;
- ExecL.FreeMem(d,SIZE(d^));
- d:=n;
- UNTIL n=NIL;
- END FreeCostChain;
-
-
-
- (*---------------------------------------------------------------------------
- FreeStat: FreeMem() a linked list of "Statistics" records
- ---------------------------------------------------------------------------*)
- PROCEDURE FreeStat(s: StatisticsPtr);
- VAR
- n: StatisticsPtr;
- BEGIN
- (* some memory may be lost if an error occurs while the XStat.data file *)
- (* is being parsed... that's because the n^.tmp field is not checked *)
- (* here. the problem is, checking n^.tmp might cause a "memory freed *)
- (* twice" guru under certain conditions. losing a few bytes seems like *)
- (* the smaller evil in this case. *)
- REPEAT
- n:=s^.next;
- ExecL.FreeMem(s,SIZE(s^));
- s:=n;
- UNTIL n=NIL;
- END FreeStat;
-
-
-
- (*---------------------------------------------------------------------------
- FreeXData: FreeMem() a linked list of "XSData" records, freeing the
- attached "Cost" record lists as well (if double=FALSE)
- ---------------------------------------------------------------------------*)
- PROCEDURE FreeXData(d: XSDataPtr);
- VAR
- n: XSDataPtr;
- BEGIN
- REPEAT
- n:=d^.next;
- IF (d^.costlist#NIL) AND (NOT d^.double) THEN
- FreeCostChain(d^.costlist);
- END;
- ExecL.FreeMem(d,SIZE(d^));
- d:=n;
- UNTIL n=NIL;
- END FreeXData;
-
-
-
- (*---------------------------------------------------------------------------
- MyError: Display error messages, terminate program if necessary
- redirect errors to STDERR if requested by new 1.08 cli switch
- ---------------------------------------------------------------------------*)
- (*$ CopyDyn:=FALSE *)
- PROCEDURE MyError(x1,x2,x3: ARRAY OF CHAR; fatal: BOOLEAN);
- VAR
- lidummy: LONGINT;
- BEGIN
- IF stdErr#NIL THEN
- IF fatal THEN
- lidummy:=DosL.Write(stdErr,SYSTEM.ADR("FATAL ERROR "),12);
- lidummy:=DosL.Write(stdErr,SYSTEM.ADR(x1),String.Length(x1));
- lidummy:=DosL.Write(stdErr,SYSTEM.ADR("\n\o"),1);
- lidummy:=DosL.Write(stdErr,SYSTEM.ADR(x2),String.Length(x2));
- lidummy:=DosL.Write(stdErr,SYSTEM.ADR("\n\o"),1);
- lidummy:=DosL.Write(stdErr,SYSTEM.ADR(x3),String.Length(x3));
- lidummy:=DosL.Write(stdErr,SYSTEM.ADR("\n\n"),2);
- Arts.Exit(DosD.fail);
- ELSIF (NOT args.quiet) OR ((x1[0]="F") & (x1[1]="A") & (x1[2]="T")) THEN
- lidummy:=DosL.Write(stdErr,SYSTEM.ADR(x1),String.Length(x1));
- lidummy:=DosL.Write(stdErr,SYSTEM.ADR("\n\o"),1);
- lidummy:=DosL.Write(stdErr,SYSTEM.ADR(x2),String.Length(x2));
- lidummy:=DosL.Write(stdErr,SYSTEM.ADR("\n\o"),1);
- lidummy:=DosL.Write(stdErr,SYSTEM.ADR(x3),String.Length(x3));
- lidummy:=DosL.Write(stdErr,SYSTEM.ADR("\n\n"),2);
- END;
- ELSE
- IF fatal THEN
- InOut.WriteString("FATAL ERROR ");
- InOut.WriteString(x1); InOut.WriteLn;
- InOut.WriteString(x2); InOut.WriteLn;
- InOut.WriteString(x3); InOut.WriteLn;
- InOut.WriteLn;
- Arts.Exit(DosD.fail);
- ELSIF (NOT args.quiet) OR ((x1[0]="F") & (x1[1]="A") & (x1[2]="T")) THEN
- InOut.WriteString(x1); InOut.WriteLn;
- InOut.WriteString(x2); InOut.WriteLn;
- InOut.WriteString(x3); InOut.WriteLn;
- InOut.WriteLn;
- END;
- END;
- END MyError;
-
-
-
- (*---------------------------------------------------------------------------
- GetHead: Remove first char from a string, return it as result
- ---------------------------------------------------------------------------*)
- PROCEDURE GetHead(VAR s: ARRAY OF CHAR): CHAR;
- VAR
- c: CHAR;
- BEGIN
- c:=s[0];
- String.DeleteChar(s,0);
- RETURN c;
- END GetHead;
-
-
-
- (*---------------------------------------------------------------------------
- KillSpaces: Remove leading spaces from a string
- ---------------------------------------------------------------------------*)
- PROCEDURE KillSpaces(VAR l1: StringT);
- VAR
- c: CHAR;
- BEGIN
- WHILE l1[0]=" " DO
- c:=GetHead(l1);
- Break.TestBreak();
- END;
- END KillSpaces;
-
-
-
- (*---------------------------------------------------------------------------
- Get: Remove first "word" from a string. "Word" means "a sequence of
- non-spaces, terminated by at least one space". Return this word
- as a separate string
- ---------------------------------------------------------------------------*)
- PROCEDURE Get(VAR s1,s2: StringT): BOOLEAN;
- VAR
- c: CHAR;
- BEGIN
- s2[0]:=ASCII.nul;
- KillSpaces(s1);
- c:=GetHead(s1);
- WHILE (c#" ") AND (c#ASCII.nul) DO
- Break.TestBreak();
- String.ConcatChar(s2,c);
- c:=GetHead(s1);
- END;
- RETURN String.Length(s2)>0;
- END Get;
-
-
-
- (*---------------------------------------------------------------------------
- ConvDate: Convert ASCII representations of date and time to Dos format
- ---------------------------------------------------------------------------*)
- (*$ CopyDyn:=FALSE *)
- PROCEDURE ConvDate(ds,ts: ARRAY OF CHAR; VAR dat: DosD.Date): BOOLEAN;
- VAR
- dt: DosD.DateTime;
- res: LONGINT;
- BEGIN
- dt.format:=DosD.formatDOS;
- dt.flags:=DosD.DateTimeFlagSet{};
- dt.strDate:=SYSTEM.ADR(ds);
- dt.strTime:=SYSTEM.ADR(ts);
- res:=DosL.StrToDate(SYSTEM.ADR(dt));
- dat:=dt.date;
- RETURN res=0;
- END ConvDate;
-
-
-
- (*---------------------------------------------------------------------------
- ParseDosArgs: Parse argument string and set "args" record accordingly
- ---------------------------------------------------------------------------*)
- PROCEDURE ParseDosArgs(VAR s: StringT);
- VAR
- ts: StringT;
- dargs: DosArgs;
- rc: SYSTEM.ADDRESS;
- pres: LONGINT;
- BEGIN
- rdargsPtr:=DosL.AllocDosObject(DosD.dosRdArgs,NIL);
- IF rdargsPtr=NIL THEN
- MyError("in command line parser",
- "not enough memory",
- "can't parse command line arguments",TRUE);
- END;
-
- rdargsPtr^.source.buffer:=SYSTEM.ADR(s);
- rdargsPtr^.source.length:=String.Length(s);
- rdargsPtr^.source.curChr:=0;
- rdargsPtr^.daList:=0;
- rdargsPtr^.buffer:=NIL;
- rdargsPtr^.bufSiz:=0;
- rdargsPtr^.extHelp:=SYSTEM.ADR(extendedhelp);
- rdargsPtr^.flags:=DosD.RDAFlagSet{};
-
- IF DosL.ReadArgs(SYSTEM.ADR(argTemplate),SYSTEM.ADR(dargs),rdargsPtr)=NIL THEN
- MyError("in command line parser","illegal command line",s,TRUE);
- END;
-
- IF dargs.file#NIL THEN
- String.Copy(args.xferstat,dargs.file^);
- END;
-
- IF dargs.data#NIL THEN
- String.Copy(args.xstatdata,dargs.data^);
- END;
-
- args.verbose:=(dargs.verb#0);
-
- IF dargs.fdate#NIL THEN
- IF ConvDate(dargs.fdate^,"00:00:00",args.from) THEN
- MyError("in command line","illegal FDATE",dargs.fdate^,TRUE);
- END;
- END;
-
- IF dargs.tdate#NIL THEN
- IF ConvDate(dargs.tdate^,"23:59:59",args.to) THEN
- MyError("in command line","illegal TDATE",dargs.tdate^,TRUE);
- END;
- END;
-
- IF dargs.host#NIL THEN
- pres:=DosL.ParsePatternNoCase(dargs.host,
- SYSTEM.ADR(args.host),
- SIZE(args.host)-2); (* just being cautious :-) *)
- IF pres<0 THEN
- MyError("in command line","pattern in HOST too complex",dargs.host^,TRUE);
- END;
- args.perhost:=(pres=1);
- END;
-
- IF args.in THEN
- args.in:=(dargs.noin=0);
- END;
-
- IF args.out THEN
- args.out:=(dargs.noout=0);
- END;
-
- args.quiet:=args.quiet OR (dargs.quiet#0);
-
- args.stderr:=(dargs.stderr#0);
-
- IF dargs.month#NIL THEN
- ts:="01-";
- String.Concat(ts,dargs.month^);
- IF ConvDate(ts,"00:00:00",args.from) THEN
- MyError("in command line","illegal MONTH",dargs.month^,TRUE);
- END;
-
- ts:="31-";
- String.Concat(ts,dargs.month^);
- IF ConvDate(ts,"23:59:59",args.to) THEN
- ts:="30-";
- String.Concat(ts,dargs.month^);
- IF ConvDate(ts,"23:59:59",args.to) THEN
- ts:="29-";
- String.Concat(ts,dargs.month^);
- IF ConvDate(ts,"23:59:59",args.to) THEN
- ts:="28-";
- String.Concat(ts,dargs.month^);
- IF ConvDate(ts,"23:59:59",args.to) THEN
- MyError("in command line","illegal MONTH",dargs.month^,TRUE);
- END;
- END;
- END;
- END;
-
- IF (DosL.dosVersion<40) THEN
- MyError("WARNING: Due to a bug in the Kickstart version you're using, the MONTHLY",
- " option will yield unexpected results under certain conditions.",
- " You should probably use FROMDATE and PERIOD instead.",
- FALSE);
- END;
- IF (DosL.dosVersion=40) THEN
- MyError("WARNING: Some revisions of your Kickstart may still have a bug which might",
- " cause the MONTHLY option to yield unexpected results under certain",
- " conditions. You should probably use FROMDATE and PERIOD instead.",
- FALSE);
- END;
-
- args.monthly:=TRUE;
- END;
-
- IF (dargs.period#NIL) & (dargs.last#NIL) & (dargs.period^#0) & (dargs.last^#0) THEN
- MyError("in command line","PREVIOUS and LAST specified simultaneously",s,TRUE);
- END;
-
- IF dargs.period#NIL THEN
- args.to:=args.from;
- args.to.days:=args.to.days+dargs.period^;
- END;
-
- IF dargs.last#NIL THEN
- args.from:=args.to;
- args.from.days:=args.from.days-dargs.last^;
- END;
-
- IF rdargsPtr#NIL THEN
- DosL.FreeArgs(rdargsPtr);
- DosL.FreeDosObject(DosD.dosRdArgs,rdargsPtr);
- rdargsPtr:=NIL;
- END;
- END ParseDosArgs;
-
-
-
- (*---------------------------------------------------------------------------
- PresetArgs: Set default values for "args" record
- ---------------------------------------------------------------------------*)
- PROCEDURE PresetArgs();
- BEGIN
- args.xstatdata:=defxstatdata;
- args.xferstat:=defxferstat;
- args.verbose:=FALSE;
- args.from.days:=0; (* "01-JAN-78" "00:00:00" *)
- args.from.minute:=0; (* "01-JAN-78" "00:00:00" *)
- args.from.tick:=0; (* "01-JAN-78" "00:00:00" *)
- DosL.DateStamp(SYSTEM.ADR(args.to));
- args.host[0]:=ASCII.nul;
- args.in:=TRUE;
- args.out:=TRUE;
- args.quiet:=FALSE;
- args.monthly:=FALSE;
- END PresetArgs;
-
-
-
- (*---------------------------------------------------------------------------
- GetLine: Read a line from the input file
- ---------------------------------------------------------------------------*)
- PROCEDURE GetLine(VAR line: StringT);
- VAR
- res: LONGINT;
- BEGIN
- IF inf.fh#NIL THEN
- res:=DosL.FGets(inf.fh,SYSTEM.ADR(line),strlen);
- inf.eof:=(res=0) AND (DosL.IoErr()=0);
- END;
- IF (String.Length(line)>0) AND (line[String.Length(line)-1]=ASCII.lf) THEN
- line[String.Length(line)-1]:="\o";
- END;
- END GetLine;
-
-
-
- (*---------------------------------------------------------------------------
- GetLog: Read a two-line log entry from Xferstat
- ---------------------------------------------------------------------------*)
- PROCEDURE GetLog(VAR l1,l2: StringT);
- BEGIN
- l1[0]:="\o";
- l2[0]:="\o";
- REPEAT
- Break.TestBreak();
- GetLine(l1);
- UNTIL (inf.eof) OR (l1[0]="<") OR (l1[0]=">");
- IF NOT inf.eof THEN
- GetLine(l2);
- ELSE
- l1[0]:="\o";
- END;
- END GetLog;
-
-
-
- (*---------------------------------------------------------------------------
- Skip: Skip the first "word" of a string; refer to "Get()" for an
- explanation ot the term "word"
- ---------------------------------------------------------------------------*)
- PROCEDURE Skip(VAR s: StringT);
- VAR
- dum: BOOLEAN;
- x: StringT;
- BEGIN
- dum:=Get(s,x);
- END Skip;
-
-
-
- (*---------------------------------------------------------------------------
- GetLNum: Get the first "word" of a string, and convert it to a LONGINT
- (if possible). Refer to "Get()" for an explanation of the
- term "word"
- ---------------------------------------------------------------------------*)
- PROCEDURE GetLNum(VAR s: StringT; VAR x: LONGINT): BOOLEAN;
- VAR
- sn: StringT;
- res: LONGINT;
- BEGIN
- IF Get(s,sn) THEN
- res:=DosL.StrToLong(SYSTEM.ADR(sn),x);
- RETURN res>0;
- END;
- RETURN FALSE;
- END GetLNum;
-
-
-
- (*---------------------------------------------------------------------------
- GetNum: Call "GetLNum()" and make sure the result is an INTEGER
- ---------------------------------------------------------------------------*)
- PROCEDURE GetNum(VAR s: StringT; VAR x: INTEGER): BOOLEAN;
- VAR
- l: LONGINT;
- BEGIN
- IF GetLNum(s,l) THEN
- IF l>MAX(INTEGER) THEN
- x:=0;
- ELSE
- x:=INTEGER(l);
- RETURN TRUE;
- END;
- ELSE
- x:=0;
- END;
- RETURN FALSE;
- END GetNum;
-
-
-
- (*---------------------------------------------------------------------------
- GetReal: Get the first "word" of a string, and convert it to a REAL
- (if possible). Refer to "Get()" for an explanation of the
- term "word"
- ---------------------------------------------------------------------------*)
- PROCEDURE GetReal(VAR s: StringT; VAR x: REAL): BOOLEAN;
- VAR
- sn: StringT;
- err: BOOLEAN;
- BEGIN
- IF Get(s,sn) THEN
- RealConversions.StrToReal(sn,x,err);
- RETURN NOT err;
- END;
- RETURN FALSE;
- END GetReal;
-
-
-
- (*---------------------------------------------------------------------------
- GetTime: Get the first two "words" of a string, and convert them to
- Dos format date and time (if possible). Refer to "Get()" for an
- explanation of the term "word"
- ---------------------------------------------------------------------------*)
- PROCEDURE GetTime(VAR s: StringT; VAR t: LONGINT; VAR dos: DosD.Date): BOOLEAN;
- VAR
- ds,ts: StringT;
- dt: DosD.DateTime;
- BEGIN
- IF Get(s,ds) THEN
- IF Get(s,ts) THEN
- dt.format:=DosD.formatCDN;
- dt.flags:=DosD.DateTimeFlagSet{};
- dt.strDate:=SYSTEM.ADR(ds);
- dt.strTime:=SYSTEM.ADR(ts);
-
- IF DosL.StrToDate(SYSTEM.ADR(dt))#0 THEN
- dos:=dt.date;
- t:=(dos.days MOD 7)*dmult+(dos.minute*mmult)+(dos.tick DIV 50);
- RETURN TRUE;
- END;
- END;
- END;
- RETURN FALSE;
- END GetTime;
-
-
-
- (*---------------------------------------------------------------------------
- TestXStatHeader: Check if the input file's header is "H XSTAT DATA"
- ---------------------------------------------------------------------------*)
- PROCEDURE TestXStatHeader();
- VAR
- s1,s,t: StringT;
- c: CHAR;
- BEGIN
- GetLine(s);
- s1:=s;
- c:=GetHead(s);
- IF c="H" THEN
- IF Get(s,t) OR (String.Compare(t,"XSTAT")#0) THEN
- IF NOT (Get(s,t) OR (String.Compare(t,"DATA")#0)) THEN
- MyError("in XStat.data header",
- s1,"keyword DATA not found",TRUE);
- END;
- ELSE
- MyError("in XStat.data header",
- s1,"keyword XSTAT not found",TRUE);
- END;
- ELSE
- MyError("in XStat.data header",
- s1,"header identifier H not found",TRUE);
- END;
- END TestXStatHeader;
-
-
-
- (*---------------------------------------------------------------------------
- ParseXSDataNEntry: Parse a "host name" line from XStat.data
- ---------------------------------------------------------------------------*)
- PROCEDURE ParseXSDataNEntry(VAR s,s1: StringT; VAR ccxsd: XSDataPtr);
- VAR
- cxsd: XSDataPtr;
- xsd: XSData;
- t: StringT;
- first: BOOLEAN;
- BEGIN
- IF (ccxsd#NIL) AND (ccxsd^.costlist=NIL) AND (NOT ccxsd^.local) AND (NOT ccxsd^.ignore) THEN
- (* -------- obsolete --------
- MyError("in XStat.data",
- "N-line without subsequent C-lines for",
- ccxsd^.host,TRUE);
- END;
- -------------------------- *)
- first:=FALSE;
- ELSE
- first:=TRUE;
- END;
-
- IF Get(s,t) THEN
- REPEAT
- xsd.next:=NIL;
- xsd.host:=t;
- xsd.costlist:=NIL;
- xsd.offset:=0;
- xsd.local:=FALSE;
- xsd.ignore:=FALSE;
- xsd.double:=FALSE;
- cxsd:=xsroot;
- IF xsroot#NIL THEN
- IF String.Compare(xsd.host,cxsd^.host)=0 THEN
- MyError("in XStat.data",
- "host name already used for",
- xsd.host,TRUE);
- END;
- WHILE cxsd^.next#NIL DO
- IF String.Compare(xsd.host,cxsd^.next^.host)=0 THEN
- MyError("in XStat.data",
- "host name already used for",
- xsd.host,TRUE);
- END;
- Break.TestBreak();
- cxsd:=cxsd^.next;
- END;
- cxsd^.next:=ExecL.AllocMem(SIZE(xsd),ExecD.MemReqSet{});
- IF cxsd^.next=NIL THEN
- MyError("","not enough memory","",TRUE);
- END;
- cxsd:=cxsd^.next;
- IF first THEN
- first:=FALSE;
- ccxsd:=cxsd;
- ELSE
- xsd.double:=TRUE;
- END;
- ELSE
- xsroot:=ExecL.AllocMem(SIZE(xsd),ExecD.MemReqSet{});
- IF xsroot=NIL THEN
- MyError("","not enough memory","",TRUE);
- END;
- cxsd:=xsroot;
- ccxsd:=cxsd;
- first:=FALSE;
- END;
- cxsd^:=xsd;
- UNTIL NOT Get(s,t);
- ELSE
- MyError("in XStat.data",
- s1,"no host name found in N-line",TRUE);
- END;
- END ParseXSDataNEntry;
-
-
-
- (*---------------------------------------------------------------------------
- ParseXSDataCEntry: Parse a "cost info" line from XStat.data
- ---------------------------------------------------------------------------*)
- PROCEDURE ParseXSDataCEntry(VAR s,s1: StringT; VAR cxsd: XSDataPtr);
- VAR
- t,t1: StringT;
- cost: Cost;
- ccp: CostPtr;
- temp: INTEGER;
- c0{R.D7},c1{R.D6}: CHAR;
- BEGIN
- IF cxsd=NIL THEN
- MyError("in XStat.data",s1,"C-line without corresponding N-line",TRUE);
- END;
-
- cost.next:=NIL;
- cost.stop:=7*dmult-1;
-
- IF NOT Get(s,t) THEN
- MyError("in XStat.data",s1,"illegal or missing start time in C-line",TRUE);
- END;
-
- IF String.Compare(t,"LOCAL")=0 THEN
- IF cxsd^.costlist#NIL THEN
- MyError("in XStat.data",s1,"LOCAL must be the only C-line for the host",TRUE);
- ELSIF cxsd^.local#FALSE THEN
- MyError("in XStat.data",s1,"duplicate LOCAL after N-line",TRUE);
- ELSIF cxsd^.ignore#FALSE THEN
- MyError("in XStat.data",s1,"LOCAL and IGNORE are mutually exclusive",TRUE);
- ELSE
- cxsd^.local:=TRUE;
- END;
-
- ELSIF String.Compare(t,"IGNORE")=0 THEN
- IF cxsd^.costlist#NIL THEN
- MyError("in XStat.data",s1,"IGNORE must be the only C-line for the host",TRUE);
- ELSIF cxsd^.ignore#FALSE THEN
- MyError("in XStat.data",s1,"duplicate IGNORE after N-line",TRUE);
- ELSIF cxsd^.local#FALSE THEN
- MyError("in XStat.data",s1,"LOCAL and IGNORE are mutually exclusive",TRUE);
- ELSE
- cxsd^.ignore:=TRUE;
- END;
-
- ELSIF (t[2]="-") AND (t[5]=":") AND (t[8]=":") AND (String.Length(t)=11) THEN
- c0:=t[0]; c1:=t[1];
- IF (c0="S") AND (c1="U") THEN
- cost.start:=dsun;
- ELSIF (c0="M") AND (c1="O") THEN
- cost.start:=dmon;
- ELSIF (c0="T") AND (c1="U") THEN
- cost.start:=dtue;
- ELSIF (c0="W") AND (c1="E") THEN
- cost.start:=dwed;
- ELSIF (c0="T") AND (c1="H") THEN
- cost.start:=dthu;
- ELSIF (c0="F") AND (c1="R") THEN
- cost.start:=dfri;
- ELSIF (c0="S") AND (c1="A") THEN
- cost.start:=dsat;
- ELSE
- MyError("in XStat.data",s1,"illegal day of week in start time in C-line",TRUE);
- END;
-
- t[0]:=" "; t[1]:=" "; t[2]:=" "; t[5]:=" "; t[8]:=" ";
-
- IF NOT GetNum(t,temp) THEN
- MyError("in XStat.data",s1,"illegal 'hours' part in start time in C-line",TRUE);
- END;
- cost.start:=cost.start+hmult*LONGINT(temp);
-
- IF NOT GetNum(t,temp) THEN
- MyError("in XStat.data",s1,"illegal 'minutes' part in start time in C-line",TRUE);
- END;
- cost.start:=cost.start+mmult*LONGINT(temp);
-
- IF NOT GetNum(t,temp) THEN
- MyError("in XStat.data",s1,"illegal 'seconds' part in start time in C-line",TRUE);
- END;
- cost.start:=cost.start+smult*LONGINT(temp);
-
- IF NOT GetReal(s,cost.unit) THEN
- MyError("in XStat.data",s1,"illegal 'seconds per unit' in C-line",TRUE);
- END;
-
- IF NOT GetReal(s,cost.mpu) THEN
- MyError("in XStat.data",s1,"illegal 'money per unit' in C-line",TRUE);
- END;
-
- IF (cxsd^.local) OR (cxsd^.ignore) THEN
- MyError("in XStat.data",s1,"no more C-lines allowed after LOCAL or IGNORE",TRUE);
- END;
-
- ccp:=cxsd^.costlist;
- IF ccp#NIL THEN
- WHILE ccp^.next#NIL DO
- Break.TestBreak();
- ccp:=ccp^.next;
- END;
- ccp^.next:=ExecL.AllocMem(SIZE(Cost),ExecD.MemReqSet{});
- IF ccp^.next=NIL THEN
- MyError("","not enough memory","",TRUE);
- END;
- ccp^.next^:=cost;
- ccp^.stop:=cost.start-1;
- IF ccp^.stop<ccp^.start THEN
- MyError("in XStat.data","connect start time sequence error",
- "(you probably swapped some C-lines, or you left out an N-line)",TRUE);
- END;
- ELSE
- cxsd^.costlist:=ExecL.AllocMem(SIZE(Cost),ExecD.MemReqSet{});
- IF cxsd^.costlist=NIL THEN
- MyError("","not enough memory","",TRUE);
- END;
- cxsd^.costlist^:=cost;
- IF cost.start#0 THEN
- MyError("in XStat.data",s1,"start time in first C-line must be SU-00:00:00",TRUE);
- END;
- END;
- ELSE
- MyError("in XStat.data",s1,"illegal or missing start time in C-line",TRUE);
- END;
-
- (* cxsd will always be #NIL here... *)
- WHILE cxsd^.next#NIL DO
- (* copy costlistPtr to next^.costlistPtr *)
- cxsd^.next^.costlist:=cxsd^.costlist;
- cxsd^.next^.local:=cxsd^.local;
- cxsd^.next^.ignore:=cxsd^.ignore;
- (* advance cxsd to cxsd#.next; note: cxsd is a VAR parameter, so this *)
- (* will only happen for the first C-record found... from then on, *)
- (* this costlistPtr copying will not be done again. *)
- cxsd:=cxsd^.next;
- END;
- END ParseXSDataCEntry;
-
-
-
- (*---------------------------------------------------------------------------
- ParseXSDataSEntry: Parse a "currency sign info" line from XStat.data
- ---------------------------------------------------------------------------*)
- PROCEDURE ParseXSDataSEntry(VAR s,s1: StringT; VAR cxsd: XSDataPtr);
- VAR
- t: StringT;
- BEGIN
- IF (cxsd#NIL) THEN
- MyError("in XStat.data",
- s1,"S-line after the first connection cost record",TRUE);
- END;
-
- IF Get(s,t) THEN
- IF currency[0]=ASCII.nul THEN
- currency:=t;
- KillSpaces(currency);
- ELSE
- MyError("in XStat.data",
- s1,"duplicate S-line found",TRUE);
- END;
- ELSE
- MyError("in XStat.data",
- s1,"missing currency sign in S-line",TRUE);
- END;
- END ParseXSDataSEntry;
-
-
-
- (*---------------------------------------------------------------------------
- ParseXSDataOEntry: Parse a "currency sign info" line from XStat.data
- ---------------------------------------------------------------------------*)
- PROCEDURE ParseXSDataOEntry(VAR s,s1: StringT; VAR cxsd: XSDataPtr);
- VAR
- t: LONGINT;
- BEGIN
- IF (cxsd=NIL) THEN
- MyError("in XStat.data",
- s1,"O-line before the first connection cost record",TRUE);
- END;
-
- IF (cxsd^.costlist#NIL) THEN
- MyError("in XStat.data",
- s1,"O-line not directly after a N-line",TRUE);
- END;
-
- IF GetLNum(s,t) THEN
- IF cxsd^.offset=0 THEN
- cxsd^.offset:=t;
- ELSE
- MyError("in XStat.data",
- s1,"duplicate O-line found",TRUE);
- END;
- ELSE
- MyError("in XStat.data",
- s1,"missing offset in O-line",TRUE);
- END;
- END ParseXSDataOEntry;
-
-
-
- (*---------------------------------------------------------------------------
- GetXStatData: Read and parse the XStat.data file
- ---------------------------------------------------------------------------*)
- PROCEDURE GetXStatData();
- VAR
- s1,s: StringT;
- c: CHAR;
- cxsd: XSDataPtr;
- BEGIN
- cxsd:=NIL;
- TestXStatHeader();
- REPEAT
- Break.TestBreak();
- GetLine(s);
- s1:=s;
- c:=GetHead(s);
- CASE c OF
- |"#": (* NOP *)
- |"N": ParseXSDataNEntry(s,s1,cxsd);
- |"O": ParseXSDataOEntry(s,s1,cxsd);
- |"C": ParseXSDataCEntry(s,s1,cxsd);
- |"S": ParseXSDataSEntry(s,s1,cxsd);
- |ELSE (* unknown: NOP *)
- END;
- UNTIL inf.eof;
- END GetXStatData;
-
-
-
- (*---------------------------------------------------------------------------
- ParseLog: Parse a two-line connection data entry from Xferstat
- ---------------------------------------------------------------------------*)
- (*$ CopyDyn:=FALSE *)
- PROCEDURE ParseLog(line1,line2: StringT; VAR log: Connect): BOOLEAN;
- VAR
- c: CHAR;
- l1,l2: StringT;
- dummy: DosD.Date;
- i: INTEGER;
- BEGIN
- l1:=line1;
- l2:=line2;
- c:=GetHead(l1);
- log.callout:=(c="<");
-
- IF Get(l1,log.host) THEN
- (* ---------------------------------------------------
- IF (log.host[0]>="0") AND (log.host[0]<="9") THEN
- MyError("invalid log entry: can't find host name",line1,line2,FALSE);
- RETURN FALSE;
- END;
- --------------------------------------------------- *)
- IF line1[2]=" " THEN
- log.host:=".illegal.";
- MyError("invalid log entry: can't find host name (substituting .illegal.)",line1,line2,FALSE);
- l1:=line1;
- c:=GetHead(l1);
- END;
- IF GetTime(l1,log.start,log.sdate) THEN
- Skip(l1);
- IF GetTime(l1,log.stop,dummy) THEN
- c:=GetHead(l2);
- IF c="|" THEN
- FOR i:=1 TO 9 DO
- c:=GetHead(l2); (* skip the initial "|" and the next 9 chars *)
- END;
- IF GetLNum(l2,log.inbb) THEN
- IF GetLNum(l2,log.outbb) THEN
- Skip(l2);
- IF GetLNum(l2,log.inbn) THEN
- IF GetLNum(l2,log.outbn) THEN
- RETURN TRUE;
- ELSE
- MyError("invalid log entry: can't get net send bytes",line1,line2,FALSE);
- END;
- ELSE
- MyError("invalid log entry: can't get net receive bytes",line1,line2,FALSE);
- END;
- ELSE
- MyError("invalid log entry: can't get gross send bytes",line1,line2,FALSE);
- END;
- ELSE
- MyError("invalid log entry: can't get gross receive bytes",line1,line2,FALSE);
- END;
- ELSE
- MyError("invalid log entry: illegal continuation line header ",line1,line2,FALSE);
- END;
- ELSE
- MyError("invalid log entry: can't compute end time",line1,line2,FALSE);
- END;
- ELSE
- MyError("invalid log entry: can't compute start time",line1,line2,FALSE);
- END;
- ELSE
- MyError("invalid log entry: can't find host name",line1,line2,FALSE);
- END;
- RETURN FALSE;
- END ParseLog;
-
-
-
- (*---------------------------------------------------------------------------
- ComputeCost: Compute the cost for a single connect
- ---------------------------------------------------------------------------*)
- PROCEDURE ComputeCost(VAR log: Connect): BOOLEAN;
- VAR
- cxsd: XSDataPtr;
- ccost: CostPtr;
- time: REAL;
- flatunits: LONGINT;
- BEGIN
- log.seconds:=log.stop-log.start+1;
- IF log.seconds<0 THEN
- log.seconds:=log.seconds+7*dmult;
- END;
-
- cxsd:=xsroot;
- WHILE (cxsd#NIL) AND (String.Compare(cxsd^.host,log.host)#0) DO
- Break.TestBreak();
- cxsd:=cxsd^.next;
- END;
- IF cxsd=NIL THEN
- cxsd:=xsroot;
- WHILE (cxsd#NIL) AND (String.Compare(cxsd^.host,".default.")#0) DO
- Break.TestBreak();
- cxsd:=cxsd^.next;
- END;
- IF cxsd=NIL THEN
- MyError("ERROR: no connection cost data found for host",log.host,
- "extend your XStat.data file or use '.default.'!",FALSE);
- RETURN FALSE;
- END;
- END;
-
- log.start:=log.start-cxsd^.offset;
- log.seconds:=log.seconds+cxsd^.offset;
-
- IF NOT (cxsd^.local OR cxsd^.ignore) THEN
- ccost:=cxsd^.costlist;
- WHILE log.start>ccost^.stop DO
- Break.TestBreak();
- ccost:=ccost^.next;
- END;
- END;
-
- IF log.stop<log.start THEN
- log.stop:=log.stop+7*dmult;
- END;
- log.cost:=0.0;
- log.units:=0;
-
- IF NOT (cxsd^.local OR cxsd^.ignore) THEN
- time:=REAL(log.start);
- REPEAT
- Break.TestBreak();
- log.cost:=log.cost+ccost^.mpu;
- IF ccost^.unit=0.0 THEN
- (* flat rate *)
- flatunits:=1;
- IF log.stop>ccost^.stop THEN
- time:=REAL(ccost^.stop)+1.0;
- ELSE
- time:=REAL(log.stop)+1.0;
- END;
- ELSE
- time:=time+ccost^.unit;
- INC(log.units);
- IF flatunits#0 THEN
- INC(log.units);
- flatunits:=0;
- END;
- END;
- IF flatunits#0 THEN
- INC(log.units);
- flatunits:=0;
- END;
-
- IF LONGINT(time)>ccost^.stop THEN
- ccost:=ccost^.next;
- IF ccost=NIL THEN
- ccost:=cxsd^.costlist;
- time:=time-REAL(7*dmult);
- log.stop:=log.stop-7*dmult;
- END;
- END;
- UNTIL LONGINT(time)>log.stop;
- END;
-
- log.local:=cxsd^.local;
- log.ignore:=cxsd^.ignore;
- RETURN TRUE;
- END ComputeCost;
-
-
-
- (*---------------------------------------------------------------------------
- Date2Str: Convert a datestamp to a string
- ---------------------------------------------------------------------------*)
- PROCEDURE Date2Str(date: DosD.Date; VAR strdate,strtime: ARRAY OF CHAR);
- VAR
- dt: DosD.DateTime;
- BEGIN
- dt.date:=date;
- dt.format:=DosD.formatDOS;
- dt.flags:=DosD.DateTimeFlagSet{};
- dt.strDay:=NIL;
- dt.strDate:=SYSTEM.ADR(strdate);
- dt.strTime:=SYSTEM.ADR(strtime);
- IF DosL.DateToStr(SYSTEM.ADR(dt))=0 THEN
- String.Copy(strdate,"??-???-??");
- String.Copy(strtime,"??:??:??");
- END;
-
- WHILE (String.Length(strdate)>0) AND (strdate[String.Length(strdate)-1]=" ") DO
- (* locale may pad localized strings with spaces under certain conditions. *)
- (* let's get rid of those unneeded blanks. *)
- strdate[String.Length(strdate)-1]:="\o";
- END;
-
- WHILE (String.Length(strtime)>0) AND (strtime[String.Length(strtime)-1]=" ") DO
- (* locale may pad localized strings with spaces under certain conditions. *)
- (* let's get rid of those unneeded blanks. *)
- strtime[String.Length(strtime)-1]:="\o";
- END;
- END Date2Str;
-
-
- (*---------------------------------------------------------------------------
- PutChar: put a char into the output buffer
- ---------------------------------------------------------------------------*)
- PROCEDURE PutChar(chr: CHAR; VAR inx: LONGINT; VAR buf: FormBuffer);
- BEGIN
- buf[inx]:=chr;
- INC(inx);
- IF inx>=formlen-1 THEN
- MyError("","output buffer overflow",
- "your output format is too complex",TRUE);
- END;
- END PutChar;
-
-
-
- (*---------------------------------------------------------------------------
- PutStr: put a string into the output buffer
- ---------------------------------------------------------------------------*)
- PROCEDURE PutStr(str: ARRAY OF CHAR; wid: LONGINT; VAR inx: LONGINT; VAR buf: FormBuffer);
- VAR
- i: LONGINT;
- BEGIN
- i:=0;
- REPEAT
- PutChar(str[i],inx,buf);
- INC(i);
- IF inx>=formlen-1 THEN
- MyError("","output buffer overflow",
- "your output format is too complex",TRUE);
- END;
- UNTIL i=String.Length(str);
-
- WHILE i<wid DO
- INC(i);
- PutChar(" ",inx,buf);
- END;
- END PutStr;
-
-
-
- (*---------------------------------------------------------------------------
- PutInt: convert int->str, put it into the output buffer
- ---------------------------------------------------------------------------*)
- PROCEDURE PutInt(val: LONGINT; wid: LONGINT; VAR inx: LONGINT; VAR buf: FormBuffer);
- VAR
- tmp: ARRAY[0..100] OF CHAR;
- err: BOOLEAN;
- BEGIN
- Conversions.ValToStr(val,TRUE,tmp,10,wid," ",err);
- PutStr(tmp,wid,inx,buf);
- END PutInt;
-
-
-
- (*---------------------------------------------------------------------------
- PutReal: convert real->str, put it into the output buffer
- ---------------------------------------------------------------------------*)
- PROCEDURE PutReal(val: REAL; wid,decimals: LONGINT; VAR inx: LONGINT; VAR buf: FormBuffer);
- VAR
- tmp: ARRAY[0..100] OF CHAR;
- err: BOOLEAN;
- BEGIN
- IF wid<decimals+2 THEN
- wid:=decimals+2;
- END;
- RealConversions.RealToStr(val,tmp,wid,decimals,FALSE,err);
- PutStr(tmp,wid,inx,buf);
- END PutReal;
-
-
-
- (*---------------------------------------------------------------------------
- NextChar: get the next char from the buffer string, increment the index
- return ASCII.nul when end of buffer is reached
- ---------------------------------------------------------------------------*)
- (*$ CopyDyn:=FALSE *)
- PROCEDURE NextChar(buf: FormBuffer; VAR inx: LONGINT): CHAR;
- VAR
- result: CHAR;
- BEGIN
- result:=buf[inx];
- IF result#"\o" THEN
- INC(inx);
- END;
- RETURN result;
- END NextChar;
-
-
-
- (*---------------------------------------------------------------------------
- FormatStats: Format the output and display it. Replaces the old
- DisplayStats procedure.
- ---------------------------------------------------------------------------*)
- (*$ CopyDyn:=FALSE *)
- PROCEDURE FormatStats(tot: Statistics; form: FormBuffer);
- VAR
- out: FormBuffer;
- c,c1: CHAR;
- inx,srcinx: LONGINT;
- width: LONGINT;
- date,time: StringT;
- BEGIN
- srcinx:=0;
- inx:=0;
- c:=NextChar(form,srcinx);
- REPEAT
- width:=0;
- IF c="&" THEN
- width:=0;
- c:=NextChar(form,srcinx);
- IF c="&" THEN
- PutChar(c,inx,out);
- c:=NextChar(form,srcinx);
- ELSE
- WHILE (c>="0") AND (c<="9") DO
- width:=width*10+INTEGER(c)-INTEGER("0");
- IF width>=100 THEN
- MyError("in STATFORM output format","illegal field width specified",
- "the field width must not exceed 99 characters",TRUE);
- END;
- c:=NextChar(form,srcinx);
- END;
- c1:=NextChar(form,srcinx);
-
- IF (c="F") AND (c1="F") THEN (* host name *)
- PutChar("\f",inx,out);
-
- ELSIF (c="L") AND (c1="F") THEN (* host name *)
- PutChar("\n",inx,out);
-
- ELSIF (c="H") AND (c1="N") THEN (* host name *)
- IF tot.name[0]="\o" THEN
- PutStr("TOTAL",width,inx,out);
- ELSE
- PutStr(tot.name,width,inx,out);
- END;
-
- ELSIF (c="C") AND (c1="D") THEN (* call direction (long) *)
- IF tot.in THEN
- PutStr("incoming",width,inx,out);
- ELSE
- PutStr("outgoing",width,inx,out);
- END;
-
- ELSIF (c="c") AND (c1="d") THEN (* call direction (short) *)
- IF tot.in THEN
- PutChar("<",inx,out);
- ELSE
- PutChar(">",inx,out);
- END;
-
- ELSIF (c="F") AND (c1="D") THEN (* from date *)
- Date2Str(args.from,date,time);
- PutStr(date,width,inx,out);
- PutChar(" ",inx,out);
- PutStr(time,width,inx,out);
-
- ELSIF (c="T") AND (c1="D") THEN (* to date *)
- Date2Str(args.to,date,time);
- PutStr(date,width,inx,out);
- PutChar(" ",inx,out);
- PutStr(time,width,inx,out);
-
- ELSIF (c="A") AND (c1="C") THEN (* number of connects (total) *)
- PutInt(tot.connects,width,inx,out);
-
- ELSIF (c="I") AND (c1="C") THEN (* number of connects (ignored) *)
- PutInt(tot.ignoreconnects,width,inx,out);
-
- ELSIF (c="L") AND (c1="C") THEN (* number of connects (local) *)
- PutInt(tot.localconnects,width,inx,out);
-
- ELSIF (c="O") AND (c1="T") THEN (* online time (total) *)
- PutInt(tot.seconds,width,inx,out);
-
- ELSIF (c="o") AND (c1="t") THEN (* online time (average) *)
- IF tot.connects=0 THEN
- PutInt(0,width,inx,out);
- ELSE
- PutInt((tot.seconds+tot.connects-1) DIV tot.connects,
- width,inx,out);
- END;
-
- ELSIF (c="U") AND (c1="N") THEN (* units (total) *)
- PutInt(tot.units,width,inx,out);
-
- ELSIF (c="u") AND (c1="n") THEN (* units (average *)
- IF tot.connects=0 THEN
- PutReal(0.0,width,3,inx,out);
- ELSE
- PutReal(REAL(tot.units)/REAL(tot.connects),
- width,3,inx,out);
- END;
-
- ELSIF (c="C") AND (c1="O") THEN (* cost (total) *)
- PutReal(tot.cost,width,3,inx,out);
-
- ELSIF (c="c") AND (c1="o") THEN (* cost (average *)
- IF tot.connects=0 THEN
- PutReal(0.0,width,3,inx,out);
- ELSE
- PutReal(tot.cost/REAL(tot.connects),
- width,3,inx,out);
- END;
-
- ELSIF (c="G") AND (c1="R") THEN (* gross read (total) *)
- PutInt(tot.inbb,width,inx,out);
-
- ELSIF (c="g") AND (c1="r") THEN (* gross read (average) *)
- IF tot.connects=0 THEN
- PutInt(0,width,inx,out);
- ELSE
- PutInt((tot.inbb+tot.connects-1) DIV tot.connects,
- width,inx,out);
- END;
-
- ELSIF (c="G") AND (c1="S") THEN (* gross send (total) *)
- PutInt(tot.outbb,width,inx,out);
-
- ELSIF (c="g") AND (c1="s") THEN (* gross send (average) *)
- IF tot.connects=0 THEN
- PutInt(0,width,inx,out);
- ELSE
- PutInt((tot.outbb+tot.connects-1) DIV tot.connects,
- width,inx,out);
- END;
-
- ELSIF (c="N") AND (c1="R") THEN (* net read (total) *)
- PutInt(tot.inbn,width,inx,out);
-
- ELSIF (c="n") AND (c1="r") THEN (* net read (average) *)
- IF tot.connects=0 THEN
- PutInt(0,width,inx,out);
- ELSE
- PutInt((tot.inbn+tot.connects-1) DIV tot.connects,
- width,inx,out);
- END;
-
- ELSIF (c="N") AND (c1="S") THEN (* net send (total) *)
- PutInt(tot.outbn,width,inx,out);
-
- ELSIF (c="n") AND (c1="s") THEN (* net send (average) *)
- IF tot.connects=0 THEN
- PutInt(0,width,inx,out);
- ELSE
- PutInt((tot.outbn+tot.connects-1) DIV tot.connects,
- width,inx,out);
- END;
-
- ELSIF (c="G") AND (c1="C") THEN (* gross cps (average) *)
- IF tot.seconds=0 THEN
- PutInt(0,width,inx,out);
- ELSE
- PutInt((tot.inbb+tot.outbb) DIV tot.seconds,
- width,inx,out);
- END;
-
- ELSIF (c="g") AND (c1="p") THEN (* gross cps (peak) *)
- PutInt(tot.bpeak,width,inx,out);
-
- ELSIF (c="N") AND (c1="C") THEN (* net cps (average) *)
- IF tot.seconds=0 THEN
- PutInt(0,width,inx,out);
- ELSE
- PutInt((tot.inbn+tot.outbn) DIV tot.seconds,
- width,inx,out);
- END;
-
- ELSIF (c="n") AND (c1="p") THEN (* net cps (peak) *)
- PutInt(tot.npeak,width,inx,out);
-
- ELSIF (c="C") AND (c1="G") THEN (* gross cost (average) *)
- IF tot.inbb+tot.outbb=0 THEN
- PutReal(0.0,width,3,inx,out);
- ELSE
- PutReal(tot.cost/REAL(tot.inbb+tot.outbb)*1048576.0,width,3,inx,out);
- END;
-
- ELSIF (c="C") AND (c1="N") THEN (* net cost (average) *)
- IF tot.inbn+tot.outbn=0 THEN
- PutReal(0.0,width,3,inx,out);
- ELSE
- PutReal(tot.cost/REAL(tot.inbn+tot.outbn)*1048576.0,width,3,inx,out);
- END;
-
- ELSIF (c="C") AND (c1="S") THEN (* currency symbol *)
- PutStr(currency,width,inx,out);
-
- ELSE
- out:="field type: ";
- out[12]:=c;
- out[13]:=c1;
- MyError("in STATFORM output format","illegal field type specified",out,TRUE);
- END;
- c:=NextChar(form,srcinx);
- END;
- ELSE
- PutChar(c,inx,out);
- c:=NextChar(form,srcinx);
- END;
- UNTIL srcinx=String.Length(form);
- InOut.WriteString(out);
- END FormatStats;
-
-
-
- (*---------------------------------------------------------------------------
- FormatVerbs: Format the output for DisplayVerb
- ---------------------------------------------------------------------------*)
- PROCEDURE FormatVerbs(log: Connect; form: FormBuffer);
- VAR
- out: FormBuffer;
- c,c1: CHAR;
- inx,srcinx: LONGINT;
- width: LONGINT;
- date,time: StringT;
- BEGIN
- inx:=0;
- c:=NextChar(form,srcinx);
- REPEAT
- width:=0;
- IF c="&" THEN
- width:=0;
- c:=NextChar(form,srcinx);
- IF c="&" THEN
- PutChar(c,inx,out);
- c:=NextChar(form,srcinx);
- ELSE
- WHILE (c>="0") AND (c<="9") DO
- width:=width*10+INTEGER(c)-INTEGER("0");
- IF width>=100 THEN
- MyError("in VERBFORM output format","illegal field width specified",
- "the field width must not exceed 99 characters",TRUE);
- END;
- c:=NextChar(form,srcinx);
- END;
- c1:=NextChar(form,srcinx);
-
- IF (c="F") AND (c1="F") THEN (* host name *)
- PutChar("\f",inx,out);
-
- ELSIF (c="L") AND (c1="F") THEN (* host name *)
- PutChar("\n",inx,out);
-
- ELSIF (c="H") AND (c1="N") THEN (* host name *)
- PutStr(log.host,width,inx,out);
-
- ELSIF (c="C") AND (c1="D") THEN (* call direction (long) *)
- IF NOT log.callout THEN
- PutStr("incoming",width,inx,out);
- ELSE
- PutStr("outgoing",width,inx,out);
- END;
-
- ELSIF (c="c") AND (c1="d") THEN (* call direction (short) *)
- IF NOT log.callout THEN
- PutChar("<",inx,out);
- ELSE
- PutChar(">",inx,out);
- END;
-
- ELSIF (c="F") AND (c1="D") THEN (* from date *)
- Date2Str(log.sdate,date,time);
- PutStr(date,width,inx,out);
- PutChar(" ",inx,out);
- PutStr(time,width,inx,out);
-
- ELSIF (c="I") AND (c1="C") THEN (* number of connects (ignored) *)
- IF log.ignore THEN
- PutChar("Y",inx,out);
- ELSE
- PutChar("N",inx,out);
- END;
-
- ELSIF (c="L") AND (c1="C") THEN (* number of connects (local) *)
- IF log.local THEN
- PutChar("Y",inx,out);
- ELSE
- PutChar("N",inx,out);
- END;
-
- ELSIF (c="O") AND (c1="T") THEN (* online time (total) *)
- PutInt(log.seconds,width,inx,out);
-
- ELSIF (c="U") AND (c1="N") THEN (* units (total) *)
- PutInt(log.units,width,inx,out);
-
- ELSIF (c="C") AND (c1="O") THEN (* cost (total) *)
- PutReal(log.cost,width,3,inx,out);
-
- ELSIF (c="G") AND (c1="R") THEN (* gross read (total) *)
- PutInt(log.inbb,width,inx,out);
-
- ELSIF (c="G") AND (c1="S") THEN (* gross send (total) *)
- PutInt(log.outbb,width,inx,out);
-
- ELSIF (c="N") AND (c1="R") THEN (* net read (total) *)
- PutInt(log.inbn,width,inx,out);
-
- ELSIF (c="N") AND (c1="S") THEN (* net send (total) *)
- PutInt(log.outbn,width,inx,out);
-
- ELSIF (c="G") AND (c1="C") THEN (* gross cps (average) *)
- IF log.seconds=0 THEN
- PutInt(0,width,inx,out);
- ELSE
- PutInt((log.inbb+log.outbb) DIV log.seconds,
- width,inx,out);
- END;
-
- ELSIF (c="N") AND (c1="C") THEN (* net cps (average) *)
- IF log.seconds=0 THEN
- PutInt(0,width,inx,out);
- ELSE
- PutInt((log.inbn+log.outbn) DIV log.seconds,
- width,inx,out);
- END;
-
- ELSIF (c="C") AND (c1="S") THEN (* currency symbol *)
- PutStr(currency,width,inx,out);
-
- ELSE
- out:="field type: ";
- out[12]:=c;
- out[13]:=c1;
- MyError("in VERBFORM output format","illegal field type specified",out,TRUE);
- END;
- c:=NextChar(form,srcinx);
- END;
- ELSE
- PutChar(c,inx,out);
- c:=NextChar(form,srcinx);
- END;
- UNTIL srcinx=String.Length(form);
- InOut.WriteString(out);
- END FormatVerbs;
-
-
-
- (*---------------------------------------------------------------------------
- UpdateStat: Update the totali/totalo records
- ---------------------------------------------------------------------------*)
- PROCEDURE UpdateStat(VAR tot: Statistics; VAR log: Connect);
- VAR
- tempspeed: LONGINT;
- BEGIN
- IF NOT log.ignore THEN
- INC(tot.connects);
- tot.seconds:=tot.seconds+log.seconds;
- tot.inbb:=tot.inbb+log.inbb;
- tot.outbb:=tot.outbb+log.outbb;
- tot.inbn:=tot.inbn+log.inbn;
- tot.outbn:=tot.outbn+log.outbn;
- tot.units:=tot.units+log.units;
- tot.cost:=tot.cost+log.cost;
-
- IF log.seconds#0 THEN
- tempspeed:=(log.inbb+log.outbb) DIV log.seconds;
- IF tempspeed>tot.bpeak THEN
- tot.bpeak:=tempspeed
- END;
-
- tempspeed:=(log.inbn+log.outbn) DIV log.seconds;
- IF tempspeed>tot.npeak THEN
- tot.npeak:=tempspeed
- END;
- END;
- END;
- IF log.ignore THEN
- INC(tot.ignoreconnects);
- END;
- IF log.local THEN
- INC(tot.localconnects);
- END;
- END UpdateStat;
-
-
-
- (*---------------------------------------------------------------------------
- ClearStat: Preset a Statistics-record with zeros
- ---------------------------------------------------------------------------*)
- PROCEDURE ClearStat(VAR stat: Statistics);
- BEGIN
- stat.next:=NIL;
- stat.name:="\o\o";
- stat.in:=FALSE;
- stat.connects:=0;
- stat.inbb:=0;
- stat.outbb:=0;
- stat.inbn:=0;
- stat.outbn:=0;
- stat.cost:=0.0;
- stat.units:=0;
- stat.seconds:=0;
- stat.bpeak:=0;
- stat.npeak:=0;
- stat.localconnects:=0;
- stat.ignoreconnects:=0;
- END ClearStat;
-
-
-
- (*---------------------------------------------------------------------------
- InsertStat: Insert a Statistics-record at the proper position
- into the linked list
- ---------------------------------------------------------------------------*)
- PROCEDURE InsertStat(stat: StatisticsPtr);
- VAR
- cur,prev: StatisticsPtr;
- done: BOOLEAN;
- BEGIN
- done:=FALSE;
- IF statroot=NIL THEN
- statroot:=stat;
- ELSE
- cur:=statroot;
- prev:=NIL;
- REPEAT
- IF String.Compare(stat^.name,cur^.name)=0 THEN
- IF stat^.in THEN
- (* insert before current *)
- IF prev=NIL THEN
- stat^.next:=statroot;
- statroot:=stat;
- done:=TRUE;
- ELSE
- stat^.next:=prev^.next;
- prev^.next:=stat;
- done:=TRUE;
- END;
- ELSE
- (* insert after current *)
- stat^.next:=cur^.next;
- cur^.next:=stat;
- done:=TRUE;
- END;
- ELSIF String.Compare(stat^.name,cur^.name)<0 THEN
- (* insert before current *)
- IF prev=NIL THEN
- stat^.next:=statroot;
- statroot:=stat;
- done:=TRUE;
- ELSE
- stat^.next:=prev^.next;
- prev^.next:=stat;
- done:=TRUE;
- END;
- ELSE
- prev:=cur;
- cur:=cur^.next;
- END;
- Break.TestBreak();
- UNTIL (cur=NIL) OR done;
- IF cur=NIL THEN
- prev^.next:=stat;
- END;
- END;
- END InsertStat;
-
-
-
- (*---------------------------------------------------------------------------
- FindStat: Search linked list for a specific Statistics-record
- Create that record if not found
- ---------------------------------------------------------------------------*)
- PROCEDURE FindStat(name: StringT; in: BOOLEAN): StatisticsPtr;
- VAR
- cur,next: StatisticsPtr;
- found: BOOLEAN;
- BEGIN
- next:=statroot;
- IF next#NIL THEN
- REPEAT
- cur:=next;
- next:=cur^.next;
- found:=(in=cur^.in) AND (String.Compare(name,cur^.name)=0);
- Break.TestBreak();
- UNTIL found OR (next=NIL);
- ELSE
- found:=FALSE;
- END;
- IF found THEN
- RETURN cur;
- ELSE
- next:=ExecL.AllocMem(SIZE(Statistics),ExecD.MemReqSet{});
- IF next=NIL THEN
- MyError("","not enough memory","",TRUE);
- END;
- ClearStat(next^);
- next^.name:=name;
- next^.in:=in;
- InsertStat(next);
- RETURN next;
- END;
- END FindStat;
-
-
-
- (*---------------------------------------------------------------------------
- DisplayStatList: Display the entire list of Statistics-records
- ---------------------------------------------------------------------------*)
- PROCEDURE DisplayStatList;
- VAR
- cur: StatisticsPtr;
- BEGIN
- cur:=statroot;
- WHILE cur#NIL DO
- IF (cur^.in AND args.in) OR ((NOT cur^.in) AND args.out) THEN
- FormatStats(cur^,statform);
- END;
- cur:=cur^.next;
- END;
- END DisplayStatList;
-
-
-
- BEGIN
- SYSTEM.SETREG(11,SYSTEM.ADR(version));
- rdargsPtr:=NIL;
- xsroot:=NIL;
- statroot:=NIL;
- inf.fh:=NIL;
- stdErr:=NIL;
- statform[0]:="\o";
- verbform[0]:="\o";
-
- IF (Arts.kickVersion<37) OR (DosL.dosVersion<37) THEN
- MyError("","You need OS 2.04 or later (dos.library V37 or later)",
- "",TRUE);
- END;
-
- Break.InstallException;
- PresetArgs();
-
- IF NOT Arts.wbStarted THEN
- IF Arts.dosCmdLen>0 THEN
- temp:=SYSTEM.CAST(StringTPtr,Arts.dosCmdBuf)^;
- IF temp[0]="?" THEN
- InOut.WriteString(extendedhelp);
- Arts.Exit(5);
- END;
- END;
- END;
-
- IF DosL.GetVar(SYSTEM.ADR("XSTATARGS"),SYSTEM.ADR(temp),
- strlen-2,DosD.VarFlagSet{})#-1 THEN
- index:=String.Length(temp);
- IF temp[index-1]#"\n" THEN
- temp[index]:="\n"; temp[index+1]:="\o";
- END;
- ParseDosArgs(temp);
- END;
-
- IF DosL.GetVar(SYSTEM.ADR("XSTATFORM"),SYSTEM.ADR(statform),
- SIZE(statform)-2,DosD.VarFlagSet{})=-1 THEN
- statform:=defstatform;
- END;
-
- IF DosL.GetVar(SYSTEM.ADR("XSTATVERBFORM"),SYSTEM.ADR(verbform),
- SIZE(verbform)-2,DosD.VarFlagSet{})=-1 THEN
- verbform:=defverbform;
- END;
-
- IF NOT Arts.wbStarted THEN
- IF Arts.dosCmdLen>0 THEN
- temp:=SYSTEM.CAST(StringTPtr,Arts.dosCmdBuf)^;
- ParseDosArgs(temp);
- END;
- END;
-
- IF NOT args.quiet THEN
- InOut.WriteString("\n\n"+vers+"\n");
- InOut.WriteString("⌐ Copyright 1992,1993 by Jⁿrgen Weinelt\n");
- (*$ IF BETA *)
- END;
- InOut.WriteString("============================================\n"+
- " THIS IS A BETA VERSION! DO NOT DISTRIBUTE! \n"+
- "============================================\n\n");
- (*$ ELSE *)
- InOut.WriteString("XStat is Freeware - read the docs for details.\n\n");
- END;
- (*$ ENDIF *)
-
- IF DosL.CompareDates(SYSTEM.ADR(args.from),SYSTEM.ADR(args.to))<0 THEN
- MyError("","illegal combination of parameters",
- "FROMDATE is later than TODATE",TRUE);
- END;
- IF NOT (args.in OR args.out) THEN
- MyError("","illegal combination of parameters",
- "you can't specify NOINCOM and NOOUTGO simultaneously",TRUE);
- END;
-
- IF args.stderr THEN
- stdErr:=DosL.Open(SYSTEM.ADR("CONSOLE:"),DosD.newFile);
- IF stdErr=NIL THEN
- MyError("ERROR:","can't open CONSOLE: for StdErr output",
- "guess you'll have to live without it :-)",FALSE);
- END;
- END;
-
- ClearStat(totali);
- totali.in:=TRUE;
- ClearStat(totalo);
- totalo.in:=FALSE;
-
- inf.fh:=DosL.Open(SYSTEM.ADR(args.xstatdata),DosD.readOnly);
- IF inf.fh=NIL THEN
- MyError("Can't open XStat.data!","filename was",args.xstatdata,TRUE);
- END;
- GetXStatData();
- DosL.Close(inf.fh);
- IF currency[0]=ASCII.nul THEN
- MyError("WARNING","no currency definition found in XStat.data",
- "using 'DM' as default",FALSE);
- currency:=" DM";
- END;
-
- inf.fh:=DosL.Open(SYSTEM.ADR(args.xferstat),DosD.readOnly);
- IF inf.fh=NIL THEN
- MyError("Can't open Xferstat!","filename was",args.xferstat,TRUE);
- END;
-
- REPEAT
- Break.TestBreak();
- GetLog(ln1,ln2);
- IF String.Length(ln1)+String.Length(ln2)>0 THEN
- IF ParseLog(ln1,ln2,log) THEN
- IF (args.host[0]=ASCII.nul) OR
- (DosL.MatchPatternNoCase(SYSTEM.ADR(args.host),SYSTEM.ADR(log.host))) THEN
- IF (DosL.CompareDates(SYSTEM.ADR(args.from),SYSTEM.ADR(log.sdate))>=0) AND
- (DosL.CompareDates(SYSTEM.ADR(log.sdate),SYSTEM.ADR(args.to))>=0) THEN
- IF ComputeCost(log) THEN
- IF args.verbose THEN
- FormatVerbs(log,verbform);
- END;
-
- IF args.perhost THEN
- IF NOT log.ignore THEN
- UpdateStat(FindStat(log.host,NOT log.callout)^,log);
- END;
- END;
- IF log.callout THEN
- UpdateStat(totalo,log);
- ELSE
- UpdateStat(totali,log);
- END;
-
- ELSE
- IF NOT args.quiet THEN
- InOut.WriteString("(ignoring this one)\n\n");
- END;
- END;
- END;
- END;
- ELSE
- IF NOT args.quiet THEN
- InOut.WriteString("(ignoring this one)\n\n");
- END;
- END;
- END;
- UNTIL inf.eof;
-
- IF args.perhost THEN
- DisplayStatList;
- END;
- IF args.in THEN
- FormatStats(totali,statform);
- END;
- IF args.out THEN
- FormatStats(totalo,statform);
- END;
- CLOSE
- IF rdargsPtr#NIL THEN
- DosL.FreeArgs(rdargsPtr);
- DosL.FreeDosObject(DosD.dosRdArgs,rdargsPtr);
- rdargsPtr:=NIL;
- END;
- IF xsroot#NIL THEN
- FreeXData(xsroot);
- xsroot:=NIL;
- END;
- IF statroot#NIL THEN
- FreeStat(statroot);
- statroot:=NIL;
- END;
- IF inf.fh#NIL THEN
- DosL.Close(inf.fh);
- inf.fh:=NIL;
- END;
- IF stdErr#NIL THEN
- DosL.Close(stdErr);
- stdErr:=NIL;
- END;
- END XStat.
-