IBM Support

File Server Exit Example (Unicode-to-EBCDIC Conversion)

Troubleshooting


Problem

This document is an example of a file server exit program that performs Unicode-to-EBCDIC conversion.

Resolving The Problem

This code is provided for illustrative purposes only and is not intended for production use. It is provided 'as is' without warranty of any kind, either expressed or implied, including but not limited to the implied warranties of merchantability and fitness for a particular purpose. The entire risk as to the quality and performance of the code is with the user. In no event shall the authors or copyright holders be liable for any claims, damages, or other liability, whether in an action of contract, tort, or otherwise, arising from, out of, or in connection with the code or the use or other dealings in the code.
IBM OS/400 and IBM i5/OS Servers - Sample File Server Exit Program:
/******************************************************************/
/*       OS/400 Servers - Sample File Server Exit Program         */
/* Exit Point Name:    QIBM_QPWFS_FILE_SERV                       */
/*                                                                */
/* Description:        The following C program shows how to       */
/*                     process the file server exit program       */
/*                     parameters.  It can be used as a shell     */
/*                     for developing a file server exit program  */
/*                     tailored for your operating environment.   */
/*                     The program includes an example of         */
/*                     converting the unicode file name           */
/*                     (CCSID  61952) to US EBCDIC (CCSID 037).   */
/*                                                                */
/* Input:              Data Area QGPL/FILEEXIT:  The first        */
/*                     character is read (if the data area)       */
/*                     to determine type of output.               */
/*                                                                */
/* Output:             Output varies according to the first       */
/*                     character in the data area:                */
/*                     'Y': Exit point information is printed     */
/*                          to a flat file: QGPL/FILEEXIT         */
/*                          (In a production environment, a user  */
/*                          defined journal entry should be used  */
/*                          instead as it offers much better      */
/*                          performance.)                         */
/*                     other:  Return w/o logging information.    */
/*                                                                */
/* References:         SC41-3740  OS/400 Server Concepts and      */
/*                                Administration, Appendix A.     */
/*                     SC41-4801  System API Reference R360       */
/*                                National Language Support APIs  */
/*                                Chapter 47: CDRA apis           */
/*                                Chapter 46: NL Conversion       */
/*                     NOTE: R310 manual did not document the     */
/*                           CDRCVRT/QTQCVRT api.                 */
/*                     SC09-1390 Character Data Representation    */
/*                               Architecture Level 2.            */
/* TO USE THE SAMPLE:  1) Create the data area FILEEXIT and       */
/*                        place a 'Y' in the first character      */
/*                        to enable the debug log                 */
/*                        CRTDTAARA DTAARA(QGPL/FILEEXIT)         */
/*                                  TYPE(*CHAR) LEN(1)            */
/*                        CHGDTAARA DTAARA(QGPL/FILEEXIT (1 1))   */
/*                                  VALUE('Y')                    */
/*                     2) Create the log file                     */
/*                        CRTPF FILE(QGPL/FILEEXIT) RCDLEN(132)   */
/*                     3) Copy the file to a source file and      */
/*                        compile (requires ILE C)                */
/*                        CRTBNDC PGM(QGPL/FILEEXIT)              */
/*                     4) Register the exit program               */
/*                        ADDEXITPGM EXITPNT(QIBM_QPWFS_FILE_SERV)*/
/*                                   FORMAT(PWFS0100) PGMNBR(1)   */
/*                                   PGM(QGPL/FILEEXIT)           */
/*                        (WRKREGINF if easier... )               */
/*                     5) Register the exit program for DOS       */
/*                        CHGNETA PCSACC(*REGFAC)                 */
/* 10/96                                                DXD 3.7   */
/******************************************************************/

/******************************************************************/
/*  Includes for api declaration and useful structures            */
/******************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <recio.h>                               /* QSYS file io  */

/* include for file server exit.  Member EPWFSEP in files H,      */
/* QRPGSRC, QRPGLESRC, QCBLSRC and QCBLLESRC in library QSYSINC   */
/* NOTE: R310 & R360 are missing some includes.  Contact support  */
/* for an updated copy.                                           */
#include "QSYSINC/H/EPWFSEP"       /* File Server exit structures */

/* QTQxxxx api is functionally equivalent to CDRxxxx.             */
/* Function declare for QTQ apis can by found in QSYSINC          */
#include "QSYSINC/H/QTQCVRT"       /* NL conversion api include   */

/* Stat can ONLY be called via ILE C programs.  It returns a      */
/* structure that we will use to determine the TYPE of object     */
/* being accessed.  Other languages will need to do a DSPLNK to   */
/* a spool file, cpysplf, then open and parse the file.           */
#include "QSYSINC/SYS/STAT"         /* Unix style IFS api include */

#include "QSYSINC/H/QWCRDTAA"       /* Retrieve Data Area         */

/* Defines for constants */
#define  MAX_FILENAME_LEN    132    /* 132 record length for log  */

/******************************************************************/
/*  Declare global variables and functions                        */
/******************************************************************/
/* function declares */
short int ConvertIt(char *, int, char *, int);   /*  conversion   */
short int PrintObjType(char *);            /* wrapper for stat()  */
char DebugLogOn(void);                     /* output debug log?   */
void xRwrite(_RFILE *, void *, int);       /* wrapper for _Rwrite */

/* globals */
Qpwfs_Exit_Parms_t   *fsPtr;              /* ptr to file exit fmt */
char                 ebcdic_file_name[MAX_FILENAME_LEN];
/* FILE              *qprint;             /* output spool file    */
char                 qprint[MAX_FILENAME_LEN +1];  /* log buffer  */
_RFILE               *ptrFile;            /* output log  file     */
short                return_code;         /* global return code   */

/******************************************************************/
/* Main program                                                   */
/*    Check if output log file should be printed.                 */
/*    Parse data passed via the exit program call.                */
/*    Convert file name parameter from UNICODE to EBCDIC          */
/*    Determine what type of object is being accessed.            */
/*    Print the information to a log file.                        */
/******************************************************************/
main (int argc, char * argv[])
{
   /* initialize the return variable to ok                        */
   strncpy(argv[1], "1",1);

   /* check whether or not to write the debug log                 */
    if (DebugLogOn() != 'Y')
       return;

   /* declare and open debug log                                  */
   if ((ptrFile =_Ropen("QGPL/FILEEXIT", "ar"))==NULL)
      return;                           /* error opening log file */


   /* Set pointer to argv to make more readable.  Argv contains   */
   /* the File Server exit point "format" defined in the          */
   /* the OS/400 Host Server Concepts manual, Appendix A.         */
   fsPtr = (Qpwfs_Exit_Parms_t *) argv[2];

   /* print input parameters to debug log */
   sprintf (qprint, "File Exit Called with:");
   xRwrite(ptrFile, &qprint, strlen(qprint));
   sprintf (qprint, "\n   User    : %s", fsPtr->User_Profile);
   xRwrite(ptrFile, &qprint, strlen(qprint));
   sprintf (qprint, "\n   Server  : %s", fsPtr->Server_ID);
   xRwrite(ptrFile, &qprint, strlen(qprint));
   switch (fsPtr->Function_ID)
   {
      case 0:
         sprintf (qprint,
           "\n   Function: %d  Change file attribute request",
           fsPtr->Function_ID);
         xRwrite(ptrFile, &qprint, strlen(qprint));
         break;
      case 1:
         sprintf (qprint,
   "\n   Function: %d  Create stream file or create directory req",
           fsPtr->Function_ID);
         xRwrite(ptrFile, &qprint, strlen(qprint));
         break;
      case 2:
         sprintf (qprint,
   "\n   Function: %d  Delete File or delete directory request",
           fsPtr->Function_ID);
         xRwrite(ptrFile, &qprint, strlen(qprint));
         break;
      case 3:
         sprintf (qprint,
      "\n   Function: %d  List file attribute request (dir cmd)",
           fsPtr->Function_ID);
         xRwrite(ptrFile, &qprint, strlen(qprint));
         break;
      case 4:
         sprintf (qprint,
           "\n   Function: %d  Move request",
           fsPtr->Function_ID);
         xRwrite(ptrFile, &qprint, strlen(qprint));
         break;
      case 5:
         sprintf (qprint,
         "\n   Function: %d  Open stream request",
           fsPtr->Function_ID);
         xRwrite(ptrFile, &qprint, strlen(qprint));
         break;
      case 6:
         sprintf (qprint,
         "\n   Function: %d  Rename request",
           fsPtr->Function_ID);
         xRwrite(ptrFile, &qprint, strlen(qprint));
         break;
      case 7:
         sprintf (qprint,
         "\n   Function: %d  Allocate conversation request",
           fsPtr->Function_ID);
         xRwrite(ptrFile, &qprint, strlen(qprint));
         break;
      default:
         sprintf (qprint,
         "\n   Function: %d  ERROR - INVALID FUNCTION ID",
           fsPtr->Function_ID);
         xRwrite(ptrFile, &qprint, strlen(qprint));
         break;
   }
   /* if an open (X0005), then log the type of open */
   if (fsPtr->Function_ID == QPWFS_OPEN)
   {
      sprintf (qprint,
          "\n   Opened with the following type of file access:");
      xRwrite(ptrFile, &qprint, strlen(qprint));
      sprintf (qprint,
          "\n      Read Access      :%c", fsPtr->RD_Open);
      xRwrite(ptrFile, &qprint, strlen(qprint));
      sprintf (qprint,
          "\n      Write Access     :%c", fsPtr->WR_Open);
      xRwrite(ptrFile, &qprint, strlen(qprint));
      sprintf (qprint,
          "\n      Read/WriteAccess :%c", fsPtr->RD_WR_Open);
      xRwrite(ptrFile, &qprint, strlen(qprint));
      sprintf (qprint,
          "\n      Delete Allowed   :%c", fsPtr->Delete_Allowed);
      xRwrite(ptrFile, &qprint, strlen(qprint));
   }
   sprintf (qprint, "\n   Name Len: %d", fsPtr->Filename_Len);
   xRwrite(ptrFile, &qprint, strlen(qprint));

   /* convert the name to EBCDIC                                  */
   if (fsPtr->Filename_Len > 0)           /* something to convert */
   {
      return_code = ConvertIt(
                 (char *) argv[2] + 40,      /* unicode file name */
                 fsPtr->Filename_Len, /* len of unicode file name */
                 ebcdic_file_name,
                 sizeof(ebcdic_file_name));
      if (return_code == 0)             /* successfull conversion */
      {
         sprintf (qprint,
           "\n   Converted File Name Successfully.  File name:");
         xRwrite(ptrFile, &qprint, strlen(qprint));
         sprintf (qprint, "\n%s", ebcdic_file_name);
         xRwrite(ptrFile, &qprint, strlen(qprint));
      }
      else                                    /* conversion error */
      {
         sprintf (qprint,
                  "\n   Conversion Failed, RC: %d",return_code);
         xRwrite(ptrFile, &qprint, strlen(qprint));
      }
   }                                         /* end conversion if */


   /* Some system administrators want to know the type of object  */
   /* being accessed (Ex: file or directory?)                     */
   /* See the PrintObjType function below for details.            */
   if (fsPtr->Filename_Len > 0)                    /* valid name  */
      return_code = PrintObjType(ebcdic_file_name);

   /* PUT CODE TO REJECT A PARTICULAR REQUEST HERE.               */
   /* This example would reject the user profile GUEST if it      */
   /* attempts to delete or rename a file or directory.           */
   /* If (! (strncmp(fsPtr->User_Profile, "GUEST     ", 10)) )    */
   /* {                                                           */
   /*    If ( (fsPtr->Function_ID == 2) ||                        */
   /*         (fsPtr->Function_ID == 6))                          */
   /*       strncpy(argv[1], "0", 1);          /* reject request  */
   /* }                                                           */

   /*   fclose(qprint); */
   _Rclose (ptrFile);                       /* close the log file */
return;
}                                           /* end main program   */

/******************************************************************/
/*  functions                                                     */
/******************************************************************/

/******************************************************************/
/* Function:  ConvertIt                                           */
/* Purpose:   Convert the UNICODE file name to EBCDIC             */
/*            There are three methods that could be used to       */
/*            convert the file name.                              */
/*            - The iconv() National Language Conversion api      */
/*              (Callable only via ILE C)                         */
/*            - The Character Data Representation Architecture    */
/*              (CDRA) API  CDRCVRT.                              */
/*            - Service Program equivalent APIs (QTQCVRT)         */
/*              These are the easiest APIs to work with.          */
/*              They can be called by all languages on the as/400 */
/*              (including CL).  They are equivalent to the       */
/*              CDRA APIs so refer to the CDRA documentation.     */
/*            This sample program uses the QTQCVRT api.           */
/* Input:     S1 - pointer to string to be converted              */
/*            nL1 - Length of string to be converted              */
/*            S2 - pointer to buffer for converted string.        */
/*            nL2 - Length of output buffer for converted string. */
/* Return:    The API returns a  pointer to a 12 byte             */
/*            value:                                              */
/*            2 byte Primary Return Code;                         */
/*            2 byte Secondary Return Code;                       */
/*            8 bytes reserved.                                   */
/*            Our function will only return the Primary Return    */
/*            code (see references for details on the QTQCVRT     */
/*            api).                                               */
/*            0 - success                                         */
/*            > 0 - error                                         */
/* Reference:                                                     */
/*            SC41-4801  System API Reference R360                */
/*                       National Language Support APIs           */
/*                       Chapter 47: CDRA apis                    */
/*                       Chapter 46: NL Conversion                */
/*                       NOTE: R310 manual did not document the   */
/*                       CDRCVRT/QTQCVRT api.                     */
/*            SC09-1390 Character Data Representation             */
/*                      Architecture Level 2.                     */
/******************************************************************/

short int ConvertIt(char * S1, int nL1, char * S2, int nL2)
{
   struct FBstruct {              /* structure for Feedback parm  */
      short   primary_rc;         /* 2 byte primary return code   */
      short   secondary_rc;       /* 2 byte secondary return code */
      char    misc[8];            /* 8 bytes undefined            */
   };
   _Packed struct FBstruct FB;    /* Make sure no byte alignment  */
   int   ebcdic = 37;             /* US English CCSID             */
   int   unicode = 61952;         /* ISO IEC 10646 character set  */
   int   nST1 = 0;                /* not a null terminated string */
   int   nST2 = 1;                /* null terminated string       */
   int   nGCCASN = 0;             /* default table                */
   int   nL3;                     /* size of converted string     */
   int   nL4;                     /* reserved                     */
   char  szTemp[5];

   /* First, we'll make sure the name fits into our maximum       */
   /* file name length since we could be passed a length up to    */
   /* 16 MB!  Unicode takes 2 bytes per ascii character so in our */
   /* case we'll make it fit  the 132 character wide spool file.  */
   if (nL1 > (2 * MAX_FILENAME_LEN))
      nL1 = 2 * MAX_FILENAME_LEN;             /* 2 bytes per char */

   /* convert the file name to EBCDIC                             */
   QTQCVRT (&unicode,        /* Input CCSID                       */
            &nST1,           /* Input string not null terminated  */
            S1,              /* Input string to be xlated         */
            &nL1,            /* L1 - length of S1, input string   */
            &ebcdic,         /* Output CCSID                      */
            &nST2,           /* 1 for null terminated C-string    */
            &nGCCASN,        /* GCCASN  - 0 default table         */
            &nL2,            /* L2 - length of output buffer      */
            S2,              /* S2                                */
            &nL3,            /* L3 - length of converted data     */
            &nL4,            /* L4 - always returns 0             */
            &FB);            /* FB: Output / feedback             */

   sprintf (qprint, "\n");
   xRwrite(ptrFile, &qprint, strlen(qprint));
   sprintf (qprint, "\n\nCalled QTQCVRT API");
   xRwrite(ptrFile, &qprint, strlen(qprint));
   sprintf (qprint,   "\n------------------");
   xRwrite(ptrFile, &qprint, strlen(qprint));
   sprintf (qprint, "\n   EBCDIC File Name Length: %d", nL3);
   xRwrite(ptrFile, &qprint, strlen(qprint));
   sprintf (qprint, "\n   Primary return code: %d", FB.primary_rc);
   xRwrite(ptrFile, &qprint, strlen(qprint));
   sprintf (qprint, "\n   Secondary rc: %d", FB.secondary_rc);
   xRwrite(ptrFile, &qprint, strlen(qprint));

return FB.primary_rc;
}

/******************************************************************/
/* Function:  PrintObjType                                        */
/* Purpose:   Determine whether the "file name" passed to the     */
/*            exit program is a file or directory.  The stat()    */
/*            api (See Reference below) is used to retrieve this  */
/*            information.  (We will also retrive the file system */
/*            being accessed).                                    */
/* Input:     path - fully qualified path passed by exit point.   */
/* Return:    return code of the call to stat()                   */
/*            0 - success                                         */
/* Reference: System API Reference                                */
/*            CH 70  Integrated File System APIs.                 */
/* NOTES:     This api can only be called via ILE C compiler.     */
/*            To retrieve this information from other languages:  */
/*            Run the command: DSPLNK OUTPUT(*PRINT)              */
/*            Retrieve the information from the spool file        */
/*            (or use CPYSPLF to dump to flat file)               */
/******************************************************************/

short int PrintObjType(char * path)
{
   struct stat info;
   int   rc;

   if ((rc = stat(path, &info)) == 0)
   {
      /* print some of the information                            */
      sprintf (qprint, "\n");
      xRwrite(ptrFile, &qprint, strlen(qprint));
      sprintf (qprint, "\n\nstat() called successfully");
      xRwrite(ptrFile, &qprint, strlen(qprint));
      sprintf (qprint,   "\n------------------------");
      xRwrite(ptrFile, &qprint, strlen(qprint));
      sprintf (qprint,
               "\n   File System ID: %d", (int) info.st_dev);
      xRwrite(ptrFile, &qprint, strlen(qprint));
      sprintf (qprint,
               "\n   Object Type: %s", (char *) info.st_objtype);
      xRwrite(ptrFile, &qprint, strlen(qprint));
   }
   else                                       /* stat call failed */
   {
      sprintf (qprint, "\n", rc);
      xRwrite(ptrFile, &qprint, strlen(qprint));
      sprintf (qprint, "\n\nstat() call failed.  RC: %d", rc);
      xRwrite(ptrFile, &qprint, strlen(qprint));
   }

return (short int) rc;
}

/******************************************************************/
/* Function:  DebugLogOn                                          */
/* Purpose:   Check whether or not to write the debug log         */
/*            to a spool file.  A data area is used to enable     */
/*            this feature.  The first character of the data      */
/*            area is read in.  If "Y", the debug log is written. */
/*            The data used is QGPL/FILEEXIT.                     */
/* Input:     NONE                                                */
/* Return:    Y - Write the debug log.                            */
/*            N - Return without writing the log                  */
/* Reference: System API Reference                                */
/******************************************************************/

char DebugLogOn()
{
   struct ErrorCodeStruct {               /* error code structure */
      int bytes_provided;                 /* bytes provided       */
      int bytes_available;                /* bytes of error info  */
      char  exception_ID[7];              /* msg id               */
      char  reserved;                     /* reserved             */
   };
   _Packed struct ErrorCodeStruct  errorcode  =
                            {sizeof(errorcode),
                            0,           /* initialize to 0 bytes */
                            "NOERROR"};  /* initialize to NOERROR */
  struct QWCRDTAA_Output          /* structure for data area data */
  {
        int  Bytes_Available;       /* Total bytes of information */
        int  Bytes_Returned;        /* Amount actually returned   */
        char Type_Value_Returned[10];  /* *CHAR, *DEC, or *LGL    */
        char Library_Name[10];
        int  Length_Value_Returned;
        int  Number_Decimal_Positions;
        char Value;                  /* Only using 1 byte of data */
   };
   _Packed struct QWCRDTAA_Output RetrieveDataAreaOutput;

   /* format is 10 character data area name and then 10 character */
   /* Library name.                                               */
   char   DataAreaName[] = "FILEEXIT  QGPL      ";

   /* Retrieve 1 byte of data from data area                      */
   QWCRDTAA (&RetrieveDataAreaOutput,       /* Receiver variable  */
            sizeof(RetrieveDataAreaOutput), /* size of Rcv var    */
            DataAreaName,                   /* data area name     */
            1,                              /* Starting position  */
            1,                              /* Length of data     */
            &errorcode);                    /* Error code         */

return RetrieveDataAreaOutput.Value;
}

/******************************************************************/
/* Function:  xRwrite                                             */
/* Purpose:   Record I/O does not recognize null terminated       */
/*            strings so initialize output string with spaces.    */
/* Input:     same as _Rwrite                                     */
/* Return:    void                                                */
/******************************************************************/
void xRwrite(_RFILE * ptrTmpFile, void * temp, int len)
{
    int i;

    if (strlen( (char *) temp) > 0)
       for (i=strlen((char *) temp); i < MAX_FILENAME_LEN; i++)
         *(((char *) temp) + i) = ' ';
    _Rwrite(ptrTmpFile, temp, MAX_FILENAME_LEN);

   return;
}

Related Information

[{"Type":"MASTER","Line of Business":{"code":"LOB68","label":"Power HW"},"Business Unit":{"code":"BU070","label":"IBM Infrastructure"},"Product":{"code":"SWG60","label":"IBM i"},"ARM Category":[{"code":"a8m0z0000000CS3AAM","label":"Host Servers-\u003EFile server"}],"ARM Case Number":"","Platform":[{"code":"PF012","label":"IBM i"}],"Version":"All Versions"}]

Historical Number

6682234

Document Information

Modified date:
11 April 2025

UID

nas8N1010410