Skip to content

Commit

Permalink
add example response/requests to doc
Browse files Browse the repository at this point in the history
Signed-off-by: Trae Yelovich <[email protected]>
  • Loading branch information
traeok committed Jan 29, 2025
1 parent 6a65b8c commit 2e52647
Show file tree
Hide file tree
Showing 4 changed files with 213 additions and 14 deletions.
72 changes: 72 additions & 0 deletions doc/server/ioserver_architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,40 @@ The command handlers can expect a stronger request type than what is expected du

Once the command handler has the required data for a response, the handler "marshals" (serializes) the response type as JSON and prints it to stdout. The output is redirected to the SSH client, where it is interpreted as a response and processed according to the corresponding response type.

See the example JSON request below for listing data sets:

```json
{
"command": "listDatasets",
"pattern": "DS.PAT.*"
}
```

This request is deserialized by ioserver and `zowex` is invoked to get the list of data sets matching the given pattern. Then the response is composed, serialized as JSON and returned to the caller, for example:

```json
{
"items": [
{
"dsname": "DS.PAT.AB",
"dsorg": "PO",
"volser": "MIGRAT"
},
{
"dsname": "DS.PAT.ABC",
"dsorg": "PO-E",
"volser": "WRKD01"
},
{
"dsname": "DS.PAT.ABCD",
"dsorg": "PS",
"volser": "WRKD03"
}
],
"returnedRows": 3
}
```

## Handling encoding for resource contents

Modern text editors expect a standardized encoding format such as UTF-8. `ioserver` implements processing for reading/writing data sets, USS files and job spools (read-only) with a given encoding.
Expand All @@ -22,10 +56,48 @@ Modern text editors expect a standardized encoding format such as UTF-8. `ioserv

When an encoding is not provided, we use the system default codepage as the source encoding for resource contents (`IBM-1047`). We convert the contents from the source encoding to UTF-8 so the contents can be rendered and edited within any modern text editor.

```json
{
"command": "readFile",
"path": "/u/users/you/file.txt",
"encoding": "ISO8859-1"
}
```

Response:

```jsonc
{
"encoding": "ISO8859-1",
"path": "/u/users/you/file.txt",
"data": [104, 101, 108, 108, 111] // "hello" in ASCII
}
```

### Write

The contents of write requests are interpreted as UTF-8 data. We convert the UTF-8 bytes to the given encoding so the data can be read by z/OSMF and other mainframe utilities. If no encoding is provided, we convert the contents from `UTF-8` to `IBM-1047`.

```jsonc
{
"command": "writeFile",
"path": "/u/users/you/file.txt",
"encoding": "IBM-939",
"contents": [
165, 131, 191, 165, 132, 192, 165, 131, 171, 165, 131, 161, 165, 131, 175,
33
] // "Hello!" in Japanese, encoded as UTF-8
}
```

Response:

```json
{
"success": true
}
```

### Data transmission

To send and receive converted contents between `ioserver` and `zowex`, we pipe the bytes of a write request to `zowex` and interpret the stdout from `zowex` during a read request. The environment variable `_BPXK_AUTOCVT` is temporarily disabled within the command handlers during write operations to prevent additional conversions of piped data.
43 changes: 29 additions & 14 deletions native/c/zds.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,33 +157,48 @@ int zds_write_to_dd(ZDS *zds, string ddname, string &data)
return 0;
}

int zds_write_to_dsn(ZDS *zds, std::string dsn, std::string &data)
int zds_write_to_dsn(ZDS *zds, string dsn, string &data)
{
const auto hasEncoding = zds->encoding_opts.data_type == eDataTypeText && strlen(zds->encoding_opts.codepage) > 0;
const auto has_encoding = zds->encoding_opts.data_type == eDataTypeText && strlen(zds->encoding_opts.codepage) > 0;
dsn = "//'" + dsn + "'";
ofstream out(dsn.c_str(), zds->encoding_opts.data_type == eDataTypeBinary ? ios::binary : ios::out);
const auto output_flags = zds->encoding_opts.data_type == eDataTypeBinary ? ios::binary : ios::out;
ofstream out(dsn.c_str(), output_flags);

if (!out.good())
{
zds->diag.e_msg_len = sprintf(zds->diag.e_msg, "Could not open '%s'", dsn.c_str());
return RTNCD_FAILURE;
}

if (hasEncoding)
if (has_encoding)
{
std::string temp = data;
try
string temp = data;
string target_codepage = string(zds->encoding_opts.codepage);
if (target_codepage == "UCS-2")
{
const auto bytes_with_encoding = zut_encode_alloc(temp, "UTF-8", string(zds->encoding_opts.codepage), zds->diag);
temp = bytes_with_encoding;
out.close();
wofstream wout(dsn.c_str(), output_flags);
if (wout.good())
{
wout << zut_utf8_to_ucs2(data);
wout.close();
}
}
catch (std::exception &e)
else
{
// TODO: error handling
}
if (!temp.empty())
{
data = temp;
try
{
const auto bytes_with_encoding = zut_encode_alloc(temp, "UTF-8", target_codepage, zds->diag);
temp = bytes_with_encoding;
if (!temp.empty())
{
data = temp;
}
}
catch (exception &e)
{
// TODO: error handling
}
}
}

Expand Down
109 changes: 109 additions & 0 deletions native/c/zut.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,115 @@ size_t zut_get_utf8_len(const char *str)
return len;
}

void zut_utf8_char_to_ucs2(const char *utf8_token, wchar_t *ucs2_char, uint32_t *utf8_token_length)
{
const unsigned char *utf8_token_unsigned = reinterpret_cast<const unsigned char *>(utf8_token);

*ucs2_char = L'?';
*utf8_token_length = 1;

if (0x80 > utf8_token_unsigned[0])
{
*ucs2_char = static_cast<const wchar_t>(utf8_token_unsigned[0]);
}
else if (0xC0 == (utf8_token_unsigned[0] & 0xE0))
{
if (0x80 != (utf8_token_unsigned[1] & 0xC0))
{
return;
}
*utf8_token_length = 2;
*ucs2_char = static_cast<const wchar_t>(
(utf8_token_unsigned[0] & 0x1F) << 6 | (utf8_token_unsigned[1] & 0x3F));
}
else if (0xE0 == (utf8_token_unsigned[0] & 0xF0))
{
if ((0x80 != (utf8_token_unsigned[1] & 0xC0)) || (0x80 != (utf8_token_unsigned[2] & 0xC0)))
{
return;
}
*utf8_token_length = 3;
*ucs2_char = static_cast<const wchar_t>(
(utf8_token_unsigned[0] & 0x0F) << 12 | (utf8_token_unsigned[1] & 0x3F) << 6 | (utf8_token_unsigned[2] & 0x3F));
}
else if (0xF0 == (utf8_token_unsigned[0] & 0xF8))
{
*utf8_token_length = 4;
}
else if ((0xF8 == utf8_token_unsigned[0] & 0xFC))
{
*utf8_token_length = 5;
}
else if (0xFC == (utf8_token_unsigned[0] & 0xFE))
{
*utf8_token_length = 6;
}
}

void zut_ucs2_char_to_utf8(const wchar_t ucs2_char, char *utf8_token)
{
uint32_t ucs2_char_value = static_cast<uint32_t>(ucs2_char);
unsigned char *utf8_token_unsigned = reinterpret_cast<unsigned char *>(utf8_token);

if (0x80 > ucs2_char_value)
{
utf8_token_unsigned[0] = static_cast<unsigned char>(ucs2_char_value);
utf8_token_unsigned[1] = '\0';
}
else if (0x800 > ucs2_char_value)
{
utf8_token_unsigned[2] = '\0';
utf8_token_unsigned[1] = static_cast<unsigned char>(0x80 | (ucs2_char_value & 0x3F));
ucs2_char_value = (ucs2_char_value >> 6);
utf8_token_unsigned[0] = static_cast<unsigned char>(0xC0 | ucs2_char_value);
}
else
{
utf8_token_unsigned[3] = '\0';
utf8_token_unsigned[2] = static_cast<unsigned char>(0x80 | (ucs2_char_value & 0x3F));
ucs2_char_value = (ucs2_char_value >> 6);
utf8_token_unsigned[1] = static_cast<unsigned char>(0x80 | (ucs2_char_value & 0x3F));
ucs2_char_value = (ucs2_char_value >> 6);
utf8_token_unsigned[0] = static_cast<unsigned char>(0xE0 | ucs2_char_value);
}
}

wstring zut_utf8_to_ucs2(const string &utf8_str)
{
wstring ucs2_result;
wchar_t ucs2_char_buf[] = {0, 0};
const char *cursor = utf8_str.c_str();
const char *const end = utf8_str.c_str() + utf8_str.length();

while (end > cursor)
{
uint32_t utf8_token_length = 1;
zut_utf8_char_to_ucs2(cursor, &ucs2_char_buf[0], &utf8_token_length);
ucs2_result.append(ucs2_char_buf);
cursor += utf8_token_length;
}

return ucs2_result;
}

string zut_ucs2_to_utf8(const wstring &ucs2_str)
{
string result;
char utf8_sequence[] = {0, 0, 0, 0, 0};
const wchar_t *cursor = ucs2_str.c_str();
const wchar_t *const end = ucs2_str.c_str() + ucs2_str.length();

while (end > cursor)
{
const wchar_t ucs2_char = *cursor;
zut_ucs2_char_to_utf8(ucs2_char, utf8_sequence);
result.append(utf8_sequence);
cursor++;
}

return result;
}

std::string zut_encode_alloc(const string &bytes, const string &from_encoding, const string &to_encoding, ZDIAG &diag)
{
iconv_t cd = iconv_open(to_encoding.c_str(), from_encoding.c_str());
Expand Down
3 changes: 3 additions & 0 deletions native/c/zut.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ bool zut_prepare_encoding(ZCLIResult &result, ZEncode *opts);
void zut_print_string_as_bytes(std::string &input);

std::string zut_encode_alloc(const string &bytes, const string &from_encoding, const string &to_encoding, ZDIAG &diag);
void zut_ucs2_char_to_utf8(const wchar_t ucs2_char, char *utf8_token);
std::string zut_ucs2_to_utf8(const std::wstring &ucs2_str);
std::wstring zut_utf8_to_ucs2(const std::string &utf8_str);
std::string zut_format_as_csv(std::vector<std::string> &fields);
std::string &zut_rtrim(std::string &s, const char *t = " ");
std::string &zut_ltrim(std::string &s, const char *t = " ");
Expand Down

0 comments on commit 2e52647

Please sign in to comment.