anchor.diff:        InsertParam( ['c', 'only-common-rows'], false, '', 'Only outputs a line if the Unique field exists in');
         InsertUsage( '                                 both file ''1'' and file ''2'' and new');
         InsertUsage( '                                 spreadsheets');
  +      InsertParam( ['int32'], false, '', 'The unique field is a signed 32 bit integer');
  +      InsertUsage( '                                 value.');
  +      InsertParam( ['int64'], false, '', 'The unique field is a signed 64 bit integer');
  +      InsertUsage( '                                 value.');
  +      InsertParam( ['word32'], false, '', 'The unique field in a unsigned 32 bit integer');
  +      InsertUsage( '                                 value.');
  +      InsertParam( ['word64'], false, '', 'The unique field in a unsigned 64 bit integer');
  +      InsertUsage( '                                 value.  This is the default type.');
  +      InsertParam( ['text'], false, '', 'The unique field in a simple text value.');
         InsertUsage();
   
         ParseParams();  // parse the command line
  
positive.code: // * main()
  // ************************************************************************
  
  var
     NewHeader:     string;
     AllowNew:      boolean;
     ReorderFilter: tCsvReorderFilter;
  begin
     InitArgvParser();
     
     NewHeader:= GetParam( 'header');
     AllowNew:=  ParamSet( 'allow-new');
     ReorderFilter:= tCsvReorderFilter.Create( NewHeader, AllowNew);
     
     CsvFilterQueue.Queue:= CsvInputFilter;
     CsvFilterQueue.Queue:= ReorderFilter;
     CsvFilterQueue.Queue:= CsvOutputFilter;
     CsvFilterQueue.Go();
  
     // Cleanup happens automatically in the  lbp_csv_filters unit.
  end.  // csv_diff program
  
negative.code:    lbp_types,
     lbp_csv,
     lbp_csv_filter,
     lbp_output_file,
     sysutils;
  
  // ************************************************************************
  
  type
     tCompareFunction = function( const S1: string; S2: string): int8;
  
  // ************************************************************************
  // * Global variables
  // ************************************************************************
  
  var
     Csv1:             tCsv;
     Csv2:             tCsv;
     OutputHeader:     tCsvCellArray;
     OutputHeaderLen:  integer; // 
     Lookup1:          array of integer;
     Lookup2:          array of integer;
     LookupM:          integer;   // The max index of Lookup1 and Lookup2
     CompareUnique:    tCompareFunction;  // The function used to compare the unique fields
     OnlyCommonRows:   boolean = false;
  
  
  // ************************************************************************
  //* CompareInt32() - Compares two Int32 values
  // ************************************************************************
  
  function CompareInt32(   S1: string; s2: string): int8;
     var
        N1: int32;
        N2: int32;
     begin
        N1:= longint.Parse( S1);
        N2:= longint.Parse( S2);
        if( N1 > N2) then begin
           result:= 1;
        end else if( N1 < N2) then begin
           result:= -1;
        end else begin
           result:= 0;
        end;
     end; // CompareInt32()
  
  
  // ************************************************************************
  //* CompareInt64() - Compares two Int64 values
  // ************************************************************************
  
  function CompareInt64( S1: string; S2: string): int8;
     var
        N1: int64;
        N2: int64;
     begin
        N1:= int64.Parse( S1);
        N2:= int64.Parse( S2);
        if( N1 > N2) then begin
           result:= 1;
        end else if( N1 < N2) then begin
           result:= -1;
        end else begin
           result:= 0;
        end;
     end; // CompareInt64()
  
  
  // ************************************************************************
  //* CompareWord32() - Compares two Word32 values
  // ************************************************************************
  
  function CompareWord32(  S1: string; S2: string): int8;
     var
        N1: word32;
        N2: word32;
     begin
        N1:= cardinal.Parse( S1);
        N2:= Cardinal.Parse( S2);
        if( N1 > N2) then begin
           result:= 1;
        end else if( N1 < N2) then begin
           result:= -1;
        end else begin
           result:= 0;
        end;
     end; // CompareWord32()
  
  
  // ************************************************************************
  //* CompareWord64() - Compares two Word64 values
  // ************************************************************************
  
  function CompareWord64(  S1: string; S2: string): int8;
     var
        N1: word64;
        N2: word64;
     begin
        N1:= qword.Parse( S1);
        N2:= qword.Parse( S2);
        if( N1 > N2) then begin
           result:= 1;
        end else if( N1 < N2) then begin
           result:= -1;
        end else begin
           result:= 0;
        end;
     end; // CompareWord64()
  
  
  // ************************************************************************
  //* CompareString() - Compares two strings
  // ************************************************************************
  
  function CompareString(  S1: string; S2: String): int8;
     begin
        if( S1 > S2) then begin
           result:= 1;
        end else if( S1 < S2) then begin
           result:= -1;
        end else begin
           result:= 0;
        end;
     end; // CompareString()
  
  
  // ************************************************************************
  // * DumpGlobals() - print the global variables
  // ************************************************************************
  
  procedure DumpGlobals();
     var
        I:  integer;
     begin
        Writeln( 'OutputHeader   = ', OutputHeader.ToCsv);
        writeln( 'OnlyCommonRows = ', OnlyCommonRows);
        write(   'Lookup1        = ' );
        for I in Lookup1 do write( I, '  ');
        writeln();
        write(   'Lookup2        = ' );
        for I in Lookup2 do write( I, '  ');
        writeln();
        writeln(   'LookupM        = ', LookupM );
  
     end; // DumpGlobals()
  
  
  // ************************************************************************
  // * CompareRows() - compare two CSV rows and output the diff it if they 
  // *                 don't match.  Assumes the Unique field is the same.
  // ************************************************************************
  
  procedure CompareRows( R1, R2: tCsvCellArray);
      var
         i:       integer; // outer loop index
         j:       integer; // source index
         k:       integer; // destination index
         OutRow:  tCsvCellArray;
     begin
        for i:= 1 to LookupM do begin
           if( R1[ Lookup1[i]] <> R2[ Lookup2[ i]]) then begin
              SetLength( OutRow, OutputHeaderLen);
              k:= 0;
              for j:= 0 to LookupM do begin
                 OutRow[ k]:= R1[ Lookup1[ j]];
                 Inc( K);
              end; 
              for j:= 1 to LookupM do begin
                 OutRow[ k]:= R2[ Lookup2[ j]];
                 Inc( K);
              end; 
              writeln( OutputFile, OutRow.ToCsv);
              exit;
           end;
        end;
     end; // compareRows()
  
  
  // ************************************************************************
  // * CompareFile() - compare two CSV files.  Returns true if they are equal
  // ************************************************************************
  
  procedure CompareFiles( C1, C2: tCsv);
     var
        R1,   R2:    tCsvCellArray;
        L1,   L2:    integer;  // The length of each row
        Empty1:      tCsvCellArray;
        Empty2:      tCsvCellArray;
        i:           integer; // for loop
        iMax:        integer; // for loop
  
     // ---------------------------------------------------------------------
     // - NextR1()
     // ---------------------------------------------------------------------
     procedure NextR1();
        begin
           if( not OnlyCommonRows) then begin
              // Output the addition of R1 as a difference
              Empty2[ 0]:= R1[ Lookup1[ 0]];
              CompareRows( R1, Empty2);
           end;
           R1:= C1.ParseRow;
           L1:= Length( R1);
        end; // NextR1()
     // ---------------------------------------------------------------------
  
     // ---------------------------------------------------------------------
     // - NextR2()
     // ---------------------------------------------------------------------
     procedure NextR2();
        begin
           if( not OnlyCommonRows) then begin
              // Output the addition of R1 as a difference
              Empty1[ 0]:= R2[ Lookup2[ 0]];
              CompareRows( Empty1, R2);
           end;
           R2:= C2.ParseRow;
           L2:= Length( R2);
        end; // NextR2()
  
     // ---------------------------------------------------------------------
  
     begin
        writeln( OutputFile, OutputHeader.ToCsv);
              
        // Step through each file until one of them is at end-of-file
        // The length of the Row array is zero if we are at the end-of-file
        R1:= C1.ParseRow;
        R2:= C2.ParseRow;
        L1:= Length( R1);
        L2:= Length( R2);
  
        // Create the empty tCsvCellArrays if needed
        if( not OnlyCommonRows) then begin
           SetLength( Empty1, L1);
           iMax:= L1 - 1;
           for i:= 1 to iMax do Empty1[ i]:= '';
           SetLength( Empty2, L2);
           iMax:= L2 - 1;
           for i:= 1 to iMax do Empty2[ i]:= '';
        end;
  
        while( (L1 <> 0) and (L2 <> 0)) do begin
           case CompareUnique( R1[ Lookup1[ 0]], R2[ Lookup2[ 0]]) of
              0: begin
                 CompareRows( R1, R2);
                 // Move both files to their next row
                 R1:= C1.ParseRow;
                 R2:= C2.ParseRow;
                 L1:= Length( R1);
                 L2:= Length( R2);
              end;
              1: NextR2;
              -1: NextR1
           else; 
           end; // case
        end; // while both files have rows available
       
       // If we are pinting out rows without a match in the other file
       //    then continue reading a file when the other file is finished
       if( not OnlyCommonRows) then begin
          while( L1 <> 0) do NextR1;
          while( L2 <> 0) do NextR2;
       end; 
     end; // compareFiles()
  
  
  // ************************************************************************
  // * InitVars() - Read parameters and initialize variables.
  // ************************************************************************
  
  procedure InitVars();
     var
        UniqueField:   string;  // The unqique (ID) field name
        LIH:           integer; // Number of the input header field names
        i:             integer;
        S:             string;  // temporary string
        Header:        tCsvCellArray;
        CsvH:          tCsv; // used parsing the header
        IFN:           integer; // Input file number
        FieldName:     string;
        HL1:           integer; // The Lenght of the header in Csv1
        HL2:           integer; // The Lenght of the header in Csv2
     begin
        // Get the Header values supplied by the user
        if( not ParamSet( 'h')) then begin
           raise tCsvException.Create( 'You must enter a list of header fields to be compared.');
        end;
        CsvH:= tCsv.Create( GetParam( 'h'));
        LIH:= CsvH.ParseHeader;
        Header:= CsvH.Header;
        CsvH.Destroy;
  
        // Create the output header
        OutputHeaderLen:= (LIH * 2) + 1;
        SetLength( OutputHeader, OutputHeaderLen);
        UniqueField:= GetParam( 'u');
        OutputHeader[ 0]:= UniqueField;
        i:= 1;
        for S in Header do begin
           OutputHeader[ i]:= '1-' + S;
           Inc( i);
        end;
        for S in Header do begin
           OutputHeader[ i]:= '2-' + S;
           Inc( i);
        end;
  
        // Open the two CSV files and read each header
        if( Length( UnnamedParams) <> 2)then begin
           raise tCsvException.Create( 'You must enter the two CSV file names on the command line!');
        end;
        Csv1:= tCsv.Create( UnnamedParams[ 0], true);
        Csv2:= tCsv.Create( UnnamedParams[ 1], true);
        HL1:= Csv1.ParseHeader;
        HL2:= Csv2.ParseHeader;
        if( (HL1 = 0) or (HL2 = 0)) then begin
           raise tCsvException.Create( 'Unable to read the header from one of the files!');
        end;
  
        // Setup lookup tables
        LookupM:= LIH;
        Inc( LIH);
        SetLength( Lookup1, LIH);
        SetLength( Lookup2, LIH);
        try
           IFN:= 1;
           FieldName:= UniqueField;
           Lookup1[ 0]:= Csv1.IndexOf( FieldName);
           for i:= 1 to LookupM do begin
              FieldName:= Header[ i - 1];
              Lookup1[ i]:= Csv1.IndexOf( FieldName);
           end;
           IFN:= 2;
           FieldName:= UniqueField;
           Lookup2[ 0]:= Csv2.IndexOf( FieldName);
           for i:= 1 to LookupM do begin
              FieldName:= Header[ i - 1];
              Lookup2[ i]:= Csv2.IndexOf( FieldName);
           end;
        except
          on E: Exception do begin
             raise tCsvException.Create( 'The field ''%s'' was not found in ''%s''!',
                       [FieldName, UnnamedParams[IFN - 1]]);
          end;
        end;
  
        // Set the Compare() function
        i:= 0;
        if( ParamSet( 'int32')) then begin
           Inc( i);
           CompareUnique:= tCompareFunction( @CompareInt32);
        end;
        if( ParamSet( 'int64')) then begin
           Inc( i);
           CompareUnique:= tCompareFunction( @CompareInt64);
        end;
        if( ParamSet( 'word32')) then begin
           Inc( i);
           CompareUnique:= tCompareFunction( @CompareWord32);
        end;
        if( ParamSet( 'word64')) then begin
           Inc( i);
           CompareUnique:= tCompareFunction( @CompareWord64);
        end;
        if( ParamSet( 'text')) then begin
           Inc( i);
           CompareUnique:= tCompareFunction( @CompareString);
        end;
        if( i = 0) then begin
            i:= 1;
           CompareUnique:= tCompareFunction( @CompareWord64);
        end;
        if( i <> 1) then begin
           raise tCsvException.Create( 'You may only specify one type for the unique field!')
        end;
  
        OnlyCommonRows:= ParamSet( 'c');
     end; // InitVars
  
  
  // ************************************************************************
  // * InitArgvParser() - Initialize the command line usage message and
  