From b4860d810b95f7d1538d81619e4a3ac3241b964a Mon Sep 17 00:00:00 2001 From: Yutaro Sakamoto Date: Fri, 17 Apr 2026 08:40:00 +0000 Subject: [PATCH 1/2] wip: wip --- .../libcobj/common/CobolUtil.java | 7 ++-- .../libcobj/data/AbstractCobolField.java | 19 ++++++++++ .../libcobj/termio/CobolTerminal.java | 5 ++- tests/jp-compat.src/special-names.at | 36 +++++++++++++++++++ 4 files changed, 64 insertions(+), 3 deletions(-) diff --git a/libcobj/app/src/main/java/jp/osscons/opensourcecobol/libcobj/common/CobolUtil.java b/libcobj/app/src/main/java/jp/osscons/opensourcecobol/libcobj/common/CobolUtil.java index 0e0dd923a..b993a4040 100755 --- a/libcobj/app/src/main/java/jp/osscons/opensourcecobol/libcobj/common/CobolUtil.java +++ b/libcobj/app/src/main/java/jp/osscons/opensourcecobol/libcobj/common/CobolUtil.java @@ -632,10 +632,13 @@ public static void setEnv(String envVarName, String envVarValue) { * * @param envVarName the name of an environment variable. The leading and trailing spaces are * ignored. - * @param envVarValue the value of an environment variable to be set. + * @param envVarValue the value of an environment variable to be set. Trailing spaces and NUL + * bytes are stripped to match libcob's {@code cob_field_to_string} behavior. */ public static void setEnv(AbstractCobolField envVarName, AbstractCobolField envVarValue) { - CobolUtil.envVarTable.setProperty(envVarName.getString().trim(), envVarValue.getString()); + CobolUtil.envVarTable.setProperty( + envVarName.getString().trim(), + envVarValue.fieldToString(AbstractCobolField.charSetSJIS)); } /** diff --git a/libcobj/app/src/main/java/jp/osscons/opensourcecobol/libcobj/data/AbstractCobolField.java b/libcobj/app/src/main/java/jp/osscons/opensourcecobol/libcobj/data/AbstractCobolField.java index 211126437..c2f300e5f 100644 --- a/libcobj/app/src/main/java/jp/osscons/opensourcecobol/libcobj/data/AbstractCobolField.java +++ b/libcobj/app/src/main/java/jp/osscons/opensourcecobol/libcobj/data/AbstractCobolField.java @@ -1024,6 +1024,25 @@ public String fieldToString() { return new String(data.getByteArray(0, i + 1)); } + /** + * 末尾の空白とNULバイトを取り除いたうえで指定されたcharsetでデコードして文字列として返す. + * 本家CのopensourceCOBOLにおける{@code cob_field_to_string}と同等の処理を行う. + * + * @param charset デコードに使用するcharset + * @return 末尾の空白/NULを取り除いた文字列 + */ + public String fieldToString(Charset charset) { + CobolDataStorage data = this.getDataStorage(); + int end; + for (end = this.getSize(); end > 0; --end) { + byte b = data.getByte(end - 1); + if (b != ' ' && b != 0) { + break; + } + } + return new String(data.getByteArray(0, end), charset); + } + /** * TODO: 準備中 * diff --git a/libcobj/app/src/main/java/jp/osscons/opensourcecobol/libcobj/termio/CobolTerminal.java b/libcobj/app/src/main/java/jp/osscons/opensourcecobol/libcobj/termio/CobolTerminal.java index 22fb3462e..87b735bdf 100644 --- a/libcobj/app/src/main/java/jp/osscons/opensourcecobol/libcobj/termio/CobolTerminal.java +++ b/libcobj/app/src/main/java/jp/osscons/opensourcecobol/libcobj/termio/CobolTerminal.java @@ -259,7 +259,10 @@ public static void displayEnvValue(AbstractCobolField f) { CobolExceptionInfo.setException(CobolExceptionId.COB_EC_IMP_DISPLAY); return; } - CobolUtil.setEnv(CobolUtil.cobLocalEnv, f.getString()); + // Strip trailing spaces and NULs so that PIC X(n) source fields do not + // leak padding into the environment variable value. Matches libcob's + // cob_field_to_string used by cob_display_env_value. + CobolUtil.setEnv(CobolUtil.cobLocalEnv, f.fieldToString(AbstractCobolField.charSetSJIS)); } /** diff --git a/tests/jp-compat.src/special-names.at b/tests/jp-compat.src/special-names.at index bfe4dd617..a625cd42f 100644 --- a/tests/jp-compat.src/special-names.at +++ b/tests/jp-compat.src/special-names.at @@ -145,6 +145,42 @@ AT_CHECK([export TESTENV=envvalue && java prog], [0], [envvalue:newvalue]) AT_CLEANUP +AT_SETUP([DISPLAY ENVIRONMENT-VALUE strips trailing spaces]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + ENVIRONMENT DIVISION. + INPUT-OUTPUT SECTION. + FILE-CONTROL. + SELECT OUT-FILE ASSIGN OUTPATH + ORGANIZATION IS LINE SEQUENTIAL. + DATA DIVISION. + FILE SECTION. + FD OUT-FILE. + 01 OUT-REC PIC X(5). + WORKING-STORAGE SECTION. + 01 WK-PATH PIC X(32). + PROCEDURE DIVISION. + MOVE SPACE TO WK-PATH. + STRING 'hello.txt' DELIMITED BY SIZE + INTO WK-PATH. + DISPLAY 'OUTPATH' UPON ENVIRONMENT-NAME. + DISPLAY WK-PATH UPON ENVIRONMENT-VALUE. + OPEN OUTPUT OUT-FILE. + MOVE 'HELLO' TO OUT-REC. + WRITE OUT-REC. + CLOSE OUT-FILE. + STOP RUN. +]) + +AT_CHECK([${COMPILE_JP_COMPAT} prog.cob], [0]) +AT_CHECK([java prog], [0]) +AT_CHECK([test -f hello.txt && cat hello.txt], [0], [HELLO +]) + +AT_CLEANUP + AT_SETUP([SPECIAL NAMES unterminated]) AT_DATA([prog.cob], [ From 9526cab87706f9061924a159cb4d3f9f7f67754d Mon Sep 17 00:00:00 2001 From: Yutaro Sakamoto Date: Mon, 20 Apr 2026 00:34:16 +0000 Subject: [PATCH 2/2] =?UTF-8?q?test:=20SET=20ENVIRONMENT=20=E3=81=A8=20DIS?= =?UTF-8?q?PLAY=20ENVIRONMENT-VALUE=20=E3=81=AE=E6=9C=AB=E5=B0=BE=E7=A9=BA?= =?UTF-8?q?=E7=99=BD=E3=83=88=E3=83=AA=E3=83=A0=E3=81=AB=E9=96=A2=E3=81=99?= =?UTF-8?q?=E3=82=8B=E5=9B=9E=E5=B8=B0=E3=83=86=E3=82=B9=E3=83=88=E3=81=A8?= =?UTF-8?q?=E3=82=B3=E3=83=A1=E3=83=B3=E3=83=88=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - tests/jp-compat.src/special-names.at に SET ENVIRONMENT x TO y 経由でも PIC X(n) フィールドの末尾空白がトリムされることを検証する統合テストを追加 - CobolTerminal#displayEnvValue のJavadocに、末尾空白/NULが削除される挙動と 本家CのopensourceCOBOLの cob_display_env_value との対応を明記 - CobolUtil#setEnv(AbstractCobolField, AbstractCobolField) に、環境変数名側と 値側でトリムの範囲が異なる理由をコメントで説明 --- .../libcobj/common/CobolUtil.java | 3 ++ .../libcobj/termio/CobolTerminal.java | 4 +++ tests/jp-compat.src/special-names.at | 35 +++++++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/libcobj/app/src/main/java/jp/osscons/opensourcecobol/libcobj/common/CobolUtil.java b/libcobj/app/src/main/java/jp/osscons/opensourcecobol/libcobj/common/CobolUtil.java index b993a4040..1e4dbea14 100755 --- a/libcobj/app/src/main/java/jp/osscons/opensourcecobol/libcobj/common/CobolUtil.java +++ b/libcobj/app/src/main/java/jp/osscons/opensourcecobol/libcobj/common/CobolUtil.java @@ -636,6 +636,9 @@ public static void setEnv(String envVarName, String envVarValue) { * bytes are stripped to match libcob's {@code cob_field_to_string} behavior. */ public static void setEnv(AbstractCobolField envVarName, AbstractCobolField envVarValue) { + // Name side uses String#trim() to drop both leading and trailing spaces, matching the + // behavior documented above. Value side uses fieldToString so only trailing spaces/NULs + // are stripped (leading spaces are meaningful in an environment value). CobolUtil.envVarTable.setProperty( envVarName.getString().trim(), envVarValue.fieldToString(AbstractCobolField.charSetSJIS)); diff --git a/libcobj/app/src/main/java/jp/osscons/opensourcecobol/libcobj/termio/CobolTerminal.java b/libcobj/app/src/main/java/jp/osscons/opensourcecobol/libcobj/termio/CobolTerminal.java index 87b735bdf..b02263679 100644 --- a/libcobj/app/src/main/java/jp/osscons/opensourcecobol/libcobj/termio/CobolTerminal.java +++ b/libcobj/app/src/main/java/jp/osscons/opensourcecobol/libcobj/termio/CobolTerminal.java @@ -252,6 +252,10 @@ public static void displayEnvironment(AbstractCobolField f) { * で設定された環境変数名に対して値を設定する. 環境変数名が未設定または空文字列の場合は{@link * CobolExceptionId#COB_EC_IMP_DISPLAY}例外を設定する. * + *

COBOL変数{@code f}の値は{@link AbstractCobolField#fieldToString(java.nio.charset.Charset)} + * によって末尾の空白とNULバイトが取り除かれてから環境変数値として設定される. これは本家CのopensourceCOBOLが + * {@code cob_display_env_value}内で{@code cob_field_to_string}を使用する挙動と一致する. + * * @param f 設定する環境変数の値を保持するCOBOL変数 */ public static void displayEnvValue(AbstractCobolField f) { diff --git a/tests/jp-compat.src/special-names.at b/tests/jp-compat.src/special-names.at index a625cd42f..2b763c54d 100644 --- a/tests/jp-compat.src/special-names.at +++ b/tests/jp-compat.src/special-names.at @@ -181,6 +181,41 @@ AT_CHECK([test -f hello.txt && cat hello.txt], [0], [HELLO AT_CLEANUP +AT_SETUP([SET ENVIRONMENT strips trailing spaces]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + ENVIRONMENT DIVISION. + INPUT-OUTPUT SECTION. + FILE-CONTROL. + SELECT OUT-FILE ASSIGN OUTPATH2 + ORGANIZATION IS LINE SEQUENTIAL. + DATA DIVISION. + FILE SECTION. + FD OUT-FILE. + 01 OUT-REC PIC X(5). + WORKING-STORAGE SECTION. + 01 WK-PATH PIC X(32). + PROCEDURE DIVISION. + MOVE SPACE TO WK-PATH. + STRING 'world.txt' DELIMITED BY SIZE + INTO WK-PATH. + SET ENVIRONMENT 'OUTPATH2' TO WK-PATH. + OPEN OUTPUT OUT-FILE. + MOVE 'WORLD' TO OUT-REC. + WRITE OUT-REC. + CLOSE OUT-FILE. + STOP RUN. +]) + +AT_CHECK([${COMPILE_JP_COMPAT} prog.cob], [0]) +AT_CHECK([java prog], [0]) +AT_CHECK([test -f world.txt && cat world.txt], [0], [WORLD +]) + +AT_CLEANUP + AT_SETUP([SPECIAL NAMES unterminated]) AT_DATA([prog.cob], [