Files
cobol-java-v3/benchmark-programs/15-csv-fb-nolf/main-15-csv-fb-nolf.cbl
T
NB-076 94400d50d4 feat: add benchmark-programs — 58 telecom COBOL test programs
作为子目录纳入系统,与核心测试管道协同

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-25 09:53:21 +08:00

994 lines
36 KiB
COBOL

*> ============================================================
*> 15-csv-fb-nolf : 外部CDR CSV取込(无LF) (CDR Import)
*> Input : FILE-IN (file-in.csv) Output: FILE-OUT (file-out.dat)
*> ERROR-REPORT (csv-errors.dat) AUDIT-FILE (csv-audit.dat)
*> Enhanced: SECTIONs, field validation, truncation detection,
*> quote check, count reconciliation, hash totals,
*> severity levels, FILE STATUS after every I/O.
*> Coverage: CF-N001, CF-N003~N006, CF-A002
*> ============================================================
IDENTIFICATION DIVISION.
PROGRAM-ID. CsvFbNoLf.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT FILE-IN ASSIGN TO 'file-in.csv'
ORGANIZATION IS LINE SEQUENTIAL
FILE STATUS IS WS-FILE-IN-STATUS.
SELECT FILE-OUT ASSIGN TO 'file-out.dat'
ORGANIZATION IS SEQUENTIAL
FILE STATUS IS WS-FILE-OUT-STATUS.
SELECT ERROR-REPORT ASSIGN TO 'csv-errors.dat'
ORGANIZATION IS LINE SEQUENTIAL
FILE STATUS IS WS-ERR-STATUS.
SELECT AUDIT-FILE ASSIGN TO 'csv-audit.dat'
ORGANIZATION IS LINE SEQUENTIAL
FILE STATUS IS WS-AUDIT-STATUS.
DATA DIVISION.
FILE SECTION.
FD FILE-IN.
01 IN-LINE PIC X(200).
FD FILE-OUT.
01 OUT-REC PIC X(100).
FD ERROR-REPORT.
01 ERR-REC PIC X(80).
FD AUDIT-FILE.
01 AUD-REC PIC X(80).
WORKING-STORAGE SECTION.
01 WS-TELECOM-REC.
COPY "telecom/TEL-CDR.cpy".
*> File status keys for all files
01 WS-FILE-IN-STATUS PIC X(02).
01 WS-FILE-OUT-STATUS PIC X(02).
01 WS-ERR-STATUS PIC X(02).
01 WS-AUDIT-STATUS PIC X(02).
*> EOF flag
01 WS-EOF PIC X(01) VALUE 'N'.
88 WS-EOF-YES VALUE 'Y' FALSE 'N'.
*> Record counters (original + expanded)
01 WS-REC-COUNT PIC 9(05) VALUE ZERO.
01 WS-LINE-COUNT PIC 9(05) VALUE ZERO.
01 WS-COUNTERS.
05 WS-TOT-READ PIC 9(09) VALUE ZERO.
05 WS-TOT-WRITTEN PIC 9(09) VALUE ZERO.
05 WS-TOT-MALFORMED PIC 9(09) VALUE ZERO.
05 WS-TOT-TRUNCATED PIC 9(09) VALUE ZERO.
05 WS-TOT-QUOTE-ERR PIC 9(09) VALUE ZERO.
05 WS-TOT-FIELD-ERR PIC 9(09) VALUE ZERO.
05 WS-TOT-WARNINGS PIC 9(09) VALUE ZERO.
05 WS-TOT-ERRORS PIC 9(09) VALUE ZERO.
05 WS-TOT-CRITICAL PIC 9(09) VALUE ZERO.
*> CSV parsing fields (original)
01 WS-FIELDS.
05 WS-FIELD1 PIC X(30).
05 WS-FIELD2 PIC X(30).
05 WS-FIELD3 PIC X(30).
01 WS-POS PIC 9(03).
01 WS-CHAR PIC X(01).
01 WS-FIELD-NUM PIC 9(01).
01 WS-CHAR-OFFSET PIC 9(02).
01 WS-IN-QUOTE PIC X(01) VALUE 'N'.
88 WS-IN-QUOTE-YES VALUE 'Y' FALSE 'N'.
01 WS-OUT-LINE PIC X(100).
*> Validation fields
01 WS-QUOTE-COUNT PIC 9(03) VALUE ZERO.
01 WS-MALFORMED-CNT PIC 9(05) VALUE ZERO.
01 WS-TRUNC-CNT PIC 9(05) VALUE ZERO.
01 WS-FIELD-COUNT-ERR PIC 9(05) VALUE ZERO.
01 WS-FIELD3-LEN PIC 9(02).
01 WS-IDX PIC 9(02).
01 WS-CHAR-CHK PIC X(01).
01 WS-ALL-NUMERIC PIC X(01) VALUE 'Y'.
01 WS-RECONCILE-OK PIC X(01) VALUE 'Y'.
01 WS-VALID-FLAG PIC X(01) VALUE 'Y'.
88 WS-VALID-REC-YES VALUE 'Y' FALSE 'N'.
88 WS-VALID-REC-NO VALUE 'N'.
*> Field-level validation status per field
01 WS-FIELD-STATUS.
05 WS-FIELD1-OK PIC X(01) VALUE 'Y'.
05 WS-FIELD2-OK PIC X(01) VALUE 'Y'.
05 WS-FIELD3-OK PIC X(01) VALUE 'Y'.
01 WS-FIELD-ERR-MSG PIC X(60).
01 WS-FIELD-TRUNC-FLAG PIC X(01) VALUE 'N'.
88 WS-FIELD-TRUNC-YES VALUE 'Y' FALSE 'N'.
*> Input record buffer for error reporting
01 WS-INPUT-BUF PIC X(200).
*> Hash totals (per field + overall)
01 WS-HASH-TOTAL PIC 9(10) VALUE ZERO.
01 WS-HASH-FIELD-LEN PIC 9(05) VALUE ZERO.
01 WS-HASH-FIELD1 PIC 9(10) VALUE ZERO.
01 WS-HASH-FIELD2 PIC 9(10) VALUE ZERO.
01 WS-HASH-FIELD3 PIC 9(10) VALUE ZERO.
*> Timestamp
01 WS-TIMESTAMP PIC X(20).
01 WS-CUR-DATE-DISP PIC X(08).
01 WS-CUR-TIME-DISP PIC X(06).
01 WS-TS-DISPLAY PIC X(26).
*> Error severity levels
01 WS-SEVERITY PIC X(01).
88 WS-SEV-INFO VALUE 'I'.
88 WS-SEV-WARNING VALUE 'W'.
88 WS-SEV-ERROR VALUE 'E'.
88 WS-SEV-CRITICAL VALUE 'C'.
*> Report and display formatting fields
01 WS-RPT-COUNT PIC Z(9)9.
01 WS-RPT-COUNT9 PIC Z(9)9.
01 WS-RPT-LINE PIC X(80).
01 WS-RPT-PCT PIC Z(9)9.9.
01 WS-LINE-DISP PIC Z(9)9.
01 WS-DISP-BUFFER PIC X(80).
PROCEDURE DIVISION.
*> ===== MAIN SECTION =====
MAIN SECTION.
MB-PROCESS.
PERFORM 1000-INIT
PERFORM 2000-OPEN-FILES
PERFORM 3000-READ-INPUT
PERFORM 4000-REPORT
PERFORM 5000-AUDIT
PERFORM 6000-ERROR-HANDLE
PERFORM 9000-EXIT
.
*> ===== 1000-INIT =====
1000-INIT SECTION.
1000-START.
MOVE FUNCTION CURRENT-DATE (1:8) TO WS-CUR-DATE-DISP.
MOVE FUNCTION CURRENT-DATE (9:6) TO WS-CUR-TIME-DISP.
STRING WS-CUR-DATE-DISP '-'
WS-CUR-TIME-DISP
DELIMITED BY SIZE INTO WS-TIMESTAMP.
STRING WS-CUR-DATE-DISP(1:4) '-'
WS-CUR-DATE-DISP(5:2) '-'
WS-CUR-DATE-DISP(7:2) ' '
WS-CUR-TIME-DISP(1:2) ':'
WS-CUR-TIME-DISP(3:2) ':'
WS-CUR-TIME-DISP(5:2)
DELIMITED BY SIZE INTO WS-TS-DISPLAY.
DISPLAY WS-TS-DISPLAY ' 1000-INIT: CsvFbNoLf START'.
*> Initialize all counters to zero
MOVE ZERO TO WS-REC-COUNT
WS-LINE-COUNT
WS-TOT-READ
WS-TOT-WRITTEN
WS-TOT-MALFORMED
WS-TOT-TRUNCATED
WS-TOT-QUOTE-ERR
WS-TOT-FIELD-ERR
WS-TOT-WARNINGS
WS-TOT-ERRORS
WS-TOT-CRITICAL
WS-MALFORMED-CNT
WS-TRUNC-CNT
WS-FIELD-COUNT-ERR
WS-HASH-TOTAL
WS-HASH-FIELD1
WS-HASH-FIELD2
WS-HASH-FIELD3
WS-HASH-FIELD-LEN.
*> Initialize flags
MOVE 'Y' TO WS-RECONCILE-OK.
MOVE 'N' TO WS-EOF.
*> ===== 2000-OPEN-FILES =====
2000-OPEN-FILES SECTION.
2000-START.
DISPLAY WS-TS-DISPLAY ' 2000-OPEN-FILES: Opening files'.
OPEN INPUT FILE-IN.
IF WS-FILE-IN-STATUS NOT = '00'
DISPLAY WS-TS-DISPLAY ' ERROR: Cannot open FILE-IN,'
' status: ' WS-FILE-IN-STATUS
MOVE 1 TO RETURN-CODE
STOP RUN
END-IF.
DISPLAY WS-TS-DISPLAY ' FILE-IN opened, status='
WS-FILE-IN-STATUS.
OPEN OUTPUT FILE-OUT.
IF WS-FILE-OUT-STATUS NOT = '00'
DISPLAY WS-TS-DISPLAY ' ERROR: Cannot open FILE-OUT,'
' status: ' WS-FILE-OUT-STATUS
MOVE 1 TO RETURN-CODE
STOP RUN
END-IF.
DISPLAY WS-TS-DISPLAY ' FILE-OUT opened, status='
WS-FILE-OUT-STATUS.
OPEN OUTPUT ERROR-REPORT.
IF WS-ERR-STATUS NOT = '00'
DISPLAY WS-TS-DISPLAY ' ERROR: Cannot open ERROR-REPORT,'
' status: ' WS-ERR-STATUS
MOVE 1 TO RETURN-CODE
STOP RUN
END-IF.
DISPLAY WS-TS-DISPLAY ' ERROR-REPORT opened, status='
WS-ERR-STATUS.
OPEN OUTPUT AUDIT-FILE.
IF WS-AUDIT-STATUS NOT = '00'
DISPLAY WS-TS-DISPLAY ' ERROR: Cannot open AUDIT-FILE,'
' status: ' WS-AUDIT-STATUS
MOVE 1 TO RETURN-CODE
STOP RUN
END-IF.
DISPLAY WS-TS-DISPLAY ' AUDIT-FILE opened, status='
WS-AUDIT-STATUS.
*> Write error report header lines
MOVE 'CSV Error Detail Report - CsvFbNoLf' TO ERR-REC.
WRITE ERR-REC.
IF WS-ERR-STATUS NOT = '00'
DISPLAY WS-TS-DISPLAY 'WRITE ERROR: ERROR-REPORT status='
WS-ERR-STATUS
END-IF.
STRING 'Run: ' WS-TIMESTAMP INTO ERR-REC.
WRITE ERR-REC.
IF WS-ERR-STATUS NOT = '00'
DISPLAY WS-TS-DISPLAY 'WRITE ERROR: ERROR-REPORT status='
WS-ERR-STATUS
END-IF.
MOVE SPACES TO ERR-REC.
MOVE 'Line Severity Field Description' TO ERR-REC.
WRITE ERR-REC.
IF WS-ERR-STATUS NOT = '00'
DISPLAY WS-TS-DISPLAY 'WRITE ERROR: ERROR-REPORT status='
WS-ERR-STATUS
END-IF.
MOVE SPACES TO ERR-REC.
WRITE ERR-REC.
IF WS-ERR-STATUS NOT = '00'
DISPLAY WS-TS-DISPLAY 'WRITE ERROR: ERROR-REPORT status='
WS-ERR-STATUS
END-IF.
*> Write audit file header lines
MOVE 'CSV Audit Report - CsvFbNoLf' TO AUD-REC.
WRITE AUD-REC.
IF WS-AUDIT-STATUS NOT = '00'
DISPLAY WS-TS-DISPLAY ' WRITE ERROR: AUDIT-FILE status='
WS-AUDIT-STATUS
END-IF.
STRING 'Run: ' WS-TIMESTAMP INTO AUD-REC.
WRITE AUD-REC.
IF WS-AUDIT-STATUS NOT = '00'
DISPLAY WS-TS-DISPLAY ' WRITE ERROR: AUDIT-FILE status='
WS-AUDIT-STATUS
END-IF.
MOVE SPACES TO AUD-REC.
WRITE AUD-REC.
IF WS-AUDIT-STATUS NOT = '00'
DISPLAY WS-TS-DISPLAY ' WRITE ERROR: AUDIT-FILE status='
WS-AUDIT-STATUS
END-IF.
DISPLAY WS-TS-DISPLAY '2000-OPEN-FILES: All files opened OK'.
*> ===== 3000-READ-INPUT =====
3000-READ-INPUT SECTION.
3000-START.
DISPLAY WS-TS-DISPLAY ' 3000-READ-INPUT: Processing CSV'.
PERFORM UNTIL WS-EOF-YES
READ FILE-IN
AT END
SET WS-EOF-YES TO TRUE
DISPLAY WS-TS-DISPLAY ' EOF reached,'
' lines=' WS-LINE-COUNT
NOT AT END
ADD 1 TO WS-LINE-COUNT
ADD 1 TO WS-TOT-READ
MOVE IN-LINE TO WS-INPUT-BUF
PERFORM 3100-VALIDATE-RECORD
END-READ
IF WS-FILE-IN-STATUS NOT = '00'
AND NOT = '10'
DISPLAY WS-TS-DISPLAY ' READ ERROR: FILE-IN status='
WS-FILE-IN-STATUS
MOVE 'E' TO WS-SEVERITY
ADD 1 TO WS-TOT-ERRORS
END-IF
END-PERFORM.
*> Close input file with STATUS check
CLOSE FILE-IN.
IF WS-FILE-IN-STATUS NOT = '00'
DISPLAY WS-TS-DISPLAY ' CLOSE ERROR: FILE-IN status='
WS-FILE-IN-STATUS
ADD 1 TO WS-TOT-ERRORS
END-IF.
DISPLAY WS-TS-DISPLAY ' 3000-READ-INPUT: Input closed,'
' total=' WS-LINE-COUNT.
*> ===== 3100-VALIDATE-RECORD =====
3100-VALIDATE-RECORD SECTION.
3100-START.
MOVE 'Y' TO WS-VALID-FLAG.
MOVE 'Y' TO WS-FIELD1-OK.
MOVE 'Y' TO WS-FIELD2-OK.
MOVE 'Y' TO WS-FIELD3-OK.
MOVE 'N' TO WS-FIELD-TRUNC-FLAG.
MOVE SPACES TO WS-FIELD-ERR-MSG.
*> Quote pairing: odd quote count = malformed
MOVE 0 TO WS-QUOTE-COUNT.
INSPECT IN-LINE TALLYING WS-QUOTE-COUNT
FOR ALL '"'.
IF FUNCTION MOD(WS-QUOTE-COUNT, 2) NOT = 0
ADD 1 TO WS-MALFORMED-CNT
ADD 1 TO WS-TOT-QUOTE-ERR
ADD 1 TO WS-TOT-ERRORS
MOVE 'E' TO WS-SEVERITY
MOVE 'Unpaired quote in CSV line' TO WS-FIELD-ERR-MSG
MOVE 'N' TO WS-VALID-FLAG
STRING WS-LINE-COUNT ' ERROR Quote '
WS-FIELD-ERR-MSG
DELIMITED BY SIZE INTO ERR-REC
WRITE ERR-REC
IF WS-ERR-STATUS NOT = '00'
DISPLAY WS-TS-DISPLAY
' WRITE ERROR: ERROR-REPORT status='
WS-ERR-STATUS
END-IF
DISPLAY WS-TS-DISPLAY ' Quote error at line '
WS-LINE-COUNT ': ' WS-FIELD-ERR-MSG
END-IF.
*> Parse using original logic -> WS-FIELD1..3
PERFORM PARSE-CSV-LINE.
*> Field count: expect 3 fields
IF WS-FIELD-NUM < 3
ADD 1 TO WS-FIELD-COUNT-ERR
ADD 1 TO WS-TOT-FIELD-ERR
ADD 1 TO WS-TOT-ERRORS
MOVE 'E' TO WS-SEVERITY
MOVE 'N' TO WS-VALID-FLAG
STRING 'Expected 3 fields, got ' WS-FIELD-NUM
INTO WS-FIELD-ERR-MSG
STRING WS-LINE-COUNT ' ERROR Field '
WS-FIELD-ERR-MSG
DELIMITED BY SIZE INTO ERR-REC
WRITE ERR-REC
IF WS-ERR-STATUS NOT = '00'
DISPLAY WS-TS-DISPLAY
' WRITE ERROR: ERROR-REPORT status='
WS-ERR-STATUS
END-IF
DISPLAY WS-TS-DISPLAY ' Field count error at line '
WS-LINE-COUNT ': ' WS-FIELD-ERR-MSG
END-IF.
*> Field 1: presence check
IF WS-FIELD1 = SPACES
MOVE 'N' TO WS-FIELD1-OK
MOVE 'W' TO WS-SEVERITY
ADD 1 TO WS-TOT-WARNINGS
MOVE 'Field1 is empty' TO WS-FIELD-ERR-MSG
STRING WS-LINE-COUNT ' WARNING Field1 '
'Empty field content'
DELIMITED BY SIZE INTO ERR-REC
WRITE ERR-REC
IF WS-ERR-STATUS NOT = '00'
DISPLAY WS-TS-DISPLAY
' WRITE ERROR: ERROR-REPORT status='
WS-ERR-STATUS
END-IF
END-IF.
*> Truncation check: Field1 > 30 chars
IF FUNCTION LENGTH(FUNCTION TRIM(WS-FIELD1)) > 30
ADD 1 TO WS-TRUNC-CNT
ADD 1 TO WS-TOT-TRUNCATED
MOVE 'Y' TO WS-FIELD-TRUNC-FLAG
MOVE 'W' TO WS-SEVERITY
ADD 1 TO WS-TOT-WARNINGS
STRING WS-LINE-COUNT ' WARNING Field1 '
'Truncated to 30 chars'
DELIMITED BY SIZE INTO ERR-REC
WRITE ERR-REC
IF WS-ERR-STATUS NOT = '00'
DISPLAY WS-TS-DISPLAY
' WRITE ERROR: ERROR-REPORT status='
WS-ERR-STATUS
END-IF
END-IF.
*> Truncation check: Field2 > 30 chars
IF FUNCTION LENGTH(FUNCTION TRIM(WS-FIELD2)) > 30
ADD 1 TO WS-TRUNC-CNT
ADD 1 TO WS-TOT-TRUNCATED
MOVE 'Y' TO WS-FIELD-TRUNC-FLAG
MOVE 'W' TO WS-SEVERITY
ADD 1 TO WS-TOT-WARNINGS
STRING WS-LINE-COUNT ' WARNING Field2 '
'Truncated to 30 chars'
DELIMITED BY SIZE INTO ERR-REC
WRITE ERR-REC
IF WS-ERR-STATUS NOT = '00'
DISPLAY WS-TS-DISPLAY
' WRITE ERROR: ERROR-REPORT status='
WS-ERR-STATUS
END-IF
END-IF.
*> Numeric check: Field2
IF WS-FIELD2 NOT = SPACES
MOVE 'Y' TO WS-ALL-NUMERIC
MOVE WS-FIELD2 TO WS-DISP-BUFFER
INSPECT WS-DISP-BUFFER REPLACING ALL SPACES BY '0'
PERFORM VARYING WS-IDX FROM 1 BY 1
UNTIL WS-IDX > 30 OR WS-ALL-NUMERIC = 'N'
MOVE WS-DISP-BUFFER(WS-IDX:1) TO WS-CHAR-CHK
IF WS-CHAR-CHK < '0' OR > '9'
MOVE 'N' TO WS-ALL-NUMERIC
END-IF
END-PERFORM
IF WS-ALL-NUMERIC = 'N'
MOVE 'N' TO WS-FIELD2-OK
MOVE 'W' TO WS-SEVERITY
ADD 1 TO WS-TOT-WARNINGS
STRING WS-LINE-COUNT ' WARNING Field2 '
'Non-numeric content'
DELIMITED BY SIZE INTO ERR-REC
WRITE ERR-REC
IF WS-ERR-STATUS NOT = '00'
DISPLAY WS-TS-DISPLAY
' WRITE ERROR: ERROR-REPORT status='
WS-ERR-STATUS
END-IF
END-IF
END-IF.
*> Truncation check: Field3 > 30 chars
IF FUNCTION LENGTH(FUNCTION TRIM(WS-FIELD3)) > 30
ADD 1 TO WS-TRUNC-CNT
ADD 1 TO WS-TOT-TRUNCATED
MOVE 'Y' TO WS-FIELD-TRUNC-FLAG
MOVE 'W' TO WS-SEVERITY
ADD 1 TO WS-TOT-WARNINGS
STRING WS-LINE-COUNT ' WARNING Field3 '
'Truncated to 30 chars'
DELIMITED BY SIZE INTO ERR-REC
WRITE ERR-REC
IF WS-ERR-STATUS NOT = '00'
DISPLAY WS-TS-DISPLAY
' WRITE ERROR: ERROR-REPORT status='
WS-ERR-STATUS
END-IF
END-IF.
*> Numeric check: Field3
IF WS-FIELD3 > SPACES
MOVE 'Y' TO WS-ALL-NUMERIC
MOVE WS-FIELD3 TO WS-DISP-BUFFER
INSPECT WS-DISP-BUFFER REPLACING ALL SPACES BY '0'
PERFORM VARYING WS-IDX FROM 1 BY 1
UNTIL WS-IDX > 30 OR WS-ALL-NUMERIC = 'N'
MOVE WS-DISP-BUFFER(WS-IDX:1) TO WS-CHAR-CHK
IF WS-CHAR-CHK < '0' OR > '9'
MOVE 'N' TO WS-ALL-NUMERIC
END-IF
END-PERFORM
IF WS-ALL-NUMERIC = 'N'
MOVE 'N' TO WS-FIELD3-OK
MOVE 'W' TO WS-SEVERITY
ADD 1 TO WS-TOT-WARNINGS
STRING WS-LINE-COUNT ' WARNING Field3 '
'Non-numeric content'
DELIMITED BY SIZE INTO ERR-REC
WRITE ERR-REC
IF WS-ERR-STATUS NOT = '00'
DISPLAY WS-TS-DISPLAY
' WRITE ERROR: ERROR-REPORT status='
WS-ERR-STATUS
END-IF
END-IF
END-IF.
*> If record is valid (no ERROR severity), process it
IF WS-VALID-FLAG = 'Y'
PERFORM 3200-PROCESS-RECORD
ELSE
DISPLAY WS-TS-DISPLAY ' Record ' WS-LINE-COUNT
' skipped due to validation errors'
END-IF.
*> ===== 3200-PROCESS-RECORD =====
3200-PROCESS-RECORD SECTION.
3200-START.
PERFORM 3300-WRITE-OUTPUT.
*> Update per-field hash totals for batch control
COMPUTE WS-HASH-FIELD1 = WS-HASH-FIELD1 +
FUNCTION LENGTH(FUNCTION TRIM(WS-FIELD1))
COMPUTE WS-HASH-FIELD2 = WS-HASH-FIELD2 +
FUNCTION LENGTH(FUNCTION TRIM(WS-FIELD2))
COMPUTE WS-HASH-FIELD3 = WS-HASH-FIELD3 +
FUNCTION LENGTH(FUNCTION TRIM(WS-FIELD3)).
*> ===== 3300-WRITE-OUTPUT =====
3300-WRITE-OUTPUT SECTION.
3300-START.
*> Build output record from parsed fields (original logic preserved)
MOVE SPACES TO WS-OUT-LINE.
STRING WS-FIELD1 DELIMITED BY SPACES
'|'
WS-FIELD2 DELIMITED BY SPACES
'|'
WS-FIELD3 DELIMITED BY SPACES
INTO WS-OUT-LINE
END-STRING.
MOVE WS-OUT-LINE TO OUT-REC.
WRITE OUT-REC.
*> FILE STATUS check after EVERY write
IF WS-FILE-OUT-STATUS NOT = '00'
DISPLAY WS-TS-DISPLAY
' WRITE ERROR: FILE-OUT status='
WS-FILE-OUT-STATUS
MOVE 'E' TO WS-SEVERITY
ADD 1 TO WS-TOT-ERRORS
END-IF.
ADD 1 TO WS-REC-COUNT.
ADD 1 TO WS-TOT-WRITTEN.
*> Combined hash total: sum of all field lengths
COMPUTE WS-HASH-FIELD-LEN =
FUNCTION LENGTH(FUNCTION TRIM(WS-FIELD1)) +
FUNCTION LENGTH(FUNCTION TRIM(WS-FIELD2)) +
FUNCTION LENGTH(FUNCTION TRIM(WS-FIELD3)).
ADD WS-HASH-FIELD-LEN TO WS-HASH-TOTAL.
*> ===== 4000-REPORT =====
4000-REPORT SECTION.
4000-START.
DISPLAY WS-TS-DISPLAY '4000-REPORT: Finalizing error report'.
*> Write error report trailer
MOVE SPACES TO ERR-REC.
MOVE '--- End of CSV Error Detail Report ---' TO ERR-REC.
WRITE ERR-REC.
IF WS-ERR-STATUS NOT = '00'
DISPLAY WS-TS-DISPLAY
' WRITE ERROR: ERROR-REPORT status='
WS-ERR-STATUS
END-IF.
*> Write error summary to error report
MOVE SPACES TO ERR-REC.
WRITE ERR-REC.
IF WS-ERR-STATUS NOT = '00'
DISPLAY WS-TS-DISPLAY
' WRITE ERROR: ERROR-REPORT status='
WS-ERR-STATUS
END-IF.
MOVE WS-LINE-COUNT TO WS-RPT-COUNT.
STRING 'Total input lines: ' WS-RPT-COUNT
INTO ERR-REC.
WRITE ERR-REC.
IF WS-ERR-STATUS NOT = '00'
DISPLAY WS-TS-DISPLAY
' WRITE ERROR: ERROR-REPORT status='
WS-ERR-STATUS
END-IF.
MOVE WS-MALFORMED-CNT TO WS-RPT-COUNT.
STRING 'Malformed records: ' WS-RPT-COUNT
INTO ERR-REC.
WRITE ERR-REC.
IF WS-ERR-STATUS NOT = '00'
DISPLAY WS-TS-DISPLAY
' WRITE ERROR: ERROR-REPORT status='
WS-ERR-STATUS
END-IF.
MOVE WS-TRUNC-CNT TO WS-RPT-COUNT.
STRING 'Truncation warns: ' WS-RPT-COUNT
INTO ERR-REC.
WRITE ERR-REC.
IF WS-ERR-STATUS NOT = '00'
DISPLAY WS-TS-DISPLAY
' WRITE ERROR: ERROR-REPORT status='
WS-ERR-STATUS
END-IF.
MOVE WS-FIELD-COUNT-ERR TO WS-RPT-COUNT.
STRING 'Field count errors: ' WS-RPT-COUNT
INTO ERR-REC.
WRITE ERR-REC.
IF WS-ERR-STATUS NOT = '00'
DISPLAY WS-TS-DISPLAY
' WRITE ERROR: ERROR-REPORT status='
WS-ERR-STATUS
END-IF.
*> Record count reconciliation summary
IF WS-LINE-COUNT = WS-REC-COUNT
MOVE 'PASS' TO WS-RPT-LINE
ELSE
MOVE 'MISMATCH (see audit)' TO WS-RPT-LINE
MOVE 'N' TO WS-RECONCILE-OK
END-IF.
STRING 'Count reconcile: ' WS-RPT-LINE
INTO ERR-REC.
WRITE ERR-REC.
IF WS-ERR-STATUS NOT = '00'
DISPLAY WS-TS-DISPLAY
' WRITE ERROR: ERROR-REPORT status='
WS-ERR-STATUS
END-IF.
*> Close error report with STATUS check
CLOSE ERROR-REPORT.
IF WS-ERR-STATUS NOT = '00'
DISPLAY WS-TS-DISPLAY
' CLOSE ERROR: ERROR-REPORT status='
WS-ERR-STATUS
END-IF.
DISPLAY WS-TS-DISPLAY ' 4000-REPORT: Error report closed'.
*> ===== 5000-AUDIT =====
5000-AUDIT SECTION.
5000-START.
DISPLAY WS-TS-DISPLAY
'5000-AUDIT: audit report and control totals'.
*> Audit header
MOVE '=== CSV Import Summary ===' TO AUD-REC.
WRITE AUD-REC.
IF WS-AUDIT-STATUS NOT = '00'
DISPLAY WS-TS-DISPLAY
' WRITE ERROR: AUDIT-FILE status='
WS-AUDIT-STATUS
END-IF.
MOVE SPACES TO AUD-REC.
WRITE AUD-REC.
IF WS-AUDIT-STATUS NOT = '00'
DISPLAY WS-TS-DISPLAY
' WRITE ERROR: AUDIT-FILE status='
WS-AUDIT-STATUS
END-IF.
*> Batch run identification
STRING 'Program: CsvFbNoLf' INTO AUD-REC.
WRITE AUD-REC.
IF WS-AUDIT-STATUS NOT = '00'
DISPLAY WS-TS-DISPLAY
' WRITE ERROR: AUDIT-FILE status='
WS-AUDIT-STATUS
END-IF.
STRING 'Run date: ' WS-CUR-DATE-DISP
INTO AUD-REC.
WRITE AUD-REC.
IF WS-AUDIT-STATUS NOT = '00'
DISPLAY WS-TS-DISPLAY
' WRITE ERROR: AUDIT-FILE status='
WS-AUDIT-STATUS
END-IF.
STRING 'Run time: ' WS-CUR-TIME-DISP
INTO AUD-REC.
WRITE AUD-REC.
IF WS-AUDIT-STATUS NOT = '00'
DISPLAY WS-TS-DISPLAY
' WRITE ERROR: AUDIT-FILE status='
WS-AUDIT-STATUS
END-IF.
MOVE SPACES TO AUD-REC.
WRITE AUD-REC.
*> Record counts
MOVE WS-LINE-COUNT TO WS-RPT-COUNT.
STRING 'Input lines read: ' WS-RPT-COUNT
INTO AUD-REC.
WRITE AUD-REC.
MOVE WS-REC-COUNT TO WS-RPT-COUNT.
STRING 'Output records written: ' WS-RPT-COUNT
INTO AUD-REC.
WRITE AUD-REC.
*> Record count reconciliation
IF WS-LINE-COUNT = WS-REC-COUNT
MOVE 'PASS' TO WS-RPT-LINE
ELSE
MOVE 'MISMATCH (see details)' TO WS-RPT-LINE
MOVE 'N' TO WS-RECONCILE-OK
END-IF.
STRING 'Line vs record reconcile: ' WS-RPT-LINE
INTO AUD-REC.
WRITE AUD-REC.
*> Error counts
MOVE WS-MALFORMED-CNT TO WS-RPT-COUNT.
STRING 'Malformed records: ' WS-RPT-COUNT
INTO AUD-REC.
WRITE AUD-REC.
MOVE WS-TRUNC-CNT TO WS-RPT-COUNT.
STRING 'Truncation warnings: ' WS-RPT-COUNT
INTO AUD-REC.
WRITE AUD-REC.
MOVE WS-FIELD-COUNT-ERR TO WS-RPT-COUNT.
STRING 'Field count errors: ' WS-RPT-COUNT
INTO AUD-REC.
WRITE AUD-REC.
MOVE WS-TOT-QUOTE-ERR TO WS-RPT-COUNT.
STRING 'Quote errors: ' WS-RPT-COUNT
INTO AUD-REC.
WRITE AUD-REC.
MOVE SPACES TO AUD-REC.
WRITE AUD-REC.
*> ------------------------
*> Batch Control Totals
*> ------------------------
MOVE '=== Batch Control Totals ===' TO AUD-REC.
WRITE AUD-REC.
MOVE WS-REC-COUNT TO WS-RPT-COUNT.
STRING 'Record count total: ' WS-RPT-COUNT
INTO AUD-REC.
WRITE AUD-REC.
MOVE WS-HASH-FIELD1 TO WS-RPT-COUNT9.
STRING 'Hash total Field1: ' WS-RPT-COUNT9
INTO AUD-REC.
WRITE AUD-REC.
MOVE WS-HASH-FIELD2 TO WS-RPT-COUNT9.
STRING 'Hash total Field2: ' WS-RPT-COUNT9
INTO AUD-REC.
WRITE AUD-REC.
MOVE WS-HASH-FIELD3 TO WS-RPT-COUNT9.
STRING 'Hash total Field3: ' WS-RPT-COUNT9
INTO AUD-REC.
WRITE AUD-REC.
MOVE WS-HASH-TOTAL TO WS-RPT-COUNT9.
STRING 'Combined hash total: ' WS-RPT-COUNT9
INTO AUD-REC.
WRITE AUD-REC.
MOVE SPACES TO AUD-REC.
WRITE AUD-REC.
*> Processing success rate
IF WS-LINE-COUNT > 0
COMPUTE WS-RPT-PCT ROUNDED =
(WS-REC-COUNT / WS-LINE-COUNT) * 100
STRING 'Processing success rate: '
WS-RPT-PCT '%'
INTO AUD-REC
WRITE AUD-REC
IF WS-AUDIT-STATUS NOT = '00'
DISPLAY WS-TS-DISPLAY
' WRITE ERROR: AUDIT-FILE status='
WS-AUDIT-STATUS
END-IF
END-IF.
MOVE SPACES TO AUD-REC.
WRITE AUD-REC.
*> Audit trailer
MOVE '=== End of CSV Audit Report ===' TO AUD-REC.
WRITE AUD-REC.
IF WS-AUDIT-STATUS NOT = '00'
DISPLAY WS-TS-DISPLAY
' WRITE ERROR: AUDIT-FILE status='
WS-AUDIT-STATUS
END-IF.
*> Close audit file with STATUS check
CLOSE AUDIT-FILE.
IF WS-AUDIT-STATUS NOT = '00'
DISPLAY WS-TS-DISPLAY
' CLOSE ERROR: AUDIT-FILE status='
WS-AUDIT-STATUS
END-IF.
DISPLAY WS-TS-DISPLAY ' 5000-AUDIT: Audit file closed'.
*> ===== 6000-ERROR-HANDLE =====
6000-ERROR-HANDLE SECTION.
6000-START.
DISPLAY WS-TS-DISPLAY
' 6000-ERROR-HANDLE: Error summary'.
*> FILE-IN status summary
IF WS-FILE-IN-STATUS NOT = '00'
AND NOT = '10'
DISPLAY WS-TS-DISPLAY ' FILE-IN status: '
WS-FILE-IN-STATUS
ADD 1 TO WS-TOT-ERRORS
ELSE
DISPLAY WS-TS-DISPLAY ' FILE-IN status OK ('
WS-FILE-IN-STATUS ')'
END-IF.
*> FILE-OUT status summary
IF WS-FILE-OUT-STATUS NOT = '00'
AND NOT = '10'
DISPLAY WS-TS-DISPLAY ' FILE-OUT status: '
WS-FILE-OUT-STATUS
ADD 1 TO WS-TOT-ERRORS
ELSE
DISPLAY WS-TS-DISPLAY ' FILE-OUT status OK ('
WS-FILE-OUT-STATUS ')'
END-IF.
*> ERROR-REPORT status summary
IF WS-ERR-STATUS NOT = '00'
AND NOT = '10'
DISPLAY WS-TS-DISPLAY ' ERROR-REPORT status: '
WS-ERR-STATUS
ADD 1 TO WS-TOT-ERRORS
ELSE
DISPLAY WS-TS-DISPLAY ' ERROR-REPORT status OK ('
WS-ERR-STATUS ')'
END-IF.
*> AUDIT-FILE status summary
IF WS-AUDIT-STATUS NOT = '00'
AND NOT = '10'
DISPLAY WS-TS-DISPLAY ' AUDIT-FILE status: '
WS-AUDIT-STATUS
ADD 1 TO WS-TOT-ERRORS
ELSE
DISPLAY WS-TS-DISPLAY ' AUDIT-FILE status OK ('
WS-AUDIT-STATUS ')'
END-IF.
*> Error count summary with severity breakdown
DISPLAY WS-TS-DISPLAY ' Severity totals:'
MOVE WS-TOT-WARNINGS TO WS-RPT-COUNT9
DISPLAY ' Warnings: ' WS-RPT-COUNT9
MOVE WS-TOT-ERRORS TO WS-RPT-COUNT9
DISPLAY ' Errors: ' WS-RPT-COUNT9
MOVE WS-TOT-CRITICAL TO WS-RPT-COUNT9
DISPLAY ' Critical: ' WS-RPT-COUNT9.
*> Record count reconciliation check
IF WS-LINE-COUNT NOT = WS-REC-COUNT
DISPLAY WS-TS-DISPLAY
' WARNING: Line/output count mismatch'
MOVE 'N' TO WS-RECONCILE-OK
END-IF.
*> ===== 9000-EXIT =====
9000-EXIT SECTION.
9000-START.
DISPLAY WS-TS-DISPLAY
' 9000-EXIT: Program CsvFbNoLf completed'.
*> Final record counts display
MOVE WS-LINE-COUNT TO WS-RPT-COUNT.
MOVE WS-REC-COUNT TO WS-LINE-DISP.
DISPLAY ' Input lines: ' WS-RPT-COUNT.
MOVE WS-REC-COUNT TO WS-RPT-COUNT.
DISPLAY ' Output recs: ' WS-RPT-COUNT.
DISPLAY ' Reconcile: ' WS-RECONCILE-OK.
*> Error counts display
MOVE WS-MALFORMED-CNT TO WS-RPT-COUNT.
DISPLAY ' Malformed: ' WS-RPT-COUNT.
MOVE WS-TRUNC-CNT TO WS-RPT-COUNT.
DISPLAY ' Truncated: ' WS-RPT-COUNT.
MOVE WS-FIELD-COUNT-ERR TO WS-RPT-COUNT.
DISPLAY ' Field err: ' WS-RPT-COUNT.
*> Hash total display
MOVE WS-HASH-TOTAL TO WS-RPT-COUNT9.
DISPLAY ' Hash total: ' WS-RPT-COUNT9.
*> Output file listing
DISPLAY ' Output file: file-out.dat'.
DISPLAY ' Error report: csv-errors.dat'.
DISPLAY ' Audit report: csv-audit.dat'.
*> Close FILE-OUT with STATUS check
CLOSE FILE-OUT.
IF WS-FILE-OUT-STATUS NOT = '00'
DISPLAY WS-TS-DISPLAY
' CLOSE ERROR: FILE-OUT status='
WS-FILE-OUT-STATUS
END-IF.
DISPLAY WS-TS-DISPLAY ' FILE-OUT closed, status='
WS-FILE-OUT-STATUS.
*> Set return code based on reconciliation status
IF WS-RECONCILE-OK = 'N'
MOVE 4 TO RETURN-CODE
DISPLAY WS-TS-DISPLAY
' RETURN-CODE=4 (reconciliation mismatch)'
ELSE
MOVE 0 TO RETURN-CODE
DISPLAY WS-TS-DISPLAY
' RETURN-CODE=0 (normal completion)'
END-IF.
*> Terminate program
STOP RUN.
*> ===== PARSE-CSV-LINE (original logic, UNCHANGED) =====
PARSE-CSV-LINE.
MOVE SPACES TO WS-FIELDS.
MOVE 1 TO WS-POS.
MOVE 1 TO WS-FIELD-NUM.
MOVE 1 TO WS-CHAR-OFFSET.
MOVE 'N' TO WS-IN-QUOTE.
PERFORM VARYING WS-POS FROM 1 BY 1
UNTIL WS-POS > 200
OR WS-FIELD-NUM > 3
MOVE IN-LINE(WS-POS:1) TO WS-CHAR
IF WS-CHAR = '"'
IF WS-IN-QUOTE-YES
MOVE 'N' TO WS-IN-QUOTE
ELSE
MOVE 'Y' TO WS-IN-QUOTE
END-IF
ELSE
IF WS-CHAR = ',' AND NOT WS-IN-QUOTE-YES
ADD 1 TO WS-FIELD-NUM
MOVE 1 TO WS-CHAR-OFFSET
ELSE
IF WS-CHAR NOT = SPACE
OR WS-CHAR-OFFSET > 1
THEN
PERFORM SAVE-CHAR-TO-FIELD
END-IF
IF WS-CHAR-OFFSET < 30
ADD 1 TO WS-CHAR-OFFSET
END-IF
END-IF
END-IF
END-PERFORM.
*> ===== SAVE-CHAR-TO-FIELD (original, UNCHANGED) =====
SAVE-CHAR-TO-FIELD.
EVALUATE WS-FIELD-NUM
WHEN 1
MOVE WS-CHAR TO WS-FIELD1(WS-CHAR-OFFSET:1)
WHEN 2
MOVE WS-CHAR TO WS-FIELD2(WS-CHAR-OFFSET:1)
WHEN 3
MOVE WS-CHAR TO WS-FIELD3(WS-CHAR-OFFSET:1)
END-EVALUATE.
END PROGRAM CsvFbNoLf.