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 value—the 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 0—the returned path value is a character string with path delimiters that are one character long, 1—the returned path value is a pointer to a character string with path delimiters that are one character long, 2—the returned path value is a character string with path delimiters that are two characters long, 3—the 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 long—and I mean really long—path 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 subfields—beyond 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
LATEST COMMENTS
MC Press Online