3

I'm trying to write a program in Control Language which creates and populates a configuration file in the IFS prior to calling a Java utility which uses it.

I've succeeded in calling the open() and close() APIs to (re)create the file but for the life of me I cannot get a write() to put something in the file. It has all been a learning experience so far, but with no error message on the write() call I'm having trouble seeing where my problem lies.

It's almost certainly the way I'm handling the variables, but a debug session suggests they are what they should be.

/* ========================================================================= */
/* Processing for the PRCMQMSG command                                       */
/* ========================================================================= */
         PGM        PARM(&ACTION &SRCMQ &TGTMQ)                            

/* Input parameters */                                                         
         DCL        VAR(&ACTION) TYPE(*CHAR) LEN(5)                        
         DCL        VAR(&SRCMQ) TYPE(*CHAR) LEN(125)                       
         DCL        VAR(&TGTMQ) TYPE(*CHAR) LEN(125)                       
/* Parameter elements */                                                       
         DCL        VAR(&SRCHOST) TYPE(*CHAR) STG(*DEFINED) +              
                      LEN(30) DEFVAR(&SRCMQ 3)                             
         DCL        VAR(&SRCPORT) TYPE(*DEC) STG(*DEFINED) +               
                      LEN(5) DEFVAR(&SRCMQ 33)                             
         DCL        VAR(&SRCQMGR) TYPE(*CHAR) STG(*DEFINED) +              
                      LEN(30) DEFVAR(&SRCMQ 36)                            
         DCL        VAR(&SRCCHNL) TYPE(*CHAR) STG(*DEFINED) +              
                      LEN(30) DEFVAR(&SRCMQ 66)                            
         DCL        VAR(&SRCQUE) TYPE(*CHAR) STG(*DEFINED) +               
                      LEN(30) DEFVAR(&SRCMQ 96)              
         DCL        VAR(&TGTHOST) TYPE(*CHAR) STG(*DEFINED) +
                      LEN(30) DEFVAR(&TGTMQ 3)               
         DCL        VAR(&TGTPORT) TYPE(*DEC) STG(*DEFINED) + 
                      LEN(5) DEFVAR(&TGTMQ 33)               
         DCL        VAR(&TGTQMGR) TYPE(*CHAR) STG(*DEFINED) +
                      LEN(30) DEFVAR(&TGTMQ 36)              
         DCL        VAR(&TGTCHNL) TYPE(*CHAR) STG(*DEFINED) +
                      LEN(30) DEFVAR(&TGTMQ 66)              
         DCL        VAR(&TGTQUE) TYPE(*CHAR) STG(*DEFINED) + 
                      LEN(30) DEFVAR(&TGTMQ 96)              
/* IFS details */                                                
         DCL        VAR(&PATH) TYPE(*CHAR) LEN(128) +        
                      VALUE('/mqutil')                       
         DCL        VAR(&CFGFILE) TYPE(*CHAR) LEN(20)        
         DCL        VAR(&JOB) TYPE(*CHAR) LEN(6)             
/* IFS API flags */                                              
         DCL        VAR(&O_RDONLY) TYPE(*INT) VALUE(1)       
         DCL        VAR(&O_WRONLY) TYPE(*INT) VALUE(2)       
         DCL        VAR(&O_RDWR) TYPE(*INT) VALUE(4)         
         DCL        VAR(&O_CREAT) TYPE(*INT) VALUE(8)          
         DCL        VAR(&O_EXCL) TYPE(*INT) VALUE(16)          
         DCL        VAR(&O_TRUNC) TYPE(*INT) VALUE(64)         
         DCL        VAR(&O_APPEND) TYPE(*INT) VALUE(256)       
         DCL        VAR(&O_CODEPAGE) TYPE(*INT) VALUE(8388608) 
         DCL        VAR(&O_TEXTDATA) TYPE(*INT) VALUE(16777216)
         DCL        VAR(&S_IRUSR) TYPE(*INT) VALUE(256)        
         DCL        VAR(&S_IWUSR) TYPE(*INT) VALUE(128)        
         DCL        VAR(&S_IXUSR) TYPE(*INT) VALUE(64)         
         DCL        VAR(&S_IRWXU) TYPE(*INT) VALUE(448)        
         DCL        VAR(&S_IRGRP) TYPE(*INT) VALUE(32)         
         DCL        VAR(&S_IWGRP) TYPE(*INT) VALUE(16)         
         DCL        VAR(&S_IXGRP) TYPE(*INT) VALUE(8)          
         DCL        VAR(&S_IRWXG) TYPE(*INT) VALUE(56)         
         DCL        VAR(&S_IROTH) TYPE(*INT) VALUE(4)          
         DCL        VAR(&S_IWOTH) TYPE(*INT) VALUE(2)          
         DCL        VAR(&S_IXOTH) TYPE(*INT) VALUE(1)          
         DCL        VAR(&S_IRWXO) TYPE(*INT) VALUE(7)          
/* IFS API parameters */                                           
         DCL        VAR(&STROPN) TYPE(*INT) /* Open flags */   
         DCL        VAR(&STRMODE) TYPE(*INT) /* Mode flags */   
         DCL        VAR(&STRPATH) TYPE(*CHAR) LEN(149)          
         DCL        VAR(&STRCODEP) TYPE(*INT) VALUE(819)        
         DCL        VAR(&STRHAND) TYPE(*INT)                    
         DCL        VAR(&NULL) TYPE(*CHAR) LEN(1) VALUE(X'00')  
         DCL        VAR(&CRLF) TYPE(*CHAR) LEN(2) VALUE(X'0D25')
         DCL        VAR(&ERRNO_PTR) TYPE(*PTR)                  
         DCL        VAR(&ERRNO) TYPE(*INT) STG(*BASED) +        
                      BASPTR(&ERRNO_PTR)                        
         DCL        VAR(&ERRNO_CHR) TYPE(*CHAR) LEN(4)          
         DCL        VAR(&MSGID) TYPE(*CHAR) LEN(7)              
         DCL        VAR(&LINE) TYPE(*CHAR) LEN(250)             
         DCL        VAR(&LINE_PTR) TYPE(*PTR) ADDRESS(&LINE)    
         DCL        VAR(&LINELEN) TYPE(*UINT)                   
         DCL        VAR(&LINELEN_D) TYPE(*DEC) LEN(5 0)         

/* Create and open a configuration file in the IFS */               
         RTVJOBA    NBR(&JOB)                                   
         CHGVAR     VAR(&CFGFILE) VALUE('mqconf_' |< &JOB)      
         CHGVAR     VAR(&STRPATH) VALUE(&PATH |< '/' |< &CFGFILE +
                      |< '.connection' |< &NULL)                  
         CHGVAR     VAR(&STROPN) VALUE(&O_WRONLY + &O_CREAT + +   
                      &O_TRUNC + &O_CODEPAGE + &O_TEXTDATA)       
         CHGVAR     VAR(&STRMODE) VALUE(&S_IRWXU + &S_IRWXG + +   
                      &S_IROTH)                                   
         CALLPRC    PRC('open') PARM((&STRPATH) (&STROPN *BYVAL) +
                      (&STRMODE *BYVAL) (&STRCODEP *BYVAL)) +     
                      RTNVAL(&STRHAND)                            
         IF         COND(&STRHAND = -1) THEN(DO)                  
         CALLPRC    PRC('__errno') RTNVAL(&ERRNO_PTR)             
         CHGVAR     VAR(&ERRNO_CHR) VALUE(&ERRNO)                 
         CHGVAR     VAR(&MSGID) VALUE('CPE' || &ERRNO_CHR)        
         SNDPGMMSG  MSGID(&MSGID) MSGF(QCPFMSG) MSGTYPE(*ESCAPE)  
         ENDDO                                                    

/* Write a line to the file */                                        
         CHGVAR     VAR(&LINE) VALUE('This is a line of text in + 
                      a file.' |< &CRLF)                          
         RTVMSG     MSGID(CPF9897) MSGF(QCPFMSG) MSGDTA(&LINE) +  
                      MSGLEN(&LINELEN_D)                   
         CHGVAR     VAR(&LINELEN) VALUE(&LINELEN_D)        
         CALLPRC    PRC('write') PARM((&STRHAND *BYREF) +  
                      (&LINE_PTR *BYREF) (&LINELEN *BYVAL))

/* Close the configuration file */                             
         CALLPRC    PRC('close') PARM((&STRHAND *BYVAL))   

         ENDPGM                                            

I had tried declaring the &LINE and &LINE_PTR variables in the same way as &ERRNO and &ERRNO_PTR but that gave me a Pointer not set error when I tried to assign the value to &LINE. I think that's because the &ERRNO_PTR is accessed first, whereas this was not the case for &LINE_PTR. Anyway, that may be a red herring.

The RTVMSG command is a trick to find the length of the string excluding trailing blanks.

I've run the code in debug and prior to the call to write() &LINE contains exactly what I expect, as does &LINELEN which is 35. Then the write() is called and causes no errors but neither is the line present in the file. The file remains empty.

A final note. All the parameters to the program are currently unused, but I have left in for completeness in case I've done something dumb with them. So essentially, the first two blocks of declarations can be ignored.

Any help appreciated.

Gilles 'SO- stop being evil'
  • 92,660
  • 35
  • 189
  • 229
zkarj
  • 617
  • 9
  • 18
  • You deserve an upvote just for the amount of time it must have taken you to get that far. – James Allman Jun 28 '12 at 07:18
  • JamesA gives a nice way to write simple IFS stream files using only built-in stuff, but if you're willing to install something (free and open source), [iSeries Python](http://www.iseriespython.com/) is a terrific alternative. The more complex the task, the better Python is going to look next to CL. – John Y Jun 28 '12 at 21:46
  • @JohnY [Groovy](http://groovy.codehaus.org/) works great on the iSeries too. All of the power of Java with the simplicity and interactivity of Python. – James Allman Jun 28 '12 at 22:13
  • @JamesA: Groovy is a great "gateway dynamic language" for Java programmers, but *iSeries Python* is particularly well-suited to the IBM i because it is a native i application (rather than a JVM application) and has tighter integration with OS/400 (I forget what the current term for the operating system is). iSeries Python is more than just a PASE-ified version of Python. – John Y Jun 28 '12 at 22:26
  • I always prefer to keep my code as 'native' as possible. I usually shy away from using TAATOOL, even though we've had it licensed for years, simply because "it could go away". Whereas using native APIs is guaranteed to continue to work and I can port it anywhere. I've got the final tool working now using the QSH method. I can now enter a simple, prompted command instead of hand editing config files each time. I'll wait a little while and see if anyone comes up with the answer to the write() question and if not, I'll mark QSH as the answer. – zkarj Jun 29 '12 at 01:57

3 Answers3

4

You can use QSH and redirection to write to the IFS a lot easier than trying to use the C API.

/* DISABLE STDOUT */
ADDENVVAR ENVVAR(QIBM_QSH_CMD_OUTPUT) VALUE('NONE') REPLACE(*YES)

/* CREATE THE FILE WITH CCSID 819 */
ADDENVVAR ENVVAR(STRPATH) VALUE(&STRPATH) REPLACE(*YES)
QSH CMD('rm $STRPATH; touch -C 819 $STRPATH')

/* APPEND A LINE TO THE FILE */
ADDENVVAR ENVVAR(LINE) VALUE(&LINE) REPLACE(*YES)
QSH CMD('echo "$LINE">>$STRPATH')

For more information:


If you need higher performance you can use a more flexible HLL to access the API. Scott Klement has a great ebook titled Working with the IFS in RPG IV.

James Allman
  • 38,410
  • 9
  • 53
  • 69
  • Thanks James, that does indeed look far simpler! Part of me (the hacker/programmer part) still wants to know why the write() isn't working, but at the end of the day, getting the job done is what counts. – zkarj Jun 28 '12 at 21:34
  • Ooh, slight correction to your code. The first parameter to ADDENVVAR is ENVVAR() not VAR(). But that's what F4 is for, right. :-) – zkarj Jun 28 '12 at 21:42
  • Hallelujah! It seems a little slow, but that doesn't matter in this case. And it works! ` ************Beginning of data************** This is a line of text in a file. ************End of Data******************** ` Now to make this useful... :-) – zkarj Jun 28 '12 at 21:47
  • @zkarj Corrected, thanks. The original answer used variable concatenation but I changed it to use environment variables for a cleaner example. – James Allman Jun 28 '12 at 22:22
  • 1
    It's never a bad thing to point someone to Scott Klement's stuff, which is uniformly excellent. And in my personal experience, RPG IV is pretty much the sweet spot in terms of balancing ease of programming and speed of execution. But when working with both QSYS.LIB files and IFS stream files, I don't think anything beats iSeries Python for ease of programming (and its performance is often better than Java's on the i). – John Y Jun 28 '12 at 22:34
0

I was looking for how to use the IFS APIs from CL and came across your code. I think your problem with the write is the file handle (first parameter) needs to be passed by value. Also, don't specify O_CODEPAGE in the &STROPN variable as it conflicts with the code page parameter. I am using this method and am able to get a nice trace file for a service program I am working on.

0

Its just a small error in calling write function, handle should passed by value, instead of byref Find below corrected write call, i tested it and its working

CALLPRC    PRC('write') PARM((&STRHAND *BYVAL) +    
         (&LINE_PTR *BYREF) (&LINELEN *BYVAL))