Extract a substring from an LMBCS string and convert it to an OS-specific string

# LMBCS BNF

Expressed in BNF as follows:

```
[BNF]
<LMBCS>           := ( <LMBCS-char> )*
<LMBCS-char>      := !<group-code> <non-prefix-char> | ( <group-code> <any-char> )+
<non-prefix-char> := <ascii-code> & !<group-code>
<ascii-code>      := 0x00 - 0x7F
<group-code>      := 0x00 - 0x1F & !<control-char>
<control-char>    := <NULL> | <BEL> | <TAB> | <LF> | <CR>
<NULL>            := 0x00
<BELL>            := 0x07
<TAB>             := 0x09
<LF>              := 0x0A
<CR>              := 0x0D
<any-char>        := ( CHAR & !<group-code> )*
CHAR              := multi-bype or single-byte char.
```
# Code

Below is a sample code to extract a substring from an LMBCS string:

### src

```vb
'[Lotus Script]
Declare Const NNOTESDLL = "nnotes.dll"
Declare Function OSTranslate Lib NNOTESDLL ( _
Byval translateMode As Integer, _
Byval inData As String, _
Byval inLength As Long,_
Byval outData As String, _
Byval outLength As Long _
) As Integer

'call notes capi
'@param sIn - LMBCS string
Function OSTranslateLMBCSToNative(sIn as String) as String
    Const OS_TRANSLATE_LMBCS_TO_NATIVE = 1
    Const WORDLEN = 65535

    Dim sOut as String
    sOut = Space$(WORDLEN)
    
    Call OSTranslate(OS_TRANSLATE_LMBCS_TO_NATIVE, sIn, LenBP(sIn), sOut, WORDLEN)

    sOut = Trim$(sOut)
    
    Dim idx as Long
    idx = InStr(sOut, Chr(0))

    If idx <> 0 Then
        sOut = Left$(sOut, idx)
    End If

    OSTranslateLMBCSToNative = sOut
    
End Function

'Determine if a character is a group code
'@param char - 1 char to check
Function CheckGroupCode(char as String) as Boolean
    Dim result as Boolean
    result = False

    ' conv char code.
    Dim code as Long
    code = Asc(char)

    ' within group-code?
    If &h00 <= code And code <= &h1F Then
        ' is group-code
        result = True

        ' is control-char?
        Select Case(code)
        Case &h00, &h07, &h09, &h0A, &h0D
            ' is control-char
            result = False
        End Select
    End If

    CheckGroupCode = result
    
End Function

'Extracting a substring from an LMBCS-string
'@param src - LMBCS-string
'@param offset - start offset.
'@param length - substring length.
Function LMBCSSubstring(src as String, offset as Long, length as Long) as String

    'get sub-string
    Dim chunk as String
    chunk = Mid(src, offset, length)

    Do
        'get terminator char
        Dim terminator as string
        terminator = Right(chunk, 1)

        ' is group-code?
        If CheckGroupCode(terminator) = True Then
            'Remove the terminating character
            chunk = Left(chunk, Len(chunk) - 1)
        Else
            ' process end.
            Exit Do
        End If
    Loop

    '[INFO]
    'Please note that the length is different from the @param-length.
    LMBCSSubstring = chunk
    
End Function
```

## usage

```vb
'[Lotus Script]
'@example - convert LMBS-string to os-string.
'@param src - LMBCS-string
Function ConvertOSString(src as String) as String
    Dim srcLength as Long
    Dim subLength as Long
    Dim offset as Long
    
    srcLength = Len(src) 'LMBCS-string length.
    subLength = 5000 'sub-string length.
    offset = 1 'start offset

    Dim osString as String
    osString = "" 'string buffer.
    
    Do
        If srcLength < offset Then
            'process end.
            Exit Do
        End If

        'get sub-string.
        Dim chunk as String
        chunk = LMBCSSubstring(src, offset, subLength)

        'next offset
        '[WARN]
        'Recalculation is required,
        'because the length of the specified substring may not match.
        offset = offset + Len(chunk)

        'Translate to os-string, and join.
        osStr = osStr & OSTranslateLMBCSToNative(chunk)
    Loop

    'return os-string.
    ConvertOSTrancate = osStr
    
End Function
```

# ref.

https://en.wikipedia.org/wiki/Lotus_Multi-Byte_Character_Set

https://opensource.hcltechsw.com/domino-c-api-docs/reference/Func/OSTranslate/?h=ost

https://takenoff.com/notes/api/467

Fin.