The API Corner: Processing the QLOCALE System Value

APIs
Typography
  • Smaller Small Medium Big Bigger
  • Default Helvetica Segoe Georgia Times

Find out how to use internationalized IFS path names.

 

This is the fourth in a series of articles related to dynamically accessing and processing system values. In the first article, "In Search of System Values," we saw how the Retrieve Command Definition (QCDRCMDD) API could be used to access the list of valid system values for the release of the IBM i our program is running on. This list of valid system values was retrieved by using the IBM-provided Display System Value (DSPSYSVAL) command and by then accessing the list of special values associated with the SYSVAL keyword.

In the second article, "Accessing System Values," we saw how the Retrieve System Values (QWCRSVAL) API could be used to access the current values for each system value and how to display the current value for each of the system values, other than QLOCALE, using a subfile.

In the third article, "Performing CCSID Conversions Under Program Control," we reviewed the mechanics of using the Code Conversion Allocation (QtqIconvOpen), Code Conversion (iconv), and Code Conversion Deallocation (iconv_close) APIs to convert the XML UTF-8 output of the QCDRCMDD API to our job CCSID. This conversion was done to simplify the parsing of the SYSVAL keyword special values of the DSPSYSVAL command and to meet the input requirements of the QWCRSVAL API. In this article, we'll look at how to process the returned value of the QLOCALE system valuethe one system value we have not previously handled.

Most system values on the i are returned to you as simple fixed-length character strings, arrays of fixed-length character strings, or binary/integer values. The QLOCALE system value is an exception to this. The QLOCALE system value is returned as a data structure where subfields of the structure define various attributes of the QLOCALE path value. This returned structure is common to many system APIs that work with IFS path names, is documented here, and is provided in member QLG of source file QSYSINC/QRPGLESRC as the data structure QLGPN. The full structure is formatted as…

  • A 4-byte integer identifying the CCSID of the returned locale path name (with a value of 0 representing the current job default CCSID)
  • A 2-byte character field identifying the country or region ID associated with the returned locale path name (with a value of x'0000' representing the current job country of region ID)
  • A 3-byte character field identifying the language ID associated with the returned locale path name (with a value of x'000000' representing the current job language ID)
  • A 3-byte character field that is reserved (and is set to all x'00's)
  • A 4-byte integer indicating the type of locale path name returned. Possible returned values are 0the returned path value is a character string with path delimiters that are one character long, 1the returned path value is a pointer to a character string with path delimiters that are one character long, 2the returned path value is a character string with path delimiters that are two characters long, 3the returned path value is a pointer to a character string with path delimiters that are two characters long
  • A 4-byte integer providing the length of the locale path name in bytes
  • A 2-byte character field identifying the delimiter character used within the locale path name to separate elements of the path. This is most often the slash (/) character and is encoded in the same CCSID as the path
  • A 10-byte character field that is reserved (and is set to all x'00's)
  • Either a variable-length character string containing the path name or a pointer to a variable-length character string containing the path name

The QWCRSVAL API does not, however, use all of the capabilities of this structure. For instance, the QWCRSVAL API will return only the character string value of the QLOCALE path name, not a pointer to the path name. Other APIs, however, do use this pointer capability (which, among other things, helps avoid having to move potentially longand I mean really longpath names across different parameter interfaces).

On the other hand, the QWCRSVAL API also has unique requirements that cause the API to extend the use of some of these data structure subfieldsbeyond what is described above. The biggest requirement is the support of special values such as *NONE, *C, and *POSIX, which you won't find on most system API IFS path interfaces. In order to support the use of these special values, the QWCRSVAL API follows these rules:

  • Special values are always returned in the default CCSID of the job while non-special values (that is, "real" path names) are returned in a Unicode CCSID (13488 or 1200).
  • The special value *NONE is returned with a path length in bytes of 0.
  • The special values *C and *POSIX are returned with a path length in bytes of 1.
  • All real path names are returned with a path length greater than or equal to 2. This minimum value of 2 is due to all real path names being returned in Unicode where the encoding has a minimum of 2 bytes per character.

Implementing the above rules, here is an updated version of the LSTSYSVAL program that displays the first 60 bytes of the value of the QLOCALE system value (earlier versions simply displayed the text 'Not displayed').

h dftactgrp(*no)                                            

                                                            

fLstSVDspf cf   e             workstn sfile(SV_Sfl :RRNS1)  

                                                            

dRtvCmdD         pr                 extpgm('QCDRCMDD')    

d Cmd                           20a   const                  

d LenRcvVar                     10i 0 const                  

d DestFormat                     8a   const                  

d RcvVar                     65535a                          

d RcvVarFormat                   8a   const                  

d ErrCde                             likeds(QUSEC)          

                                                            

dConvertBuffer   pr                                        

                                                            

dDoLocale         pr                                        

                                                            

dmyHandler       pr           10i 0                      

d Controls                           likeds(HandlerInfo)  

d Event                         10i 0 value                

d StringPtr                       *   value                

d LenString                     20i 0 value                

d ExceptionID                   10i 0 value                

                                                            

dGetSysVals       pr                 extpgm('QWCRSVAL')  

d RcvVar                     65535a                        

d LenRcvVar                     10i 0 const                

d NbrSysVals                    10i 0 const                

d SysVals                       10a   dim(MaxSysVals)      

d ErrCde                             likeds(QUSEC)        

                                                          

dCmdD_Job         s         65527a                        

dRRNS1           s             4s 0                      

                                                          

dMaxSysVals       c                   const(300)          

dSysVals         s             10a   dim(MaxSysVals)      

                                                          

dCmdD_RcvVar     ds                 qualified            

d Ctl                                 likeds(QCDD0100)    

d CmdD_UTF8                 65527a                        

                                                         

dSysVal_RcvVar   ds         65535                        

d NbrSysValsRtn                 10i 0                      

                                                          

dSysValOfsPtr    s               *                        

dSysValOfs       s             10i 0 based(SysValOfsPtr)  

                                                          

dSVPtr           s               *                        

dSV               ds                qualified            

d                                     based(SVPtr)        

d Ctl                                 likeds(QWCRSVT)      

d CData                     10000a                            

d BData                         10i 0 overlay(CData :1)        

                                                              

dControls         ds                 likeds(HandlerInfo)      

dHandlerInfo     ds                 qualified based(NoPtr)  

d TopSysVal                    10i 0                          

d ParmFnd                         n                            

d KwdFnd                         n                            

d SysValFnd                       n                            

d SpcValFnd                      n                            

d ValueFnd                       n                            

d ValFnd                         n                            

                                                              

/copy qsysinc/qrpglesrc,qusec                                

/copy qsysinc/qrpglesrc,qcdrcmdd                              

/copy qsysinc/qrpglesrc,qwcrsval                              

                                                              

/free                                                          

                                                                

// Get command definition as a XML document. The document is  

// encoded in UTF8 (CCSID 1208).                              

                                                                

QUSBPrv = 0;                                                  

RtvCmdD('DSPSYSVAL QSYS' :%size(CmdD_RcvVar) :'DEST0100'      

         :CmdD_RcvVar :'CMDD0100' :QUSEC);                      

                                                                

// CmdD_UTF8 now contains the XML document in CCSID 1208.      

// The system values can be found in <Parm Kwd="SYSVAL" under  

// <SpcVal>. The first special value, for instance, is        

// <Value Val="QABNORMSW" MapTo="QABNORMSW"/>. The end of the  

// special values is, naturally, delimited by </SpcVal>.      

                                                                

// Convert command definition to job CCSID so you can "see"    

// the generated XML                                          

                                                              

ConvertBuffer();                                            

                                                                

// CmdD_Job now contains the XML document in the job CCSID.  

// Parse the XML document for SYSVAL special values          

                                                              

xml-sax %handler(myHandler :Controls) %xml(CmdD_Job);        

                                                              

if Controls.TopSysVal > 0;                                  

     // If any system values found then use QWCRSVAL to access

     // their current values                                  

                                                              

     GetSysVals(SysVal_RcvVar :%size(SysVal_RcvVar)            

               :Controls.TopSysVal :SysVals :QUSEC);          

                                                                

     *in32 = *on;                                              

     write SV_Ctl;                                            

     *in32 = *off;                                                

                                                                    

     SysValOfsPtr = %addr(SysVal_RcvVar) + %size(NbrSysValsRtn);  

     for RRNS1 = 1 to NbrSysValsRtn;                              

         SVPtr = %addr(SysVal_RcvVar) + SysValOfs;                

         SysValName = SV.Ctl.QWCSV;                                

         select;                                                  

           when SV.Ctl.QWCIS02 = 'L';                            

                 SVValue = '*LOCKED';                              

                                                                  

           when SysValName = 'QLOCALE';                          

                 DoLocale();                                      

                                                                  

           when SV.Ctl.QWCTD00 = 'C';                            

                 if SV.Ctl.QWCLD00 <= %size(SVValue);              

                   SVValue = %subst(SV.CData :1 :SV.Ctl.QWCLD00);

                 else;                                            

                   SVValue = %subst(SV.CData :1 :%size(SVValue));

                 endif;                                            

                                                                    

           when SV.Ctl.QWCTD00 = 'B';                            

                 SVValue = %editc(SV.BData :'X');                  

                                                                  

           other;                                                  

                 SVValue = '** PROBLEM **';                        

         endsl;                                                    

                                                                  

        write SV_Sfl;                                            

         SysValOfsPtr += 4;                                        

     endfor;                                                      

endif;                                                          

                                                                  

*in31 = (RRNS1 > 0);                                            

write SV_Keys;                                                  

dow (not *in03);                                                

     exfmt SV_Ctl;                                              

enddo;                                                          

                                                                  

*inlr = *on;                                                    

return;                                                        

                                                                  

/end-free                                                        

****************************************************************

pConvertBuffer   b                                              

dConvertBuffer   pi                                              

                                                                  

dIconvOpen       pr           52   extproc('QtqIconvOpen')    

d ToCode                       32   const                      

d FromCode                     32   const                      

                                                                  

dIconv           pr           10i 0 extproc('iconv')            

d iconv_t                       52   value                          

d InputPtr                       *                                  

d BytesToCvt                  10u 0                                

d OutputPtr                       *                                  

d BytesAvlForCvt               10u 0                                

                                                                      

dIConvClose       pr           10i 0 extproc('iconv_close')          

d iconv_t                       52   value                          

                                                                      

dInputPtr         s              *   inz(%addr(CmdD_RcvVar.CmdD_UTF8))

dOutputPtr       s               *   inz(%addr(CmdD_Job))            

dBytAvl_CmdD     s             10u 0 inz(%size(CmdD_Job))            

dRtnVal           s             10i 0                                  

                                                                      

dFromCode         ds                 qualified                      

d CCSID                         10i 0 inz(1208)                      

d ConvAlt                       10i 0 inz(0)                          

d SubstAlt                     10i 0 inz(0)          

d SSAlt                         10i 0 inz(0)          

d InpLenOpt                     10i 0 inz(0)          

d ErrOpt                       10i 0 inz(0)          

d                               8   inz(*ALLx'00')  

                                                      

dToCode           ds                 qualified      

d CCSID                         10i 0 inz(0)          

d ConvAlt                      10i 0 inz(0)          

d SubstAlt                     10i 0 inz(0)          

d SSAlt                         10i 0 inz(0)          

d InpLenOpt                     10i 0 inz(0)          

d ErrOpt                       10i 0 inz(0)          

d                               8   inz(*ALLx'00')  

                                                      

diconv_t         ds                                  

d                               10i 0 dim(13)        

                                                                

/free                                                          

                                                                

iconv_t = IconvOpen(ToCode :FromCode);                        

RtnVal = Iconv(iconv_t :InputPtr :CmdD_RcvVar.Ctl.QCDBRtn01    

                         :OutputPtr :BytAvl_CmdD);              

RtnVal = IconvClose(iconv_t);                                  

                                                                

/end-free                                                      

                                                                

pConvertBuffer   e                                              

****************************************************************

pmyHandler       b                                              

dmyHandler       pi           10i 0                            

d Controls                           likeds(HandlerInfo)        

d Event                         10i 0 value                      

d StringPtr                       *   value                      

d LenString                     20i 0 value                

d ExceptionID                   10i 0 value                

                                                            

dString           s         65535a   based(StringPtr)    

                                                          

/free                                                    

                                                          

select;                                                  

     when Event = *XML_START_DOCUMENT;                    

                                                          

         Controls.TopSysVal = 0;                          

         Controls.ParmFnd = *off;                        

         Controls.KwdFnd = *off;                          

         Controls.SpcValFnd = *off;                      

         Controls.ValueFnd = *off;                        

         Controls.ValFnd = *off;                          

                                                          

     when ((Event = *XML_START_ELEMENT) and              

           (%subst(String :1 :LenString)) = 'Parm');    

                                                        

         Controls.ParmFnd = *on;                        

                                                        

     when ((Event = *XML_ATTR_NAME) and                  

           (Controls.ParmFnd) and                        

          (%subst(String :1 :LenString)) = 'Kwd');      

                                                        

         Controls.KwdFnd = *on;                        

                                                        

     when ((Event = *XML_ATTR_CHARS) and                

           (Controls.KwdFnd));                          

                                                        

         if %subst(String :1 :LenString) = 'SYSVAL';    

             Controls.SysValFnd = *on;                    

         endif;                                        

         Controls.KwdFnd = *off;                            

                                                              

     when ((Event = *XML_START_ELEMENT) and                  

           (Controls.SysValFnd) and                          

           (%subst(String :1 :LenString)) = 'SpcVal');        

                                                              

         Controls.SpcValFnd = *on;                          

                                                              

     when ((Event = *XML_START_ELEMENT) and                  

           (Controls.SpcValFnd));                            

                                                              

         Controls.ValueFnd =                                

                   (%subst(String :1 :LenString) = 'Value');  

                                                              

     when ((Event = *XML_ATTR_NAME) and                      

           (Controls.ValueFnd));                              

                                                              

         Controls.ValFnd =                                  

                   (%subst(String :1 :LenString) = 'Val');    

                                                              

     when ((Event = *XML_ATTR_CHARS) and                      

           (Controls.ValFnd));                                

                                                              

         Controls.TopSysVal += 1;                            

         SysVals(Controls.TopSysVal) =                      

                 %subst(String :1 :LenString);              

         Controls.ValFnd = *off;                            

                                                              

     when ((Event = *XML_END_ELEMENT) and                    

           (Controls.SpcValFnd) and                          

           (%subst(String :1 :LenString) = 'Value'));        

                                                              

         Controls.ValueFnd = *off;                          

                                                              

     when ((Event = *XML_END_ELEMENT) and              

           (Controls.SysValFnd) and                    

           (%subst(String :1 :LenString)) = 'SpcVal');  

                                                        

         Controls.SpcValFnd = *off;                    

                                                        

     when ((Event = *XML_END_ELEMENT) and              

           (%subst(String :1 :LenString)) = 'Parm');    

                                                        

         Controls.SysValFnd = *off;                    

         Controls.ParmFnd = *off;                      

                                                        

     other;                                            

         // Ignore                                    

endsl;                                                

                                                        

return 0;                                            

                                                                

/end-free                                                      

                                                                

pmyHandler       e                                              

****************************************************************

pDoLocale        b                                              

dDoLocale         pi                                            

                                                                

dIconvOpen       pr           52   extproc('QtqIconvOpen')    

d ToCode                       32   const                      

d FromCode                     32   const                      

                                                                

dIconv           pr           10i 0 extproc('iconv')            

d iconv_t                       52   value                      

d InputPtr                       *                              

d BytesToCvt                   10u 0                            

d OutputPtr                       *                              

d BytesAvlForCvt               10u 0                            

                                                                

dIConvClose       pr           10i 0 extproc('iconv_close')    

d iconv_t                     52   value                      

                                                                

dInputPtr         s               *                              

dOutputPtr       s               *   inz(%addr(SVValue))        

dBytesToCvt       s             10u 0                            

dBytAvl_Path     s             10u 0 inz(%size(SVValue))        

dRtnVal           s             10i 0                            

                                                                

dFromCode         ds                 qualified                  

d CCSID                         10i 0                            

d ConvAlt                       10i 0 inz(0)                    

d SubstAlt                     10i 0 inz(0)                    

d SSAlt                         10i 0 inz(0)                    

d InpLenOpt                     10i 0 inz(0)                    

d ErrOpt                       10i 0 inz(0)            

d                               8   inz(*ALLx'00')    

                                                      

dToCode           ds                 qualified        

d CCSID                         10i 0 inz(0)            

d ConvAlt                       10i 0 inz(0)            

d SubstAlt                      10i 0 inz(0)            

d SSAlt                         10i 0 inz(0)            

d InpLenOpt                     10i 0 inz(0)            

d ErrOpt                       10i 0 inz(0)            

d                               8   inz(*ALLx'00')    

                                                        

diconv_t         ds                                    

d                               10i 0 dim(13)          

                                                        

dPathDSPtr        s               *                    

dPathDS           ds                 qualified        

d                                     based(PathDSPtr)    

d Hdr                                 likeds(QLGPN)      

d Path                       2048a                      

                                                          

/copy qsysinc/qrpglesrc,qlg                              

                                                          

/free                                                    

                                                          

PathDSPtr = %addr(SV.CData);                            

                                                          

if ((PathDS.Hdr.QLGPL = 0) or                          

     (PathDS.Hdr.QLGPL = 1));                            

     SVValue = PathDS.Path;                              

else;                                                  

     FromCode.CCSID = PathDS.Hdr.QLGCCSID02;              

     iconv_t = IconvOpen(ToCode :FromCode);              

                                                          

     InputPtr = %addr(PathDS.Path);                    

     BytesToCvt = PathDS.Hdr.QLGPL;                    

     SVValue = *blanks;                                

     RtnVal = Iconv(iconv_t :InputPtr :BytesToCvt      

                           :OutputPtr :BytAvl_Path);  

                                                      

     RtnVal = IconvClose(iconv_t);                    

endif;                                              

                                                      

/end-free                                            

                                                      

pDoLocale         e                                    

The changes from last month are the…

  • addition of a prototype for subprocedure DoLocale()
  • change in processing when the QLOCALE system value is encountered. Previously, LSTSYSVAL displayed the text 'Not displayed' while now the subprocedure DoLocale() is run
  • addition of subprocedure DoLocale()

Assuming that the source shown previously is stored in member LSTSYSVAL of source file QRPGLESRC, you can create the program using the command CRTBNDRPG PGM(LSTSYSVAL). To run the program, use the command CALL PGM(LSTSYSVAL).

Subprocedure DoLocale() duplicates many prototype and local variables of subprocedure ConvertBuffer(). This is done so that we can concentrate on the actual processing of DoLocale() and does not imply that I would really implement LSTSYSVAL with all of this duplication.

The processing within DoLocale() is straightforward enough. As an instance of the QSYSINC-provided QLGPN structure is defined as PathDS and based on the pointer PathDSPtr, the first thing done in DoLocale is the setting of PathDSPtr to the address of the system value locale information returned by QWCRSVAL. With addressability to the structure established, DoLocale() checks to see if the length of the returned path is 0 or 1. If so, then a special value has been specified for the QLOCALE system value and this special value is copied to the subfile field SVValue for display. If the length of the returned path is not 0 or 1, then DoLocale() performs a CCSID conversion of the returned path name. This CCSID conversion is done using the same APIs that were discussed last month in the article "Performing CCSID Conversions Under Program Control."

The differences between last month's discussion of ConvertBuffer() and this month's DoLocale() are…

  • The CCSID to convert from (FromCode.CCSID) is set to the Unicode CCSID value returned by the QWCRSVAL API (PathDS.Hdr.QLGCCSID02).
  • The variable InputPtr is set to the address of the returned Unicode-encoded path (PathDS.Path).
  • The number of bytes to convert is set to the length of the returned Unicode-encoded path (PathDS.Hdr.QLGPL).
  • The variable OutputPtr is set to the address of the subfile field SVValue (SVValue being used to display system value values).
  • The number of bytes available for the CCSID converted path data, BytAvl_Path, is set to the size of the SVValue subfile field.

As I know that LSTSYSVAL (as currently implemented) will be displaying only the first 60 bytes of the iconv() converted QLOCALE path, DoLocale() is taking advantage of a feature of the iconv() API. The feature is that the iconv() API will return only that number of full characters that will fit in the number of bytes available for converted data (BytAvl_Path). So if the number of bytes available for the CCSID converted path is 60, and the actual length of the converted path is, say, 100 bytes, then iconv() will stop after writing the first 60 bytes to SVValue, which is exactly what we want. If the actual length of the converted path is less than 60 bytes, then iconv() will write only the actual number of converted bytes to SVValue, which is again exactly what we want (as DoLocale() previously set SVValue to blanks, so there is no concern about residual values from previous system value values).

If we did want LSTSYSVAL to display the full QLOCALE path value, as opposed to only the first 60 bytes, then we could take one of two approaches. One method would be to convert the QWCRSVAL returned path into a buffer sufficiently large to hold all converted data and then write a subfile record for the first 60 bytes, a subfile record for the next 60 bytes, and so on. A second approach would be to have iconv() iteratively return 60 bytes of converted data where the first iteration returns the fist 60 bytes, the second iteration the next 60 bytes, and so on (with each iteration writing one subfile record).

In a future article, we'll look at how to have the iconv() API restart a data conversion within a buffer of data to be converted. Following the current discussion, that would be starting the conversion at the first byte of PathDS.Path, restarting the conversion at the input byte of PathDS.Path associated with the 61st converted byte, restarting the conversion at the input byte of PathDS.Path associated with the 121st converted byte, and so on.

As usual, if you have any API questions, send them to me at This email address is being protected from spambots. You need JavaScript enabled to view it.. I'll see what I can do about answering your burning questions in future columns.

BLOG COMMENTS POWERED BY DISQUS