From bc9f11c64eba6781434e9b81414a103d56fae0aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Gro=C3=9Fmann?= Date: Thu, 19 Mar 2026 20:05:27 +0100 Subject: [PATCH 1/4] feat: unified ASCII/EBCDIC translation subsystem (httpxlat) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the hybrid CP037/IBM-1047 translation tables (asc2ebc.c, ebc2asc.c, httpetoa.c, httpatoe.c) with a single httpxlat module providing three codepage pairs: - CP037 (default) — symmetric NL/LF roundtrip, no data corruption - IBM-1047 — for Open Systems / z/OS USS conventions (Zowe, mvsmf) - LEGACY — exact 3.3.x behavior preserved for backward compatibility The server-default codepage is selected at startup via http_xlate_init(). CGI modules can choose a specific codepage per call through the HTTPX vector table (http_xlate + codepage pair pointers appended at offsets 0x110-0x11C). Existing http_etoa/http_atoe stay at HTTPX offsets 0x74/0x78 with unchanged signatures. Global pointers asc2ebc/ebc2asc are retained for backward compatibility with old httpd code. Fixes the pipe character corruption bug where EBCDIC 0x4F was mapped to ASCII ']' instead of '|' in the old hybrid tables. Fixes #30 --- doc/httpxlat-integration.md | 191 +++++++++++++++ doc/httpxlat.c | 464 +++++++++++++++++++++++++++++++++++ doc/httpxlat.h | 55 +++++ include/httpcgi.h | 9 + include/httpd.h | 11 +- include/httpxlat.h | 55 +++++ project.toml | 2 +- src/asc2ebc.c | 37 --- src/ebc2asc.c | 37 --- src/httpatoe.c | 20 -- src/httpconf.c | 3 + src/httpetoa.c | 36 --- src/httpx.c | 4 + src/httpxlat.c | 465 ++++++++++++++++++++++++++++++++++++ 14 files changed, 1257 insertions(+), 132 deletions(-) create mode 100644 doc/httpxlat-integration.md create mode 100644 doc/httpxlat.c create mode 100644 doc/httpxlat.h create mode 100644 include/httpxlat.h delete mode 100644 src/asc2ebc.c delete mode 100644 src/ebc2asc.c delete mode 100644 src/httpatoe.c delete mode 100644 src/httpetoa.c create mode 100644 src/httpxlat.c diff --git a/doc/httpxlat-integration.md b/doc/httpxlat-integration.md new file mode 100644 index 0000000..1653d91 --- /dev/null +++ b/doc/httpxlat-integration.md @@ -0,0 +1,191 @@ +# HTTPD Translation Subsystem — Integration Guide + +## New Files + +- `src/httpxlat.c` — All tables, codepage management, http_xlate(), httpetoa(), httpatoe() +- `include/httpxlat.h` — HTTPCP typedef, function declarations + +## Files to DELETE + +- `src/asc2ebc.c` — Replaced by tables in httpxlat.c +- `src/ebc2asc.c` — Replaced by tables in httpxlat.c +- `src/httpetoa.c` — httpetoa() now in httpxlat.c (no switch hacks) +- `src/httpatoe.c` — httpatoe() now in httpxlat.c + +## Files to MODIFY + +Both `httpd.h` and `httpcgi.h` define their own copy of `struct httpx` and +the macro layer. **Both must be updated in sync.** + +- `httpd.h` — used by the server itself (full crent370/libufs includes, + `extern` declarations with `asm()` labels, `#ifndef HTTP_PRIVATE` guard) +- `httpcgi.h` — used by external CGI modules (lightweight, no crent370 + dependency, macros only) + +### include/httpd.h + +1. Add `#include "httpxlat.h"` near top (after existing includes) + +2. Add `HTTPCP` typedef alongside the other typedefs (after `HTTPCGI`): + +```c +typedef struct httpcp HTTPCP; /* Codepage pair */ +``` + +3. In the HTTPX struct, **append at end** (after mqtc_pub at offset 0x10C): + +```c + unsigned char *(*http_xlate)(unsigned char *, int, const unsigned char *); + /* 110 translate with explicit table */ + HTTPCP *xlate_cp037; /* 114 CP037 codepage pair */ + HTTPCP *xlate_1047; /* 118 IBM-1047 codepage pair */ + HTTPCP *xlate_legacy; /* 11C legacy hybrid codepage pair */ +``` + +4. Add `extern` with `asm()` label (near the other extern declarations): + +```c +extern unsigned char *http_xlate(unsigned char *, int, const unsigned char *) asm("HTTPXLAT"); +``` + +5. In the `#ifndef HTTP_PRIVATE` macro section, add CGI-accessible macro: + +```c +#define http_xlate(buf,len,tbl) \ + ((httpx->http_xlate)((buf),(len),(tbl))) +``` + +6. Keep the `extern` declarations for old globals (lines 81-82) — they + still exist (defined in httpxlat.c for backward compat) but should not + be used in new code: + +```c +extern UCHAR *ebc2asc; /* backward compat — points to active default */ +extern UCHAR *asc2ebc; /* backward compat — points to active default */ +``` + +### include/httpcgi.h + +1. Add `HTTPCP` typedef alongside the other forward declarations: + +```c +typedef struct httpcp HTTPCP; /* Codepage pair — opaque */ +``` + +2. In the HTTPX struct, **append at end** (after mqtc_pub at offset 0x10C): + +```c + unsigned char *(*http_xlate)(unsigned char *, int, const unsigned char *); + /* 110 translate with explicit table */ + HTTPCP *xlate_cp037; /* 114 CP037 codepage pair */ + HTTPCP *xlate_1047; /* 118 IBM-1047 codepage pair */ + HTTPCP *xlate_legacy; /* 11C legacy hybrid codepage pair */ +``` + +3. Add `http_xlate` macro (after the `mqtc_pub` macro): + +```c +#define http_xlate(buf,len,tbl) \ + ((httpx->http_xlate)((buf),(len),(tbl))) +``` + +### src/httpx.c + +Add the four new entries at the end of the static vector: + +```c + mqtc_pub, /* 10C mqtc_pub() */ + http_xlate, /* 110 http_xlate() */ + &http_cp037, /* 114 CP037 codepage pair */ + &http_cp1047, /* 118 IBM-1047 codepage pair */ + &http_legacy, /* 11C legacy hybrid codepage pair */ +}; +``` + +### src/httpconf.c (Lua version, until Parmlib replaces it) + +Add `http_xlate_init()` call during startup: + +```c +/* After process_httpd() or at the end of http_config(): */ +http_xlate_init("CP037"); /* or read from Lua/config */ +``` + +When Parmlib replaces Lua, the parser reads `CODEPAGE` keyword and calls +`http_xlate_init(value)`. + +### Parmlib addition + +``` +CODEPAGE CP037 # default if omitted +# CODEPAGE IBM1047 +# CODEPAGE LEGACY +``` + +## How mvsMF Uses This + +### Before (private tables in xlate.c) + +```c +#include "xlate.h" /* mvsMF private IBM-1047 tables */ +mvsmf_atoe(buf, len); +mvsmf_etoa(buf, len); +``` + +### After (via HTTPX vector) + +```c +/* Use server default (what CODEPAGE says in Parmlib) */ +http_etoa(buf, len); +http_atoe(buf, len); + +/* Or use explicit codepage */ +HTTPX *httpx = http_get_httpx(session->httpd); +http_xlate(buf, len, httpx->xlate_1047->etoa); +http_xlate(buf, len, httpx->xlate_cp037->atoe); +``` + +mvsMF can then delete `src/xlate.c` and `include/xlate.h`. + +## Key Design Decisions + +1. **CP037 as default** — Symmetric NL/LF roundtrip. No data corruption. +2. **Legacy tables preserved exactly** — Including the httpetoa.c switch + behavior baked into legacy_etoa[0x15]=0x0A. No existing behavior lost. +3. **HTTPX backward compatible** — http_etoa/http_atoe stay at offsets + 0x74/0x78 with identical signature. New entries appended at end. +4. **512 bytes per codepage** (256 atoe + 256 etoa). Total: 1,536 bytes + for all three. Negligible. +5. **Global pointers asc2ebc/ebc2asc retained** — Old httpd code that + references them directly (httpgets.c, httpdeco.c, etc.) continues + to work. They point to the active default tables. + +## Roundtrip Verification (Critical Characters) + +### CP037 (default) +``` +ASCII LF (0x0A) -> atoe -> 0x25 -> etoa -> 0x0A OK +ASCII | (0x7C) -> atoe -> 0x4F -> etoa -> 0x7C OK +ASCII [ (0x5B) -> atoe -> 0xAD -> etoa -> 0x5B OK +ASCII ] (0x5D) -> atoe -> 0xBD -> etoa -> 0x5D OK +ASCII ^ (0x5E) -> atoe -> 0xB0 -> etoa -> 0x5E OK +ASCII \ (0x5C) -> atoe -> 0xE0 -> etoa -> 0x5C OK +``` + +### IBM-1047 +``` +ASCII LF (0x0A) -> atoe -> 0x25 -> etoa -> 0x85 ASYMMETRIC (known) +EBCDIC NEL(0x15) -> etoa -> 0x0A (NEL -> LF, 1047 convention) +ASCII | (0x7C) -> atoe -> 0x4F -> etoa -> 0x7C OK +ASCII [ (0x5B) -> atoe -> 0xAD -> etoa -> 0x5B OK +ASCII ] (0x5D) -> atoe -> 0xBD -> etoa -> 0x5D OK +``` + +### LEGACY (preserved 3.3.x behavior) +``` +ASCII | (0x7C) -> atoe -> 0x6A -> etoa -> 0x7C OK (self-consistent but non-standard) +ASCII [ (0x5B) -> atoe -> 0xAD -> etoa -> 0x5B OK +ASCII ] (0x5D) -> atoe -> 0xBD -> etoa -> 0x5D OK +EBCDIC 0x4A -> etoa -> 0x5B [ (maps to [ like 0xAD — double mapping) +EBCDIC 0x4F -> etoa -> 0x5D ] (maps to ] — legacy bug preserved) +``` diff --git a/doc/httpxlat.c b/doc/httpxlat.c new file mode 100644 index 0000000..3a609d2 --- /dev/null +++ b/doc/httpxlat.c @@ -0,0 +1,464 @@ +/* HTTPXLAT.C +** ASCII/EBCDIC translation tables and codepage management. +** +** Three codepage options: +** CP037 — IBM CECP for US/Canada (symmetric NL/LF, default) +** IBM1047 — IBM Open Systems Latin-1 (used by z/OS USS, Zowe) +** LEGACY — HTTPD 3.3.x hybrid tables (backward compatibility) +** +** The server default is selected via Parmlib CODEPAGE parameter. +** CGI modules can choose a specific codepage per call via the +** HTTPX vector table. +*/ +#include "httpd.h" + +/* ------------------------------------------------------------------ */ +/* Codepage pair struct */ +/* ------------------------------------------------------------------ */ + +/* defined in httpxlat.h — kept here for reference: +** typedef struct httpcp { +** const unsigned char *atoe; +** const unsigned char *etoa; +** } HTTPCP; +*/ + +/* ------------------------------------------------------------------ */ +/* IBM Code Page 037 (CECP US/Canada) */ +/* */ +/* Symmetric NL/LF mapping: */ +/* ASCII LF (0x0A) <-> EBCDIC LF (0x25) — roundtrip clean */ +/* ASCII NEL (0x85) <-> EBCDIC NEL (0x15) — roundtrip clean */ +/* ------------------------------------------------------------------ */ + +static const unsigned char cp037_atoe[256] = { + /* 0x00-0x07 */ 0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F, + /* 0x08-0x0F */ 0x16, 0x05, 0x25, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + /* 0x10-0x17 */ 0x10, 0x11, 0x12, 0x13, 0x3C, 0x3D, 0x32, 0x26, + /* 0x18-0x1F */ 0x18, 0x19, 0x3F, 0x27, 0x1C, 0x1D, 0x1E, 0x1F, + /* 0x20-0x27 sp ! " # $ % & ' */ + 0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D, + /* 0x28-0x2F ( ) * + , - . / */ + 0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61, + /* 0x30-0x37 0 1 2 3 4 5 6 7 */ + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + /* 0x38-0x3F 8 9 : ; < = > ? */ + 0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F, + /* 0x40-0x47 @ A B C D E F G */ + 0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + /* 0x48-0x4F H I J K L M N O */ + 0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, + /* 0x50-0x57 P Q R S T U V W */ + 0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, + /* 0x58-0x5F X Y Z [ \ ] ^ _ */ + 0xE7, 0xE8, 0xE9, 0xAD, 0xE0, 0xBD, 0xB0, 0x6D, + /* 0x60-0x67 ` a b c d e f g */ + 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + /* 0x68-0x6F h i j k l m n o */ + 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + /* 0x70-0x77 p q r s t u v w */ + 0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, + /* 0x78-0x7F x y z { | } ~ DEL */ + 0xA7, 0xA8, 0xA9, 0xC0, 0x4F, 0xD0, 0xA1, 0x07, + /* 0x80-0x87 */ 0x20, 0x21, 0x22, 0x23, 0x24, 0x15, 0x06, 0x17, + /* 0x88-0x8F */ 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x09, 0x0A, 0x1B, + /* 0x90-0x97 */ 0x30, 0x31, 0x1A, 0x33, 0x34, 0x35, 0x36, 0x08, + /* 0x98-0x9F */ 0x38, 0x39, 0x3A, 0x3B, 0x04, 0x14, 0x3E, 0xFF, + /* 0xA0-0xA7 */ 0x41, 0xAA, 0x4A, 0xB1, 0x9F, 0xB2, 0x6A, 0xB5, + /* 0xA8-0xAF */ 0xBB, 0xB4, 0x9A, 0x8A, 0x5F, 0xCA, 0xAF, 0xBC, + /* 0xB0-0xB7 */ 0x90, 0x8F, 0xEA, 0xFA, 0xBE, 0xA0, 0xB6, 0xB3, + /* 0xB8-0xBF */ 0x9D, 0xDA, 0x9B, 0x8B, 0xB7, 0xB8, 0xB9, 0xAB, + /* 0xC0-0xC7 */ 0x64, 0x65, 0x62, 0x66, 0x63, 0x67, 0x9E, 0x68, + /* 0xC8-0xCF */ 0x74, 0x71, 0x72, 0x73, 0x78, 0x75, 0x76, 0x77, + /* 0xD0-0xD7 */ 0xAC, 0x69, 0xED, 0xEE, 0xEB, 0xEF, 0xEC, 0xBF, + /* 0xD8-0xDF */ 0x80, 0xFD, 0xFE, 0xFB, 0xFC, 0xBA, 0xAE, 0x59, + /* 0xE0-0xE7 */ 0x44, 0x45, 0x42, 0x46, 0x43, 0x47, 0x9C, 0x48, + /* 0xE8-0xEF */ 0x54, 0x51, 0x52, 0x53, 0x58, 0x55, 0x56, 0x57, + /* 0xF0-0xF7 */ 0x8C, 0x49, 0xCD, 0xCE, 0xCB, 0xCF, 0xCC, 0xE1, + /* 0xF8-0xFF */ 0x70, 0xDD, 0xDE, 0xDB, 0xDC, 0x8D, 0x8E, 0xDF +}; + +static const unsigned char cp037_etoa[256] = { + /* 0x00-0x07 */ 0x00, 0x01, 0x02, 0x03, 0x9C, 0x09, 0x86, 0x7F, + /* 0x08-0x0F */ 0x97, 0x8D, 0x8E, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + /* 0x10-0x17 */ 0x10, 0x11, 0x12, 0x13, 0x9D, 0x85, 0x08, 0x87, + /* 0x18-0x1F */ 0x18, 0x19, 0x92, 0x8F, 0x1C, 0x1D, 0x1E, 0x1F, + /* 0x20-0x27 */ 0x80, 0x81, 0x82, 0x83, 0x84, 0x0A, 0x17, 0x1B, + /* 0x28-0x2F */ 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x05, 0x06, 0x07, + /* 0x30-0x37 */ 0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04, + /* 0x38-0x3F */ 0x98, 0x99, 0x9A, 0x9B, 0x14, 0x15, 0x9E, 0x1A, + /* 0x40-0x47 sp */ + 0x20, 0xA0, 0xE2, 0xE4, 0xE0, 0xE1, 0xE3, 0xE5, + /* 0x48-0x4F cent . < ( + | */ + 0xE7, 0xF1, 0xA2, 0x2E, 0x3C, 0x28, 0x2B, 0x7C, + /* 0x50-0x57 & */ + 0x26, 0xE9, 0xEA, 0xEB, 0xE8, 0xED, 0xEE, 0xEF, + /* 0x58-0x5F ! $ * ) ; not */ + 0xEC, 0xDF, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0xAC, + /* 0x60-0x67 - / */ + 0x2D, 0x2F, 0xC2, 0xC4, 0xC0, 0xC1, 0xC3, 0xC5, + /* 0x68-0x6F brkvbar , % _ > ? */ + 0xC7, 0xD1, 0xA6, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, + /* 0x70-0x77 */ + 0xF8, 0xC9, 0xCA, 0xCB, 0xC8, 0xCD, 0xCE, 0xCF, + /* 0x78-0x7F ` : # @ ' = " */ + 0xCC, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, + /* 0x80-0x87 a b c d e f g */ + 0xD8, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + /* 0x88-0x8F h i */ + 0x68, 0x69, 0xAB, 0xBB, 0xF0, 0xFD, 0xFE, 0xB1, + /* 0x90-0x97 deg j k l m n o p */ + 0xB0, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, + /* 0x98-0x9F q r */ + 0x71, 0x72, 0xAA, 0xBA, 0xE6, 0xB8, 0xC6, 0xA4, + /* 0xA0-0xAF mu ~ s t u v w x */ + 0xB5, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + /* 0xA8-0xAF y z [ */ + 0x79, 0x7A, 0xA1, 0xBF, 0xD0, 0x5B, 0xDE, 0xAE, + /* 0xB0-0xB7 ^ */ + 0x5E, 0xA3, 0xA5, 0xB7, 0xA9, 0xA7, 0xB6, 0xBC, + /* 0xB8-0xBF ] */ + 0xBD, 0xBE, 0xDD, 0xA8, 0xAF, 0x5D, 0xB4, 0xD7, + /* 0xC0-0xC7 { A B C D E F G */ + 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + /* 0xC8-0xCF H I */ + 0x48, 0x49, 0xAD, 0xF4, 0xF6, 0xF2, 0xF3, 0xF5, + /* 0xD0-0xD7 } J K L M N O P */ + 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, + /* 0xD8-0xDF Q R */ + 0x51, 0x52, 0xB9, 0xFB, 0xFC, 0xF9, 0xFA, 0xFF, + /* 0xE0-0xE7 \ div S T U V W X */ + 0x5C, 0xF7, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + /* 0xE8-0xEF Y Z */ + 0x59, 0x5A, 0xB2, 0xD4, 0xD6, 0xD2, 0xD3, 0xD5, + /* 0xF0-0xF7 0 1 2 3 4 5 6 7 */ + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + /* 0xF8-0xFF 8 9 */ + 0x38, 0x39, 0xB3, 0xDB, 0xDC, 0xD9, 0xDA, 0x9F +}; + +/* ------------------------------------------------------------------ */ +/* IBM Code Page 1047 (Latin-1 / Open Systems) */ +/* */ +/* Asymmetric NL/LF mapping (the well-known 1047 problem): */ +/* ASCII LF (0x0A) -> EBCDIC LF (0x25) */ +/* EBCDIC NEL(0x15) -> ASCII LF (0x0A) <- NB: NOT symmetric */ +/* EBCDIC LF (0x25) -> ASCII NEL (0x85) <- NB: NOT symmetric */ +/* */ +/* Tables from mvsMF (verified, in production). */ +/* ------------------------------------------------------------------ */ + +static const unsigned char ibm1047_atoe[256] = { + /* 0x00-0x07 */ 0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F, + /* 0x08-0x0F */ 0x16, 0x05, 0x25, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + /* 0x10-0x17 */ 0x10, 0x11, 0x12, 0x13, 0x3C, 0x3D, 0x32, 0x26, + /* 0x18-0x1F */ 0x18, 0x19, 0x3F, 0x27, 0x1C, 0x1D, 0x1E, 0x1F, + /* 0x20-0x27 sp ! " # $ % & ' */ + 0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D, + /* 0x28-0x2F ( ) * + , - . / */ + 0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61, + /* 0x30-0x37 0 1 2 3 4 5 6 7 */ + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + /* 0x38-0x3F 8 9 : ; < = > ? */ + 0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F, + /* 0x40-0x47 @ A B C D E F G */ + 0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + /* 0x48-0x4F H I J K L M N O */ + 0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, + /* 0x50-0x57 P Q R S T U V W */ + 0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, + /* 0x58-0x5F X Y Z [ \ ] ^ _ */ + 0xE7, 0xE8, 0xE9, 0xAD, 0xE0, 0xBD, 0x5F, 0x6D, + /* 0x60-0x67 ` a b c d e f g */ + 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + /* 0x68-0x6F h i j k l m n o */ + 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + /* 0x70-0x77 p q r s t u v w */ + 0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, + /* 0x78-0x7F x y z { | } ~ DEL */ + 0xA7, 0xA8, 0xA9, 0xC0, 0x4F, 0xD0, 0xA1, 0x07, + /* 0x80-0x87 */ 0x20, 0x21, 0x22, 0x23, 0x24, 0x15, 0x06, 0x17, + /* 0x88-0x8F */ 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x09, 0x0A, 0x1B, + /* 0x90-0x97 */ 0x30, 0x31, 0x1A, 0x33, 0x34, 0x35, 0x36, 0x08, + /* 0x98-0x9F */ 0x38, 0x39, 0x3A, 0x3B, 0x04, 0x14, 0x3E, 0xFF, + /* 0xA0-0xA7 */ 0x41, 0xAA, 0x4A, 0xB1, 0x9F, 0xB2, 0x6A, 0xB5, + /* 0xA8-0xAF */ 0xBB, 0xB4, 0x9A, 0x8A, 0xB0, 0xCA, 0xAF, 0xBC, + /* 0xB0-0xB7 */ 0x90, 0x8F, 0xEA, 0xFA, 0xBE, 0xA0, 0xB6, 0xB3, + /* 0xB8-0xBF */ 0x9D, 0xDA, 0x9B, 0x8B, 0xB7, 0xB8, 0xB9, 0xAB, + /* 0xC0-0xC7 */ 0x64, 0x65, 0x62, 0x66, 0x63, 0x67, 0x9E, 0x68, + /* 0xC8-0xCF */ 0x74, 0x71, 0x72, 0x73, 0x78, 0x75, 0x76, 0x77, + /* 0xD0-0xD7 */ 0xAC, 0x69, 0xED, 0xEE, 0xEB, 0xEF, 0xEC, 0xBF, + /* 0xD8-0xDF */ 0x80, 0xFD, 0xFE, 0xFB, 0xFC, 0xBA, 0xAE, 0x59, + /* 0xE0-0xE7 */ 0x44, 0x45, 0x42, 0x46, 0x43, 0x47, 0x9C, 0x48, + /* 0xE8-0xEF */ 0x54, 0x51, 0x52, 0x53, 0x58, 0x55, 0x56, 0x57, + /* 0xF0-0xF7 */ 0x8C, 0x49, 0xCD, 0xCE, 0xCB, 0xCF, 0xCC, 0xE1, + /* 0xF8-0xFF */ 0x70, 0xDD, 0xDE, 0xDB, 0xDC, 0x8D, 0x8E, 0xDF +}; + +static const unsigned char ibm1047_etoa[256] = { + /* 0x00-0x07 */ 0x00, 0x01, 0x02, 0x03, 0x9C, 0x09, 0x86, 0x7F, + /* 0x08-0x0F */ 0x97, 0x8D, 0x8E, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + /* 0x10-0x17 */ 0x10, 0x11, 0x12, 0x13, 0x9D, 0x0A, 0x08, 0x87, + /* 0x18-0x1F */ 0x18, 0x19, 0x92, 0x8F, 0x1C, 0x1D, 0x1E, 0x1F, + /* 0x20-0x27 */ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x17, 0x1B, + /* 0x28-0x2F */ 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x05, 0x06, 0x07, + /* 0x30-0x37 */ 0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04, + /* 0x38-0x3F */ 0x98, 0x99, 0x9A, 0x9B, 0x14, 0x15, 0x9E, 0x1A, + /* 0x40-0x47 sp */ + 0x20, 0xA0, 0xE2, 0xE4, 0xE0, 0xE1, 0xE3, 0xE5, + /* 0x48-0x4F cent . < ( + | */ + 0xE7, 0xF1, 0xA2, 0x2E, 0x3C, 0x28, 0x2B, 0x7C, + /* 0x50-0x57 & */ + 0x26, 0xE9, 0xEA, 0xEB, 0xE8, 0xED, 0xEE, 0xEF, + /* 0x58-0x5F ! $ * ) ; ^ */ + 0xEC, 0xDF, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0x5E, + /* 0x60-0x67 - / */ + 0x2D, 0x2F, 0xC2, 0xC4, 0xC0, 0xC1, 0xC3, 0xC5, + /* 0x68-0x6F brkvbar , % _ > ? */ + 0xC7, 0xD1, 0xA6, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, + /* 0x70-0x77 */ + 0xF8, 0xC9, 0xCA, 0xCB, 0xC8, 0xCD, 0xCE, 0xCF, + /* 0x78-0x7F ` : # @ ' = " */ + 0xCC, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, + /* 0x80-0x87 a b c d e f g */ + 0xD8, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + /* 0x88-0x8F h i */ + 0x68, 0x69, 0xAB, 0xBB, 0xF0, 0xFD, 0xFE, 0xB1, + /* 0x90-0x97 deg j k l m n o p */ + 0xB0, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, + /* 0x98-0x9F q r */ + 0x71, 0x72, 0xAA, 0xBA, 0xE6, 0xB8, 0xC6, 0xA4, + /* 0xA0-0xAF mu ~ s t u v w x */ + 0xB5, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + /* 0xA8-0xAF y z [ */ + 0x79, 0x7A, 0xA1, 0xBF, 0xD0, 0x5B, 0xDE, 0xAE, + /* 0xB0-0xB7 not */ + 0xAC, 0xA3, 0xA5, 0xB7, 0xA9, 0xA7, 0xB6, 0xBC, + /* 0xB8-0xBF ] */ + 0xBD, 0xBE, 0xDD, 0xA8, 0xAF, 0x5D, 0xB4, 0xD7, + /* 0xC0-0xC7 { A B C D E F G */ + 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + /* 0xC8-0xCF H I */ + 0x48, 0x49, 0xAD, 0xF4, 0xF6, 0xF2, 0xF3, 0xF5, + /* 0xD0-0xD7 } J K L M N O P */ + 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, + /* 0xD8-0xDF Q R */ + 0x51, 0x52, 0xB9, 0xFB, 0xFC, 0xF9, 0xFA, 0xFF, + /* 0xE0-0xE7 \ sup2 S T U V W X */ + 0x5C, 0xB2, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + /* 0xE8-0xEF Y Z */ + 0x59, 0x5A, 0xB2, 0xD4, 0xD6, 0xD2, 0xD3, 0xD5, + /* 0xF0-0xF7 0 1 2 3 4 5 6 7 */ + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + /* 0xF8-0xFF 8 9 */ + 0x38, 0x39, 0xB3, 0xDB, 0xDC, 0xD9, 0xDA, 0x9F +}; + +/* ------------------------------------------------------------------ */ +/* LEGACY — HTTPD 3.3.x hybrid tables (backward compatibility) */ +/* */ +/* This is the exact behavior of httpd 3.3.x, including the switch */ +/* overrides from httpetoa.c baked into the etoa table. */ +/* */ +/* Known issues: */ +/* - Brackets [/] patched to IBM-1047 positions (0xAD/0xBD) */ +/* - Pipe | left at non-standard position (0x6A atoe, wrong etoa) */ +/* - NEL(0x15)->LF(0x0A) from httpetoa.c switch baked in */ +/* ------------------------------------------------------------------ */ + +static const unsigned char legacy_atoe[256] = { + /* 0x00-0x07 */ 0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F, + /* 0x08-0x0F */ 0x16, 0x05, 0x25, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + /* 0x10-0x17 */ 0x10, 0x11, 0x12, 0x13, 0x3C, 0x3D, 0x32, 0x26, + /* 0x18-0x1F */ 0x18, 0x19, 0x3F, 0x27, 0x1C, 0x1D, 0x1E, 0x1F, + /* 0x20-0x27 */ 0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D, + /* 0x28-0x2F */ 0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61, + /* 0x30-0x37 */ 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + /* 0x38-0x3F */ 0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F, + /* 0x40-0x47 */ 0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + /* 0x48-0x4F */ 0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, + /* 0x50-0x57 */ 0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, + /* 0x58-0x5F [ = 0xAD, ] = 0xBD (1047), | = 0x6A (broken) */ + 0xE7, 0xE8, 0xE9, 0xAD, 0xE0, 0xBD, 0x5F, 0x6D, + /* 0x60-0x67 */ 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + /* 0x68-0x6F */ 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + /* 0x70-0x77 */ 0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, + /* 0x78-0x7F | at 0x6A instead of 0x4F (KNOWN BUG) */ + 0xA7, 0xA8, 0xA9, 0xC0, 0x6A, 0xD0, 0xA1, 0x07, + /* 0x80-0x87 */ 0x20, 0x21, 0x22, 0x23, 0x24, 0x15, 0x06, 0x17, + /* 0x88-0x8F */ 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x09, 0x0A, 0x1B, + /* 0x90-0x97 */ 0x30, 0x31, 0x1A, 0x33, 0x34, 0x35, 0x36, 0x08, + /* 0x98-0x9F */ 0x38, 0x39, 0x3A, 0x3B, 0x04, 0x14, 0x3E, 0xE1, + /* 0xA0-0xA7 */ 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, + /* 0xA8-0xAF */ 0x49, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + /* 0xB0-0xB7 */ 0x58, 0x59, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + /* 0xB8-0xBF */ 0x68, 0x69, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, + /* 0xC0-0xC7 */ 0x76, 0x77, 0x78, 0x80, 0x8A, 0x8B, 0x8C, 0x8D, + /* 0xC8-0xCF */ 0x8E, 0x8F, 0x90, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, + /* 0xD0-0xD7 */ 0x9F, 0xA0, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, + /* 0xD8-0xDF */ 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, + /* 0xE0-0xE7 */ 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, + /* 0xE8-0xEF */ 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xDA, 0xDB, + /* 0xF0-0xF7 */ 0xDC, 0xDD, 0xDE, 0xDF, 0xEA, 0xEB, 0xEC, 0xED, + /* 0xF8-0xFF */ 0xEE, 0xEF, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF +}; + +static const unsigned char legacy_etoa[256] = { + /* 0x00-0x07 */ 0x00, 0x01, 0x02, 0x03, 0x9C, 0x09, 0x86, 0x7F, + /* 0x08-0x0F */ 0x97, 0x8D, 0x8E, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + /* 0x10-0x17 NB: [0x15] = 0x0A (httpetoa.c switch baked in) */ + 0x10, 0x11, 0x12, 0x13, 0x9D, 0x0A, 0x08, 0x87, + /* 0x18-0x1F */ 0x18, 0x19, 0x92, 0x8F, 0x1C, 0x1D, 0x1E, 0x1F, + /* 0x20-0x27 */ 0x80, 0x81, 0x82, 0x83, 0x84, 0x0A, 0x17, 0x1B, + /* 0x28-0x2F */ 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x05, 0x06, 0x07, + /* 0x30-0x37 */ 0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04, + /* 0x38-0x3F */ 0x98, 0x99, 0x9A, 0x9B, 0x14, 0x15, 0x9E, 0x1A, + /* 0x40-0x47 */ 0x20, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, + /* 0x48-0x4F NB: [0x4A] = 0x5B([), [0x4F] = 0x5D(]) — legacy */ + 0xA7, 0xA8, 0x5B, 0x2E, 0x3C, 0x28, 0x2B, 0x5D, + /* 0x50-0x57 */ 0x26, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, + /* 0x58-0x5F */ 0xB0, 0xB1, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0x5E, + /* 0x60-0x67 */ 0x2D, 0x2F, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, + /* 0x68-0x6F */ 0xB8, 0xB9, 0x7C, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, + /* 0x70-0x77 */ 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, 0xC0, 0xC1, + /* 0x78-0x7F */ 0xC2, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, + /* 0x80-0x87 */ 0xC3, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + /* 0x88-0x8F */ 0x68, 0x69, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, + /* 0x90-0x97 */ 0xCA, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, + /* 0x98-0x9F */ 0x71, 0x72, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, + /* 0xA0-0xA7 */ 0xD1, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + /* 0xA8-0xAF NB: [0xAD] = 0x5B([) — 1047 patch */ + 0x79, 0x7A, 0xD2, 0xD3, 0xD4, 0x5B, 0xD6, 0xD7, + /* 0xB0-0xB7 */ 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, + /* 0xB8-0xBF NB: [0xBD] = 0x5D(]) — 1047 patch */ + 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0x5D, 0xE6, 0xE7, + /* 0xC0-0xC7 */ 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + /* 0xC8-0xCF */ 0x48, 0x49, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, + /* 0xD0-0xD7 */ 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, + /* 0xD8-0xDF */ 0x51, 0x52, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, + /* 0xE0-0xE7 */ 0x5C, 0x9F, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + /* 0xE8-0xEF */ 0x59, 0x5A, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, + /* 0xF0-0xF7 */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + /* 0xF8-0xFF */ 0x38, 0x39, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF +}; + +/* ------------------------------------------------------------------ */ +/* Codepage pair structs (exported to CGIs via HTTPX) */ +/* ------------------------------------------------------------------ */ + +HTTPCP http_cp037 = { cp037_atoe, cp037_etoa }; +HTTPCP http_cp1047 = { ibm1047_atoe, ibm1047_etoa }; +HTTPCP http_legacy = { legacy_atoe, legacy_etoa }; + +/* ------------------------------------------------------------------ */ +/* Server-default table pointers */ +/* */ +/* These are the "active" tables. http_etoa() and http_atoe() use */ +/* these. Set once at startup by http_xlate_init(). */ +/* */ +/* The legacy global symbols "ebc2asc" and "asc2ebc" (used by old */ +/* code throughout httpd) also point here for backward compatibility. */ +/* ------------------------------------------------------------------ */ + +static const unsigned char *default_atoe = cp037_atoe; +static const unsigned char *default_etoa = cp037_etoa; + +/* backward-compatible global pointers (referenced by old httpd code) */ +unsigned char *asc2ebc = (unsigned char *)cp037_atoe; +unsigned char *ebc2asc = (unsigned char *)cp037_etoa; + +/* ------------------------------------------------------------------ */ +/* http_xlate_init() — select server-default codepage */ +/* */ +/* Called once during startup (from httpconf.c). */ +/* codepage: "CP037" | "IBM1047" | "LEGACY" (case insensitive) */ +/* Returns: 0 on success, -1 on unknown codepage */ +/* ------------------------------------------------------------------ */ + +int +http_xlate_init(const char *codepage) +{ + const unsigned char *atoe; + const unsigned char *etoa; + + if (!codepage || http_cmp(codepage, "CP037") == 0) { + atoe = cp037_atoe; + etoa = cp037_etoa; + } + else if (http_cmp(codepage, "IBM1047") == 0) { + atoe = ibm1047_atoe; + etoa = ibm1047_etoa; + } + else if (http_cmp(codepage, "LEGACY") == 0) { + atoe = legacy_atoe; + etoa = legacy_etoa; + } + else { + wtof("HTTPD070E Unknown codepage \"%s\", using CP037", codepage); + atoe = cp037_atoe; + etoa = cp037_etoa; + return -1; + } + + default_atoe = atoe; + default_etoa = etoa; + + /* update legacy global pointers */ + asc2ebc = (unsigned char *)atoe; + ebc2asc = (unsigned char *)etoa; + + wtof("HTTPD071I Codepage: %s", codepage ? codepage : "CP037"); + return 0; +} + +/* ------------------------------------------------------------------ */ +/* http_xlate() — translate buffer using an explicit table */ +/* */ +/* tbl: 256-byte translation table (atoe or etoa). */ +/* Use httpcp->atoe or httpcp->etoa from the HTTPX vector. */ +/* Pass NULL for the server-default etoa table. */ +/* ------------------------------------------------------------------ */ + +__asm__("\n&FUNC SETC 'http_xlate'"); +unsigned char * +http_xlate(unsigned char *buf, int len, const unsigned char *tbl) +{ + int i; + + if (!buf) return buf; + if (!len) len = strlen((char *)buf); + if (len <= 0) return buf; + + if (!tbl) tbl = default_etoa; + + for (i = 0; i < len; i++) { + buf[i] = tbl[buf[i]]; + } + + return buf; +} + +/* ------------------------------------------------------------------ */ +/* http_etoa() — EBCDIC to ASCII using server-default table */ +/* */ +/* Exposed via HTTPX at offset 0x74 (unchanged signature). */ +/* No switch hacks — the table handles everything. */ +/* ------------------------------------------------------------------ */ + +unsigned char * +httpetoa(unsigned char *buf, int len) +{ + return http_xlate(buf, len, default_etoa); +} + +/* ------------------------------------------------------------------ */ +/* http_atoe() — ASCII to EBCDIC using server-default table */ +/* */ +/* Exposed via HTTPX at offset 0x78 (unchanged signature). */ +/* ------------------------------------------------------------------ */ + +unsigned char * +httpatoe(unsigned char *buf, int len) +{ + return http_xlate(buf, len, default_atoe); +} diff --git a/doc/httpxlat.h b/doc/httpxlat.h new file mode 100644 index 0000000..9d037e6 --- /dev/null +++ b/doc/httpxlat.h @@ -0,0 +1,55 @@ +/* HTTPXLAT.H +** ASCII/EBCDIC codepage translation interface. +** +** Provides three codepage table pairs (CP037, IBM1047, LEGACY) +** and a generic translation function for use by HTTPD and CGI modules. +*/ +#ifndef HTTPXLAT_H +#define HTTPXLAT_H + +/* ------------------------------------------------------------------ */ +/* Codepage pair — one table for each direction */ +/* ------------------------------------------------------------------ */ + +typedef struct httpcp { + const unsigned char *atoe; /* ASCII-to-EBCDIC table (256 bytes) */ + const unsigned char *etoa; /* EBCDIC-to-ASCII table (256 bytes) */ +} HTTPCP; + +/* ------------------------------------------------------------------ */ +/* Pre-defined codepage pairs */ +/* ------------------------------------------------------------------ */ + +extern HTTPCP http_cp037; /* IBM CP037 (CECP US/Canada) */ +extern HTTPCP http_cp1047; /* IBM-1047 (Open Systems Latin-1) */ +extern HTTPCP http_legacy; /* HTTPD 3.3.x hybrid (compat only) */ + +/* ------------------------------------------------------------------ */ +/* Functions */ +/* ------------------------------------------------------------------ */ + +/** + * Select the server-default codepage. + * Called once at startup from httpconf.c. + * + * @param codepage "CP037", "IBM1047", or "LEGACY" (case insensitive) + * @return 0 on success, -1 on unknown codepage (falls back to CP037) + */ +int http_xlate_init(const char *codepage); + +/** + * Translate a buffer in-place using an explicit table. + * + * @param buf Buffer to translate + * @param len Length (0 = strlen) + * @param tbl 256-byte translation table (use httpcp->atoe or ->etoa). + * NULL = server-default etoa table. + * @return buf + * + * Usage from CGI via HTTPX: + * httpx->http_xlate(buf, len, httpx->xlate_1047->etoa); + */ +unsigned char *http_xlate(unsigned char *buf, int len, + const unsigned char *tbl) asm("HTTPXLAT"); + +#endif /* HTTPXLAT_H */ diff --git a/include/httpcgi.h b/include/httpcgi.h index 4b1b8ea..225f277 100644 --- a/include/httpcgi.h +++ b/include/httpcgi.h @@ -28,6 +28,7 @@ #include "dbg.h" /* debug helpers */ #include "errors.h" /* errno values */ +#include "httpxlat.h" /* ASCII/EBCDIC translation */ /* ------------------------------------------------------------------ */ /* Basic typedefs (no dependency) */ @@ -292,6 +293,11 @@ struct httpx { const char *topic_name, const char *application_message); /* 10C publish MQTT topic */ + unsigned char *(*http_xlate)(unsigned char *, int, const unsigned char *); + /* 110 translate with explicit table */ + HTTPCP *xlate_cp037; /* 114 CP037 codepage pair */ + HTTPCP *xlate_1047; /* 118 IBM-1047 codepage pair */ + HTTPCP *xlate_legacy; /* 11C legacy hybrid codepage pair */ }; /* Eye-catcher for HTTPD pointer identification (ABI constant) */ @@ -513,4 +519,7 @@ struct httpx { #define mqtc_pub(mqtc,qos,retain,topic,message) \ ((httpx->mqtc_pub)((mqtc),(qos),(retain),(topic),(message))) +#define http_xlate(buf,len,tbl) \ + ((httpx->http_xlate)((buf),(len),(tbl))) + #endif /* HTTPCGI_H */ diff --git a/include/httpd.h b/include/httpd.h index 652e341..3f351f4 100644 --- a/include/httpd.h +++ b/include/httpd.h @@ -44,6 +44,7 @@ #include "ftpd.h" /* FTP daemon and client */ #include "cred.h" /* Credentials */ #include "mqtc370.h" /* MQTT Client */ +#include "httpxlat.h" /* ASCII/EBCDIC translation */ #define HTTPLUAX (httpd->luax) /* use this pointer */ #include "httpluax.h" /* Lua function vector struct */ @@ -424,7 +425,11 @@ struct httpx { /* 108 process CGI request */ int (*mqtc_pub)(MQTC *mqtc, unsigned qos, unsigned retain, const char* topic_name, const char* application_message); /* 10C publish topic */ - + unsigned char *(*http_xlate)(unsigned char *, int, const unsigned char *); + /* 110 translate with explicit table */ + HTTPCP *xlate_cp037; /* 114 CP037 codepage pair */ + HTTPCP *xlate_1047; /* 118 IBM-1047 codepage pair */ + HTTPCP *xlate_legacy; /* 11C legacy hybrid codepage pair */ }; extern int http_in(HTTPC*) asm("HTTPIN"); @@ -483,6 +488,7 @@ extern int http_link(HTTPC *, const char *) asm("HTT extern HTTPCGI *http_find_cgi(HTTPD *httpd, const char *path) asm("HTTPFCGI"); extern HTTPCGI *http_add_cgi(HTTPD *httpd, const char *pgm, const char *path, int login) asm("HTTPACGI"); extern int http_process_cgi(HTTPC *httpc, HTTPCGI *cgi) asm("HTTPPCGI"); +extern unsigned char *http_xlate(unsigned char *, int, const unsigned char *) asm("HTTPXLAT"); extern double httpsecs(double *psecs) asm("HTTPSECS"); extern int httpcred(HTTPC *httpc) asm("HTTPCRED"); extern int httpd048(HTTPD *httpd) asm("HTTPD048"); @@ -710,6 +716,9 @@ extern int http_gets(HTTPC *httpc, UCHAR *buf, unsigned max) asm("HTT #define mqtc_pub(mqtc,qos,retain,topic,message) \ ((httpx->mqtc_pub)((mqtc),(qos),(retain),(topic),(message))) +#define http_xlate(buf,len,tbl) \ + ((httpx->http_xlate)((buf),(len),(tbl))) + #endif /* ifndef HTTPX_H */ #endif diff --git a/include/httpxlat.h b/include/httpxlat.h new file mode 100644 index 0000000..9d037e6 --- /dev/null +++ b/include/httpxlat.h @@ -0,0 +1,55 @@ +/* HTTPXLAT.H +** ASCII/EBCDIC codepage translation interface. +** +** Provides three codepage table pairs (CP037, IBM1047, LEGACY) +** and a generic translation function for use by HTTPD and CGI modules. +*/ +#ifndef HTTPXLAT_H +#define HTTPXLAT_H + +/* ------------------------------------------------------------------ */ +/* Codepage pair — one table for each direction */ +/* ------------------------------------------------------------------ */ + +typedef struct httpcp { + const unsigned char *atoe; /* ASCII-to-EBCDIC table (256 bytes) */ + const unsigned char *etoa; /* EBCDIC-to-ASCII table (256 bytes) */ +} HTTPCP; + +/* ------------------------------------------------------------------ */ +/* Pre-defined codepage pairs */ +/* ------------------------------------------------------------------ */ + +extern HTTPCP http_cp037; /* IBM CP037 (CECP US/Canada) */ +extern HTTPCP http_cp1047; /* IBM-1047 (Open Systems Latin-1) */ +extern HTTPCP http_legacy; /* HTTPD 3.3.x hybrid (compat only) */ + +/* ------------------------------------------------------------------ */ +/* Functions */ +/* ------------------------------------------------------------------ */ + +/** + * Select the server-default codepage. + * Called once at startup from httpconf.c. + * + * @param codepage "CP037", "IBM1047", or "LEGACY" (case insensitive) + * @return 0 on success, -1 on unknown codepage (falls back to CP037) + */ +int http_xlate_init(const char *codepage); + +/** + * Translate a buffer in-place using an explicit table. + * + * @param buf Buffer to translate + * @param len Length (0 = strlen) + * @param tbl 256-byte translation table (use httpcp->atoe or ->etoa). + * NULL = server-default etoa table. + * @return buf + * + * Usage from CGI via HTTPX: + * httpx->http_xlate(buf, len, httpx->xlate_1047->etoa); + */ +unsigned char *http_xlate(unsigned char *buf, int len, + const unsigned char *tbl) asm("HTTPXLAT"); + +#endif /* HTTPXLAT_H */ diff --git a/project.toml b/project.toml index 492d48e..0e442eb 100644 --- a/project.toml +++ b/project.toml @@ -159,5 +159,5 @@ version_files = ["VERSION"] [artifacts] headers = true -header_files = ["httpcgi.h", "dbg.h", "errors.h"] +header_files = ["httpcgi.h", "httpxlat.h", "dbg.h", "errors.h"] loads = true diff --git a/src/asc2ebc.c b/src/asc2ebc.c deleted file mode 100644 index cad5add..0000000 --- a/src/asc2ebc.c +++ /dev/null @@ -1,37 +0,0 @@ -static unsigned char a2e[256] = { - 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, /* x00-x07 */ - 0x16, 0x05, 0x25, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* x08-x0F */ - 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, /* x10-x17 */ - 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, /* x18-x1F */ - 0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d, /* x20-x27 */ - 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61, /* x28-x2F */ - 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* x30-x37 */ - 0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f, /* x38-x3F */ - 0x7c, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* x40-x47 */ - 0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, /* x48-x4F */ - 0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, /* x50-x57 */ - /* 0xe7, 0xe8, 0xe9, 0x4a, 0xe0, 0x4f, 0x5f, 0x6d, /* x58-x5F */ - 0xe7, 0xe8, 0xe9, 0xad, 0xe0, 0xbd, 0x5f, 0x6d, /* x58-x5F */ - 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* x60-x67 */ - 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, /* x68-x6F */ - 0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, /* x70-x77 */ - 0xa7, 0xa8, 0xa9, 0xc0, 0x6a, 0xd0, 0xa1, 0x07, /* x78-x7F */ - 0x20, 0x21, 0x22, 0x23, 0x24, 0x15, 0x06, 0x17, /* x80-x87 */ - 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x09, 0x0a, 0x1b, /* x88-x8F */ - 0x30, 0x31, 0x1a, 0x33, 0x34, 0x35, 0x36, 0x08, /* x90-x97 */ - 0x38, 0x39, 0x3a, 0x3b, 0x04, 0x14, 0x3e, 0xe1, /* x98-x9F */ - 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, /* xA0-xA7 */ - 0x49, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* xA8-xAF */ - 0x58, 0x59, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* xB0-xB7 */ - 0x68, 0x69, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, /* xB8-xBF */ - 0x76, 0x77, 0x78, 0x80, 0x8a, 0x8b, 0x8c, 0x8d, /* xC0-xC7 */ - 0x8e, 0x8f, 0x90, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, /* xC8-xCF */ - 0x9f, 0xa0, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* xD0-xD7 */ - 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* xD8-xDF */ - 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* xE0-xE7 */ - 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xda, 0xdb, /* xE8-xEF */ - 0xdc, 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xec, 0xed, /* xF0-xF7 */ - 0xee, 0xef, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff /* xF8-xFF */ -}; - -unsigned char *asc2ebc = a2e; diff --git a/src/ebc2asc.c b/src/ebc2asc.c deleted file mode 100644 index dc08a11..0000000 --- a/src/ebc2asc.c +++ /dev/null @@ -1,37 +0,0 @@ -static unsigned char e2a[256] = { - 0x00, 0x01, 0x02, 0x03, 0x9c, 0x09, 0x86, 0x7f, /* x00-x07 */ - 0x97, 0x8d, 0x8e, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* x08-x0F */ - 0x10, 0x11, 0x12, 0x13, 0x9d, 0x85, 0x08, 0x87, /* x10-x17 */ - 0x18, 0x19, 0x92, 0x8f, 0x1c, 0x1d, 0x1e, 0x1f, /* x18-x1F */ - 0x80, 0x81, 0x82, 0x83, 0x84, 0x0a, 0x17, 0x1b, /* x20-x27 */ - 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x05, 0x06, 0x07, /* x28-x2F */ - 0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04, /* x30-x37 */ - 0x98, 0x99, 0x9a, 0x9b, 0x14, 0x15, 0x9e, 0x1a, /* x38-x3F */ - 0x20, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, /* x40-x47 */ - 0xa7, 0xa8, 0x5b, 0x2e, 0x3c, 0x28, 0x2b, 0x5d, /* x48-x4F */ - 0x26, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* x50-x57 */ - 0xb0, 0xb1, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x5e, /* x58-x5F */ - 0x2d, 0x2f, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* x60-x67 */ - 0xb8, 0xb9, 0x7c, 0x2c, 0x25, 0x5f, 0x3e, 0x3f, /* x68-x6F */ - 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, /* x70-x77 */ - 0xc2, 0x60, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22, /* x78-x7F */ - 0xc3, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* x80-x87 */ - 0x68, 0x69, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, /* x88-x8F */ - 0xca, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, /* x90-x97 */ - 0x71, 0x72, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, /* x98-x9F */ - 0xd1, 0x7e, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, /* xA0-xA7 */ - /* 0x79, 0x7a, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* xA8-xAF */ - 0x79, 0x7a, 0xd2, 0xd3, 0xd4, 0x5b, 0xd6, 0xd7, /* xA8-xAF */ - 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* xB0-xB7 */ - /* 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* xB8-xBF */ - 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0x5d, 0xe6, 0xe7, /* xB8-xBF */ - 0x7b, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* xC0-xC7 */ - 0x48, 0x49, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, /* xC8-xCF */ - 0x7d, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, /* xD0-xD7 */ - 0x51, 0x52, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, /* xD8-xDF */ - 0x5c, 0x9f, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, /* xE0-xE7 */ - 0x59, 0x5a, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, /* xE8-xEF */ - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* xF0-xF7 */ - 0x38, 0x39, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff /* xF8-xFF */ -}; -unsigned char *ebc2asc = e2a; diff --git a/src/httpatoe.c b/src/httpatoe.c deleted file mode 100644 index 100968b..0000000 --- a/src/httpatoe.c +++ /dev/null @@ -1,20 +0,0 @@ -/* HTTPATOE.C -** Convert ASCII to EBCDIC -*/ -#include "httpd.h" - -extern UCHAR * -httpatoe(UCHAR *buf, int len) -{ - int i; - - if (!len) len = strlen(buf); - if (len<=0) goto quit; - - for(i=0; i < len; i++) { - buf[i] = asc2ebc[buf[i]]; - } - -quit: - return buf; -} \ No newline at end of file diff --git a/src/httpconf.c b/src/httpconf.c index 7972a40..25fdb2f 100644 --- a/src/httpconf.c +++ b/src/httpconf.c @@ -96,6 +96,9 @@ http_config(HTTPD *httpd, const char *member) } } + /* Initialize codepage translation tables (default: CP037) */ + http_xlate_init("CP037"); + /* Process the configuration */ rc = process_configuration(L, httpd); diff --git a/src/httpetoa.c b/src/httpetoa.c deleted file mode 100644 index a0f81c8..0000000 --- a/src/httpetoa.c +++ /dev/null @@ -1,36 +0,0 @@ -/* HTTPETOA.C -** Convert EBCDIC to ASCII -*/ -#include "httpd.h" - -extern UCHAR * -httpetoa(UCHAR *buf, int len) -{ - int i; - - if (!len) len = strlen(buf); - if (len<=0) goto quit; - - for(i=0; i < len; i++) { - /* we translate special control characters ourselves */ - switch(buf[i]) { - case '\r': /* EBCDIC CR */ - buf[i]=0x0D; /* ASCII CR */ - break; - case '\n': /* EBCDIC NL */ - buf[i]=0x0A; /* ASCII LF */ - break; - case 0xAD: /* EBCDIC Left Square Bracket */ - buf[i]=0x5B; /* ASCII Left Square Bracket */ - break; - case 0xBD: /* EBCDIC Right Square Bracket */ - buf[i]=0x5D; /* ASCII Right Square Bracket */ - break; - default: - buf[i] = ebc2asc[buf[i]]; - } - } - -quit: - return buf; -} diff --git a/src/httpx.c b/src/httpx.c index 3ea5bcd..e002fdd 100644 --- a/src/httpx.c +++ b/src/httpx.c @@ -73,6 +73,10 @@ static HTTPX vect = { http_add_cgi, /* 104 http_add_cgi() */ http_process_cgi, /* 108 http_process_cgi() */ mqtc_pub, /* 10C mqtc_pub() */ + http_xlate, /* 110 http_xlate() */ + &http_cp037, /* 114 CP037 codepage pair */ + &http_cp1047, /* 118 IBM-1047 codepage pair */ + &http_legacy, /* 11C legacy hybrid codepage pair */ }; HTTPX *httpx = &vect; diff --git a/src/httpxlat.c b/src/httpxlat.c new file mode 100644 index 0000000..ab8304d --- /dev/null +++ b/src/httpxlat.c @@ -0,0 +1,465 @@ +/* HTTPXLAT.C +** ASCII/EBCDIC translation tables and codepage management. +** +** Three codepage options: +** CP037 — IBM CECP for US/Canada (symmetric NL/LF, default) +** IBM1047 — IBM Open Systems Latin-1 (used by z/OS USS, Zowe) +** LEGACY — HTTPD 3.3.x hybrid tables (backward compatibility) +** +** The server default is selected via Parmlib CODEPAGE parameter. +** CGI modules can choose a specific codepage per call via the +** HTTPX vector table. +*/ +#define HTTP_PRIVATE +#include "httpd.h" + +/* ------------------------------------------------------------------ */ +/* Codepage pair struct */ +/* ------------------------------------------------------------------ */ + +/* defined in httpxlat.h — kept here for reference: +** typedef struct httpcp { +** const unsigned char *atoe; +** const unsigned char *etoa; +** } HTTPCP; +*/ + +/* ------------------------------------------------------------------ */ +/* IBM Code Page 037 (CECP US/Canada) */ +/* */ +/* Symmetric NL/LF mapping: */ +/* ASCII LF (0x0A) <-> EBCDIC LF (0x25) — roundtrip clean */ +/* ASCII NEL (0x85) <-> EBCDIC NEL (0x15) — roundtrip clean */ +/* ------------------------------------------------------------------ */ + +static const unsigned char cp037_atoe[256] = { + /* 0x00-0x07 */ 0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F, + /* 0x08-0x0F */ 0x16, 0x05, 0x25, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + /* 0x10-0x17 */ 0x10, 0x11, 0x12, 0x13, 0x3C, 0x3D, 0x32, 0x26, + /* 0x18-0x1F */ 0x18, 0x19, 0x3F, 0x27, 0x1C, 0x1D, 0x1E, 0x1F, + /* 0x20-0x27 sp ! " # $ % & ' */ + 0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D, + /* 0x28-0x2F ( ) * + , - . / */ + 0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61, + /* 0x30-0x37 0 1 2 3 4 5 6 7 */ + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + /* 0x38-0x3F 8 9 : ; < = > ? */ + 0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F, + /* 0x40-0x47 @ A B C D E F G */ + 0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + /* 0x48-0x4F H I J K L M N O */ + 0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, + /* 0x50-0x57 P Q R S T U V W */ + 0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, + /* 0x58-0x5F X Y Z [ \ ] ^ _ */ + 0xE7, 0xE8, 0xE9, 0xAD, 0xE0, 0xBD, 0xB0, 0x6D, + /* 0x60-0x67 ` a b c d e f g */ + 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + /* 0x68-0x6F h i j k l m n o */ + 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + /* 0x70-0x77 p q r s t u v w */ + 0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, + /* 0x78-0x7F x y z { | } ~ DEL */ + 0xA7, 0xA8, 0xA9, 0xC0, 0x4F, 0xD0, 0xA1, 0x07, + /* 0x80-0x87 */ 0x20, 0x21, 0x22, 0x23, 0x24, 0x15, 0x06, 0x17, + /* 0x88-0x8F */ 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x09, 0x0A, 0x1B, + /* 0x90-0x97 */ 0x30, 0x31, 0x1A, 0x33, 0x34, 0x35, 0x36, 0x08, + /* 0x98-0x9F */ 0x38, 0x39, 0x3A, 0x3B, 0x04, 0x14, 0x3E, 0xFF, + /* 0xA0-0xA7 */ 0x41, 0xAA, 0x4A, 0xB1, 0x9F, 0xB2, 0x6A, 0xB5, + /* 0xA8-0xAF */ 0xBB, 0xB4, 0x9A, 0x8A, 0x5F, 0xCA, 0xAF, 0xBC, + /* 0xB0-0xB7 */ 0x90, 0x8F, 0xEA, 0xFA, 0xBE, 0xA0, 0xB6, 0xB3, + /* 0xB8-0xBF */ 0x9D, 0xDA, 0x9B, 0x8B, 0xB7, 0xB8, 0xB9, 0xAB, + /* 0xC0-0xC7 */ 0x64, 0x65, 0x62, 0x66, 0x63, 0x67, 0x9E, 0x68, + /* 0xC8-0xCF */ 0x74, 0x71, 0x72, 0x73, 0x78, 0x75, 0x76, 0x77, + /* 0xD0-0xD7 */ 0xAC, 0x69, 0xED, 0xEE, 0xEB, 0xEF, 0xEC, 0xBF, + /* 0xD8-0xDF */ 0x80, 0xFD, 0xFE, 0xFB, 0xFC, 0xBA, 0xAE, 0x59, + /* 0xE0-0xE7 */ 0x44, 0x45, 0x42, 0x46, 0x43, 0x47, 0x9C, 0x48, + /* 0xE8-0xEF */ 0x54, 0x51, 0x52, 0x53, 0x58, 0x55, 0x56, 0x57, + /* 0xF0-0xF7 */ 0x8C, 0x49, 0xCD, 0xCE, 0xCB, 0xCF, 0xCC, 0xE1, + /* 0xF8-0xFF */ 0x70, 0xDD, 0xDE, 0xDB, 0xDC, 0x8D, 0x8E, 0xDF +}; + +static const unsigned char cp037_etoa[256] = { + /* 0x00-0x07 */ 0x00, 0x01, 0x02, 0x03, 0x9C, 0x09, 0x86, 0x7F, + /* 0x08-0x0F */ 0x97, 0x8D, 0x8E, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + /* 0x10-0x17 */ 0x10, 0x11, 0x12, 0x13, 0x9D, 0x85, 0x08, 0x87, + /* 0x18-0x1F */ 0x18, 0x19, 0x92, 0x8F, 0x1C, 0x1D, 0x1E, 0x1F, + /* 0x20-0x27 */ 0x80, 0x81, 0x82, 0x83, 0x84, 0x0A, 0x17, 0x1B, + /* 0x28-0x2F */ 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x05, 0x06, 0x07, + /* 0x30-0x37 */ 0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04, + /* 0x38-0x3F */ 0x98, 0x99, 0x9A, 0x9B, 0x14, 0x15, 0x9E, 0x1A, + /* 0x40-0x47 sp */ + 0x20, 0xA0, 0xE2, 0xE4, 0xE0, 0xE1, 0xE3, 0xE5, + /* 0x48-0x4F cent . < ( + | */ + 0xE7, 0xF1, 0xA2, 0x2E, 0x3C, 0x28, 0x2B, 0x7C, + /* 0x50-0x57 & */ + 0x26, 0xE9, 0xEA, 0xEB, 0xE8, 0xED, 0xEE, 0xEF, + /* 0x58-0x5F ! $ * ) ; not */ + 0xEC, 0xDF, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0xAC, + /* 0x60-0x67 - / */ + 0x2D, 0x2F, 0xC2, 0xC4, 0xC0, 0xC1, 0xC3, 0xC5, + /* 0x68-0x6F brkvbar , % _ > ? */ + 0xC7, 0xD1, 0xA6, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, + /* 0x70-0x77 */ + 0xF8, 0xC9, 0xCA, 0xCB, 0xC8, 0xCD, 0xCE, 0xCF, + /* 0x78-0x7F ` : # @ ' = " */ + 0xCC, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, + /* 0x80-0x87 a b c d e f g */ + 0xD8, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + /* 0x88-0x8F h i */ + 0x68, 0x69, 0xAB, 0xBB, 0xF0, 0xFD, 0xFE, 0xB1, + /* 0x90-0x97 deg j k l m n o p */ + 0xB0, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, + /* 0x98-0x9F q r */ + 0x71, 0x72, 0xAA, 0xBA, 0xE6, 0xB8, 0xC6, 0xA4, + /* 0xA0-0xAF mu ~ s t u v w x */ + 0xB5, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + /* 0xA8-0xAF y z [ */ + 0x79, 0x7A, 0xA1, 0xBF, 0xD0, 0x5B, 0xDE, 0xAE, + /* 0xB0-0xB7 ^ */ + 0x5E, 0xA3, 0xA5, 0xB7, 0xA9, 0xA7, 0xB6, 0xBC, + /* 0xB8-0xBF ] */ + 0xBD, 0xBE, 0xDD, 0xA8, 0xAF, 0x5D, 0xB4, 0xD7, + /* 0xC0-0xC7 { A B C D E F G */ + 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + /* 0xC8-0xCF H I */ + 0x48, 0x49, 0xAD, 0xF4, 0xF6, 0xF2, 0xF3, 0xF5, + /* 0xD0-0xD7 } J K L M N O P */ + 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, + /* 0xD8-0xDF Q R */ + 0x51, 0x52, 0xB9, 0xFB, 0xFC, 0xF9, 0xFA, 0xFF, + /* 0xE0-0xE7 \ div S T U V W X */ + 0x5C, 0xF7, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + /* 0xE8-0xEF Y Z */ + 0x59, 0x5A, 0xB2, 0xD4, 0xD6, 0xD2, 0xD3, 0xD5, + /* 0xF0-0xF7 0 1 2 3 4 5 6 7 */ + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + /* 0xF8-0xFF 8 9 */ + 0x38, 0x39, 0xB3, 0xDB, 0xDC, 0xD9, 0xDA, 0x9F +}; + +/* ------------------------------------------------------------------ */ +/* IBM Code Page 1047 (Latin-1 / Open Systems) */ +/* */ +/* Asymmetric NL/LF mapping (the well-known 1047 problem): */ +/* ASCII LF (0x0A) -> EBCDIC LF (0x25) */ +/* EBCDIC NEL(0x15) -> ASCII LF (0x0A) <- NB: NOT symmetric */ +/* EBCDIC LF (0x25) -> ASCII NEL (0x85) <- NB: NOT symmetric */ +/* */ +/* Tables from mvsMF (verified, in production). */ +/* ------------------------------------------------------------------ */ + +static const unsigned char ibm1047_atoe[256] = { + /* 0x00-0x07 */ 0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F, + /* 0x08-0x0F */ 0x16, 0x05, 0x25, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + /* 0x10-0x17 */ 0x10, 0x11, 0x12, 0x13, 0x3C, 0x3D, 0x32, 0x26, + /* 0x18-0x1F */ 0x18, 0x19, 0x3F, 0x27, 0x1C, 0x1D, 0x1E, 0x1F, + /* 0x20-0x27 sp ! " # $ % & ' */ + 0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D, + /* 0x28-0x2F ( ) * + , - . / */ + 0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61, + /* 0x30-0x37 0 1 2 3 4 5 6 7 */ + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + /* 0x38-0x3F 8 9 : ; < = > ? */ + 0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F, + /* 0x40-0x47 @ A B C D E F G */ + 0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + /* 0x48-0x4F H I J K L M N O */ + 0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, + /* 0x50-0x57 P Q R S T U V W */ + 0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, + /* 0x58-0x5F X Y Z [ \ ] ^ _ */ + 0xE7, 0xE8, 0xE9, 0xAD, 0xE0, 0xBD, 0x5F, 0x6D, + /* 0x60-0x67 ` a b c d e f g */ + 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + /* 0x68-0x6F h i j k l m n o */ + 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + /* 0x70-0x77 p q r s t u v w */ + 0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, + /* 0x78-0x7F x y z { | } ~ DEL */ + 0xA7, 0xA8, 0xA9, 0xC0, 0x4F, 0xD0, 0xA1, 0x07, + /* 0x80-0x87 */ 0x20, 0x21, 0x22, 0x23, 0x24, 0x15, 0x06, 0x17, + /* 0x88-0x8F */ 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x09, 0x0A, 0x1B, + /* 0x90-0x97 */ 0x30, 0x31, 0x1A, 0x33, 0x34, 0x35, 0x36, 0x08, + /* 0x98-0x9F */ 0x38, 0x39, 0x3A, 0x3B, 0x04, 0x14, 0x3E, 0xFF, + /* 0xA0-0xA7 */ 0x41, 0xAA, 0x4A, 0xB1, 0x9F, 0xB2, 0x6A, 0xB5, + /* 0xA8-0xAF */ 0xBB, 0xB4, 0x9A, 0x8A, 0xB0, 0xCA, 0xAF, 0xBC, + /* 0xB0-0xB7 */ 0x90, 0x8F, 0xEA, 0xFA, 0xBE, 0xA0, 0xB6, 0xB3, + /* 0xB8-0xBF */ 0x9D, 0xDA, 0x9B, 0x8B, 0xB7, 0xB8, 0xB9, 0xAB, + /* 0xC0-0xC7 */ 0x64, 0x65, 0x62, 0x66, 0x63, 0x67, 0x9E, 0x68, + /* 0xC8-0xCF */ 0x74, 0x71, 0x72, 0x73, 0x78, 0x75, 0x76, 0x77, + /* 0xD0-0xD7 */ 0xAC, 0x69, 0xED, 0xEE, 0xEB, 0xEF, 0xEC, 0xBF, + /* 0xD8-0xDF */ 0x80, 0xFD, 0xFE, 0xFB, 0xFC, 0xBA, 0xAE, 0x59, + /* 0xE0-0xE7 */ 0x44, 0x45, 0x42, 0x46, 0x43, 0x47, 0x9C, 0x48, + /* 0xE8-0xEF */ 0x54, 0x51, 0x52, 0x53, 0x58, 0x55, 0x56, 0x57, + /* 0xF0-0xF7 */ 0x8C, 0x49, 0xCD, 0xCE, 0xCB, 0xCF, 0xCC, 0xE1, + /* 0xF8-0xFF */ 0x70, 0xDD, 0xDE, 0xDB, 0xDC, 0x8D, 0x8E, 0xDF +}; + +static const unsigned char ibm1047_etoa[256] = { + /* 0x00-0x07 */ 0x00, 0x01, 0x02, 0x03, 0x9C, 0x09, 0x86, 0x7F, + /* 0x08-0x0F */ 0x97, 0x8D, 0x8E, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + /* 0x10-0x17 */ 0x10, 0x11, 0x12, 0x13, 0x9D, 0x0A, 0x08, 0x87, + /* 0x18-0x1F */ 0x18, 0x19, 0x92, 0x8F, 0x1C, 0x1D, 0x1E, 0x1F, + /* 0x20-0x27 */ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x17, 0x1B, + /* 0x28-0x2F */ 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x05, 0x06, 0x07, + /* 0x30-0x37 */ 0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04, + /* 0x38-0x3F */ 0x98, 0x99, 0x9A, 0x9B, 0x14, 0x15, 0x9E, 0x1A, + /* 0x40-0x47 sp */ + 0x20, 0xA0, 0xE2, 0xE4, 0xE0, 0xE1, 0xE3, 0xE5, + /* 0x48-0x4F cent . < ( + | */ + 0xE7, 0xF1, 0xA2, 0x2E, 0x3C, 0x28, 0x2B, 0x7C, + /* 0x50-0x57 & */ + 0x26, 0xE9, 0xEA, 0xEB, 0xE8, 0xED, 0xEE, 0xEF, + /* 0x58-0x5F ! $ * ) ; ^ */ + 0xEC, 0xDF, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0x5E, + /* 0x60-0x67 - / */ + 0x2D, 0x2F, 0xC2, 0xC4, 0xC0, 0xC1, 0xC3, 0xC5, + /* 0x68-0x6F brkvbar , % _ > ? */ + 0xC7, 0xD1, 0xA6, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, + /* 0x70-0x77 */ + 0xF8, 0xC9, 0xCA, 0xCB, 0xC8, 0xCD, 0xCE, 0xCF, + /* 0x78-0x7F ` : # @ ' = " */ + 0xCC, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, + /* 0x80-0x87 a b c d e f g */ + 0xD8, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + /* 0x88-0x8F h i */ + 0x68, 0x69, 0xAB, 0xBB, 0xF0, 0xFD, 0xFE, 0xB1, + /* 0x90-0x97 deg j k l m n o p */ + 0xB0, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, + /* 0x98-0x9F q r */ + 0x71, 0x72, 0xAA, 0xBA, 0xE6, 0xB8, 0xC6, 0xA4, + /* 0xA0-0xAF mu ~ s t u v w x */ + 0xB5, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + /* 0xA8-0xAF y z [ */ + 0x79, 0x7A, 0xA1, 0xBF, 0xD0, 0x5B, 0xDE, 0xAE, + /* 0xB0-0xB7 not */ + 0xAC, 0xA3, 0xA5, 0xB7, 0xA9, 0xA7, 0xB6, 0xBC, + /* 0xB8-0xBF ] */ + 0xBD, 0xBE, 0xDD, 0xA8, 0xAF, 0x5D, 0xB4, 0xD7, + /* 0xC0-0xC7 { A B C D E F G */ + 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + /* 0xC8-0xCF H I */ + 0x48, 0x49, 0xAD, 0xF4, 0xF6, 0xF2, 0xF3, 0xF5, + /* 0xD0-0xD7 } J K L M N O P */ + 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, + /* 0xD8-0xDF Q R */ + 0x51, 0x52, 0xB9, 0xFB, 0xFC, 0xF9, 0xFA, 0xFF, + /* 0xE0-0xE7 \ sup2 S T U V W X */ + 0x5C, 0xB2, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + /* 0xE8-0xEF Y Z */ + 0x59, 0x5A, 0xB2, 0xD4, 0xD6, 0xD2, 0xD3, 0xD5, + /* 0xF0-0xF7 0 1 2 3 4 5 6 7 */ + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + /* 0xF8-0xFF 8 9 */ + 0x38, 0x39, 0xB3, 0xDB, 0xDC, 0xD9, 0xDA, 0x9F +}; + +/* ------------------------------------------------------------------ */ +/* LEGACY — HTTPD 3.3.x hybrid tables (backward compatibility) */ +/* */ +/* This is the exact behavior of httpd 3.3.x, including the switch */ +/* overrides from httpetoa.c baked into the etoa table. */ +/* */ +/* Known issues: */ +/* - Brackets [/] patched to IBM-1047 positions (0xAD/0xBD) */ +/* - Pipe | left at non-standard position (0x6A atoe, wrong etoa) */ +/* - NEL(0x15)->LF(0x0A) from httpetoa.c switch baked in */ +/* ------------------------------------------------------------------ */ + +static const unsigned char legacy_atoe[256] = { + /* 0x00-0x07 */ 0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F, + /* 0x08-0x0F */ 0x16, 0x05, 0x25, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + /* 0x10-0x17 */ 0x10, 0x11, 0x12, 0x13, 0x3C, 0x3D, 0x32, 0x26, + /* 0x18-0x1F */ 0x18, 0x19, 0x3F, 0x27, 0x1C, 0x1D, 0x1E, 0x1F, + /* 0x20-0x27 */ 0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D, + /* 0x28-0x2F */ 0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61, + /* 0x30-0x37 */ 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + /* 0x38-0x3F */ 0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F, + /* 0x40-0x47 */ 0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + /* 0x48-0x4F */ 0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, + /* 0x50-0x57 */ 0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, + /* 0x58-0x5F [ = 0xAD, ] = 0xBD (1047), | = 0x6A (broken) */ + 0xE7, 0xE8, 0xE9, 0xAD, 0xE0, 0xBD, 0x5F, 0x6D, + /* 0x60-0x67 */ 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + /* 0x68-0x6F */ 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + /* 0x70-0x77 */ 0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, + /* 0x78-0x7F | at 0x6A instead of 0x4F (KNOWN BUG) */ + 0xA7, 0xA8, 0xA9, 0xC0, 0x6A, 0xD0, 0xA1, 0x07, + /* 0x80-0x87 */ 0x20, 0x21, 0x22, 0x23, 0x24, 0x15, 0x06, 0x17, + /* 0x88-0x8F */ 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x09, 0x0A, 0x1B, + /* 0x90-0x97 */ 0x30, 0x31, 0x1A, 0x33, 0x34, 0x35, 0x36, 0x08, + /* 0x98-0x9F */ 0x38, 0x39, 0x3A, 0x3B, 0x04, 0x14, 0x3E, 0xE1, + /* 0xA0-0xA7 */ 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, + /* 0xA8-0xAF */ 0x49, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + /* 0xB0-0xB7 */ 0x58, 0x59, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + /* 0xB8-0xBF */ 0x68, 0x69, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, + /* 0xC0-0xC7 */ 0x76, 0x77, 0x78, 0x80, 0x8A, 0x8B, 0x8C, 0x8D, + /* 0xC8-0xCF */ 0x8E, 0x8F, 0x90, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, + /* 0xD0-0xD7 */ 0x9F, 0xA0, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, + /* 0xD8-0xDF */ 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, + /* 0xE0-0xE7 */ 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, + /* 0xE8-0xEF */ 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xDA, 0xDB, + /* 0xF0-0xF7 */ 0xDC, 0xDD, 0xDE, 0xDF, 0xEA, 0xEB, 0xEC, 0xED, + /* 0xF8-0xFF */ 0xEE, 0xEF, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF +}; + +static const unsigned char legacy_etoa[256] = { + /* 0x00-0x07 */ 0x00, 0x01, 0x02, 0x03, 0x9C, 0x09, 0x86, 0x7F, + /* 0x08-0x0F */ 0x97, 0x8D, 0x8E, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + /* 0x10-0x17 NB: [0x15] = 0x0A (httpetoa.c switch baked in) */ + 0x10, 0x11, 0x12, 0x13, 0x9D, 0x0A, 0x08, 0x87, + /* 0x18-0x1F */ 0x18, 0x19, 0x92, 0x8F, 0x1C, 0x1D, 0x1E, 0x1F, + /* 0x20-0x27 */ 0x80, 0x81, 0x82, 0x83, 0x84, 0x0A, 0x17, 0x1B, + /* 0x28-0x2F */ 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x05, 0x06, 0x07, + /* 0x30-0x37 */ 0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04, + /* 0x38-0x3F */ 0x98, 0x99, 0x9A, 0x9B, 0x14, 0x15, 0x9E, 0x1A, + /* 0x40-0x47 */ 0x20, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, + /* 0x48-0x4F NB: [0x4A] = 0x5B([), [0x4F] = 0x5D(]) — legacy */ + 0xA7, 0xA8, 0x5B, 0x2E, 0x3C, 0x28, 0x2B, 0x5D, + /* 0x50-0x57 */ 0x26, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, + /* 0x58-0x5F */ 0xB0, 0xB1, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0x5E, + /* 0x60-0x67 */ 0x2D, 0x2F, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, + /* 0x68-0x6F */ 0xB8, 0xB9, 0x7C, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, + /* 0x70-0x77 */ 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, 0xC0, 0xC1, + /* 0x78-0x7F */ 0xC2, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, + /* 0x80-0x87 */ 0xC3, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + /* 0x88-0x8F */ 0x68, 0x69, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, + /* 0x90-0x97 */ 0xCA, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, + /* 0x98-0x9F */ 0x71, 0x72, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, + /* 0xA0-0xA7 */ 0xD1, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + /* 0xA8-0xAF NB: [0xAD] = 0x5B([) — 1047 patch */ + 0x79, 0x7A, 0xD2, 0xD3, 0xD4, 0x5B, 0xD6, 0xD7, + /* 0xB0-0xB7 */ 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, + /* 0xB8-0xBF NB: [0xBD] = 0x5D(]) — 1047 patch */ + 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0x5D, 0xE6, 0xE7, + /* 0xC0-0xC7 */ 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + /* 0xC8-0xCF */ 0x48, 0x49, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, + /* 0xD0-0xD7 */ 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, + /* 0xD8-0xDF */ 0x51, 0x52, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, + /* 0xE0-0xE7 */ 0x5C, 0x9F, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + /* 0xE8-0xEF */ 0x59, 0x5A, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, + /* 0xF0-0xF7 */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + /* 0xF8-0xFF */ 0x38, 0x39, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF +}; + +/* ------------------------------------------------------------------ */ +/* Codepage pair structs (exported to CGIs via HTTPX) */ +/* ------------------------------------------------------------------ */ + +HTTPCP http_cp037 = { cp037_atoe, cp037_etoa }; +HTTPCP http_cp1047 = { ibm1047_atoe, ibm1047_etoa }; +HTTPCP http_legacy = { legacy_atoe, legacy_etoa }; + +/* ------------------------------------------------------------------ */ +/* Server-default table pointers */ +/* */ +/* These are the "active" tables. http_etoa() and http_atoe() use */ +/* these. Set once at startup by http_xlate_init(). */ +/* */ +/* The legacy global symbols "ebc2asc" and "asc2ebc" (used by old */ +/* code throughout httpd) also point here for backward compatibility. */ +/* ------------------------------------------------------------------ */ + +static const unsigned char *default_atoe = cp037_atoe; +static const unsigned char *default_etoa = cp037_etoa; + +/* backward-compatible global pointers (referenced by old httpd code) */ +unsigned char *asc2ebc = (unsigned char *)cp037_atoe; +unsigned char *ebc2asc = (unsigned char *)cp037_etoa; + +/* ------------------------------------------------------------------ */ +/* http_xlate_init() — select server-default codepage */ +/* */ +/* Called once during startup (from httpconf.c). */ +/* codepage: "CP037" | "IBM1047" | "LEGACY" (case insensitive) */ +/* Returns: 0 on success, -1 on unknown codepage */ +/* ------------------------------------------------------------------ */ + +int +http_xlate_init(const char *codepage) +{ + const unsigned char *atoe; + const unsigned char *etoa; + + if (!codepage || http_cmp(codepage, "CP037") == 0) { + atoe = cp037_atoe; + etoa = cp037_etoa; + } + else if (http_cmp(codepage, "IBM1047") == 0) { + atoe = ibm1047_atoe; + etoa = ibm1047_etoa; + } + else if (http_cmp(codepage, "LEGACY") == 0) { + atoe = legacy_atoe; + etoa = legacy_etoa; + } + else { + wtof("HTTPD070E Unknown codepage \"%s\", using CP037", codepage); + atoe = cp037_atoe; + etoa = cp037_etoa; + return -1; + } + + default_atoe = atoe; + default_etoa = etoa; + + /* update legacy global pointers */ + asc2ebc = (unsigned char *)atoe; + ebc2asc = (unsigned char *)etoa; + + wtof("HTTPD071I Codepage: %s", codepage ? codepage : "CP037"); + return 0; +} + +/* ------------------------------------------------------------------ */ +/* http_xlate() — translate buffer using an explicit table */ +/* */ +/* tbl: 256-byte translation table (atoe or etoa). */ +/* Use httpcp->atoe or httpcp->etoa from the HTTPX vector. */ +/* Pass NULL for the server-default etoa table. */ +/* ------------------------------------------------------------------ */ + +__asm__("\n&FUNC SETC 'http_xlate'"); +unsigned char * +http_xlate(unsigned char *buf, int len, const unsigned char *tbl) +{ + int i; + + if (!buf) return buf; + if (!len) len = strlen((char *)buf); + if (len <= 0) return buf; + + if (!tbl) tbl = default_etoa; + + for (i = 0; i < len; i++) { + buf[i] = tbl[buf[i]]; + } + + return buf; +} + +/* ------------------------------------------------------------------ */ +/* http_etoa() — EBCDIC to ASCII using server-default table */ +/* */ +/* Exposed via HTTPX at offset 0x74 (unchanged signature). */ +/* No switch hacks — the table handles everything. */ +/* ------------------------------------------------------------------ */ + +unsigned char * +httpetoa(unsigned char *buf, int len) +{ + return http_xlate(buf, len, default_etoa); +} + +/* ------------------------------------------------------------------ */ +/* http_atoe() — ASCII to EBCDIC using server-default table */ +/* */ +/* Exposed via HTTPX at offset 0x78 (unchanged signature). */ +/* ------------------------------------------------------------------ */ + +unsigned char * +httpatoe(unsigned char *buf, int len) +{ + return http_xlate(buf, len, default_atoe); +} From 0726659489a5efd184c78321925901ab5ddce67a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Gro=C3=9Fmann?= Date: Thu, 19 Mar 2026 20:20:28 +0100 Subject: [PATCH 2/4] fix: CP037 etoa table must map NEL (0x15) to LF (0x0A) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The C compiler generates 0x15 (EBCDIC NEL) for '\n'. HTTP requires CR+LF (0x0D 0x0A) as line terminator. Pure CP037 maps 0x15 to 0x85 (Latin-1 NEL) which browsers don't recognize as a line feed, causing empty responses. Patch cp037_etoa[0x15] from 0x85 to 0x0A — same override that the old httpetoa.c switch-case provided. --- doc/httpxlat-integration.md | 6 ++++-- src/httpxlat.c | 14 +++++++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/doc/httpxlat-integration.md b/doc/httpxlat-integration.md index 1653d91..3f119c7 100644 --- a/doc/httpxlat-integration.md +++ b/doc/httpxlat-integration.md @@ -149,7 +149,8 @@ mvsMF can then delete `src/xlate.c` and `include/xlate.h`. ## Key Design Decisions -1. **CP037 as default** — Symmetric NL/LF roundtrip. No data corruption. +1. **CP037 as default** — HTTP-safe variant: NEL (0x15) maps to LF (0x0A) in + etoa direction because the C compiler generates 0x15 for `'\n'`. 2. **Legacy tables preserved exactly** — Including the httpetoa.c switch behavior baked into legacy_etoa[0x15]=0x0A. No existing behavior lost. 3. **HTTPX backward compatible** — http_etoa/http_atoe stay at offsets @@ -162,9 +163,10 @@ mvsMF can then delete `src/xlate.c` and `include/xlate.h`. ## Roundtrip Verification (Critical Characters) -### CP037 (default) +### CP037 (default, HTTP-safe variant) ``` ASCII LF (0x0A) -> atoe -> 0x25 -> etoa -> 0x0A OK +EBCDIC NEL(0x15) -> etoa -> 0x0A HTTP override (pure CP037 = 0x85) ASCII | (0x7C) -> atoe -> 0x4F -> etoa -> 0x7C OK ASCII [ (0x5B) -> atoe -> 0xAD -> etoa -> 0x5B OK ASCII ] (0x5D) -> atoe -> 0xBD -> etoa -> 0x5D OK diff --git a/src/httpxlat.c b/src/httpxlat.c index ab8304d..9cf1f20 100644 --- a/src/httpxlat.c +++ b/src/httpxlat.c @@ -25,11 +25,14 @@ */ /* ------------------------------------------------------------------ */ -/* IBM Code Page 037 (CECP US/Canada) */ +/* IBM Code Page 037 (CECP US/Canada) — HTTP-safe variant */ /* */ -/* Symmetric NL/LF mapping: */ -/* ASCII LF (0x0A) <-> EBCDIC LF (0x25) — roundtrip clean */ -/* ASCII NEL (0x85) <-> EBCDIC NEL (0x15) — roundtrip clean */ +/* atoe: ASCII LF (0x0A) -> EBCDIC LF (0x25) — standard CP037 */ +/* etoa: EBCDIC NEL (0x15) -> ASCII LF (0x0A) — HTTP override */ +/* */ +/* Pure CP037 maps NEL (0x15) -> 0x85 (Latin-1 NEL), but the C */ +/* compiler generates 0x15 for '\n'. HTTP requires CR+LF (0x0D 0x0A) */ +/* so the etoa table must map 0x15 -> 0x0A for HTTP to work. */ /* ------------------------------------------------------------------ */ static const unsigned char cp037_atoe[256] = { @@ -82,7 +85,8 @@ static const unsigned char cp037_atoe[256] = { static const unsigned char cp037_etoa[256] = { /* 0x00-0x07 */ 0x00, 0x01, 0x02, 0x03, 0x9C, 0x09, 0x86, 0x7F, /* 0x08-0x0F */ 0x97, 0x8D, 0x8E, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - /* 0x10-0x17 */ 0x10, 0x11, 0x12, 0x13, 0x9D, 0x85, 0x08, 0x87, + /* 0x10-0x17 NB: [0x15] = 0x0A (NEL->LF for HTTP) */ + 0x10, 0x11, 0x12, 0x13, 0x9D, 0x0A, 0x08, 0x87, /* 0x18-0x1F */ 0x18, 0x19, 0x92, 0x8F, 0x1C, 0x1D, 0x1E, 0x1F, /* 0x20-0x27 */ 0x80, 0x81, 0x82, 0x83, 0x84, 0x0A, 0x17, 0x1B, /* 0x28-0x2F */ 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x05, 0x06, 0x07, From 186ec741cf258a3b5d25da28388d306c69c50c75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Gro=C3=9Fmann?= Date: Thu, 19 Mar 2026 20:30:29 +0100 Subject: [PATCH 3/4] fix: use IBM1047 as default codepage, revert CP037 table patch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revert the CP037 etoa NEL→LF patch — keep CP037 tables pure. Switch default codepage to IBM1047 which naturally maps NEL (0x15) to LF (0x0A), required for HTTP line terminators. CP037 maps NEL to 0x85 (Latin-1 NEL) which breaks HTTP. Rather than patching the table, use the correct codepage for HTTP workloads. --- src/httpconf.c | 4 ++-- src/httpxlat.c | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/httpconf.c b/src/httpconf.c index 25fdb2f..b33e826 100644 --- a/src/httpconf.c +++ b/src/httpconf.c @@ -96,8 +96,8 @@ http_config(HTTPD *httpd, const char *member) } } - /* Initialize codepage translation tables (default: CP037) */ - http_xlate_init("CP037"); + /* Initialize codepage translation tables */ + http_xlate_init("IBM1047"); /* Process the configuration */ rc = process_configuration(L, httpd); diff --git a/src/httpxlat.c b/src/httpxlat.c index 9cf1f20..9360968 100644 --- a/src/httpxlat.c +++ b/src/httpxlat.c @@ -25,14 +25,15 @@ */ /* ------------------------------------------------------------------ */ -/* IBM Code Page 037 (CECP US/Canada) — HTTP-safe variant */ +/* IBM Code Page 037 (CECP US/Canada) */ /* */ -/* atoe: ASCII LF (0x0A) -> EBCDIC LF (0x25) — standard CP037 */ -/* etoa: EBCDIC NEL (0x15) -> ASCII LF (0x0A) — HTTP override */ +/* Symmetric NL/LF mapping: */ +/* ASCII LF (0x0A) <-> EBCDIC LF (0x25) — roundtrip clean */ +/* ASCII NEL (0x85) <-> EBCDIC NEL (0x15) — roundtrip clean */ /* */ -/* Pure CP037 maps NEL (0x15) -> 0x85 (Latin-1 NEL), but the C */ -/* compiler generates 0x15 for '\n'. HTTP requires CR+LF (0x0D 0x0A) */ -/* so the etoa table must map 0x15 -> 0x0A for HTTP to work. */ +/* NOTE: Pure CP037 maps NEL (0x15) -> 0x85, NOT 0x0A. Since the */ +/* C compiler generates 0x15 for '\n', this codepage is unsuitable */ +/* as HTTP server default — use IBM1047 instead. */ /* ------------------------------------------------------------------ */ static const unsigned char cp037_atoe[256] = { @@ -85,8 +86,7 @@ static const unsigned char cp037_atoe[256] = { static const unsigned char cp037_etoa[256] = { /* 0x00-0x07 */ 0x00, 0x01, 0x02, 0x03, 0x9C, 0x09, 0x86, 0x7F, /* 0x08-0x0F */ 0x97, 0x8D, 0x8E, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - /* 0x10-0x17 NB: [0x15] = 0x0A (NEL->LF for HTTP) */ - 0x10, 0x11, 0x12, 0x13, 0x9D, 0x0A, 0x08, 0x87, + /* 0x10-0x17 */ 0x10, 0x11, 0x12, 0x13, 0x9D, 0x85, 0x08, 0x87, /* 0x18-0x1F */ 0x18, 0x19, 0x92, 0x8F, 0x1C, 0x1D, 0x1E, 0x1F, /* 0x20-0x27 */ 0x80, 0x81, 0x82, 0x83, 0x84, 0x0A, 0x17, 0x1B, /* 0x28-0x2F */ 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x05, 0x06, 0x07, From 22db875ddad73ac185965713a41401053692b070 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Gro=C3=9Fmann?= Date: Thu, 19 Mar 2026 20:32:28 +0100 Subject: [PATCH 4/4] feat: read codepage from Lua config (httpd.codepage) Move http_xlate_init() from hardcoded call to Lua config-driven. Default: httpd.codepage = "IBM1047". User can override in the Lua config file with httpd.codepage = "CP037" or "LEGACY". When Parmlib replaces Lua, this becomes: CODEPAGE IBM1047 --- src/httpconf.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/httpconf.c b/src/httpconf.c index b33e826..1debf18 100644 --- a/src/httpconf.c +++ b/src/httpconf.c @@ -96,9 +96,6 @@ http_config(HTTPD *httpd, const char *member) } } - /* Initialize codepage translation tables */ - http_xlate_init("IBM1047"); - /* Process the configuration */ rc = process_configuration(L, httpd); @@ -879,6 +876,11 @@ process_httpd_settings(lua_State *L, HTTPD *httpd) int i; const char *p; + lua_getfield(L,-1,"codepage"); + p = lua_tostring(L, -1); + http_xlate_init(p); + lua_pop(L,1); + lua_getfield(L,-1,"maxtask"); i = (int) lua_tointeger(L, -1); if (i>0) { @@ -1131,6 +1133,9 @@ setHttpdDefaults(lua_State *L) lua_pushstring(L, "255"); lua_setfield(L,-2,"cgi_context_pointers"); // table.cgi_context_pointers="255" + lua_pushstring(L, "IBM1047"); + lua_setfield(L,-2,"codepage"); // table.codepage="IBM1047" + lua_setglobal(L, "httpd"); // httpd = table // Create new table for httpd.cgi