PROGRAM fcount;

 {Program to read a disk file }
 {and count the number of chars and lines. }
 {Program will also allow splitting a long file }
 {into several pieces. }


CONST
   version = '1.0';
   sector_size = 128;   {#bytes in a sector}

   carriage_return = 13; {^M}
   line_feed  = 10;      {^J}
   eof_char  = 26;       {^Z}

TYPE
   byte = 0..255;
   sector_array = PACKED ARRAY [1..sector_size] OF byte;
   sector_file  = FILE OF sector_array;

   ctr_array  = PACKED ARRAY [1..2] OF INTEGER;  {1=units, 2=thousands}

   outch_array = PACKED ARRAY [1..3] OF byte;
   char12 = PACKED ARRAY [1..12] OF CHAR;

VAR
   infile   :sector_file;
   infilename   :char12;

   outf_flag   :BOOLEAN;  {true if outfile present}
   outfile   :sector_file;
   outfilename   :char12;

   list_flag   :BOOLEAN;    {list output}


   in_buffer   :sector_array;
   in_bufptr   :INTEGER;
 
   out_buffer   :sector_array;
   out_bufptr   :INTEGER;

   char_ctr   :ctr_array;
   line_ctr   :ctr_array;
   line_mod_ctr   :ctr_array;
   line_thousands_limit  :INTEGER;


   status   :INTEGER;
   i        :INTEGER;

{----------------------------------------------------------}
{----------------------------------------------------------}
{ Increment a symbolic name.  Eg  XXX021 to XXX022. }

PROCEDURE incr_name (VAR name :char12);

VAR
   i    :INTEGER;
   col   :INTEGER;
   flag   :BOOLEAN;

BEGIN{PROCEDURE}
   col := 12;
   WHILE (col>=1) AND (name[col]=' ') DO  col := col - 1;

   flag := TRUE;
   WHILE flag AND (col>=1) DO BEGIN
      i := ORD (name[col]) + 1;
      IF i <= ORD('9') THEN BEGIN
         flag := FALSE;
         name[col] := CHR(i);
        END
      ELSE  BEGIN
         name[col] := '0';
         col := col - 1;
      END{IF};
   END{WHILE};
END{PROCEDURE};


{--------------------------------------------------}
{Reset a big-counter to zero } 

PROCEDURE ctr_reset (VAR ctr :ctr_array);

BEGIN{PROCEDURE}
   ctr[1] := 0;
   ctr[2] := 0;
END{PROCEDURE};

{--------------------------------------------------}
{Increments a big-counter. }

PROCEDURE ctr_count  (VAR ctr :ctr_array);

BEGIN{PROCEDURE}
   ctr[1] := ctr[1] + 1;
   IF ctr[1] >= 1000 THEN BEGIN
      ctr[2] := ctr[2] + 1;
      ctr[1] := 0;
   END{IF};
END{PROCEDURE};


{-------------------------------------------------------------}
{Test a counter against another counter}
{Returns TRUE if counter A is bigger than counter B}

FUNCTION ctr_gtr (ctra :ctr_array;
                  ctrb :ctr_array )
                 : BOOLEAN;
BEGIN{FUNCTION} 
   ctr_gtr := FALSE;

   IF ctra[2] > ctrb[2] THEN ctr_gtr := TRUE;

   IF ctra[2] = ctrb[2] THEN ctr_gtr :=  ctra[1] > ctrb[1];
END{FUNCTION};


{-------------------------------------------------------------}
{Print a big-counter }

PROCEDURE ctr_print (ctr :ctr_array);

BEGIN{PROCEDURE}
   WRITE (ctr[2], ',' ,  ctr[1]:3 );
END{PROCEDURE};


{-------------------------------------------------------------}
PROCEDURE get_outfilename;

BEGIN{PROCEDURE}
   WRITE('Enter the output filename: ');
   outfilename := '            ';
   READLN (outfilename);

   outf_flag := TRUE;
   IF outfilename = '            '  THEN outf_flag := FALSE;

END{PROCEDURE};

{-------------------------------------------------------------}
PROCEDURE get_infilename;

BEGIN{PROCEDURE}
   WRITE('Enter the input filename: ');
   infilename := '            ';
   READLN (infilename);
END{PROCEDURE};

{------------------------------------------------------------}
FUNCTION get_limit  :INTEGER; 

VAR 
   result   :INTEGER;

BEGIN{FUNCTION}
   READLN (result);
   IF result=0 THEN result := MAXINT-1;
   get_limit := result;
END{FUNCTION};


{------------------------------------------------------------}

FUNCTION open_infile  :INTEGER;

VAR
   result   :INTEGER; 

BEGIN{FUNCTION}
   RESET(infilename,infile);

   in_bufptr := sector_size + 1;

   result := 0;
   IF EOF(infile) THEN result := -1;

   WRITELN('Open input file: ',infilename:12,
           '   result=', result );

   open_infile := result;

END{FUNCTION};

{-------------------------------------------------------------}

FUNCTION open_outfile   :INTEGER;

VAR
   result   :INTEGER;

BEGIN{FUNCTION}
   REWRITE (outfilename, outfile);

   out_bufptr := 0;

   result := 0;

   WRITELN('Open output file: ', outfilename,
           '   result=', result );

END{FUNCTION};

{--------------------------------------------------------}
{Opens the next output file in sequence.}
{Returns 0 if no error, <0 if error. }

FUNCTION open_next_outfile  :INTEGER;

VAR
   result   :INTEGER;

BEGIN{FUNCTION}
   incr_name (outfilename);

   result := open_outfile;
   
   open_next_outfile := result;
END{FUNCTION};


{--------------------------------------------------------}
{Reads the next sector from the input file. }
{Returns 0 = normal;  -1 = error or EOF. }

FUNCTION read_infile  :INTEGER;

BEGIN{FUNCTION}
   IF EOF(infile) THEN BEGIN
      read_infile := -1;
      in_bufptr := sector_size + 1;
      END
   ELSE BEGIN
      READ (infile, in_buffer);
      in_bufptr := 0;
      read_infile := 0;
   END{IF};
END{FUNCTION};

{--------------------------------------------------------}
{Writes the next sector into the output file. }
{Returns 0 = normal,  <0 if error. }

FUNCTION write_outfile    :INTEGER;

BEGIN{FUNCTION}
   WRITE(outfile, out_buffer);
   out_bufptr := 0;
   write_outfile := 0;
END{FUNCTION};
  

{--------------------------------------------------------}

FUNCTION close_infile  :INTEGER;

BEGIN{FUNCTION}
   close_infile := 0;
END{FUNCTION};


{--------------------------------------------------------}

FUNCTION close_outfile  :INTEGER;
BEGIN{FUNCTION}
   close_outfile := 0;
END{FUNCTION};


{--------------------------------------------------------}
{Gets the next char (pseudochar, a byte) from the input buffer.}
{Signals EOF by returning -1.  Returns 0 if get a char. }


FUNCTION get_char ( VAR in_char :byte )  :INTEGER; 

VAR
   status   :INTEGER;

BEGIN{FUNCTION}
   status := 0;
   IF in_bufptr >= sector_size THEN BEGIN
      status := read_infile;
   END{IF};

   IF status = 0 THEN BEGIN
      in_bufptr := in_bufptr + 1;
      in_char := in_buffer[in_bufptr];
      IF in_char = eof_char THEN status := -1;
   END{IF};

   get_char := status;
END{FUNCTION};

{--------------------------------------------------------}

FUNCTION put_char (out_char :byte)  :INTEGER;

VAR
   status   :INTEGER;

BEGIN
   status := 0;

   out_bufptr := out_bufptr + 1;
   out_buffer[out_bufptr] := out_char;
   
   IF out_bufptr >= sector_size THEN BEGIN
      status := write_outfile;
   END{IF};

   put_char := status;
END{FUNCTION};


{--------------------------------------------------------}
{Purge any chars still remaining in the output buffer}

PROCEDURE put_purge;

VAR
   i       :INTEGER;
   remaining   :INTEGER;
   status   :INTEGER;

BEGIN{PROCEDURE}
   status := put_char (eof_char);  {ensure at least 1 EOL}
   remaining := sector_size - out_bufptr;
   FOR i:= 1 TO remaining DO BEGIN
      status := put_char (eof_char);
   END{FOR};
END{PROCEDURE};


{--------------------------------------------------------}

PROCEDURE putout_char (in_char :byte);

VAR
   result   :INTEGER;

BEGIN{PROCEDURE}
   IF outf_flag THEN BEGIN
      result := put_char (in_char);
      IF line_mod_ctr[2] > line_thousands_limit THEN BEGIN
         put_purge;
         result := open_next_outfile;
         ctr_reset (line_mod_ctr);
      END{IF};
   END{IF};
END{PROCEDURE};

{----------------------------------------------------}

PROCEDURE count_char (in_char :byte);

BEGIN{PROCEDURE}
   ctr_count (char_ctr);

   IF in_char = carriage_return THEN BEGIN
      ctr_count (line_ctr);
      ctr_count (line_mod_ctr);
   END{IF};
END{PROCEDURE};


{--------------------------------------------------}

FUNCTION count_file   :INTEGER;

VAR
   i        :INTEGER;
   status   :INTEGER;
   in_char  :byte;
   out_chars :outch_array;
   îchars   :INTEGER;

BEGIN{FUNCTION}
   status := 0;
   ctr_reset (line_ctr);
   ctr_reset (line_mod_ctr);
   ctr_reset (char_ctr);

   WHILE status = 0  DO BEGIN
      status := get_char (in_char);
      IF (status<>0) AND outf_flag THEN BEGIN
         put_purge;
        END
      ELSE BEGIN
         count_char (in_char);
         IF outf_flag THEN putout_char (in_char);
      END{IF};
   END{WHILE};
   count_file := status;
END{FUNCTION};


{--------------------------------------------}
{--------------------------------------------}

BEGIN{PROGRAM}
   WRITELN ('Fcount  Version ', version);

   get_infilename;
   status := open_infile;
   IF status<>0 THEN BEGIN
      WRITELN('*** Could not open input file ', infilename);
   END{IF};

   IF status=0  THEN BEGIN
      get_outfilename;
      IF outf_flag THEN BEGIN
         status := open_outfile;
         IF status<>0 THEN BEGIN
            WRITELN('*** Could not open ouput file ', outfilename);
         END{IF}; 
      END{IF};
   END{IF};
     
   IF status=0 THEN BEGIN
      WRITE('Enter max #lines per file (in thousands: ');
      line_thousands_limit := get_limit;
      IF line_thousands_limit > 0 THEN BEGIN
         WRITELN('NOTE that filename should be xxxxx.001');
      END{IF};
   END{IF};

   IF status=0 THEN BEGIN
      status := count_file;
   END{IF};
 
   ctr_print (line_ctr);
   WRITE (' lines. ');
   ctr_print (char_ctr);
   WRITE (' characters.');
   WRITELN;

   status := close_input;

   IF outf_flag THEN BEGIN
      status := close_output;
   END{IF};

END{PROGRAM}.

