FLUC iconv Interface

Dieses Modul bietet eine libiconv-kompatible Schnittstelle für die Memory-to-Memory Zeichenkonvertierung. Alle Features des FLUC Zeichenwandlungsmoduls sind über die TO und FROM strings der Funktion fliconv_open() nutzbar.

iconvlist(NULL,NULL);
h=iconv_open("UTF16LE//BOM","1141//ELF2NL//IGNORE//TRANSLIT//REPORT(report.txt)");
r=iconv(h,&inDat,&inLen,&outDat,&outLen);
iconv_close(h);

Es folgt ein Beispiel, das ein Kommandozeilenprogramm für die Zeichenkonvertierung auf Basis dieser Bibliothek in C implementiert (fliconv), welches dem aus Linux bekannten iconv Programm sehr ähnlich ist. Die DLL kann auf der Mainframe auch von COBOL aus genutzt werden. Wie dies geht, haben wir in einem COBOL-Beispielprogramm veranschaulicht, welche einen PS-FB80 Dataset in IBM1141 liest und die Zeichen in UTF-8 wandelt, wobei die Records in einen PS-VB244 weggeschreiben werden.

/**
 * @file   FLICONV.c
 * @brief  Sample program in C to convert files via the FLCICV interface
 *         The program is compatible with the iconv utility on linux systems
 * @author limes datentechnik gmbh
 ******************************************************************************/
 
#include<ctype.h>
#include<errno.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
 
#define  FICONV /* enable "iconv" macros for "fliconv" functions*/
#include"FLCICV.h"
 
static void printHelp(void) {
   fprintf(stderr,"\n");
   fprintf(stderr,"Usage: fliconv [OPTION...] [FILE...]\n");
   fprintf(stderr,"Convert given files from one character set to another\n");
   fprintf(stderr,"\n");
   fprintf(stderr,"  Information:\n");
   fprintf(stderr,"   -?            Prints this help list\n");
   fprintf(stderr,"   -h            Prints this help list\n");
   fprintf(stderr,"   -l            List all supported character sets\n");
   fprintf(stderr,"   -v            Print program version\n");
   fprintf(stderr,"   -a            Print about information\n");
   fprintf(stderr,"   -z            Print license text\n");
   fprintf(stderr,"   -p            Print progress information\n");
   fprintf(stderr,"   -s            Skip incomplete characters at the end of the data\n");
   fprintf(stderr,"\n");
   fprintf(stderr,"  Input/Output file control:\n");
   fprintf(stderr,"   FILE...       Input file list [STDIN]\n");
   fprintf(stderr,"   -o FILE       Output file [STDOUT]\n");
   fprintf(stderr,"\n");
   fprintf(stderr,"  Input/Output format specification:\n");
   fprintf(stderr,"   -f NAME       Encoding string or CCSID of original text [UTF-8]\n");
   fprintf(stderr,"   -t NAME       Encoding string or CCSID for output [UTF-8]\n");
   fprintf(stderr,"\n");
   fprintf(stderr,"  Input encoding string enhancements:\n");
   fprintf(stderr,"   //BOM         Manage byte order change inside data (only if same bit width)\n");
   fprintf(stderr,"   //ENL2LF      Convert EBCDIC new line (0x85/0x15) to line feed (0x0A/0x25)\n");
   fprintf(stderr,"\n");
   fprintf(stderr,"  Output encoding string enhancements:\n");
   fprintf(stderr,"   //BOM         Write byte order mark\n");
   fprintf(stderr,"   //ELF2NL      Convert EBCDIC line feed (0x0A/0x25) to new line (0x85/0x15)\n");
   fprintf(stderr,"   //TOUPPER     Upper case mapping\n");
   fprintf(stderr,"   //TOLOWER     Lower case mapping\n");
   fprintf(stderr,"   //TOSUPPER    Special upper case mapping\n");
   fprintf(stderr,"   //TOSLOWER    Special lower case mapping\n");
   fprintf(stderr,"   //TOFOLD      Special case folding\n");
   fprintf(stderr,"   //TOUSER      Use case mapping defined in user table/module (^CP=cp_list)\n");
   fprintf(stderr,"   //IGNORE      Ignore invalid characters\n");
   fprintf(stderr,"   //TRANSLIT['('[systab]')']            Transliterate invalid characters [ICONV]\n");
   fprintf(stderr,"   //SUBSTITUTE['('[codepoint_list]')']  Substitute invalid characters [0x1A]\n");
#ifdef __ZOS__
   fprintf(stderr,"   //USRMOD'('module_name')'   Use a predefined user table module (CCUTNPAS/CCUTSEPA/CCUTDELA/CCUTDLAX/...)\n");
#else
   fprintf(stderr,"   //USRMOD'('module_name')'   Use a predefined user table module (ccutnpas/ccutsepa/ccutdela/ccutdlax/...)\n");
#endif
   fprintf(stderr,"   //USRTAB'('file_name')'     Use a custom text file containing a user table (see FLCL manual for more information)\n");
   fprintf(stderr,"   //REPORT['('[file_name]')'] Write a report file [STDERR]\n");
   fprintf(stderr,"\n");
   fprintf(stderr,"  Examples:\n");
   fprintf(stderr,"    fliconv -l\n");
   fprintf(stderr,"    fliconv -p -f UTF16LE//BOM -t 1141//ELF2NL//IGNORE//TRANSLIT//REPORT(report.txt) -o outdat.txt input1.txt input2.txt\n");
   fprintf(stderr,"\n");
   fprintf(stderr,"  Support:\n");
   fprintf(stderr,"    Web : http://www.flam.de/\n");
   fprintf(stderr,"    Mail: support@flam.de\n");
   fprintf(stderr,"\n");
}
 
/**
 * This test program implements the iconv utility with the FLUC character conversion module.
 * Use fliconv -? for the syntax, help and a sample
 */
int main(int argc, char * argv[])
{
   FILE*                   pfIn=stdin;
   FILE*                   pfOut=stdout;
   static char             acIn[65536];
   size_t                  i,uiOut,uiSiz,uiLen,uiRst,uiErr,uiCnt=0;
   off_t                   x,y,uiFil,uiSum,uiTot=0;
   char*                   h;
   char*                   a[256];
   char*                   pcFrom="UTF-8";
   char*                   pcTo="UTF-8";
   char*                   pcOutFil=NULL;
   char*                   pcOutBuf=NULL;
   char*                   pcHlpIn;
   char*                   pcHlpOut;
   void*                   pvIcv;
   int                     k;
   int                     isProgress=0;
   int                     isSkip=0;
 
   if (argc==2) { /* parse HOST parameter string (comma separation) */
      a[0]=argv[0]; a[1]=argv[1];
      for (i=2,h=strchr(a[i-1],',');i<256 && h!=NULL;i++,h=strchr(a[i-1],',')) {
         h[0]=0x00;
         a[i]=h+1;
      }
      argc=i; argv=a;
   }
 
   for (i=1;i<argc;i++) { /* evaluate command line arguments */
      if (argv[i][0]=='-' && (toupper(argv[i][1])=='?' || toupper(argv[i][1])=='H')) {
         printHelp();
         exit(0);
      } else if (argv[i][0]=='-' && toupper(argv[i][1])=='L') {
         fliconv_list(NULL,NULL);
         exit(0);
      } else if (argv[i][0]=='-' && toupper(argv[i][1])=='V') {
         fprintf(stderr,"%s",iconv_version());
         exit(0);
      } else if (argv[i][0]=='-' && toupper(argv[i][1])=='A') {
         fprintf(stderr,"%s",iconv_about());
         exit(0);
      } else if (argv[i][0]=='-' && toupper(argv[i][1])=='Z') {
         fprintf(stderr,"%s",iconv_license());
         exit(0);
      } else if (argv[i][0]=='-' && toupper(argv[i][1])=='P') {
         isProgress=1;
      } else if (argv[i][0]=='-' && toupper(argv[i][1])=='S') {
         isSkip=1;
      } else if (argv[i][0]=='-' && toupper(argv[i][1])=='F') {
         if (argv[i+1][0]=='-') {
            fprintf(stderr,"\nNo FROM encoding string specified\n");
            printHelp();
            exit(8);
         }
         pcFrom=argv[i+1];
         i++;
      } else if (argv[i][0]=='-' && toupper(argv[i][1])=='T') {
         if (argv[i+1][0]=='-') {
            fprintf(stderr,"\nNo TO encoding string specified\n");
            printHelp();
            exit(8);
         }
         pcTo=argv[i+1];
         i++;
      } else if (argv[i][0]=='-' && toupper(argv[i][1])=='O') {
         if (argv[i+1][0]=='-') {
            fprintf(stderr,"\nNo output file specified\n");
            printHelp();
            exit(8);
         }
         pcOutFil=argv[i+1];
         i++;
      } else break;
   }
 
   if (pcOutFil!=NULL) { /* open output file */
      errno=0;
      pfOut=fopen(pcOutFil,"wb");
      if (pfOut==NULL) {
         fprintf(stderr,"\nOpen of output file failed (%d - %s)\n",errno,strerror(errno));
         exit(6);
      }
   }
 
   pvIcv=iconv_open(pcTo,pcFrom); /* open character conversion module */
   if (pvIcv==NULL) {
      fprintf(stderr,"\nOpen character conversion module failed (%d - %s)\n",errno,fliconv_strerror(errno));
      fprintf(stderr,"%s",iconv_error_trace());
      exit(5);
   }
 
   uiSiz=iconv_expansion(pvIcv)*sizeof(acIn); /* determine output buffer size */
   if (uiSiz==0) {
      fprintf(stderr,"\nDetermination of expansion factor failed\n");
      fprintf(stderr,"%s",iconv_error_trace());
      iconv_close(pvIcv);
      exit(5);
   }
 
   errno=0;
   pcOutBuf=(char*)malloc(uiSiz); /* allocation of output buffer */
   if (pcOutBuf==NULL) {
      fprintf(stderr,"\nAllocation of %d byte for output buffer failed (%d - %s)\n",(int)uiSiz,errno,strerror(errno));
      iconv_close(pvIcv);
      exit(4);
   }
 
   if (isProgress) fprintf(stderr,"\n");
 
   if (i==argc) { /* if no input file specified, activate use of STDIN */
      i--; argv[i]="";
   }
   for (k=0; i<argc; k++) {
      uiFil=uiSum=0;
      if (argv[i]!=NULL && strlen(argv[i])) { /* use input file */
         if (pcOutFil!=NULL && *pcOutFil && strcmp(argv[i],pcOutFil)==0) { /* skip output file as input file to prevent recursive endless read */
            i++; continue;
         }
         errno=0;
         pfIn=fopen(argv[i],"rb");
         if (pfIn==NULL) {
            fprintf(stderr,"\nOpen of input file (%s) failed (%d - %s)\n",argv[i],errno,strerror(errno));
            iconv_close(pvIcv);
            free(pcOutBuf);
            exit(6);
         }
         if (isProgress) {
            fseek(pfIn,0,SEEK_END); uiFil=ftell(pfIn); rewind(pfIn);
            fprintf(stderr,"%04d/%010"PRIi64" %s\n[",k+1,iconv_position(pvIcv),argv[i]);
         }
      } else { /* use STDIN */
         if (isProgress) fprintf(stderr,"%05d/%010"PRIi64" STDIN\n",k+1,iconv_position(pvIcv));
      }
 
      uiRst=0; errno=0; x=0; /* conversion loop */
      for (uiLen=fread(acIn+uiRst,1,sizeof(acIn)-uiRst,pfIn); (uiLen+uiRst) && (ferror(pfIn)==0 || feof(pfIn));
           uiLen=fread(acIn+uiRst,1,sizeof(acIn)-uiRst,pfIn)) {
         if (uiLen==0 && uiRst) { /* no new data but still an rest --> the last character is incomplete */
            if (isSkip) break; else {
               fprintf(stderr,"\nIncomplete multibyte character sequence at end of the data (Offset %"PRIi64")\n",iconv_position(pvIcv)+(int64_t)1);
               iconv_close(pvIcv);
               free(pcOutBuf);
               exit(1);
            }
         }
         uiSum+=uiLen; pcHlpIn=acIn; uiLen+=uiRst; pcHlpOut=pcOutBuf; uiOut=uiSiz;
         uiErr=iconv(pvIcv,&pcHlpIn,&uiLen,&pcHlpOut,&uiOut);
         uiOut=uiSiz-uiOut; uiTot+=uiOut;
         if (uiErr!=(size_t)-1) uiCnt+=uiErr;
         if ((uiErr==-1 && errno!=EINVAL) || (feof(pfIn) && uiRst)) { /* conversion error */
            fprintf(stderr,"\nCharacter conversion failed at byte offset %"PRIi64" (%d - %s)\n",iconv_position(pvIcv)+(int64_t)1,errno,fliconv_strerror(errno));
            fprintf(stderr,"%s",iconv_error_trace());
            fwrite(pcOutBuf,1,uiOut,pfOut);
            iconv_close(pvIcv);
            free(pcOutBuf);
            exit(1);
         } else { /* write converted data */
            uiErr=fwrite(pcOutBuf,1,uiOut,pfOut);
            if (uiErr!=uiOut) {
               fprintf(stderr,"\nWrite data to file failed (%d - %s)\n",errno,strerror(errno));
               iconv_close(pvIcv);
               free(pcOutBuf);
               exit(2);
            }
            memcpy(acIn,pcHlpIn,uiLen); /* save rest (incomplete multibyte sequence) at the beginning of the buffer */
            uiRst=uiLen;
         }
         if (uiFil) { /* progress bar */
            for (y=(uiSum*70)/uiFil;x<y;x++) fprintf(stderr,"#");
         }
      }
      if (uiFil) fprintf(stderr,"]\n");
 
      if (ferror(pfIn)!=0 && ferror(pfIn)!=EOF) { /* conversion loop and based on a read error */
         fprintf(stderr,"\nRead data from file failed (%d - %s)\n",errno,strerror(errno));
         iconv_close(pvIcv);
         free(pcOutBuf);
         exit(3);
      }
 
      if (pfIn !=stdin) fclose(pfIn);
      i++;
   }
 
   if (isProgress) { /* print statistic information */
      fprintf(stderr,"\nStatistic:\n");
      fprintf(stderr,"Files converted:            %d\n",k);
      fprintf(stderr,"Total bytes converted:      %"PRIi64"\n",iconv_position(pvIcv));
      fprintf(stderr,"Total bytes written:        %"PRIi64"\n",(int64_t)uiTot);
      fprintf(stderr,"Non-reversible conversions: %"PRIi64"",(int64_t)uiCnt);
      if (uiCnt) fprintf(stderr," (use report file for more information)\n"); else fprintf(stderr,"\n");
      fprintf(stderr,"\n");
   }
 
   iconv_close(pvIcv);
   if (pfOut!=stdout) fclose(pfOut);
   free(pcOutBuf);
   exit(0);
}
 
/******************************************************************************/

Die open Funktion unterstützt bei der Eingabe Encoding Strings und CCSIDS. Die Funktion fliconv_list() gibt alle unterstützten CCSIDs, CHARSETs und die entsprechenden Encoding Strings hierfür zurück.

Wenn Sie eine weitere Code-Page benötigen (wie zum Beispiel DIN-66003, welche wir mit der Vesion 5.1.2 als CCSID 66003 und mit dem Alias DE-ASCII bereitgestellt haben), dann stellen Sie uns einfach ein Issue ein. Die Zeichensatzkonvertierung mit allen Featuren und Encodings steht auf allen von uns unterstützten Plattformen gleichermaßen zur Verfügung. Es gibt keine Unterschiede, weder in den unterstützten Zeichensätzen noch im Verhalten.

Wenn der Compiler-Schalter FICONV definiert ist, dann sind alle  fliconv* Funktionen auch als iconv* Funktionen verfügbar. D.h. um ihren bestehenden Code von der libiconv auf FLCICV umzustellen, müssen Sie lediglich folgende Ersetzung durchführen:

-#include<iconv.h>
+#define  FICONV
+#include"FLCICV.h"

Nach der Ersetzung müssen Sie lediglich neu kompilieren und mit unserer DLL linken. Um unsere Features zu nutzen, müssen Sie eine oder mehrere der im Folgenden beschriebenen Erweiterungen an Ihren Encoding String anhängen.

Erweiterungen für den Input Encoding String:

  • //BOM - Reagiert auf den Wechsel der Byte Order (z.B. bei konkatinierten Dateien)
  • //ENL2LF - Konvertiert EBCDIC New Line (0x15) nach Line Feed (0x0A)

Erweiterungen für den Output Encoding String:

  • //BOM - Schreibt die Byte Order Mark (BOM) an den Anfang der Daten
  • //ELF2NL - Konvertiert EBCDIC Line Feed (0x0A) to New Line (0x15)
  • //TOUPPER - Upper Case Mapping
  • //TOLOWER - Lower Case Mapping
  • //TOSUPPER - Special Upper Case Mapping
  • //TOSLOWER - Special Lower Case Mapping
  • //TOFOLD - Special Case Folding
  • //TOUSER - Case Mapping laut der/des User Table/Module (^CP=cp_list)
  • //IGNORE - Ignoriere invalide Zeichen (Substitution durch nichts)
  • //TRANSLIT['('[systab]')'] - Transliteriere invalide Zeichen [ICONV]
  • //SUBSTITUTE['('[codepoint_list]')'] - Substituiere invalide Zeichen [0x1A]
  • //USRMOD'('module_name')' - Verwende ein User Table Module
  • //USRTAB'('file_name')' - Verwende ein User Table Text File
  • //REPORT['('[file_name]')'] - Erzeuge ein Report File [STDERR]
  • //NFD Normalisation Form D (Canonical Decomposition)
  • //NFC Normalisation Form D (Canonical Decomposition, followed by Canonical Composition)
  • //COMBINED Character conversion with combined character support

 Derzeit unterstütze System Transliteration Tables:

  • ICONV - Transliteration Table der libiconv

Derzeit unterstützte User Table Modules:

  • CCUTNPAS - UCS subset for String-Latin (XOEV/NPA, with best fit mapping and case folding)
  • CCUTSPEA - UCS subset for SEPA (all valid UTF-8 character < 128, with transliteration)
  • CCUTDELA - UCS subset of IBM1141, CP1252 and ISO8859-15 (only CP check)
  • CCUTDLAX - UCS Subset of IBM1141, CP1252, ISO8859-15 and XÖV (only CP check)

Für genauere Informationen über User Table Text Files (wie zum Beispiel CCUTNPAS.txt) schauen Sie bitte in das FLCL User Manual. Sie können ganze Subsets oder Manipulationen an den systemeigenen Tabellen über ein eigenes User Table Text File definieren und wenn alles soweit funktioniert über unseren Support ein vorberechnetes Lademodul, DLL oder Shard Object (so) im Rahmen der Wartung dafür bestellen. Wir haben ein Tool, welches das Textfile in ausführbaren Code umwandelt. Hierdurch müssen diese Berechnungen nicht mehr bei jedem Open getan werden, was vorallem die Abarbeitung von vielen kleinen Dateien beschleunigt. Die Mitgelieferten Beispieldateien CCUTNPAS/SEPA/DELA/DLAX entsprechen 1 zu 1 den dazugehörigen Lademodulen. Des weiteren kann man mit einer USRTAB die angegebene USRMOD manipulieren bzw. ergänzen.

Verbesserungen im Vergleich zu gewöhnlichen iconv-Implementierungen:

  • Support von Encoding Strings und CCSIDs
  • Auflistung unterstützter CCSIDs, Encoding Strings und Charsets
  • EBCDIC New Line (0x15) zu Line Feed (0x0A) Umwandlung
  • Subset Support (String.Latin, SEPA, ...) und benutzerdefinierter User Tables
  • Rekursives N zu M Mapping und Transliteration ('Ü' -> 'U:' -> 'UE')
    • Combined character support
    • Tagging und Eingabehilfe
    • NFD/NFC Mappings
  • Case mapping, umfangreiches Reporting, Byte Order Change Handling
  • Liest 5 und 6 Bytes lange UTF-8 Zeichen (Zeichenwerte mit vorangestellten Nullen)
  • Schneller und geringerer Speicher- sowie CPU-Konsum

Unterschiede zu gewöhnlichen iconv-Implementierungen:

  • Unterstützt weitere errno Werte (hauptsächlich fürs User Table Parsing, nicht für die Konvertierung selbst (hier bleiben wir vollkommen kompatible zur libiconv))
  • E2BIG wird gesetzt, wenn der Output Puffer kleiner ist als die Eingabedaten multipliziert mit dem maximalen Expansionsfaktor. In diesem Fall werden keine Daten konvertiert. Sprich erst wenn der Ausgabepuffer groß genug ist, wird der Block transformiert, was die Performance signifikant steigert.
  • Zusätzliche Funktionen für das Abrufen von  About-, Versions-, Lizenz-, Statistik, Fehler- und anderer Informationen
  • Das Byte Order Mark wird nur geschrieben, wenn das BOM Schlüsselwort im TO-String angegeben ist, die CCSID bzw. der Encoding string haben hier keinen Enfluß.
  • Die List-Funktion ruft verfügbare Encoding Strings, ihre CCSIDs und zugehörige Zeichensatzinformationen (UTF/ASCII/EBCDIC) ab.

Mehr Information können Sie der Schnittstellenspezifikation im Downloadbereich entnehmen.