Files
cobol-java-v3/benchmark-programs/12-divide-100/main-12-divide-100.cbl
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

1089 lines
39 KiB
COBOL

*> ============================================================
*> 12-divide-100 : CDR数据100分割 (CDR 100-Split)
*> Input : FILE-IN.DAT (CDR数据: 100件毎分割)
*> Output: FILE-OUT-NN (100件毎の分割出力文件: FILE-OUT-01, 02...)
*> Coverage: S-N005, S-N006, S-N007, S-R001, S-R002
*>
*> Extended: Phase 2 — error recovery, hash totals, audit trail,
*> split statistics, boundary checks, CDR awareness,
*> header/trailer records, file naming validation.
*> ============================================================
IDENTIFICATION DIVISION.
PROGRAM-ID. Divide100.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT FILE-IN
ASSIGN TO WS-IN-FILE
ORGANIZATION IS LINE SEQUENTIAL
FILE STATUS IS WS-FILE-STATUS-IN.
SELECT FILE-OUT
ASSIGN TO WS-OUT-FILE
ORGANIZATION IS LINE SEQUENTIAL
FILE STATUS IS WS-FILE-STATUS-OUT.
SELECT AUDIT-REPORT
ASSIGN TO "AUDIT-RPT.TXT"
ORGANIZATION IS LINE SEQUENTIAL
FILE STATUS IS WS-FILE-STATUS-AUDIT.
SELECT SPLIT-STATS
ASSIGN TO "SPLIT-STATS.TXT"
ORGANIZATION IS LINE SEQUENTIAL
FILE STATUS IS WS-FILE-STATUS-STATS.
DATA DIVISION.
FILE SECTION.
FD FILE-IN.
01 FILE-IN-REC.
05 IN-RECORD PIC X(45).
FD FILE-OUT.
01 FILE-OUT-REC.
05 OUT-RECORD PIC X(45).
FD AUDIT-REPORT.
01 AUDIT-REC PIC X(120).
FD SPLIT-STATS.
01 STATS-REC PIC X(120).
WORKING-STORAGE SECTION.
01 WS-TELECOM-REC.
COPY "telecom/TEL-INVOICE.cpy".
*> CDR record mapping
01 WS-CDR-REC.
COPY "telecom/TEL-CDR.cpy".
*> File status indicators
01 WS-FILE-STATUS-IN PIC X(02).
01 WS-FILE-STATUS-OUT PIC X(02).
01 WS-FILE-STATUS-AUDIT PIC X(02).
01 WS-FILE-STATUS-STATS PIC X(02).
*> EOF and control flags
01 WS-STATUS.
05 WS-EOF-FLAG PIC X VALUE 'N'.
88 WS-EOF VALUE 'Y' FALSE 'N'.
05 WS-READ-OK PIC X VALUE 'N'.
88 WS-READ-SUCCESS VALUE 'Y' FALSE 'N'.
05 WS-FIRST-RECORD PIC X VALUE 'Y'.
88 WS-IS-FIRST-RECORD VALUE 'Y' FALSE 'N'.
*> Error severity levels
01 WS-SEVERITY PIC X(07).
88 WS-SEVERITY-WARNING VALUE 'WARNING'.
88 WS-SEVERITY-ERROR VALUE 'ERROR'.
88 WS-SEVERITY-FATAL VALUE 'FATAL'.
*> Open retry control
01 WS-OPEN-RETRY.
05 WS-OPEN-RETRY-COUNT PIC 9(02) VALUE 0.
05 WS-OPEN-RETRY-MAX PIC 9(02) VALUE 3.
05 WS-ALT-SUFFIX PIC X(04).
88 WS-ALT-NONE VALUE 'NONE'.
88 WS-ALT-BAK VALUE '.BAK'.
88 WS-ALT-TMP VALUE '.TMP'.
*> File naming validation
01 WS-FILE-NAME-CHECK.
05 WS-FILE-NAME-VALID PIC X VALUE 'N'.
88 WS-NAME-IS-VALID VALUE 'Y' FALSE 'N'.
05 WS-FILE-NAME-PATTERN PIC X(30).
05 WS-FILE-NAME-PREFIX PIC X(08) VALUE 'FILE-OUT'.
05 WS-FILE-NAME-SUFFIX PIC X(04) VALUE '.DAT'.
05 WS-FILE-NAME-SEQ-BEG PIC 9(02).
05 WS-FILE-NAME-SEQ-END PIC 9(02).
*> Counters and accumulators
01 WS-COUNTERS.
05 WS-REC-COUNT PIC 9(05) VALUE 0.
05 WS-FILE-NUM PIC 9(02) VALUE 0.
05 WS-DIVISOR PIC 9(03) VALUE 100.
05 WS-QUOTIENT PIC 9(05).
05 WS-REMAINDER PIC 9(05).
05 WS-WRITE-COUNT PIC 9(05) VALUE 0.
05 WS-READ-COUNT PIC 9(05) VALUE 0.
*> Input file path (dynamic for retry support)
01 WS-IN-FILE PIC X(30).
*> Output file path
01 WS-OUT-FILE PIC X(30).
01 WS-FILE-NUM-ED PIC 99.
*> Display formatting
01 WS-DISPLAY-COUNT PIC Z(05)9.
01 WS-DISPLAY-HASH PIC Z(09)9.
01 WS-DISPLAY-STR.
05 WS-DISPLAY-STR-TEXT PIC X(50).
*> Simulated timestamp for DISPLAY tracing
01 WS-TIMESTAMP PIC X(21).
01 WS-TIMESTAMP-OUT PIC X(26).
*> Hash temp for byte counting
01 WS-HASH-TEMP PIC 9(10).
*> Hash totals for data integrity
01 WS-HASH-TOTALS.
05 WS-HASH-TOTAL-IN PIC 9(10) VALUE 0.
05 WS-HASH-TOTAL-OUT PIC 9(10) VALUE 0.
05 WS-HASH-TOTAL-VERIFY PIC 9(10) VALUE 0.
05 WS-HASH-VERIFIED PIC X VALUE 'N'.
88 WS-HASH-MATCH VALUE 'Y' FALSE 'N'.
88 WS-HASH-MISMATCH VALUE 'N'.
05 WS-HASH-SPLIT OCCURS 99 TIMES.
10 WS-HASH-SPLIT-VAL PIC 9(10).
*> Split statistics
01 WS-SPLIT-STATS.
05 WS-SPLIT-REC-COUNT PIC 9(05) VALUE 0.
05 WS-SPLIT-FILE-TOTAL PIC 9(02) VALUE 0.
05 WS-SPLIT-MIN-RECS PIC 9(05) VALUE 99999.
05 WS-SPLIT-MAX-RECS PIC 9(05) VALUE 0.
05 WS-SPLIT-EMPTY-FLAG PIC X VALUE 'N'.
88 WS-SPLIT-EMPTY VALUE 'Y' FALSE 'N'.
05 WS-SPLIT-SINGLE-FLAG PIC X VALUE 'N'.
88 WS-SPLIT-SINGLE VALUE 'Y' FALSE 'N'.
*> Boundary condition checks
01 WS-BOUNDARY-FLAG.
05 WS-BOUNDARY-EMPTY PIC X VALUE 'N'.
88 WS-BOUNDARY-IS-EMPTY VALUE 'Y' FALSE 'N'.
05 WS-BOUNDARY-EXACT-MULT PIC X VALUE 'N'.
88 WS-BOUNDARY-IS-EXACT VALUE 'Y' FALSE 'N'.
05 WS-BOUNDARY-SINGLE-REC PIC X VALUE 'N'.
88 WS-BOUNDARY-IS-SINGLE VALUE 'Y' FALSE 'N'.
05 WS-BOUNDARY-TRAILING PIC X VALUE 'N'.
88 WS-BOUNDARY-HAS-TRAIL VALUE 'Y' FALSE 'N'.
05 WS-BOUNDARY-MULTIPLIER PIC 9(05).
*> Trailing record handling
01 WS-TRAILER-REC-COUNT PIC 9(05) VALUE 0.
01 WS-TRAILER-RECORD.
05 WS-TRAILER-TYPE PIC X(08) VALUE 'TRAILER'.
05 WS-TRAILER-FILE PIC 9(02).
05 WS-TRL-REC-COUNT PIC 9(05).
05 FILLER PIC X(30) VALUE SPACES.
*> Header record for split files
01 WS-HEADER-RECORD.
05 WS-HDR-TYPE PIC X(08) VALUE 'HEADER'.
05 WS-HDR-FILE PIC 9(02).
05 WS-HDR-TIMESTAMP PIC X(12).
05 WS-HDR-RESERVED PIC X(23).
05 FILLER PIC X VALUE SPACE.
*> CDR-aware split header
01 WS-CDR-HEADER.
05 WS-CDR-HDR-TYPE PIC X(08) VALUE 'CDR-HDR'.
05 WS-CDR-HDR-FILE PIC 9(02).
05 WS-CDR-HDR-EXPECTED PIC 9(05).
05 WS-CDR-HDR-RESERVED PIC X(30).
*> Audit report variables
01 WS-AUDIT-LINE PIC X(120).
01 WS-AUDIT-COUNT PIC 9(05) VALUE 0.
*> Batch control totals
01 WS-BATCH-CTRL.
05 WS-BATCH-START-TIME PIC 9(08).
05 WS-BATCH-END-TIME PIC 9(08).
05 WS-BATCH-TOTAL-FILES PIC 9(02) VALUE 0.
05 WS-BATCH-TOTAL-RECS PIC 9(05) VALUE 0.
05 WS-BATCH-ERROR-COUNT PIC 9(03) VALUE 0.
05 WS-BATCH-WARN-COUNT PIC 9(03) VALUE 0.
*> File open retry state
01 WS-RETRY-STATE.
05 WS-RETRY-ATTEMPT PIC 9(02) VALUE 0.
05 WS-RETRY-FILE-NAME PIC X(30).
05 WS-RETRY-SUCCESS PIC X VALUE 'N'.
88 WS-RETRY-OK VALUE 'Y' FALSE 'N'.
* ============================================================
* PROCEDURE DIVISION
* ============================================================
PROCEDURE DIVISION.
MAIN SECTION.
MAIN-PROCEDURE.
PERFORM 1000-INIT-SECTION
PERFORM 2000-OPEN-FILES-SECTION
PERFORM 3000-PROCESS-SECTION
PERFORM 4000-REPORT-SECTION
PERFORM 5000-AUDIT-SECTION
PERFORM 6000-ERROR-HANDLE-SECTION
PERFORM 9000-EXIT-SECTION
STOP RUN.
*> ============================================================
*> 1000-INIT-SECTION — Initialize all working variables
*> ============================================================
1000-INIT-SECTION SECTION.
1000-INIT.
DISPLAY " "
DISPLAY "=== Divide100 — Initialize ==="
MOVE FUNCTION CURRENT-DATE TO WS-TIMESTAMP
DISPLAY "[" WS-TIMESTAMP "] Initializing program"
*> Zero counters
MOVE 0 TO WS-REC-COUNT
MOVE 0 TO WS-FILE-NUM
MOVE 0 TO WS-READ-COUNT
MOVE 0 TO WS-WRITE-COUNT
MOVE 0 TO WS-TRAILER-REC-COUNT
MOVE 0 TO WS-AUDIT-COUNT
MOVE 0 TO WS-BATCH-ERROR-COUNT
MOVE 0 TO WS-BATCH-WARN-COUNT
MOVE 0 TO WS-OPEN-RETRY-COUNT
MOVE 0 TO WS-HASH-TOTAL-IN
MOVE 0 TO WS-HASH-TOTAL-OUT
*> Reset boundary flags
MOVE 'N' TO WS-BOUNDARY-EMPTY
MOVE 'N' TO WS-BOUNDARY-EXACT-MULT
MOVE 'N' TO WS-BOUNDARY-SINGLE-REC
MOVE 'N' TO WS-BOUNDARY-TRAILING
MOVE 'Y' TO WS-FIRST-RECORD
*> Reset retry state
MOVE 'NONE' TO WS-ALT-SUFFIX
MOVE 0 TO WS-RETRY-ATTEMPT
*> Reset split stats
MOVE 0 TO WS-SPLIT-REC-COUNT
MOVE 0 TO WS-SPLIT-FILE-TOTAL
MOVE 99999 TO WS-SPLIT-MIN-RECS
MOVE 0 TO WS-SPLIT-MAX-RECS
MOVE 'N' TO WS-SPLIT-EMPTY-FLAG
MOVE 'N' TO WS-SPLIT-SINGLE-FLAG
DISPLAY "[" WS-TIMESTAMP "] Initialization complete"
.
1000-EXIT.
EXIT.
*> ============================================================
*> 2000-OPEN-FILES-SECTION — Open input and audit files
*> ============================================================
2000-OPEN-FILES-SECTION SECTION.
2000-OPEN-FILES.
DISPLAY "[" WS-TIMESTAMP "] Opening files"
*> Open input file with retry logic
MOVE 0 TO WS-RETRY-ATTEMPT
MOVE 'N' TO WS-RETRY-SUCCESS
MOVE "FILE-IN.DAT" TO WS-IN-FILE
PERFORM WITH TEST AFTER UNTIL WS-RETRY-OK
OR WS-RETRY-ATTEMPT >= WS-OPEN-RETRY-MAX
ADD 1 TO WS-RETRY-ATTEMPT
OPEN INPUT FILE-IN
IF WS-FILE-STATUS-IN NOT = '00'
MOVE 'ERROR' TO WS-SEVERITY
DISPLAY "[" WS-TIMESTAMP "] "
"ERROR: FILE-IN open failed "
"status=" WS-FILE-STATUS-IN
" attempt=" WS-RETRY-ATTEMPT
*> Try alternate file name
IF WS-RETRY-ATTEMPT = 1
MOVE '.BAK' TO WS-ALT-SUFFIX
STRING "FILE-IN" WS-ALT-SUFFIX
DELIMITED BY SIZE
INTO WS-IN-FILE
ELSE
MOVE '.TMP' TO WS-ALT-SUFFIX
STRING "FILE-IN" WS-ALT-SUFFIX
DELIMITED BY SIZE
INTO WS-IN-FILE
END-IF
DISPLAY "[" WS-TIMESTAMP "] "
"Retrying with: " WS-IN-FILE
ELSE
MOVE WS-IN-FILE TO WS-RETRY-FILE-NAME
MOVE 'Y' TO WS-RETRY-SUCCESS
DISPLAY "[" WS-TIMESTAMP "] "
"FILE-IN opened: " WS-IN-FILE
" (attempt " WS-RETRY-ATTEMPT ")"
END-IF
END-PERFORM
IF NOT WS-RETRY-OK
MOVE 'FATAL' TO WS-SEVERITY
DISPLAY "[" WS-TIMESTAMP "] "
"FATAL: Cannot open FILE-IN after "
WS-OPEN-RETRY-MAX " attempts"
ADD 1 TO WS-BATCH-ERROR-COUNT
PERFORM 9000-EXIT-SECTION
END-IF
*> Open audit report file
OPEN OUTPUT AUDIT-REPORT
IF WS-FILE-STATUS-AUDIT NOT = '00'
MOVE 'ERROR' TO WS-SEVERITY
DISPLAY "[" WS-TIMESTAMP "] "
"ERROR: AUDIT-REPORT open failed "
"status=" WS-FILE-STATUS-AUDIT
ADD 1 TO WS-BATCH-ERROR-COUNT
ELSE
DISPLAY "[" WS-TIMESTAMP "] "
"AUDIT-REPORT opened successfully"
MOVE SPACES TO AUDIT-REC
STRING "*** AUDIT REPORT — Divide100 ***"
DELIMITED BY SIZE
INTO AUDIT-REC
WRITE AUDIT-REC
ADD 1 TO WS-AUDIT-COUNT
END-IF
*> Open split stats file
OPEN OUTPUT SPLIT-STATS
IF WS-FILE-STATUS-STATS NOT = '00'
MOVE 'ERROR' TO WS-SEVERITY
DISPLAY "[" WS-TIMESTAMP "] "
"ERROR: SPLIT-STATS open failed "
"status=" WS-FILE-STATUS-STATS
ADD 1 TO WS-BATCH-ERROR-COUNT
ELSE
DISPLAY "[" WS-TIMESTAMP "] "
"SPLIT-STATS opened successfully"
MOVE SPACES TO STATS-REC
STRING "*** SPLIT STATISTICS REPORT ***"
DELIMITED BY SIZE
INTO STATS-REC
WRITE STATS-REC
END-IF
*> Capture batch start time
MOVE FUNCTION CURRENT-DATE TO WS-TIMESTAMP
.
2000-EXIT.
EXIT.
*> ============================================================
*> 3000-PROCESS-SECTION — Main processing loop
*> ============================================================
3000-PROCESS-SECTION SECTION.
3000-PROCESS.
DISPLAY "[" WS-TIMESTAMP "] Starting record processing"
DISPLAY "=== 100-DIVISION PROCESSING ==="
DISPLAY "Records per file: " WS-DIVISOR
*> Write audit header
MOVE SPACES TO AUDIT-REC
STRING "=== 100-DIVISION PROCESSING ==="
DELIMITED BY SIZE
INTO AUDIT-REC
WRITE AUDIT-REC
ADD 1 TO WS-AUDIT-COUNT
MOVE SPACES TO AUDIT-REC
STRING "Records per file: " WS-DIVISOR
DELIMITED BY SIZE
INTO AUDIT-REC
WRITE AUDIT-REC
ADD 1 TO WS-AUDIT-COUNT
PERFORM VARYING WS-REC-COUNT FROM 1 BY 1
UNTIL WS-EOF
READ FILE-IN INTO FILE-IN-REC
AT END
SET WS-EOF TO TRUE
IF WS-IS-FIRST-RECORD
MOVE 'Y' TO WS-BOUNDARY-EMPTY
MOVE 'WARNING' TO WS-SEVERITY
DISPLAY "[" WS-TIMESTAMP "] "
"WARNING: Input file is empty"
END-IF
NOT AT END
SET WS-READ-SUCCESS TO TRUE
ADD 1 TO WS-READ-COUNT
IF WS-IS-FIRST-RECORD
MOVE 'N' TO WS-FIRST-RECORD
END-IF
PERFORM 3100-READ-INPUT-SECTION
PERFORM 3200-VALIDATE-SECTION
PERFORM 3300-APPLY-RULES-SECTION
PERFORM 3400-WRITE-OUTPUT-SECTION
END-READ
IF WS-FILE-STATUS-IN NOT = '00' AND NOT WS-EOF
MOVE 'ERROR' TO WS-SEVERITY
DISPLAY "[" WS-TIMESTAMP "] "
"ERROR: FILE-IN read failed "
"record=" WS-REC-COUNT
" status=" WS-FILE-STATUS-IN
ADD 1 TO WS-BATCH-ERROR-COUNT
END-IF
END-PERFORM
*> Boundary checks after processing
IF WS-BOUNDARY-IS-EMPTY
MOVE SPACES TO AUDIT-REC
STRING "WARNING: Input file was empty — "
"no output files created"
DELIMITED BY SIZE
INTO AUDIT-REC
WRITE AUDIT-REC
ADD 1 TO WS-AUDIT-COUNT
ELSE
DIVIDE WS-READ-COUNT BY WS-DIVISOR
GIVING WS-BOUNDARY-MULTIPLIER
REMAINDER WS-REMAINDER
IF WS-REMAINDER = 0
MOVE 'Y' TO WS-BOUNDARY-EXACT-MULT
DISPLAY "[" WS-TIMESTAMP "] "
"NOTE: Exact multiple of divisor — "
"no trailing records"
ELSE
MOVE 'Y' TO WS-BOUNDARY-TRAILING
MOVE WS-REMAINDER TO WS-TRAILER-REC-COUNT
DISPLAY "[" WS-TIMESTAMP "] "
"NOTE: " WS-TRAILER-REC-COUNT
" trailing record(s) in last file"
END-IF
IF WS-READ-COUNT = 1
MOVE 'Y' TO WS-BOUNDARY-SINGLE-REC
END-IF
END-IF
*> Close last output file if any were opened
IF WS-FILE-NUM > 0
PERFORM WRITE-TRAILER-RECORD
CLOSE FILE-OUT
IF WS-FILE-STATUS-OUT NOT = '00'
MOVE 'ERROR' TO WS-SEVERITY
DISPLAY "[" WS-TIMESTAMP "] "
"ERROR: Close failed for "
WS-OUT-FILE
" status=" WS-FILE-STATUS-OUT
ADD 1 TO WS-BATCH-ERROR-COUNT
ELSE
DISPLAY "[" WS-TIMESTAMP "] "
"Closed final file: " WS-OUT-FILE
END-IF
END-IF
MOVE WS-REC-COUNT TO WS-DISPLAY-COUNT
DISPLAY " "
DISPLAY "[" WS-TIMESTAMP "] Total records read: "
WS-DISPLAY-COUNT
DISPLAY "[" WS-TIMESTAMP "] Output files created: "
WS-FILE-NUM
.
3000-EXIT.
EXIT.
*> ============================================================
*> 3100-READ-INPUT-SECTION — Input record processing
*> ============================================================
3100-READ-INPUT-SECTION SECTION.
3100-READ-INPUT.
*> Accumulate hash total (sum of numeric representation
*> of the input record bytes)
PERFORM COMPUTE-INPUT-HASH
*> Classify record type based on known layouts
MOVE IN-RECORD TO WS-TELECOM-REC
MOVE IN-RECORD TO WS-CDR-REC
*> Check if record resembles a CDR by CDR-ID pattern
IF CDR-ID (1:3) = 'CDR'
DISPLAY "[" WS-TIMESTAMP "] "
"Record " WS-REC-COUNT
": CDR record detected: " CDR-ID
ELSE
IF INV-ID (1:3) = 'INV'
DISPLAY "[" WS-TIMESTAMP "] "
"Record " WS-REC-COUNT
": Invoice record detected: " INV-ID
ELSE
DISPLAY "[" WS-TIMESTAMP "] "
"Record " WS-REC-COUNT
": Generic record"
END-IF
END-IF
.
3100-EXIT.
EXIT.
*> ============================================================
*> 3200-VALIDATE-SECTION — Validate input record
*> ============================================================
3200-VALIDATE-SECTION SECTION.
3200-VALIDATE.
*> Validate output file naming convention before writing
IF WS-FILE-NUM > 0
MOVE WS-OUT-FILE TO WS-FILE-NAME-PATTERN
IF WS-FILE-NAME-PATTERN (1:8) = WS-FILE-NAME-PREFIX
MOVE 'Y' TO WS-FILE-NAME-VALID
ELSE
MOVE 'N' TO WS-FILE-NAME-VALID
MOVE 'WARNING' TO WS-SEVERITY
DISPLAY "[" WS-TIMESTAMP "] "
"WARNING: Unconventional file name: "
WS-OUT-FILE
ADD 1 TO WS-BATCH-WARN-COUNT
END-IF
END-IF
.
3200-EXIT.
EXIT.
*> ============================================================
*> 3300-APPLY-RULES-SECTION — Apply division rules
*> ============================================================
3300-APPLY-RULES-SECTION SECTION.
3300-APPLY-RULES.
*> Use DIVIDE to determine file number
*> === ORIGINAL LOGIC PRESERVED (unaltered) ===
DIVIDE WS-REC-COUNT BY WS-DIVISOR
GIVING WS-QUOTIENT REMAINDER WS-REMAINDER
*> If remainder = 1, start a new output file
*> === ORIGINAL LOGIC PRESERVED (unaltered) ===
IF WS-REMAINDER = 1
IF WS-FILE-NUM > 0
PERFORM WRITE-TRAILER-RECORD
CLOSE FILE-OUT
IF WS-FILE-STATUS-OUT NOT = '00'
MOVE 'ERROR' TO WS-SEVERITY
DISPLAY "[" WS-TIMESTAMP "] "
"ERROR: Close failed for "
WS-OUT-FILE
" status=" WS-FILE-STATUS-OUT
ADD 1 TO WS-BATCH-ERROR-COUNT
ELSE
DISPLAY "[" WS-TIMESTAMP "] "
"Closed: " WS-OUT-FILE
END-IF
END-IF
ADD 1 TO WS-FILE-NUM
MOVE WS-FILE-NUM TO WS-FILE-NUM-ED
STRING "FILE-OUT-" DELIMITED BY SIZE
WS-FILE-NUM-ED DELIMITED BY SIZE
".DAT" DELIMITED BY SIZE
INTO WS-OUT-FILE
OPEN OUTPUT FILE-OUT
IF WS-FILE-STATUS-OUT NOT = '00'
MOVE 'FATAL' TO WS-SEVERITY
DISPLAY "[" WS-TIMESTAMP "] "
"FATAL: Cannot open " WS-OUT-FILE
" status=" WS-FILE-STATUS-OUT
ADD 1 TO WS-BATCH-ERROR-COUNT
PERFORM 6000-ERROR-HANDLE-SECTION
ELSE
DISPLAY "[" WS-TIMESTAMP "] "
"Opened: " WS-OUT-FILE
PERFORM WRITE-HEADER-RECORD
END-IF
END-IF
.
3300-EXIT.
EXIT.
*> ============================================================
*> 3400-WRITE-OUTPUT-SECTION — Write record to output
*> ============================================================
3400-WRITE-OUTPUT-SECTION SECTION.
3400-WRITE-OUTPUT.
*> Hash the output record before writing
PERFORM COMPUTE-OUTPUT-HASH
*> Write record to current output file
*> === ORIGINAL LOGIC PRESERVED (unaltered) ===
MOVE IN-RECORD TO OUT-RECORD
WRITE FILE-OUT-REC
IF WS-FILE-STATUS-OUT NOT = '00'
MOVE 'ERROR' TO WS-SEVERITY
DISPLAY "[" WS-TIMESTAMP "] "
"ERROR: Write failed for record "
WS-REC-COUNT
" status=" WS-FILE-STATUS-OUT
ADD 1 TO WS-BATCH-ERROR-COUNT
ELSE
ADD 1 TO WS-WRITE-COUNT
ADD 1 TO WS-SPLIT-REC-COUNT
ADD 1 TO WS-HASH-SPLIT-VAL (WS-FILE-NUM)
END-IF
.
3400-EXIT.
EXIT.
*> ============================================================
*> 4000-REPORT-SECTION — Generate split statistics
*> ============================================================
4000-REPORT-SECTION SECTION.
4000-REPORT.
DISPLAY " "
DISPLAY "[" WS-TIMESTAMP "] === Split Statistics ==="
DISPLAY "Total input records: " WS-READ-COUNT
DISPLAY "Total output records: " WS-WRITE-COUNT
DISPLAY "Output files created: " WS-FILE-NUM
*> Populate split stats
MOVE WS-FILE-NUM TO WS-SPLIT-FILE-TOTAL
IF WS-READ-COUNT = 0
MOVE 'Y' TO WS-SPLIT-EMPTY-FLAG
END-IF
IF WS-READ-COUNT = 1
MOVE 'Y' TO WS-SPLIT-SINGLE-FLAG
END-IF
*> Build min/max split sizes
PERFORM VARYING WS-FILE-NUM-ED FROM 1 BY 1
UNTIL WS-FILE-NUM-ED > WS-FILE-NUM
IF WS-HASH-SPLIT-VAL (WS-FILE-NUM-ED)
< WS-SPLIT-MIN-RECS
MOVE WS-HASH-SPLIT-VAL (WS-FILE-NUM-ED)
TO WS-SPLIT-MIN-RECS
END-IF
IF WS-HASH-SPLIT-VAL (WS-FILE-NUM-ED)
> WS-SPLIT-MAX-RECS
MOVE WS-HASH-SPLIT-VAL (WS-FILE-NUM-ED)
TO WS-SPLIT-MAX-RECS
END-IF
END-PERFORM
DISPLAY "Min records per split: " WS-SPLIT-MIN-RECS
DISPLAY "Max records per split: " WS-SPLIT-MAX-RECS
*> Write detailed stats to SPLIT-STATS file
MOVE SPACES TO STATS-REC
STRING "SPLIT STATS: files=" WS-FILE-NUM
" total-records=" WS-READ-COUNT
" min=" WS-SPLIT-MIN-RECS
" max=" WS-SPLIT-MAX-RECS
DELIMITED BY SIZE
INTO STATS-REC
WRITE STATS-REC
*> Sequential output file inventory
MOVE SPACES TO STATS-REC
STRING "=== Split File Inventory ==="
DELIMITED BY SIZE
INTO STATS-REC
WRITE STATS-REC
PERFORM VARYING WS-FILE-NUM-ED FROM 1 BY 1
UNTIL WS-FILE-NUM-ED > WS-FILE-NUM
MOVE SPACES TO STATS-REC
STRING "FILE-OUT-"
WS-FILE-NUM-ED
".DAT: "
WS-HASH-SPLIT-VAL (WS-FILE-NUM-ED)
" records"
DELIMITED BY SIZE
INTO STATS-REC
WRITE STATS-REC
END-PERFORM
*> Boundary condition summary
MOVE SPACES TO STATS-REC
STRING "Boundary: EMPTY=" WS-BOUNDARY-EMPTY
" EXACT-MULT=" WS-BOUNDARY-EXACT-MULT
" SINGLE=" WS-BOUNDARY-SINGLE-REC
" TRAILING=" WS-BOUNDARY-TRAILING
" TRAIL-COUNT=" WS-TRAILER-REC-COUNT
DELIMITED BY SIZE
INTO STATS-REC
WRITE STATS-REC
.
4000-EXIT.
EXIT.
*> ============================================================
*> 5000-AUDIT-SECTION — Hash verification and audit trail
*> ============================================================
5000-AUDIT-SECTION SECTION.
5000-AUDIT.
DISPLAY " "
DISPLAY "[" WS-TIMESTAMP "] === Audit Trail ==="
*> Display hash totals
MOVE WS-HASH-TOTAL-IN TO WS-DISPLAY-HASH
DISPLAY "Hash total (input): " WS-DISPLAY-HASH
MOVE WS-HASH-TOTAL-OUT TO WS-DISPLAY-HASH
DISPLAY "Hash total (output): " WS-DISPLAY-HASH
*> Verify hash totals: input hash must equal sum of
*> all split file hashes
MOVE 0 TO WS-HASH-TOTAL-VERIFY
PERFORM VARYING WS-FILE-NUM-ED FROM 1 BY 1
UNTIL WS-FILE-NUM-ED > WS-FILE-NUM
ADD WS-HASH-SPLIT-VAL (WS-FILE-NUM-ED)
TO WS-HASH-TOTAL-VERIFY
END-PERFORM
IF WS-HASH-TOTAL-IN = WS-HASH-TOTAL-VERIFY
MOVE 'Y' TO WS-HASH-VERIFIED
DISPLAY "[" WS-TIMESTAMP "] "
"PASS: Hash total verified OK"
ELSE
MOVE 'N' TO WS-HASH-VERIFIED
MOVE 'ERROR' TO WS-SEVERITY
DISPLAY "[" WS-TIMESTAMP "] "
"FAIL: Hash total mismatch!"
DISPLAY " Input hash: " WS-HASH-TOTAL-IN
DISPLAY " Sum of splits: " WS-HASH-TOTAL-VERIFY
ADD 1 TO WS-BATCH-ERROR-COUNT
END-IF
*> Verify output hash matches input hash
IF WS-HASH-TOTAL-IN NOT = WS-HASH-TOTAL-OUT
MOVE 'ERROR' TO WS-SEVERITY
DISPLAY "[" WS-TIMESTAMP "] "
"FAIL: Output hash != Input hash!"
ADD 1 TO WS-BATCH-ERROR-COUNT
ELSE
DISPLAY "[" WS-TIMESTAMP "] "
"PASS: Output hash matches input hash"
END-IF
*> Write final audit records
MOVE SPACES TO AUDIT-REC
STRING "=== Audit Summary ==="
DELIMITED BY SIZE
INTO AUDIT-REC
WRITE AUDIT-REC
ADD 1 TO WS-AUDIT-COUNT
MOVE SPACES TO AUDIT-REC
STRING "Total input records: " WS-READ-COUNT
DELIMITED BY SIZE
INTO AUDIT-REC
WRITE AUDIT-REC
ADD 1 TO WS-AUDIT-COUNT
MOVE SPACES TO AUDIT-REC
STRING "Total output records: " WS-WRITE-COUNT
DELIMITED BY SIZE
INTO AUDIT-REC
WRITE AUDIT-REC
ADD 1 TO WS-AUDIT-COUNT
MOVE SPACES TO AUDIT-REC
STRING "Output files created: " WS-FILE-NUM
DELIMITED BY SIZE
INTO AUDIT-REC
WRITE AUDIT-REC
ADD 1 TO WS-AUDIT-COUNT
MOVE SPACES TO AUDIT-REC
STRING "Hash total (input): " WS-HASH-TOTAL-IN
DELIMITED BY SIZE
INTO AUDIT-REC
WRITE AUDIT-REC
ADD 1 TO WS-AUDIT-COUNT
MOVE SPACES TO AUDIT-REC
STRING "Hash total (output): " WS-HASH-TOTAL-OUT
DELIMITED BY SIZE
INTO AUDIT-REC
WRITE AUDIT-REC
ADD 1 TO WS-AUDIT-COUNT
IF WS-HASH-MATCH
MOVE SPACES TO AUDIT-REC
STRING "Hash verification: PASS"
DELIMITED BY SIZE
INTO AUDIT-REC
WRITE AUDIT-REC
ADD 1 TO WS-AUDIT-COUNT
ELSE
MOVE SPACES TO AUDIT-REC
STRING "Hash verification: FAIL"
DELIMITED BY SIZE
INTO AUDIT-REC
WRITE AUDIT-REC
ADD 1 TO WS-AUDIT-COUNT
END-IF
MOVE SPACES TO AUDIT-REC
STRING "Error count: " WS-BATCH-ERROR-COUNT
" Warning count: " WS-BATCH-WARN-COUNT
DELIMITED BY SIZE
INTO AUDIT-REC
WRITE AUDIT-REC
ADD 1 TO WS-AUDIT-COUNT
*> Log batch completion
MOVE FUNCTION CURRENT-DATE TO WS-TIMESTAMP
MOVE SPACES TO AUDIT-REC
STRING "Batch completed at: " WS-TIMESTAMP
DELIMITED BY SIZE
INTO AUDIT-REC
WRITE AUDIT-REC
ADD 1 TO WS-AUDIT-COUNT
DISPLAY "[" WS-TIMESTAMP "] "
"Audit report written: " WS-AUDIT-COUNT " lines"
.
5000-EXIT.
EXIT.
*> ============================================================
*> 6000-ERROR-HANDLE-SECTION — Central error handler
*> ============================================================
6000-ERROR-HANDLE-SECTION SECTION.
6000-ERROR-HANDLE.
DISPLAY "[" WS-TIMESTAMP "] "
"*** ERROR HANDLER INVOKED ***"
EVALUATE TRUE
WHEN WS-SEVERITY-FATAL
DISPLAY "[" WS-TIMESTAMP "] "
"FATAL: Aborting program"
MOVE SPACES TO AUDIT-REC
STRING "FATAL ERROR — Batch aborted"
DELIMITED BY SIZE
INTO AUDIT-REC
WRITE AUDIT-REC
ADD 1 TO WS-AUDIT-COUNT
PERFORM 9000-EXIT-SECTION
WHEN WS-SEVERITY-ERROR
DISPLAY "[" WS-TIMESTAMP "] "
"ERROR: Continuing with error"
MOVE SPACES TO AUDIT-REC
STRING "ERROR: Continuing — "
"error count=" WS-BATCH-ERROR-COUNT
DELIMITED BY SIZE
INTO AUDIT-REC
WRITE AUDIT-REC
ADD 1 TO WS-AUDIT-COUNT
WHEN WS-SEVERITY-WARNING
DISPLAY "[" WS-TIMESTAMP "] "
"WARNING: Non-critical issue"
MOVE SPACES TO AUDIT-REC
STRING "WARNING issued — "
"warn count=" WS-BATCH-WARN-COUNT
DELIMITED BY SIZE
INTO AUDIT-REC
WRITE AUDIT-REC
ADD 1 TO WS-AUDIT-COUNT
WHEN OTHER
CONTINUE
END-EVALUATE
.
6000-EXIT.
EXIT.
*> ============================================================
*> 9000-EXIT-SECTION — Program cleanup and exit
*> ============================================================
9000-EXIT-SECTION SECTION.
9000-EXIT.
DISPLAY " "
DISPLAY "[" WS-TIMESTAMP "] === Program Termination ==="
*> Close any open files safely
CLOSE FILE-IN
IF WS-FILE-STATUS-IN NOT = '00'
DISPLAY "[" WS-TIMESTAMP "] "
"NOTE: FILE-IN close status="
WS-FILE-STATUS-IN
END-IF
CLOSE AUDIT-REPORT
IF WS-FILE-STATUS-AUDIT NOT = '00'
DISPLAY "[" WS-TIMESTAMP "] "
"NOTE: AUDIT-REPORT close status="
WS-FILE-STATUS-AUDIT
END-IF
CLOSE SPLIT-STATS
IF WS-FILE-STATUS-STATS NOT = '00'
DISPLAY "[" WS-TIMESTAMP "] "
"NOTE: SPLIT-STATS close status="
WS-FILE-STATUS-STATS
END-IF
*> Final summary display
MOVE WS-REC-COUNT TO WS-DISPLAY-COUNT
DISPLAY " "
DISPLAY "=== FINAL SUMMARY ==="
DISPLAY "Total records read: " WS-DISPLAY-COUNT
DISPLAY "Output files created: " WS-FILE-NUM
DISPLAY "Errors: " WS-BATCH-ERROR-COUNT
DISPLAY "Warnings: " WS-BATCH-WARN-COUNT
IF WS-HASH-MATCH
DISPLAY "Hash integrity: PASS"
ELSE
DISPLAY "Hash integrity: FAIL"
END-IF
DISPLAY " "
DISPLAY "=== Divide100 ended ==="
STOP RUN
.
*> ============================================================
*> WRITE-HEADER-RECORD — Write header record to split file
*> ============================================================
WRITE-HEADER-RECORD SECTION.
WRITE-HDR.
*> Write standard header
MOVE SPACES TO WS-HEADER-RECORD
MOVE 'HEADER' TO WS-HDR-TYPE
MOVE WS-FILE-NUM TO WS-HDR-FILE
MOVE FUNCTION CURRENT-DATE TO WS-TIMESTAMP
MOVE WS-TIMESTAMP (1:12) TO WS-HDR-TIMESTAMP
MOVE WS-HEADER-RECORD TO OUT-RECORD
WRITE FILE-OUT-REC
IF WS-FILE-STATUS-OUT = '00'
ADD 1 TO WS-WRITE-COUNT
ADD 1 TO WS-HASH-SPLIT-VAL (WS-FILE-NUM)
DISPLAY "[" WS-TIMESTAMP "] "
"Header written to " WS-OUT-FILE
ELSE
MOVE 'WARNING' TO WS-SEVERITY
DISPLAY "[" WS-TIMESTAMP "] "
"WARNING: Header write failed for "
WS-OUT-FILE
ADD 1 TO WS-BATCH-WARN-COUNT
END-IF
*> Write CDR-aware header (second header line with metadata)
MOVE SPACES TO WS-CDR-HEADER
MOVE 'CDR-HDR' TO WS-CDR-HDR-TYPE
MOVE WS-FILE-NUM TO WS-CDR-HDR-FILE
MOVE 100 TO WS-CDR-HDR-EXPECTED
MOVE WS-CDR-HEADER TO OUT-RECORD
WRITE FILE-OUT-REC
IF WS-FILE-STATUS-OUT = '00'
ADD 1 TO WS-WRITE-COUNT
ADD 1 TO WS-HASH-SPLIT-VAL (WS-FILE-NUM)
ELSE
MOVE 'WARNING' TO WS-SEVERITY
DISPLAY "[" WS-TIMESTAMP "] "
"WARNING: CDR header write failed for "
WS-OUT-FILE
ADD 1 TO WS-BATCH-WARN-COUNT
END-IF
.
WRITE-HDR-EXIT.
EXIT.
*> ============================================================
*> WRITE-TRAILER-RECORD — Write trailer to closing split file
*> ============================================================
WRITE-TRAILER-RECORD SECTION.
WRITE-TRL.
MOVE SPACES TO WS-TRAILER-RECORD
MOVE 'TRAILER' TO WS-TRAILER-TYPE
MOVE WS-FILE-NUM TO WS-TRAILER-FILE
*> Count = current split count minus header records
*> (we wrote 2 header records per file)
IF WS-HASH-SPLIT-VAL (WS-FILE-NUM) > 2
COMPUTE WS-TRL-REC-COUNT =
WS-HASH-SPLIT-VAL (WS-FILE-NUM) - 2
ELSE
MOVE 0 TO WS-TRL-REC-COUNT
END-IF
MOVE WS-TRAILER-RECORD TO OUT-RECORD
WRITE FILE-OUT-REC
IF WS-FILE-STATUS-OUT = '00'
ADD 1 TO WS-WRITE-COUNT
ADD 1 TO WS-HASH-SPLIT-VAL (WS-FILE-NUM)
DISPLAY "[" WS-TIMESTAMP "] "
"Trailer written to " WS-OUT-FILE
" count=" WS-TRL-REC-COUNT
ELSE
MOVE 'WARNING' TO WS-SEVERITY
DISPLAY "[" WS-TIMESTAMP "] "
"WARNING: Trailer write failed for "
WS-OUT-FILE
ADD 1 TO WS-BATCH-WARN-COUNT
END-IF
.
WRITE-TRL-EXIT.
EXIT.
*> ============================================================
*> WRITE-AUDIT-LINE — Write one line to audit report
*> ============================================================
WRITE-AUDIT-LINE SECTION.
WRITE-AUDIT.
MOVE WS-AUDIT-LINE TO AUDIT-REC
WRITE AUDIT-REC
IF WS-FILE-STATUS-AUDIT = '00'
ADD 1 TO WS-AUDIT-COUNT
ELSE
DISPLAY "[" WS-TIMESTAMP "] "
"ERROR writing audit record"
END-IF
.
WRITE-AUDIT-EXIT.
EXIT.
*> ============================================================
*> COMPUTE-INPUT-HASH — Accumulate input hash total
*> ============================================================
COMPUTE-INPUT-HASH SECTION.
COMP-HASH-IN.
*> Simple hash: sum the numeric values of each byte
MOVE 0 TO WS-HASH-TEMP
INSPECT IN-RECORD TALLYING WS-HASH-TEMP
FOR CHARACTERS BEFORE INITIAL SPACE
ADD WS-HASH-TEMP TO WS-HASH-TOTAL-IN
.
COMP-HASH-IN-EXIT.
EXIT.
*> ============================================================
*> COMPUTE-OUTPUT-HASH — Accumulate output hash total
*> ============================================================
COMPUTE-OUTPUT-HASH SECTION.
COMP-HASH-OUT.
*> Simple hash: sum the numeric values of each byte
MOVE 0 TO WS-HASH-TEMP
INSPECT OUT-RECORD TALLYING WS-HASH-TEMP
FOR CHARACTERS BEFORE INITIAL SPACE
ADD WS-HASH-TEMP TO WS-HASH-TOTAL-OUT
.
COMP-HASH-OUT-EXIT.
EXIT.