feat: add benchmark-programs — 58 telecom COBOL test programs

作为子目录纳入系统,与核心测试管道协同

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
NB-076
2026-06-25 09:53:21 +08:00
parent 50f9f0f52f
commit 94400d50d4
278 changed files with 44125 additions and 0 deletions
@@ -0,0 +1,841 @@
*> ============================================================
*> 27-validation-halfwidth : Phone Number Validation
*> Extended with SECTION structure, prefix validation,
*> phone length rules, audit/report files, severity levels,
*> batch control totals, FILE STATUS checks, DISPLAY tracing.
*> Input : FILE-IN (file-in.dat)
*> Output: FILE-OUT-GOOD, -BAD, -REPORT, -AUDIT
*> Coverage: VF-N005, VF-N006, VF-A001, VF-A002, VF-R001
*> VF-P001, VF-P002, VF-P003, VF-L001, VF-S001
*> ============================================================
IDENTIFICATION DIVISION.
PROGRAM-ID. ValidationHalfwidth.
*>
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT FILE-IN ASSIGN TO 'file-in.dat'
ORGANIZATION IS SEQUENTIAL
ACCESS MODE IS SEQUENTIAL
FILE STATUS IS WS-FILE-IN-STATUS.
SELECT FILE-OUT-GOOD ASSIGN TO 'file-out-good.dat'
ORGANIZATION IS SEQUENTIAL
FILE STATUS IS WS-FILE-GOOD-STATUS.
SELECT FILE-OUT-BAD ASSIGN TO 'file-out-bad.dat'
ORGANIZATION IS SEQUENTIAL
FILE STATUS IS WS-FILE-BAD-STATUS.
SELECT FILE-OUT-REPORT
ASSIGN TO 'file-out-report.dat'
ORGANIZATION IS SEQUENTIAL
FILE STATUS IS WS-FILE-REPORT-STATUS.
SELECT FILE-OUT-AUDIT
ASSIGN TO 'file-out-audit.dat'
ORGANIZATION IS SEQUENTIAL
FILE STATUS IS WS-FILE-AUDIT-STATUS.
*>
DATA DIVISION.
FILE SECTION.
FD FILE-IN.
01 IN-REC.
05 IN-TEXT PIC X(30).
*>
FD FILE-OUT-GOOD.
01 GOOD-REC.
05 GOOD-TEXT PIC X(30).
*>
FD FILE-OUT-BAD.
01 BAD-REC.
05 BAD-TEXT PIC X(30).
05 BAD-ERR-CODE PIC X(02).
*>
FD FILE-OUT-REPORT.
01 REPORT-REC.
05 REPORT-LINE PIC X(80).
*>
FD FILE-OUT-AUDIT.
01 AUDIT-REC.
05 AUDIT-LINE PIC X(80).
*>
WORKING-STORAGE SECTION.
01 WS-TELECOM-REC.
COPY "telecom/TEL-BILLING.cpy".
*> File status fields
01 WS-FILE-IN-STATUS PIC X(02).
01 WS-FILE-GOOD-STATUS PIC X(02).
01 WS-FILE-BAD-STATUS PIC X(02).
01 WS-FILE-REPORT-STATUS PIC X(02).
01 WS-FILE-AUDIT-STATUS PIC X(02).
*> EOF and loop control
01 WS-EOF PIC X(01) VALUE 'N'.
88 WS-EOF-YES VALUE 'Y' FALSE 'N'.
*> Record counts
01 WS-TOTAL-READ PIC 9(09) VALUE ZERO.
01 WS-GOOD-COUNT PIC 9(09) VALUE ZERO.
01 WS-BAD-COUNT PIC 9(09) VALUE ZERO.
01 WS-SKIP-COUNT PIC 9(09) VALUE ZERO.
01 WS-WARN-COUNT PIC 9(09) VALUE ZERO.
01 WS-ERROR-COUNT PIC 9(09) VALUE ZERO.
*> Batch control totals
01 WS-TOTAL-CHARS PIC 9(15) VALUE ZERO.
01 WS-TOTAL-DIGITS PIC 9(15) VALUE ZERO.
01 WS-HASH-TOTAL PIC 9(15) VALUE ZERO.
*> Validation fields
01 WS-IDX PIC 9(02).
01 WS-CHAR PIC X(01).
01 WS-ALL-HALF PIC X(01) VALUE 'Y'.
88 WS-ALL-HALF-YES VALUE 'Y' FALSE 'N'.
01 WS-TEXT-LEN PIC 9(02).
*> Multiple validation results
01 WS-VALID-RESULT.
05 WS-V-FORMAT PIC X(01) VALUE 'P'.
88 WS-V-FORMAT-PASS VALUE 'P'.
88 WS-V-FORMAT-FAIL VALUE 'F'.
05 WS-V-LENGTH PIC X(01) VALUE 'P'.
88 WS-V-LENGTH-PASS VALUE 'P'.
88 WS-V-LENGTH-FAIL VALUE 'F'.
05 WS-V-PREFIX PIC X(01) VALUE 'P'.
88 WS-V-PREFIX-PASS VALUE 'P'.
88 WS-V-PREFIX-FAIL VALUE 'F'.
05 WS-V-HALF PIC X(01) VALUE 'P'.
88 WS-V-HALF-PASS VALUE 'P'.
88 WS-V-HALF-FAIL VALUE 'F'.
01 WS-V-FORMAT-ERR PIC X(02).
01 WS-V-LENGTH-ERR PIC X(02).
01 WS-V-PREFIX-ERR PIC X(02).
01 WS-V-HALF-ERR PIC X(02).
*> Error codes and severity
01 WS-ERROR-CODE PIC X(02).
01 WS-ERROR-SEVERITY PIC X(01).
88 WS-SEVERITY-WARNING VALUE 'W'.
88 WS-SEVERITY-ERROR VALUE 'E'.
01 WS-ERROR-DETAIL PIC X(50).
*> Half-width range constants
01 WS-HW-LOWER PIC X(01) VALUE X'20'.
01 WS-HW-UPPER PIC X(01) VALUE X'7E'.
*> Phone number analysis
01 WS-PHONE-TYPE PIC X(01).
88 WS-PHONE-MOBILE VALUE 'M'.
88 WS-PHONE-LANDLINE VALUE 'L'.
88 WS-PHONE-UNKNOWN VALUE 'U'.
01 WS-PHONE-DIGITS PIC X(30).
01 WS-PHONE-DIGIT-COUNT PIC 9(02).
01 WS-PREFIX-STR PIC X(06).
01 WS-PREFIX-LEN PIC 9(02).
01 WS-HAS-LEADING-PLUS PIC X(01) VALUE 'N'.
88 WS-LEADING-PLUS-YES VALUE 'Y' FALSE 'N'.
*>
*> Known prefixes — China mobile (8613x, 8615x, 8618x)
01 WS-MOBILE-PREFIX-TBL.
05 FILLER PIC X(05) VALUE '86130'.
05 FILLER PIC X(05) VALUE '86131'.
05 FILLER PIC X(05) VALUE '86132'.
05 FILLER PIC X(05) VALUE '86133'.
05 FILLER PIC X(05) VALUE '86135'.
05 FILLER PIC X(05) VALUE '86136'.
05 FILLER PIC X(05) VALUE '86137'.
05 FILLER PIC X(05) VALUE '86138'.
05 FILLER PIC X(05) VALUE '86139'.
05 FILLER PIC X(05) VALUE '86150'.
05 FILLER PIC X(05) VALUE '86151'.
05 FILLER PIC X(05) VALUE '86152'.
05 FILLER PIC X(05) VALUE '86153'.
05 FILLER PIC X(05) VALUE '86155'.
05 FILLER PIC X(05) VALUE '86156'.
05 FILLER PIC X(05) VALUE '86157'.
05 FILLER PIC X(05) VALUE '86158'.
05 FILLER PIC X(05) VALUE '86159'.
05 FILLER PIC X(05) VALUE '86180'.
05 FILLER PIC X(05) VALUE '86181'.
05 FILLER PIC X(05) VALUE '86182'.
05 FILLER PIC X(05) VALUE '86183'.
05 FILLER PIC X(05) VALUE '86185'.
05 FILLER PIC X(05) VALUE '86186'.
05 FILLER PIC X(05) VALUE '86187'.
05 FILLER PIC X(05) VALUE '86188'.
05 FILLER PIC X(05) VALUE '86189'.
01 WS-MOBILE-TABLE REDEFINES WS-MOBILE-PREFIX-TBL.
05 WS-MOBILE-ITEM PIC X(05) OCCURS 27 TIMES.
01 WS-MOBILE-PREFIX-COUNT PIC 9(02) VALUE 27.
*>
*> Known prefixes — China landline (city codes)
01 WS-LL-PREFIX-TBL.
05 FILLER PIC X(05) VALUE '86010'.
05 FILLER PIC X(05) VALUE '86020'.
05 FILLER PIC X(05) VALUE '86021'.
05 FILLER PIC X(05) VALUE '86022'.
05 FILLER PIC X(05) VALUE '86023'.
05 FILLER PIC X(05) VALUE '86024'.
05 FILLER PIC X(05) VALUE '86025'.
05 FILLER PIC X(05) VALUE '86027'.
05 FILLER PIC X(05) VALUE '86028'.
05 FILLER PIC X(05) VALUE '86029'.
05 FILLER PIC X(05) VALUE '86031'.
05 FILLER PIC X(05) VALUE '86041'.
05 FILLER PIC X(05) VALUE '86051'.
05 FILLER PIC X(05) VALUE '86052'.
05 FILLER PIC X(05) VALUE '86053'.
05 FILLER PIC X(05) VALUE '86054'.
05 FILLER PIC X(05) VALUE '86055'.
05 FILLER PIC X(05) VALUE '86056'.
05 FILLER PIC X(05) VALUE '86057'.
05 FILLER PIC X(05) VALUE '86058'.
05 FILLER PIC X(05) VALUE '86059'.
05 FILLER PIC X(05) VALUE '86063'.
05 FILLER PIC X(05) VALUE '86066'.
05 FILLER PIC X(05) VALUE '86067'.
05 FILLER PIC X(05) VALUE '86069'.
05 FILLER PIC X(05) VALUE '86071'.
05 FILLER PIC X(05) VALUE '86072'.
05 FILLER PIC X(05) VALUE '86073'.
05 FILLER PIC X(05) VALUE '86074'.
05 FILLER PIC X(05) VALUE '86075'.
05 FILLER PIC X(05) VALUE '86076'.
05 FILLER PIC X(05) VALUE '86077'.
05 FILLER PIC X(05) VALUE '86078'.
05 FILLER PIC X(05) VALUE '86079'.
05 FILLER PIC X(05) VALUE '86081'.
05 FILLER PIC X(05) VALUE '86082'.
05 FILLER PIC X(05) VALUE '86083'.
05 FILLER PIC X(05) VALUE '86084'.
05 FILLER PIC X(05) VALUE '86085'.
05 FILLER PIC X(05) VALUE '86086'.
05 FILLER PIC X(05) VALUE '86087'.
05 FILLER PIC X(05) VALUE '86088'.
05 FILLER PIC X(05) VALUE '86089'.
05 FILLER PIC X(05) VALUE '86091'.
05 FILLER PIC X(05) VALUE '86092'.
05 FILLER PIC X(05) VALUE '86093'.
05 FILLER PIC X(05) VALUE '86094'.
05 FILLER PIC X(05) VALUE '86095'.
05 FILLER PIC X(05) VALUE '86096'.
05 FILLER PIC X(05) VALUE '86097'.
05 FILLER PIC X(05) VALUE '86098'.
05 FILLER PIC X(05) VALUE '86099'.
01 WS-LL-TABLE REDEFINES WS-LL-PREFIX-TBL.
05 WS-LL-ITEM PIC X(05) OCCURS 51 TIMES.
01 WS-LL-PREFIX-COUNT PIC 9(02) VALUE 51.
*>
*> Timestamp and trace fields
01 WS-CURRENT-TIME.
05 WS-CURR-YEAR PIC X(04).
05 WS-CURR-MONTH PIC X(02).
05 WS-CURR-DAY PIC X(02).
05 WS-CURR-HOUR PIC X(02).
05 WS-CURR-MIN PIC X(02).
05 WS-CURR-SEC PIC X(02).
01 WS-TIMESTAMP PIC X(20).
01 WS-TRACE-TS PIC X(20).
*> Report editing fields
01 WS-ED-PASS PIC Z(09)9.
01 WS-ED-FAIL PIC Z(09)9.
01 WS-ED-SKIP PIC Z(09)9.
01 WS-ED-WARN PIC Z(09)9.
01 WS-ED-ERROR PIC Z(09)9.
01 WS-ED-TOTAL PIC Z(09)9.
01 WS-ED-HASH PIC Z(14)9.
*> Local variables
01 WS-J PIC 9(02).
01 WS-K PIC 9(02).
*>
*> ============================================================
PROCEDURE DIVISION.
*> ============================================================
MAIN SECTION.
MB-PROCESS.
PERFORM 1000-INIT.
PERFORM 2000-OPEN-FILES.
PERFORM 3000-READ-INPUT
UNTIL WS-EOF-YES.
PERFORM 4000-REPORT.
PERFORM 5000-AUDIT.
PERFORM 9000-EXIT.
STOP RUN.
*>
*> ============================================================
*> 1000-INIT : Initialize counters, tables, batch timestamp
*> ============================================================
1000-INIT SECTION.
I1000-START.
DISPLAY 'ValidationHalfwidth: 1000-INIT starting...'.
MOVE ZERO TO WS-TOTAL-READ
WS-GOOD-COUNT
WS-BAD-COUNT
WS-SKIP-COUNT
WS-WARN-COUNT
WS-ERROR-COUNT
WS-TOTAL-CHARS
WS-TOTAL-DIGITS
WS-HASH-TOTAL.
MOVE 'N' TO WS-EOF.
MOVE FUNCTION CURRENT-DATE TO WS-CURRENT-TIME.
STRING WS-CURR-YEAR '-' WS-CURR-MONTH '-'
WS-CURR-DAY ' ' WS-CURR-HOUR ':'
WS-CURR-MIN ':' WS-CURR-SEC
INTO WS-TIMESTAMP.
DISPLAY 'ValidationHalfwidth: Batch started at '
WS-TIMESTAMP.
EXIT.
*>
*> ============================================================
*> 2000-OPEN-FILES : Open all files with FILE STATUS checks
*> ============================================================
2000-OPEN-FILES SECTION.
I2000-START.
DISPLAY 'ValidationHalfwidth: 2000-OPEN-FILES starting...'.
OPEN INPUT FILE-IN.
IF WS-FILE-IN-STATUS NOT = '00'
MOVE 'E' TO WS-ERROR-SEVERITY
MOVE '10' TO WS-ERROR-CODE
STRING 'FILE-IN open failed, status='
WS-FILE-IN-STATUS INTO WS-ERROR-DETAIL
PERFORM 6000-ERROR-HANDLE
END-IF.
OPEN OUTPUT FILE-OUT-GOOD.
IF WS-FILE-GOOD-STATUS NOT = '00'
MOVE 'E' TO WS-ERROR-SEVERITY
MOVE '11' TO WS-ERROR-CODE
STRING 'FILE-OUT-GOOD open failed, status='
WS-FILE-GOOD-STATUS INTO WS-ERROR-DETAIL
PERFORM 6000-ERROR-HANDLE
END-IF.
OPEN OUTPUT FILE-OUT-BAD.
IF WS-FILE-BAD-STATUS NOT = '00'
MOVE 'E' TO WS-ERROR-SEVERITY
MOVE '12' TO WS-ERROR-CODE
STRING 'FILE-OUT-BAD open failed, status='
WS-FILE-BAD-STATUS INTO WS-ERROR-DETAIL
PERFORM 6000-ERROR-HANDLE
END-IF.
OPEN OUTPUT FILE-OUT-REPORT.
IF WS-FILE-REPORT-STATUS NOT = '00'
MOVE 'E' TO WS-ERROR-SEVERITY
MOVE '13' TO WS-ERROR-CODE
STRING 'FILE-OUT-REPORT open failed, status='
WS-FILE-REPORT-STATUS INTO WS-ERROR-DETAIL
PERFORM 6000-ERROR-HANDLE
END-IF.
OPEN OUTPUT FILE-OUT-AUDIT.
IF WS-FILE-AUDIT-STATUS NOT = '00'
MOVE 'E' TO WS-ERROR-SEVERITY
MOVE '14' TO WS-ERROR-CODE
STRING 'FILE-OUT-AUDIT open failed, status='
WS-FILE-AUDIT-STATUS INTO WS-ERROR-DETAIL
PERFORM 6000-ERROR-HANDLE
END-IF.
DISPLAY 'ValidationHalfwidth: All files opened OK'.
EXIT.
*>
*> ============================================================
*> 3000-READ-INPUT : Read loop — read record and dispatch
*> ============================================================
3000-READ-INPUT SECTION.
I3000-START.
PERFORM 3100-VALIDATE-RECORD THRU I3100-EXIT.
IF NOT WS-EOF-YES
PERFORM 3200-PROCESS-RECORD
PERFORM 3300-WRITE-OUTPUT
END-IF.
EXIT.
*>
*> ============================================================
*> 3100-VALIDATE-RECORD : Full validation pipeline
*> R1: Half-width check
*> R2: Character set (digits 0-9, optional leading +)
*> R3: Length check (mobile=11, landline=10-12)
*> R4: Prefix lookup (China mobile/landline)
*> R5: Format rules for mobile vs landline
*> ============================================================
3100-VALIDATE-RECORD SECTION.
I3100-START.
MOVE 'P' TO WS-V-FORMAT WS-V-LENGTH WS-V-PREFIX WS-V-HALF.
MOVE SPACES TO WS-V-FORMAT-ERR WS-V-LENGTH-ERR
WS-V-PREFIX-ERR WS-V-HALF-ERR
WS-ERROR-CODE WS-ERROR-DETAIL
WS-PHONE-TYPE.
MOVE FUNCTION CURRENT-DATE TO WS-CURRENT-TIME.
STRING WS-CURR-HOUR ':' WS-CURR-MIN ':'
WS-CURR-SEC INTO WS-TRACE-TS.
DISPLAY '3100-VALIDATE-RECORD: ['
WS-TRACE-TS '] Processing: "'
FUNCTION TRIM(IN-TEXT) '"'.
READ FILE-IN
AT END
SET WS-EOF-YES TO TRUE
DISPLAY '3100-VALIDATE-RECORD: EOF reached'
EXIT PARAGRAPH
NOT AT END
DISPLAY '3100-VALIDATE-RECORD: Read OK status='
WS-FILE-IN-STATUS
END-READ.
IF WS-EOF-YES
EXIT
END-IF.
IF WS-FILE-IN-STATUS NOT = '00'
MOVE 'E' TO WS-ERROR-SEVERITY
MOVE '15' TO WS-ERROR-CODE
STRING 'READ failed, status=' WS-FILE-IN-STATUS
INTO WS-ERROR-DETAIL
PERFORM 6000-ERROR-HANDLE
END-IF.
ADD 1 TO WS-TOTAL-READ.
*>
*> R1: Half-width check (original logic preserved)
MOVE 'Y' TO WS-ALL-HALF.
PERFORM VARYING WS-IDX FROM 1 BY 1 UNTIL WS-IDX > 30
MOVE IN-TEXT(WS-IDX:1) TO WS-CHAR
IF WS-CHAR = SPACE
CONTINUE
ELSE
IF WS-CHAR < WS-HW-LOWER
MOVE 'N' TO WS-ALL-HALF
END-IF
IF WS-CHAR > WS-HW-UPPER
MOVE 'N' TO WS-ALL-HALF
END-IF
END-IF
END-PERFORM.
IF NOT WS-ALL-HALF-YES
MOVE 'F' TO WS-V-HALF
MOVE '01' TO WS-V-HALF-ERR
MOVE 'E' TO WS-ERROR-SEVERITY
DISPLAY '3100-VALIDATE-RECORD: HALF-WIDTH FAIL'
END-IF.
*>
*> R2: Character set check — extract digits, allow leading +
MOVE SPACES TO WS-PHONE-DIGITS.
MOVE ZERO TO WS-PHONE-DIGIT-COUNT.
MOVE 'N' TO WS-HAS-LEADING-PLUS.
PERFORM VARYING WS-IDX FROM 1 BY 1 UNTIL WS-IDX > 30
MOVE IN-TEXT(WS-IDX:1) TO WS-CHAR
IF WS-CHAR = SPACE
EXIT PERFORM
END-IF
IF WS-IDX = 1 AND WS-CHAR = '+'
MOVE 'Y' TO WS-HAS-LEADING-PLUS
CONTINUE
END-IF
IF WS-CHAR >= '0' AND WS-CHAR <= '9'
ADD 1 TO WS-PHONE-DIGIT-COUNT
MOVE WS-CHAR TO
WS-PHONE-DIGITS(WS-PHONE-DIGIT-COUNT:1)
ELSE
IF WS-CHAR NOT = SPACE
MOVE 'F' TO WS-V-FORMAT
MOVE '02' TO WS-V-FORMAT-ERR
MOVE 'E' TO WS-ERROR-SEVERITY
END-IF
END-IF
END-PERFORM.
ADD WS-PHONE-DIGIT-COUNT TO WS-TOTAL-DIGITS.
ADD FUNCTION NUMVAL(WS-PHONE-DIGITS) TO WS-HASH-TOTAL
ON SIZE ERROR
ADD 1 TO WS-HASH-TOTAL
END-ADD.
*>
*> R3: Determine phone type from prefix, validate length
MOVE SPACES TO WS-PREFIX-STR.
MOVE ZERO TO WS-PREFIX-LEN.
IF WS-PHONE-DIGIT-COUNT >= 5
MOVE WS-PHONE-DIGITS(1:5) TO WS-PREFIX-STR
MOVE 5 TO WS-PREFIX-LEN
END-IF.
MOVE 'U' TO WS-PHONE-TYPE.
IF WS-PREFIX-LEN = 5
PERFORM VARYING WS-J FROM 1 BY 1
UNTIL WS-J > WS-MOBILE-PREFIX-COUNT
IF WS-PREFIX-STR = WS-MOBILE-ITEM(WS-J)
MOVE 'M' TO WS-PHONE-TYPE
DISPLAY '3100-VALIDATE-RECORD: Mobile prefix: '
WS-PREFIX-STR
EXIT PERFORM
END-IF
END-PERFORM
IF WS-PHONE-TYPE = 'U'
PERFORM VARYING WS-K FROM 1 BY 1
UNTIL WS-K > WS-LL-PREFIX-COUNT
IF WS-PREFIX-STR = WS-LL-ITEM(WS-K)
MOVE 'L' TO WS-PHONE-TYPE
DISPLAY '3100-VALIDATE-RECORD: Landline '
'prefix: ' WS-PREFIX-STR
EXIT PERFORM
END-IF
END-PERFORM
END-IF
END-IF.
*>
*> R4: Length validation per phone type
EVALUATE TRUE
WHEN WS-PHONE-MOBILE
IF WS-PHONE-DIGIT-COUNT NOT = 11
MOVE 'F' TO WS-V-LENGTH
MOVE '03' TO WS-V-LENGTH-ERR
MOVE 'E' TO WS-ERROR-SEVERITY
DISPLAY '3100-VALIDATE-RECORD: LENGTH FAIL '
'mobile expected 11 got '
WS-PHONE-DIGIT-COUNT
END-IF
WHEN WS-PHONE-LANDLINE
IF WS-PHONE-DIGIT-COUNT < 10 OR
WS-PHONE-DIGIT-COUNT > 12
MOVE 'F' TO WS-V-LENGTH
MOVE '04' TO WS-V-LENGTH-ERR
MOVE 'E' TO WS-ERROR-SEVERITY
DISPLAY '3100-VALIDATE-RECORD: LENGTH FAIL '
'landline expected 10-12 got '
WS-PHONE-DIGIT-COUNT
END-IF
WHEN WS-PHONE-UNKNOWN
MOVE 'W' TO WS-ERROR-SEVERITY
MOVE '05' TO WS-V-PREFIX-ERR
DISPLAY '3100-VALIDATE-RECORD: PREFIX UNKNOWN for "'
FUNCTION TRIM(IN-TEXT) '"'
IF WS-PHONE-DIGIT-COUNT > 0
IF WS-PHONE-DIGIT-COUNT NOT >= 10 AND
WS-PHONE-DIGIT-COUNT NOT <= 12
MOVE 'F' TO WS-V-LENGTH
MOVE '06' TO WS-V-LENGTH-ERR
MOVE 'E' TO WS-ERROR-SEVERITY
END-IF
END-IF
END-EVALUATE.
*>
*> Compute content length — trim trailing spaces (original)
MOVE 30 TO WS-TEXT-LEN.
PERFORM VARYING WS-IDX FROM 30 BY -1 UNTIL WS-IDX = 0
IF IN-TEXT(WS-IDX:1) = SPACE
SUBTRACT 1 FROM WS-TEXT-LEN
ELSE
EXIT PERFORM
END-IF
END-PERFORM.
ADD WS-TEXT-LEN TO WS-TOTAL-CHARS.
*>
*> Apply original validation rules (preserved)
MOVE SPACES TO WS-ERROR-CODE.
IF NOT WS-ALL-HALF-YES
MOVE '01' TO WS-ERROR-CODE
ELSE
IF WS-TEXT-LEN > 20
MOVE '02' TO WS-ERROR-CODE
END-IF
END-IF.
*>
*> Combine all rule results — priority: HALF > FORMAT > LENGTH
IF WS-V-HALF-FAIL
IF WS-ERROR-CODE = SPACES
MOVE '01' TO WS-ERROR-CODE
END-IF
END-IF.
IF WS-V-FORMAT-FAIL
MOVE '07' TO WS-ERROR-CODE
END-IF.
IF WS-V-LENGTH-FAIL
MOVE '08' TO WS-ERROR-CODE
END-IF.
IF WS-V-PREFIX-FAIL
IF WS-ERROR-CODE = SPACES
MOVE '09' TO WS-ERROR-CODE
END-IF
END-IF.
IF WS-SEVERITY-ERROR
ADD 1 TO WS-ERROR-COUNT
END-IF.
IF WS-SEVERITY-WARNING
ADD 1 TO WS-WARN-COUNT
END-IF.
I3100-EXIT.
EXIT.
*>
*> ============================================================
*> 3200-PROCESS-RECORD : Route to GOOD/BAD/SKIP
*> ============================================================
3200-PROCESS-RECORD SECTION.
I3200-START.
DISPLAY '3200-PROCESS-RECORD: error-code='
WS-ERROR-CODE ' severity=' WS-ERROR-SEVERITY.
EVALUATE TRUE
WHEN WS-ERROR-CODE = SPACES
ADD 1 TO WS-GOOD-COUNT
DISPLAY '3200-PROCESS-RECORD: -> GOOD'
WHEN WS-SEVERITY-WARNING
ADD 1 TO WS-WARN-COUNT
ADD 1 TO WS-GOOD-COUNT
DISPLAY '3200-PROCESS-RECORD: -> GOOD (warn)'
WHEN WS-SEVERITY-ERROR
ADD 1 TO WS-ERROR-COUNT
ADD 1 TO WS-BAD-COUNT
DISPLAY '3200-PROCESS-RECORD: -> BAD code='
WS-ERROR-CODE
WHEN OTHER
ADD 1 TO WS-SKIP-COUNT
DISPLAY '3200-PROCESS-RECORD: -> SKIP'
END-EVALUATE.
EXIT.
*>
*> ============================================================
*> 3300-WRITE-OUTPUT : Write record to GOOD or BAD file
*> ============================================================
3300-WRITE-OUTPUT SECTION.
I3300-START.
IF WS-ERROR-CODE = SPACES
MOVE IN-TEXT TO GOOD-TEXT
WRITE GOOD-REC
DISPLAY '3300-WRITE-OUTPUT: Wrote GOOD record'
IF WS-FILE-GOOD-STATUS NOT = '00'
MOVE 'E' TO WS-ERROR-SEVERITY
MOVE '20' TO WS-ERROR-CODE
STRING 'WRITE GOOD-REC failed, status='
WS-FILE-GOOD-STATUS INTO WS-ERROR-DETAIL
PERFORM 6000-ERROR-HANDLE
END-IF
ELSE
MOVE IN-TEXT TO BAD-TEXT
MOVE WS-ERROR-CODE TO BAD-ERR-CODE
WRITE BAD-REC
DISPLAY '3300-WRITE-OUTPUT: Wrote BAD record code='
WS-ERROR-CODE
IF WS-FILE-BAD-STATUS NOT = '00'
MOVE 'E' TO WS-ERROR-SEVERITY
MOVE '21' TO WS-ERROR-CODE
STRING 'WRITE BAD-REC failed, status='
WS-FILE-BAD-STATUS INTO WS-ERROR-DETAIL
PERFORM 6000-ERROR-HANDLE
END-IF
END-IF.
EXIT.
*>
*> ============================================================
*> 4000-REPORT : Generate validation report
*> ============================================================
4000-REPORT SECTION.
I4000-START.
DISPLAY 'ValidationHalfwidth: 4000-REPORT starting...'.
MOVE WS-GOOD-COUNT TO WS-ED-PASS.
MOVE WS-BAD-COUNT TO WS-ED-FAIL.
MOVE WS-SKIP-COUNT TO WS-ED-SKIP.
MOVE WS-WARN-COUNT TO WS-ED-WARN.
MOVE WS-ERROR-COUNT TO WS-ED-ERROR.
MOVE WS-TOTAL-READ TO WS-ED-TOTAL.
MOVE WS-HASH-TOTAL TO WS-ED-HASH.
MOVE SPACES TO REPORT-LINE.
STRING '=== VALIDATION REPORT ===' INTO REPORT-LINE.
WRITE REPORT-REC.
IF WS-FILE-REPORT-STATUS NOT = '00'
DISPLAY '4000-REPORT: WRITE header failed, status='
WS-FILE-REPORT-STATUS
END-IF.
MOVE SPACES TO REPORT-LINE.
STRING 'Timestamp: ' WS-TIMESTAMP INTO REPORT-LINE.
WRITE REPORT-REC.
MOVE SPACES TO REPORT-LINE.
STRING 'Records read : ' WS-ED-TOTAL INTO REPORT-LINE.
WRITE REPORT-REC.
MOVE SPACES TO REPORT-LINE.
STRING 'Records passed : ' WS-ED-PASS INTO REPORT-LINE.
WRITE REPORT-REC.
MOVE SPACES TO REPORT-LINE.
STRING 'Records failed : ' WS-ED-FAIL INTO REPORT-LINE.
WRITE REPORT-REC.
MOVE SPACES TO REPORT-LINE.
STRING 'Records skipped : ' WS-ED-SKIP INTO REPORT-LINE.
WRITE REPORT-REC.
MOVE SPACES TO REPORT-LINE.
STRING 'Warnings issued : ' WS-ED-WARN INTO REPORT-LINE.
WRITE REPORT-REC.
MOVE SPACES TO REPORT-LINE.
STRING 'Errors detected : ' WS-ED-ERROR INTO REPORT-LINE.
WRITE REPORT-REC.
MOVE SPACES TO REPORT-LINE.
STRING 'Total chars : ' WS-TOTAL-CHARS
INTO REPORT-LINE.
WRITE REPORT-REC.
MOVE SPACES TO REPORT-LINE.
STRING 'Total digits : ' WS-TOTAL-DIGITS
INTO REPORT-LINE.
WRITE REPORT-REC.
MOVE SPACES TO REPORT-LINE.
STRING 'Hash total : ' WS-ED-HASH INTO REPORT-LINE.
WRITE REPORT-REC.
MOVE SPACES TO REPORT-LINE.
STRING 'R1: Half-width check (X''20''-X''7E'')'
INTO REPORT-LINE.
WRITE REPORT-REC.
MOVE SPACES TO REPORT-LINE.
STRING 'R2: Character set (digits 0-9, leading +)'
INTO REPORT-LINE.
WRITE REPORT-REC.
MOVE SPACES TO REPORT-LINE.
STRING 'R3: Length (mobile=11, landline=10-12)'
INTO REPORT-LINE.
WRITE REPORT-REC.
MOVE SPACES TO REPORT-LINE.
STRING 'R4: Prefix lookup (China mobile/landline)'
INTO REPORT-LINE.
WRITE REPORT-REC.
MOVE SPACES TO REPORT-LINE.
STRING '=== END OF REPORT ===' INTO REPORT-LINE.
WRITE REPORT-REC.
DISPLAY 'ValidationHalfwidth: 4000-REPORT complete'.
EXIT.
*>
*> ============================================================
*> 5000-AUDIT : Write audit summary with timestamps
*> ============================================================
5000-AUDIT SECTION.
I5000-START.
DISPLAY 'ValidationHalfwidth: 5000-AUDIT starting...'.
MOVE FUNCTION CURRENT-DATE TO WS-CURRENT-TIME.
STRING WS-CURR-YEAR '-' WS-CURR-MONTH '-'
WS-CURR-DAY ' ' WS-CURR-HOUR ':'
WS-CURR-MIN ':' WS-CURR-SEC
INTO WS-TIMESTAMP.
MOVE SPACES TO AUDIT-LINE.
STRING '=== AUDIT LOG ===' INTO AUDIT-LINE.
WRITE AUDIT-REC.
IF WS-FILE-AUDIT-STATUS NOT = '00'
DISPLAY '5000-AUDIT: WRITE header failed, status='
WS-FILE-AUDIT-STATUS
END-IF.
MOVE SPACES TO AUDIT-LINE.
STRING 'Program: ValidationHalfwidth' INTO AUDIT-LINE.
WRITE AUDIT-REC.
MOVE SPACES TO AUDIT-LINE.
STRING 'Timestamp: ' WS-TIMESTAMP INTO AUDIT-LINE.
WRITE AUDIT-REC.
MOVE SPACES TO AUDIT-LINE.
STRING 'Records read : ' WS-TOTAL-READ INTO AUDIT-LINE.
WRITE AUDIT-REC.
MOVE SPACES TO AUDIT-LINE.
STRING 'Records good : ' WS-GOOD-COUNT INTO AUDIT-LINE.
WRITE AUDIT-REC.
MOVE SPACES TO AUDIT-LINE.
STRING 'Records bad : ' WS-BAD-COUNT INTO AUDIT-LINE.
WRITE AUDIT-REC.
MOVE SPACES TO AUDIT-LINE.
STRING 'Records skip : ' WS-SKIP-COUNT INTO AUDIT-LINE.
WRITE AUDIT-REC.
MOVE SPACES TO AUDIT-LINE.
STRING 'Warnings : ' WS-WARN-COUNT INTO AUDIT-LINE.
WRITE AUDIT-REC.
MOVE SPACES TO AUDIT-LINE.
STRING 'Errors : ' WS-ERROR-COUNT INTO AUDIT-LINE.
WRITE AUDIT-REC.
MOVE SPACES TO AUDIT-LINE.
STRING 'Hash total : ' WS-HASH-TOTAL INTO AUDIT-LINE.
WRITE AUDIT-REC.
MOVE SPACES TO AUDIT-LINE.
STRING 'Rules: HALF-FORMAT-LENGTH-PREFIX'
INTO AUDIT-LINE.
WRITE AUDIT-REC.
MOVE SPACES TO AUDIT-LINE.
STRING 'Severity: W=Warning, E=Error'
INTO AUDIT-LINE.
WRITE AUDIT-REC.
MOVE SPACES TO AUDIT-LINE.
STRING 'Input : file-in.dat' INTO AUDIT-LINE.
WRITE AUDIT-REC.
MOVE SPACES TO AUDIT-LINE.
STRING 'Output: file-out-good.dat' INTO AUDIT-LINE.
WRITE AUDIT-REC.
MOVE SPACES TO AUDIT-LINE.
STRING 'Output: file-out-bad.dat' INTO AUDIT-LINE.
WRITE AUDIT-REC.
MOVE SPACES TO AUDIT-LINE.
STRING 'Output: file-out-report.dat' INTO AUDIT-LINE.
WRITE AUDIT-REC.
MOVE SPACES TO AUDIT-LINE.
STRING 'Output: file-out-audit.dat' INTO AUDIT-LINE.
WRITE AUDIT-REC.
MOVE SPACES TO AUDIT-LINE.
STRING '=== END AUDIT ===' INTO AUDIT-LINE.
WRITE AUDIT-REC.
DISPLAY 'ValidationHalfwidth: 5000-AUDIT complete'.
EXIT.
*>
*> ============================================================
*> 6000-ERROR-HANDLE : Centralized error handler
*> WARNING: display and continue
*> ERROR: display, close files, stop run
*> ============================================================
6000-ERROR-HANDLE SECTION.
I6000-START.
MOVE FUNCTION CURRENT-DATE TO WS-CURRENT-TIME.
STRING WS-CURR-HOUR ':' WS-CURR-MIN ':'
WS-CURR-SEC INTO WS-TRACE-TS.
EVALUATE TRUE
WHEN WS-SEVERITY-WARNING
DISPLAY '6000-ERROR-HANDLE: [' WS-TRACE-TS
'] WARNING code=' WS-ERROR-CODE
' ' WS-ERROR-DETAIL
ADD 1 TO WS-WARN-COUNT
WHEN WS-SEVERITY-ERROR
DISPLAY '6000-ERROR-HANDLE: [' WS-TRACE-TS
'] ** ERROR ** code=' WS-ERROR-CODE
' ' WS-ERROR-DETAIL
ADD 1 TO WS-ERROR-COUNT
ADD 1 TO WS-BAD-COUNT
DISPLAY 'ValidationHalfwidth: ABORTING'
PERFORM 9000-EXIT
WHEN OTHER
DISPLAY '6000-ERROR-HANDLE: [' WS-TRACE-TS
'] UNKNOWN severity=' WS-ERROR-SEVERITY
END-EVALUATE.
EXIT.
*>
*> ============================================================
*> 9000-EXIT : Close files, display summary, stop run
*> ============================================================
9000-EXIT SECTION.
I9000-START.
DISPLAY 'ValidationHalfwidth: 9000-EXIT closing files...'.
CLOSE FILE-IN.
IF WS-FILE-IN-STATUS NOT = '00'
DISPLAY 'CLOSE FILE-IN status=' WS-FILE-IN-STATUS
END-IF.
CLOSE FILE-OUT-GOOD.
IF WS-FILE-GOOD-STATUS NOT = '00'
DISPLAY 'CLOSE FILE-OUT-GOOD status='
WS-FILE-GOOD-STATUS
END-IF.
CLOSE FILE-OUT-BAD.
IF WS-FILE-BAD-STATUS NOT = '00'
DISPLAY 'CLOSE FILE-OUT-BAD status='
WS-FILE-BAD-STATUS
END-IF.
CLOSE FILE-OUT-REPORT.
IF WS-FILE-REPORT-STATUS NOT = '00'
DISPLAY 'CLOSE FILE-OUT-REPORT status='
WS-FILE-REPORT-STATUS
END-IF.
CLOSE FILE-OUT-AUDIT.
IF WS-FILE-AUDIT-STATUS NOT = '00'
DISPLAY 'CLOSE FILE-OUT-AUDIT status='
WS-FILE-AUDIT-STATUS
END-IF.
MOVE FUNCTION CURRENT-DATE TO WS-CURRENT-TIME.
STRING WS-CURR-YEAR '-' WS-CURR-MONTH '-'
WS-CURR-DAY ' ' WS-CURR-HOUR ':'
WS-CURR-MIN ':' WS-CURR-SEC
INTO WS-TIMESTAMP.
DISPLAY ' '.
DISPLAY '========================================'.
DISPLAY 'ValidationHalfwidth: FINAL SUMMARY'.
DISPLAY 'Batch ended: ' WS-TIMESTAMP.
DISPLAY 'Records read : ' WS-TOTAL-READ.
DISPLAY 'Records passed : ' WS-GOOD-COUNT.
DISPLAY 'Records failed : ' WS-BAD-COUNT.
DISPLAY 'Records skipped : ' WS-SKIP-COUNT.
DISPLAY 'Warnings issued : ' WS-WARN-COUNT.
DISPLAY 'Errors detected : ' WS-ERROR-COUNT.
DISPLAY 'Total chars : ' WS-TOTAL-CHARS.
DISPLAY 'Total digits : ' WS-TOTAL-DIGITS.
DISPLAY 'Hash total : ' WS-HASH-TOTAL.
DISPLAY '========================================'.
MOVE SPACES TO AUDIT-LINE.
STRING 'Batch ended: ' WS-TIMESTAMP INTO AUDIT-LINE.
WRITE AUDIT-REC.
DISPLAY 'ValidationHalfwidth: Done.'.
STOP RUN.
*>
*> ============================================================
END PROGRAM ValidationHalfwidth.