home *** CD-ROM | disk | FTP | other *** search
/ ftp.wwiv.com / ftp.wwiv.com.zip / ftp.wwiv.com / pub / UTILITY / USTATV20.ARJ / USERSTAT.PAS < prev   
Pascal/Delphi Source File  |  1989-02-15  |  23KB  |  607 lines

  1. {$G1}
  2. {$P1}
  3. {$V-}
  4. Program UserStat;
  5.  
  6. {
  7.  
  8.     Well, I've decided to release this into the public domain
  9.    for two reasons, A) I don't have the time necessary to put into
  10.    this as I'm working on getting USENET feeds into WWIV, and at
  11.    getting WWIV v4.0 ported to the Macintosh (IF I ever get my Mac
  12.    back from the shop..).
  13.    B) This program could get really tricky with the right amount of
  14.    imagination and time.
  15.  
  16. I ask only two little things. 
  17.  
  18. A) You send me the changes you make.
  19.  
  20. B) You DO NOT remove my name from the credits, and you do not
  21.    place my or your name into the actual text file.
  22.    
  23.    That's it.. Call my BBS @ (213) 479-7043 300/1200/2400/18k (PEP)
  24.    Fred's Floating Bar and Grill.
  25.  
  26.                                         -JGG 2/15/89
  27. }
  28.  
  29.  
  30.   Const
  31.     VER          = '2.0';
  32.     MODDATE      = 'February 15, 1989';
  33.     NormalAttr   = '[0;33;44m';
  34.     BlinkingAttr = '[1;41;44;5m';
  35.     ResetAttr    = '[0m';
  36.  
  37.   Type
  38.     userrec   = Record
  39.                   Name : String [31];
  40.                   LastOn : String [9];
  41.                   Posts : Integer;
  42.                   Logons : Integer;
  43.                   UploadK : Real;
  44.                   DownloadK : Real;
  45.                 End;
  46.     DataBlock = Array [1..700] of Char;
  47.     str       = String [80];
  48.     regs      = record
  49.                   ax, bx, cx,
  50.                   dx, bp, si,
  51.                   di, ds, es,
  52.                   flags       : integer;
  53.                 end;
  54.  
  55.   Var
  56.     Ansi,
  57.     PostPrint,
  58.     UDPrint,
  59.     MakeTbls,
  60.     FormPrint  : Boolean;
  61.     RawData    : DataBlock;
  62.     MinLogon,
  63.     PValue,
  64.     HowMany,
  65.     UserNum,
  66.     Loop       : Integer;
  67.     FstSeek,
  68.     Count      : Byte;
  69.     UserData   : Array [1..1000] of userrec;
  70.     userfile   : File of DataBlock;
  71.     configfile : File of Char;
  72.     TempStr,
  73.     ResetStr,
  74.     RegStr,
  75.     BlnkStr,
  76.     PVS,
  77.     datapath,
  78.     gfilepath  : String [80];
  79.     data       : Char;
  80.     prntfile   : Text;
  81.  
  82. {$I CLCKSTFF.PRO }
  83.  
  84. Procedure ReadInit;
  85.  
  86.      Begin
  87.      {$I READINIT.PRO}
  88.      {  This procedure returns the datafile path in datapath
  89.         and the gfiles path in gfilepath.                     }
  90.      End;
  91.  
  92.  
  93. Procedure ReadUserData;
  94.  
  95.   {  This Procedure reads the user data the necessary user info in }
  96.  
  97.     Begin
  98.       Assign (userfile, datapath+'USER.LST');
  99.       Reset (userfile);
  100.       Seek (userfile, FstSeek);
  101.       UserNum := 1;
  102.       Repeat
  103.         Read (userfile, RawData);
  104.         If Ord (RawData [386]) = 0     { User not deleted }
  105.           then                         { Read in data }
  106.             With UserData [UserNum] do
  107.               Begin
  108.                 {  Converts the \00 terminated 'C' type string to
  109.                    a pascal string which is not terminated, but uses
  110.                    the first byte of the string to indicate the length
  111.                    ( ie. Name [0] is the length of Name )
  112.                 }
  113.                 UserNum := UserNum + 1;
  114.                 Count := 0;
  115.                 Loop := 1;
  116.                 Name := '';
  117.                 Repeat
  118.                   Name := Name + RawData [Loop + Count];
  119.                   Count := Count + 1;
  120.                 Until Ord (RawData [Loop + Count]) = 0; { End of 'C' string }
  121.                 Count := 0;
  122.                 Loop := 82;               { This is the same as above, but }
  123.                 LastOn := '';             { with the Last date on string.  }
  124.                 Repeat
  125.                   LastOn := LastOn + RawData [Loop + Count];
  126.                   Count := Count + 1;
  127.                 Until Ord (RawData [Loop + Count]) = 0;
  128.                 Posts := Ord (RawData [435]) + (Ord (RawData [436]) * 256);
  129.                 Logons := Ord (RawData [459]) + (Ord (RawData [460]) * 256);
  130.                 UploadK := Ord (RawData [465]) + (Ord (RawData [466]) * 256.0) +
  131.                            (Ord (RawData [467]) * 65536.0) +
  132.                            (Ord (RawData [468]) * 4294967296.0);
  133.                 DownloadK := Ord (RawData [469]) + (Ord (RawData [470]) * 256.0) +
  134.                              (Ord (RawData [471]) * 65536.0) +
  135.                              (Ord (RawData [472]) * 4294967296.0);  { <- Really! }
  136.                 If (Logons < MinLogon) or
  137.                    ((UDprint or FormPrint) and
  138.                    ((DownloadK = 0) and (UploadK < 200.0)))
  139.                   then
  140.                     { Not enough Activity -- Ignore }
  141.                     UserNum := UserNum - 1;
  142.               End;
  143.         Until EOF (UserFile);
  144.         UserNum := UserNum - 1;
  145.         Close (UserFile);
  146.     End;
  147.  
  148. Function UDRatio (Data : userrec):Real;
  149.  
  150.     Begin
  151.       With Data do
  152.         Begin
  153.           If (DownloadK = 0) and (UploadK = 0)          {Should Never occur}
  154.             then                                        {filtered out earlier}
  155.               UDRatio := 0.00;
  156.           If (DownloadK = 0) and (UploadK <> 0)
  157.             then
  158.               UDRatio := UploadK;
  159.           If (UploadK = 0) and (DownloadK <> 0)
  160.             then
  161.               UDRatio := 0.00 - (DownloadK);
  162.           If (DownloadK <> 0) and (UploadK <> 0)
  163.             then
  164.               UDRatio := UploadK / DownloadK;
  165.         End;
  166.     End;
  167.  
  168. Function PostRatio (Data : userrec):Real;
  169.  
  170.     Begin
  171.       With Data do
  172.         Begin
  173.           If Logons = 0
  174.             then
  175.               Begin
  176.                 Writeln (' How the heck can a user have zero logons? ');
  177.                 Writeln (' Something is REALLY screwed. Find that user');
  178.                 Writeln (' And delete it, or I won''t continue.');
  179.                 Writeln (' Abnormal Program Termination.   LOG ZERO');
  180.                 Halt;
  181.               End;
  182.           PostRatio := Posts / Logons;
  183.         End;
  184.       End;
  185.  
  186. Function FormRatio (Data : userrec):Real;
  187.  
  188.     Begin
  189.       With Data do
  190.         Begin
  191.           If (DownloadK = 0) and ((UploadK + (Posts * PValue)) = 0)
  192.             then
  193.               FormRatio := 0.00;
  194.           If (DownloadK = 0) and ((UploadK + (Posts * PValue)) <> 0)
  195.             then
  196.               FormRatio := UploadK + (Posts * PValue);
  197.           If ((UploadK + (Posts * PValue)) = 0) and (DownloadK <> 0)
  198.             then
  199.               FormRatio := 0.00 - (DownloadK);
  200.           If (DownloadK <> 0) and ((UploadK + (Posts * PValue)) <> 0)
  201.             then
  202.               FormRatio := (UploadK + (Posts * PValue)) / DownloadK;
  203.         End;
  204.     End;
  205.  
  206. Function Ratio (NameCode : Str; Data : userrec):Real;
  207.  
  208.     Var
  209.       PrntRatio : Real;
  210.  
  211.     Begin
  212.       If NameCode = 'UD'
  213.         then
  214.           PrntRatio := UDRatio (Data);
  215.       If NameCode = 'FM'
  216.         then
  217.           PrntRatio := FormRatio (Data);
  218.       If NameCode = 'PL'
  219.         then
  220.           PrntRatio := PostRatio (Data);
  221.       Ratio := PrntRatio;
  222.       If PrntRatio >= 100.0
  223.         then
  224.           Ratio := 99.99;
  225.       If PrntRatio <= -100.0
  226.         then
  227.           Ratio := -99.99;
  228.     End;
  229.  
  230.  
  231. Procedure WrtGfile (NameCode : Str; Title : Str; Tbl : Boolean);
  232.  
  233.  
  234.     Var
  235.       Fill,
  236.       Num       : String [2];
  237.       Spaces    : String [80];
  238.  
  239.     Begin
  240.       If Tbl
  241.         then
  242.           Begin
  243.             Spaces := '                                                                                ';
  244.             Write ('Writing File ');
  245.             If Ansi
  246.               then
  247.                 Assign (prntfile, gfilepath+'USERSTAT\'+NameCode+'TABLE.ANS')
  248.               else
  249.                 Assign (prntfile, gfilepath+'USERSTAT\'+NameCode+'TABLE.MSG');
  250.             Rewrite (prntfile);
  251.             Writeln (prntfile, RegStr+Title+Copy (Spaces, 1, Length (Title)));
  252.             Writeln (prntfile, Spaces);
  253.             Writeln (prntfile, 'Name                     | Ratio |  Up K  |  Dn K  | Posts | Logons |  Last On ');
  254.             Writeln (prntfile, '-------------------------------------------------------------------------------');
  255.             {                   The Jolly German Giant    -99.999   28000k  14000k     500     1057   01/01/89}
  256.             With UserData [1]  do
  257.               Begin
  258.                 Write ('.');
  259.                 If Ratio (NameCode, UserData [1]) < 0
  260.                   then
  261.                     Fill := ''
  262.                   else
  263.                     Fill := ' ';
  264.                 Writeln (prntfile, BlnkStr+Copy (Name, 1, 26)+
  265.                                    Copy (Spaces, 1, 26 - Length (Name)), Fill,
  266.                                    Ratio (NameCode, UserData [1]):6:3, '   ',
  267.                                    UploadK:5:0, 'k   ',
  268.                                    DownloadK:5:0, 'k   ',
  269.                                    Posts:5, '    ',
  270.                                    Logons:5, '   ', Laston);
  271.               End;
  272.             For Loop := 2 to UserNum do
  273.               With UserData [Loop]  do
  274.                 Begin
  275.                   Write ('.');
  276.                   If Ratio (NameCode, UserData [Loop]) < 0
  277.                     then
  278.                       Fill := ''
  279.                     else
  280.                       Fill := ' ';
  281.                   Writeln (prntfile, RegStr+Copy (Name, 1, 26)+
  282.                                      Copy (Spaces, 1, 26 - Length (Name)), Fill,
  283.                                      Ratio (NameCode, UserData [Loop]):6:3, '   ',
  284.                                      UploadK:5:0, 'k   ',
  285.                                      DownloadK:5:0, 'k   ',
  286.                                      Posts:5, '    ',
  287.                                      Logons:5, '   ',
  288.                                      Laston);
  289.                 End;
  290.             Writeln (prntfile, RegStr+Spaces);
  291.             Writeln (prntfile,RegStr+' Please note, ratios greater than 100 and less than -100 are truncated         ');
  292.             Writeln (prntfile,RegStr+' all ratios rounded off to 2 decimal places.                                   ');
  293.             Writeln (prntfile, RegStr+Spaces);
  294.             If FstSeek = 1
  295.               then
  296.                 Writeln (prntfile, RegStr+' Sysop is included in list')
  297.               else
  298.                 Writeln (prntfile, RegStr+' Sysop is NOT included in list');
  299.           end
  300.       else
  301.         Begin
  302.           Spaces := '                                                                                ';
  303.           Write ('Writing File ');
  304.           If Ansi
  305.             then
  306.               Assign (prntfile, gfilepath+'USERSTAT\'+NameCode+'RATIO.ANS')
  307.             else
  308.               Assign (prntfile, gfilepath+'USERSTAT\'+NameCode+'RATIO.MSG');
  309.           Rewrite (prntfile);
  310.        (* {$I-}
  311.           If IoResult <> 0
  312.             then
  313.               Begin
  314.                 MakeDir (gfilepath+'\USERSTAT');
  315.                 Rewrite (prntfile);
  316.                 If IoResult <> 0
  317.                   then
  318.                     Begin
  319.                       Writeln ('Couldn''t write to output file. Sorry.');
  320.                       Halt;
  321.                     End;
  322.               End;
  323.           {$I+}
  324.     *)    Writeln (prntfile, RegStr+Spaces);
  325.           Writeln (prntfile, RegStr+Title+Copy(Spaces, 1, (80 - Length (Title))));
  326.           TempStr := '                     Run at: ' + dat;
  327.           Writeln (prntfile, RegStr+TempStr+Copy(Spaces, 1, (80 - Length (TempStr))));
  328.           Writeln (prntfile, RegStr+Spaces);
  329.           Writeln (prntfile, RegStr+'                   BEST                             WORST                      ');
  330.           Writeln (prntfile, RegStr+Spaces);
  331.           Loop := 1;
  332.           Num := ' 1';
  333.           Write ('.');
  334.           With UserData [Loop] do
  335.             Name := Name + Copy (Spaces, 1, 30 - Length (Name));
  336.           With UserData [UserNum - (Loop - 1)] do
  337.             Name := Name + Copy (Spaces, 1, 30 - Length (Name));
  338.           Writeln (prntfile, BlnkStr+Num, ' ',
  339.                              UserData [Loop].Name,
  340.                              Ratio(NameCode, UserData [Loop]):5:2, '  ',
  341.                              Num, ' ',
  342.                              UserData [UserNum - (Loop - 1)].Name,
  343.                              Ratio (NameCode, UserData [UserNum - (Loop - 1)]):5:2);
  344.           For Loop := 2 to HowMany do
  345.             Begin
  346.               Write ('.');
  347.               If Loop > 9
  348.                 then
  349.                   Num := Chr (48 + (Loop DIV 10)) + Chr (48 + (Loop - ((Loop DIV 10) * 10)))
  350.                 else
  351.                   Num := ' ' + Chr (48 + Loop);
  352.               With UserData [Loop] do
  353.                 Name := Name + Copy (Spaces, 1, 30 - Length (Name));
  354.               With UserData [UserNum - (Loop - 1)] do
  355.                 Name := Name + Copy (Spaces, 1, 30 - Length (Name));
  356.               Writeln (prntfile, RegStr+Num, ' ',
  357.                                  UserData [Loop].Name,
  358.                                  Ratio(NameCode, UserData [Loop]):5:2, '  ',
  359.                                  Num, ' ',
  360.                                  UserData [UserNum - (Loop - 1)].Name,
  361.                                  Ratio (NameCode, UserData [UserNum - (Loop - 1)]):5:2);
  362.             End;
  363.           Writeln (prntfile, RegStr+Spaces);
  364.           Writeln (prntfile,RegStr+' Please note, ratios greater than 100 and less than -100 are truncated ');
  365.           Writeln (prntfile,RegStr+' all ratios rounded off to 2 decimal places. ');
  366.           Writeln (prntfile, RegStr+Spaces);
  367.           If FstSeek = 1
  368.             then
  369.               Writeln (prntfile, RegStr+' Sysop is included in list')
  370.             else
  371.               Writeln (prntfile, RegStr+' Sysop is NOT included in list');
  372.         End;
  373.       Writeln (prntfile, ResetStr);
  374.       Close (prntfile);
  375.     End;
  376.  
  377. Procedure ProcessData;
  378.  
  379.     Var
  380.       Exit      : Boolean;
  381.       Temp      : UserRec;
  382.  
  383.     Begin
  384.       { NOTE: UserNum when entering this procedure contains the number of
  385.               users that were read in. (ie Active, non-deleted, and with
  386.               activity)           }
  387.       If (UserNum < (HowMany * 2)) AND Not (MakeTbls)
  388.         then
  389.           Begin
  390.             Writeln ('   I''m sorry, you have less than ', Howmany * 2, ' active users. USERSTAT');
  391.             Writeln ('   cannot accurately process your userlist.  For more help');
  392.             Writeln ('   contact 1@16 WWIVnet The Jolly German Giant     USR LST SHRT');
  393.             Writeln ('   Abnormal Program Termination ');
  394.             Halt;
  395.           End;
  396.       If UDPrint
  397.         then
  398.           Begin
  399.             Repeat
  400.               Exit := True;
  401.               For Loop := 1 to (UserNum - 1) do
  402.                 Begin
  403.                   If UDRatio (UserData [Loop]) < UDRatio (UserData [Loop + 1])
  404.                     then
  405.                       Begin
  406.                         Temp := UserData [Loop];
  407.                         UserData [Loop] := UserData [Loop + 1];
  408.                         UserData [Loop + 1] := Temp;
  409.                         Exit := False;
  410.                       End;
  411.                 End;
  412.               Until Exit;
  413.             WrtGfile ('UD','                         Upload / Download Ratio', MakeTbls);
  414.           End;
  415.       If FormPrint
  416.         then
  417.           Begin
  418.             Repeat
  419.               Exit := True;
  420.               For Loop := 1 to (UserNum - 1) do
  421.                 Begin
  422.                   If FormRatio (UserData [Loop]) < FormRatio (UserData [Loop + 1])
  423.                     then
  424.                       Begin
  425.                         Temp := UserData [Loop];
  426.                         UserData [Loop] := UserData [Loop + 1];
  427.                         UserData [Loop + 1] := Temp;
  428.                         Exit := False;
  429.                       End;
  430.                 End;
  431.               Until Exit;
  432.             WrtGfile ('FM', '             [Upload K + (Posts x '+PVS+'K)] / Download K Ratio', MakeTbls);
  433.           End;
  434.       If PostPrint
  435.         then
  436.           Begin
  437.             If FormPrint or UDPrint
  438.               then   { ReRead the user data, this time ignore transfer activity}
  439.                 Begin
  440.                   FormPrint := False;
  441.                   UDPrint := False;
  442.                   ReadUserData;
  443.                 End;
  444.             Repeat
  445.               Exit := True;
  446.               For Loop := 1 to (UserNum - 1) do
  447.                 Begin
  448.                   If PostRatio (UserData [Loop]) < PostRatio (UserData [Loop + 1])
  449.                     then
  450.                       Begin
  451.                         Temp := UserData [Loop];
  452.                         UserData [Loop] := UserData [Loop + 1];
  453.                         UserData [Loop + 1] := Temp;
  454.                         Exit := False;
  455.                       End;
  456.                 End;
  457.               Until Exit;
  458.             WrtGfile ('PL', '                         Post to Logon Ratio', MakeTbls);
  459.           End;
  460.     End;
  461.  
  462. Procedure ReadCmdLine;
  463.  
  464.     Var
  465.       Param : Char;
  466.       Code  : Integer;
  467.  
  468.     Begin
  469.       {  /kxx   sets the k per post                  defaults to 3
  470.          /s     tells it not to count the sysop
  471.          /u     will print out the up/down ratios
  472.          /p     will print the post to logon ratios
  473.          /f     will print the "formula" ratios
  474.          /l     sets the minimum logons               defaults to 10
  475.          /dia   prints out special message
  476.          /a     generates ANSI color messages
  477.          /t     generates tables (all users)
  478.       }
  479.       HowMany := 10;
  480.       MakeTbls := False;
  481.       UDPrint := False;
  482.       PostPrint := False;
  483.       FstSeek := 1;
  484.       FormPrint := False;
  485.       Ansi := False;
  486.       For Loop := 1 to ParamCount do
  487.         Begin
  488.           Param := Copy (ParamSTR (loop), 2, 1);
  489.           Case Upcase (Param) of
  490.             'A' : Ansi := True;
  491.             'D' : Begin
  492.                     Writeln (' This software written by The Jolly German Giant 1@16 WWIVnet ');
  493.                     Writeln (' Fred''s Floating Bar and Grill (213) 479-7043 300/1200/2400/19.2k baud');
  494.                     Writeln (' Software dedicated to Dia Warren.. <aww> ');
  495.                   End;
  496.             'F' : FormPrint := True;
  497.             'K' : Begin
  498.                     Val (Copy (ParamSTR (Loop), 3, Length (ParamSTR (loop)) - 2),
  499.                          PValue, Code);
  500.                     PVS := Copy (ParamSTR (Loop), 3, Length (ParamSTR (loop)) - 2);
  501.                     If Code <> 0
  502.                       then
  503.                         Begin
  504.                           Writeln ('  P value ignored, defaults to 3K per post ');
  505.                           PValue := 3;
  506.                           PVS := '3';
  507.                         End
  508.                       else
  509.                         Begin
  510.                           Writeln;
  511.                           Writeln ('*** Posts now count as ', PVS,'K uploads');
  512.                           Writeln;
  513.                         End;
  514.                   End;
  515.             'L' : Begin
  516.                     Val (Copy (ParamSTR (Loop), 3, Length (ParamSTR (loop)) - 2),
  517.                          MinLogon, Code);
  518.                     Writeln;
  519.                     Writeln;
  520.                     Writeln ('*** Must have ',MinLogon, ' logons to be included.');
  521.                     Writeln;
  522.                     Writeln;
  523.                     If Code <> 0
  524.                       then
  525.                         Begin
  526.                           Writeln ('  L value ignored, defaults to 10 logons ');
  527.                           MinLogon := 10;
  528.                         End;
  529.                   End;
  530.             'N' : Begin
  531.                     Val (Copy (ParamSTR (Loop), 3, Length (ParamSTR (loop)) - 2),
  532.                          HowMany, Code);
  533.                     Writeln;
  534.                     Writeln;
  535.                     Writeln ('*** Will print top and bottom ',HowMany, ' users.');
  536.                     Writeln;
  537.                     Writeln;
  538.                     If (Code <> 0) or (HowMany > 99) or (HowMany < 1)
  539.                       then
  540.                         Begin
  541.                           Writeln ('  N value ignored, defaults to top & bottom 10. ');
  542.                           HowMany := 10;
  543.                         End;
  544.                   End;
  545.             'P' : PostPrint := True;
  546.             'S' : FstSeek := 2;
  547.             'T' : MakeTbls := True;
  548.             'U' : UDPrint := True;
  549.             '?' : Begin
  550.                     Writeln;
  551.                     Writeln;
  552.                     Writeln ('     USERSTAT ',VER,' by JGG');
  553.                     Writeln ('     Release Date: ', ModDate);
  554.                     Writeln;
  555.                     Writeln (' Contact 1@16 or 502@1 for help');
  556.                     Writeln;
  557.                     Writeln (' This program should be run from the main WWIV directory ');
  558.                     Writeln (' as it accesses the CONFIG.DAT file to find the user data');
  559.                     Writeln;
  560.                     Writeln (' The following options are available:');
  561.                     Writeln;
  562.                     Writeln ('           /kxx   sets the k per post                   defaults to 3');
  563.                     Writeln ('           /s     tells it not to count the sysop');
  564.                     Writeln ('           /u     will print out the up/down ratios');
  565.                     Writeln ('           /p     will print the post to logon ratios');
  566.                     Writeln ('           /f     will print the "formula" ratios');
  567.                     Writeln ('           /lxx   sets the minimum logons               defaults to 10');
  568.                     Writeln ('           /a     enables ANSI output                   defaults to off');
  569.                     Writeln ('           /nxx   prints top & bottom xx users          defaults to 10');
  570.                     Writeln ('           /t     generates table for "formula" ratio');
  571.                   End;
  572.           End;
  573.         End;
  574.     End;
  575.  
  576. Begin
  577.   Writeln ('Running USERSTAT v' + VER + ' '+ MODDATE);
  578.   Writeln ('Written by The Jolly German Giant. 1@16 WWIVnet.');
  579.   Writeln ('/? provides help screen');
  580.   Writeln;
  581.   MinLogon := 10;                         { Set Defaults }
  582.   Pvalue := 3;
  583.   PVS := '3';
  584.   FstSeek := 1;
  585.   ReadCmdLine;
  586.   If NOT (FormPrint or UDPrint or PostPrint)
  587.     then
  588.       Halt;
  589.   If Ansi
  590.     then
  591.       Begin
  592.         RegStr := #27+NormalAttr;
  593.         BlnkStr := #27+BlinkingAttr;
  594.         ResetStr := #27+ResetAttr;
  595.       End
  596.     else
  597.       Begin
  598.         RegStr := '';
  599.         BlnkStr := '';
  600.         ResetStr := '';
  601.       End;
  602.   ReadInit;
  603.   ReadUserData;
  604.   ProcessData;
  605.   Writeln (#27+'[0m');
  606. End.
  607.