|
Hi Dan... I'll quote back your message, and reply at the bottom... "Dan Bale" <dbale@genfast.com> wrote: > > I am looking for example code that shows how to process > an API-created list in a user space. The V4R2 ILE RPG > Reference has an example program in chapter 10 under a > section titled "Basing Pointer Data Type"; the Softcopy > reference is 2.3.8.1. This example program happens to > show how to use the QUSLMBR api, which is exactly the > one I was looking to use. Unfortunately, it was (IMHO) > poorly written, and did not compile due to a syntax > error. I was able to fix that error plus others so that > the program compiled and runs, (correctly, I think), but > I'm having a difficult time fully understanding how > exactly list processing works. > > In the particular example program, even though the > initial size of the user space is 9,999,999 bytes, it > would appear that the maximum length of the usable user > space is 32,767 bytes (based on the D-spec for the SP1 > variable). But I also see a reference to DIM(32767) for > a 10-character array size that seems to reference the > list in the user space. > > Is there reference material out there that explains this > stuff? Does anyone have example code they could share? > I would also be interested in example code that shows > how to process a user index. > I'm not really sure what you're trying to do here, or why you've got the initial size of the user space set so high! (I'd be very surprised to find a database with so many members that the list was 10 mb!) You also (in the above message) made references to both user spaces and user indexes. These are two completely different things. A user space is basically an area of memory/disk. You might think of it as being like a data area, except that it can be VERY big... A user index is more like a database file. You can look things up by "key", and you have data thats associated with that key. Since you need to use the QUSLMBR API, you want to use a user space. The way it works is, the QUSLMBR API puts data into the user space containing the information that you want. That makes it possible for it to send huge amounts of data without multiple calls to the API. The QUSLMBR API puts data in a "generic list format", which is also used by most (if not all) of the "list to userspace" APIs that IBM provides. The data in the user space starts with a "generic header". And this header basically contains everything your program will need to know in order to be able to read the list. It tells you where the API-specific header information is, where the input parameter is, and most importantly, where the list data begins, how big each entry in the list is, and how many entries there are. The entries themselves will be in the format that you specify in the "format" parameter to the API, in this case "MBRL0100" For detailed info on the generic list format see: http://publib.boulder.ibm.com:80/cgi-bin/bookmgr/DOCNUM/SC41-5801-01/ HDRFMTRTVI#HDRFMTRTVI In the example code (below) this is what I'm doing: 1) Creating a user space with an initial size of 1 byte. (The QUSLMBR API will expand it as necessary, so we're specifying a minimal size to save some disk space & some effort...) Since I'm making the user space in QTEMP, and specifying *YES for the Replace option, I'm not worried about the space already existing. In a "real" application, you'd probably want to check for an error after calling the create user space API, by checking the value of dsECBytesA. For this sample, I'm skipping that :) 2) Telling the API to send a list of all members in the file LIBSOR/QRPGLESRC (thats my ILE RPG/400 source file) to the user space. I'm telling it that each entry in the user space should be in the MBRL0100 format. If I wanted more info about each member, I might use the MBRL0200 format, which would give me info like source type, create date, change date, etc. Again, I should REALLY be checking for an error here... but for this sample, I will not. You SHOULD however, so if it can't use/find the file I gave, it doesn't cause problems... When the QUSLMBR API is complete, my list of members will be in the MBRDATA user space in library QTEMP. Now I've got to READ that space :) 3) Now I'm asking OS/400 to set a pointer (from my program) to point to the area of memory/disk that the user space is in. This is handy, because its a fast and (semi-) easy way to read the user space. Since the pointer thats returned is the start of the user space, I'm using the pointer for my "generic header info" data structure. (p_LH -- which in my little world means "pointer to list header") Now, I've got the dsLH (List Header Data Structure) "BASED" on that same pointer. This means that after the QUSPTRUS API completes, my dsLH data structure will be located in the same area of memory/disk as the user space is... Since my data struct is in the format of the List Header, this effectively populates my data structure with the header info. 4) Really, all I'm interested in is getting the member names from the user space. So, all I need from the header is: a) Where is the list? b) How many items are in the list? c) How far to I have to advance my pointer to go to the next entry in the list? The answer to (a) is in dsLHLstOff, which is the offset from the start of the user space to the list data. In other words, its how many bytes I have to advance my pointer from dsLH to get to the start of the list... The answer to (b) is dsLHLstCnt. The number of entries returned from the QUSLMBR API. The answer to (c) is dsLHEntSiz. The size of each entry is also the number of bytes I need to advance my pointer to get to the next entry. Make sense? the first entry is dsLHLstOff bytes from the start... the second is dsLHLstOff + dsLHEntSiz bytes from the start, the third is dsLHLstOff + (dsLHEntSize*2) bytes from the start, etc. So, I loop through each entry, calculate the offset and move the pointer for my list data (p_MbrName -- pointer to the member name) to the new offset. Then I can read the member name from the BASED variable (MbrName) NOTE: The OffsetPtr sub-proc simply advances the pointer by however many bytes I specify. It does this by putting a character string in the area of memory specified by its first parameter, and then finding the address of the position thats (parm 2) bytes later in the string. The reason I'm using this routine is because I'm at V3R2 of OS/400, which doesn't support directly adding to the value of a pointer! At V4R2, however, this should not be necessary You should be able to do the pointer arithmetic directly.... Instead of doing: p_MbrName = OffsetPtr(p_LH: Offset) You could do: p_MbrName = p_LH + Offset Hope that helps... Scott Klement Information Systems Manager Klement's Sausage Co, Inc. D* Create User Space API D* D CrtUsrSpc PR ExtPgm('QUSCRTUS') D UsrSpc 20A CONST D ExtAttr 10A CONST D InitSiz 10I 0 CONST D InitVal 1A CONST D PubAuth 10A CONST D Text 50A CONST D Replace 10A CONST D ErrorCode 256A D* Retrieve Pointer to User Space API D* D RtvPtrUS PR ExtPgm('QUSPTRUS') D UsrSpc 20A CONST D PtrUsrSpc * D* Sub-Procedure to Offset a Pointer (LOCAL) D* D OffsetPtr PR * D pePointer * D peOffset 10I 0 Value D* API Error Code Structure D* D dsEC DS D* Bytes Provided (size of struct) D dsECBytesP 1 4B 0 D* Bytes Available (returned by API D dsECBytesA 5 8B 0 D* Msg ID of Error Msg Returned D dsECMsgID 9 15 D* Reserved D dsECReserv 16 16 D* Msg Data of Error Msg Returned D dsECMsgDta 17 256 D* List Header Data Structure D* D p_LH S * D dsLH DS BASED(p_LH) D* Filler D dsLHFill1 103A D* Status (I=Incomplete,C=Complete D* F=Partially Complete) D dsLHStatus 1A D* Filler D dsLHFill2 12A D* Header Offset D dsLHHdrOff 10I 0 D* Header Size D dsLHHdrSiz 10I 0 D* List Offset D dsLHLstOff 10I 0 D* List Size D dsLHLstSiz 10I 0 D* Count of Entries in List D dsLHEntCnt 10I 0 D* Size of a single entry D dsLHEntSiz 10I 0 D* List Members (QUSLMBR) API D* D ListMbrs PR ExtPgm('QUSLMBR') D UsrSpc 20A CONST D Format 8A CONST D DataBase 20A CONST D Member 10A CONST D Override 1A CONST D ErrorCode 256A D* Data Section of List for QUSLMBR API D* D p_MbrName S * D MbrName S 10A BASED(p_MbrName) D Offset S 10I 0 c* set length of Error Code Structure c eval dsECBytesP = 256 C* (1) create a user space C callp CrtUsrSpc('MBRDATA QTEMP':'USRSPC': C 1:x'00':'*ALL':'List of Members in File' c '*YES': dsEC) C* (2) list members into user space C callp ListMbrs('MBRDATA QTEMP':'MBRL0100': C 'QRPGLESRC LIBSOR':'*ALL':'0':dsEC) C* (3) get a pointer to the start C* of the user space... C callp RtvPtrUS('MBRDATA QTEMP':p_LH) C* (4) Loop through all the C* list entries returned. C do dsLHEntCnt Entry 4 0 C* calculate the number of bytes from the start C* of the user space, and put our pointer there... c eval Offset = ((Entry-1) * dsLHEntSiz) c + dsLHLstOff c eval p_MbrName = OffsetPtr(p_LH: Offset) C* show the result c MbrName dsply c enddo C* thats it! c eval *INLR = *On c Return P*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ P* Return a pointer at a specified offset value from another ptr P* P* (Since IBM hates me and won't let me do pointer arithmetic P* without spending a fortune on a RISC box) P*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ P OffsetPtr B D OffsetPtr PI * D pePointer * D peOffset 10I 0 Value D p_NewPtr S * D wkMove S 1A DIM(4097) BASED(p_NewPtr) c eval p_NewPtr = pePointer c if peOffset > 0 C dow peOffset > 4096 C eval p_NewPtr = %addr(wkMove(4097)) c eval peOffset = peOffset - 4096 c enddo C eval p_NewPtr = %addr(wkMove(peOffset+1)) c endif c return p_NewPtr P E * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * This is the RPG/400 Discussion Mailing List! To submit a new * * message, send your mail to "RPG400-L@midrange.com". To unsubscribe * * from this list send email to MAJORDOMO@midrange.com and specify * * 'unsubscribe RPG400-L' in the body of your message. Questions should * * be directed to the list owner / operator: david@midrange.com * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
As an Amazon Associate we earn from qualifying purchases.
This mailing list archive is Copyright 1997-2024 by midrange.com and David Gibbs as a compilation work. Use of the archive is restricted to research of a business or technical nature. Any other uses are prohibited. Full details are available on our policy page. If you have questions about this, please contact [javascript protected email address].
Operating expenses for this site are earned using the Amazon Associate program and Google Adsense.