************************************************************
Hinweis: Der folgende Abschnitt wurde mit UnterstĂĽtzung von ChatGPT erstellt,
von mir jedoch getestet und erfolgreich im Einsatz.
Note: The following section was generated with the assistance of ChatGPT,
but has already been tested and is successfully in use.
************************************************************
Zur zuverlässigen Übergabe von DBF-Daten an den PHP-Proxy wird im Microservice eine C-Funktion per #pragma BEGINDUMP eingebunden. Hintergrund: Viele ältere DBF-Dateien sind in DEWIN/Windows-1252 codiert und enthalten teilweise Steuerzeichen im Bereich 0x00–0x1F oder Sonderzeichen 0x80–0x9F (z. B. €, „, …). Diese Bytes sind in UTF-8 nicht gültig und führen beim hb_jsonEncode() auf der PHP-Seite zu „Bad JSON“-Fehlern.
Die drei bereitgestellten Funktionen:
ISVALIDUTF8( cText ) -> lOk
PrĂĽft, ob der ĂĽbergebene String bereits korrekt UTF-8-codiert ist (.T. oder .F.).
CP1252TOUTF8( cText ) -> cUtf8
Wandelt einen 8-Bit-String im Zeichensatz Windows-1252/DEWIN in gĂĽltiges UTF-8 um.
SAFEUTF8( cText ) -> cUtf8
Kombiniert beide Schritte:
Entfernt unzulässige Steuerzeichen (< 32, außer TAB/CR/LF).
Erkennt, ob die Eingabe schon UTF-8 ist. Falls nein, konvertiert sie von CP1252/ISO-8859-1 nach UTF-8.
Führt ein Mapping der problematischen 0x80–0x9F-Zeichen durch (€, „…“, —, ™ usw.).
Gibt garantiert valide UTF-8 zurĂĽck, sodass JSON-Serialisierung nicht mehr abbricht.
Performance:
Die C-Implementierung arbeitet in einem einzigen Durchlauf über die Bytes (O(n)), mit kleinem Lookup-Array. Dadurch ist die Geschwindigkeit im Bereich von hunderten MB/s und für typische DBF-Feldlängen praktisch ohne spürbare Mehrbelastung.
Integration:
Die Funktionen werden in einer .prg-Datei eingebunden (#pragma BEGINDUMP).
In den DBF-Leseroutinen wird bei allen CHAR-Feldern vor dem hb_jsonEncode() ein Aufruf SAFEUTF8( AllTrim( cValue ) ) gemacht.
So ist sichergestellt, dass die erzeugte JSON-Antwort auch bei alten Datenbeständen mit Umlauten oder Steuerzeichen immer korrekt und ohne Timeout/Decode-Fehler beim PHP-Proxy ankommt.
Hinweis: Der folgende Abschnitt wurde mit UnterstĂĽtzung von ChatGPT erstellt,
von mir jedoch getestet und erfolgreich im Einsatz.
Note: The following section was generated with the assistance of ChatGPT,
but has already been tested and is successfully in use.
************************************************************
Zur zuverlässigen Übergabe von DBF-Daten an den PHP-Proxy wird im Microservice eine C-Funktion per #pragma BEGINDUMP eingebunden. Hintergrund: Viele ältere DBF-Dateien sind in DEWIN/Windows-1252 codiert und enthalten teilweise Steuerzeichen im Bereich 0x00–0x1F oder Sonderzeichen 0x80–0x9F (z. B. €, „, …). Diese Bytes sind in UTF-8 nicht gültig und führen beim hb_jsonEncode() auf der PHP-Seite zu „Bad JSON“-Fehlern.
Die drei bereitgestellten Funktionen:
ISVALIDUTF8( cText ) -> lOk
PrĂĽft, ob der ĂĽbergebene String bereits korrekt UTF-8-codiert ist (.T. oder .F.).
CP1252TOUTF8( cText ) -> cUtf8
Wandelt einen 8-Bit-String im Zeichensatz Windows-1252/DEWIN in gĂĽltiges UTF-8 um.
SAFEUTF8( cText ) -> cUtf8
Kombiniert beide Schritte:
Entfernt unzulässige Steuerzeichen (< 32, außer TAB/CR/LF).
Erkennt, ob die Eingabe schon UTF-8 ist. Falls nein, konvertiert sie von CP1252/ISO-8859-1 nach UTF-8.
Führt ein Mapping der problematischen 0x80–0x9F-Zeichen durch (€, „…“, —, ™ usw.).
Gibt garantiert valide UTF-8 zurĂĽck, sodass JSON-Serialisierung nicht mehr abbricht.
Performance:
Die C-Implementierung arbeitet in einem einzigen Durchlauf über die Bytes (O(n)), mit kleinem Lookup-Array. Dadurch ist die Geschwindigkeit im Bereich von hunderten MB/s und für typische DBF-Feldlängen praktisch ohne spürbare Mehrbelastung.
Integration:
Die Funktionen werden in einer .prg-Datei eingebunden (#pragma BEGINDUMP).
In den DBF-Leseroutinen wird bei allen CHAR-Feldern vor dem hb_jsonEncode() ein Aufruf SAFEUTF8( AllTrim( cValue ) ) gemacht.
So ist sichergestellt, dass die erzeugte JSON-Antwort auch bei alten Datenbeständen mit Umlauten oder Steuerzeichen immer korrekt und ohne Timeout/Decode-Fehler beim PHP-Proxy ankommt.
#pragma BEGINDUMP
/* C89, Harbour, BCC7-kompatibel */
#include "hbapi.h"
#include <string.h>
/* ---------- Prototypen (gegen W8065) ---------- */
static int is_valid_utf8( const unsigned char *s, long n );
static int utf8_emit( unsigned char *dst, unsigned long cp );
static int is_bad_ctrl( unsigned char c );
static void cp1252_to_utf8( const unsigned char *src, long n,
unsigned char **outBuf, long *outLen );
/* ---------- Globale Map (verhindert E2451 cp1252_map) ---------- */
/* Windows-1252: 0x80–0x9F → Unicode Codepoints */
static unsigned short cp1252_map[32] = {
0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x0000, 0x017D, 0x0000,
0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x0000, 0x017E, 0x0178
};
/* ---------- Implementierungen ---------- */
static int is_valid_utf8( const unsigned char *s, long n )
{
long i = 0;
while( i < n )
{
unsigned char c = s[i];
if( c < 0x80 )
{
i++;
}
else if( c >= 0xC2 && c <= 0xDF )
{
if( i + 1 >= n ) return 0;
if( ( s[i+1] & 0xC0 ) != 0x80 ) return 0;
i += 2;
}
else if( c >= 0xE0 && c <= 0xEF )
{
if( i + 2 >= n ) return 0;
if( ( s[i+1] & 0xC0 ) != 0x80 || ( s[i+2] & 0xC0 ) != 0x80 ) return 0;
i += 3;
}
else if( c >= 0xF0 && c <= 0xF4 )
{
if( i + 3 >= n ) return 0;
if( ( s[i+1] & 0xC0 ) != 0x80 || ( s[i+2] & 0xC0 ) != 0x80 || ( s[i+3] & 0xC0 ) != 0x80 ) return 0;
i += 4;
}
else
return 0;
}
return 1;
}
/* Unicode codepoint → UTF-8 */
static int utf8_emit( unsigned char *dst, unsigned long cp )
{
if( cp <= 0x7F ) {
dst[0] = (unsigned char) cp; return 1;
} else if( cp <= 0x7FF ) {
dst[0] = 0xC0 | ( cp >> 6 );
dst[1] = 0x80 | ( cp & 0x3F );
return 2;
} else if( cp <= 0xFFFF ) {
dst[0] = 0xE0 | ( cp >> 12 );
dst[1] = 0x80 | ( ( cp >> 6 ) & 0x3F );
dst[2] = 0x80 | ( cp & 0x3F );
return 3;
} else {
dst[0] = 0xF0 | ( cp >> 18 );
dst[1] = 0x80 | ( ( cp >> 12 ) & 0x3F );
dst[2] = 0x80 | ( ( cp >> 6 ) & 0x3F );
dst[3] = 0x80 | ( cp & 0x3F );
return 4;
}
}
/* harte Steuerzeichen (<32) auĂźer TAB/LF/CR entfernen */
static int is_bad_ctrl( unsigned char c )
{
return ( c < 32 ) && !( c == 9 || c == 10 || c == 13 );
}
/* Kernpfad: CP1252 → UTF-8 (mit Control-Strip). Worst case 3x Größe reicht. */
static void cp1252_to_utf8( const unsigned char *src, long n,
unsigned char **outBuf, long *outLen )
{
long i, j = 0;
unsigned char *dst;
if( n < 0 ) n = 0;
dst = (unsigned char *) hb_xgrab( (HB_SIZE) ( n * 3 + 1 ) );
for( i = 0; i < n; ++i )
{
unsigned char c = src[i];
if( is_bad_ctrl( c ) )
{
dst[j++] = ' ';
continue;
}
if( c < 0x80 )
{
dst[j++] = c;
}
else if( c >= 0x80 && c <= 0x9F )
{
unsigned short u = cp1252_map[ c - 0x80 ];
if( u == 0 )
dst[j++] = '?';
else
j += utf8_emit( dst + j, (unsigned long) u );
}
else
{
/* 0xA0–0xFF → U+00A0..U+00FF */
j += utf8_emit( dst + j, (unsigned long) c );
}
}
dst[j] = '\0';
*outBuf = dst;
*outLen = j;
}
/* ---------- Harbour-Exports ---------- */
HB_FUNC( ISVALIDUTF8 )
{
const char *p = hb_parc( 1 );
long n = hb_parclen( 1 );
if( p == NULL ) { hb_retl( 1 ); return; }
hb_retl( is_valid_utf8( (const unsigned char *) p, n ) );
}
HB_FUNC( CP1252TOUTF8 )
{
const unsigned char *p = (const unsigned char *) hb_parc( 1 );
long n = hb_parclen( 1 );
unsigned char *out = NULL;
long outLen = 0;
if( p == NULL || n <= 0 ) { hb_retclen( "", 0 ); return; }
cp1252_to_utf8( p, n, &out, &outLen );
/* Harbour ĂĽbernimmt das Freeing des Buffers */
hb_retclen_buffer( (char *) out, (HB_SIZE) outLen );
}
HB_FUNC( SAFEUTF8 )
{
const unsigned char *p = (const unsigned char *) hb_parc( 1 );
long n = hb_parclen( 1 );
if( p == NULL || n <= 0 ) { hb_retclen( "", 0 ); return; }
if( is_valid_utf8( p, n ) )
{
/* Zero-copy fast path, falls keine harten Steuerzeichen */
long i;
for( i = 0; i < n; ++i )
{
unsigned char c = p[i];
if( is_bad_ctrl( c ) )
{
/* kopierende Variante mit Strip */
long k, j = 0;
unsigned char *dst = (unsigned char *) hb_xgrab( (HB_SIZE) n + 1 );
for( k = 0; k < n; ++k )
{
unsigned char d = p[k];
dst[j++] = is_bad_ctrl( d ) ? ' ' : d;
}
dst[ j ] = '\0';
hb_retclen_buffer( (char *) dst, (HB_SIZE) j );
return;
}
}
/* keine Bad-Controls → Original zurückgeben */
hb_retclen( (const char *) p, (HB_SIZE) n );
}
else
{
unsigned char *out = NULL; long outLen = 0;
cp1252_to_utf8( p, n, &out, &outLen );
hb_retclen_buffer( (char *) out, (HB_SIZE) outLen );
}
}
#pragma ENDDUMP