4.4. improvement #4: Creating a Write Line utility

In the last section, we wrote a routine that would read lines of text into a variable. This would make a nice utility routine that we can put into a service program.

In this section, we'll add another utility routine, one that can be used to send lines of text.

The requirements of this routine are a little different, because it's output-only. That means we don't need to write any data to a buffer, just take data that we already have and send it out. Since the routines that are calling this will often want to pass literals, constants, and expressions, it makes sense for us to accept an 'const' parameter, rather than a pointer.

Also, it'd be a bit of a pain, especially when working with literals, for the calling routine to have to calculate the length of the string each time it calls us. So, instead we'll figure that out ourselves by looking for the last non-blank character. We can still give an optional 'length' parameter, just in case the caller wishes to override this.

This method of searching for the last 'non-blank' character will be slow in most cases with a 32k buffer. Since 99% of the time, a line is less than 80 bytes long, this means that it'd have to search over 32,000 blank spaces each time we send a line. To make this a bit more realistic, the buffer on a call to the 'send line' procedure will only be 256 bytes.

Like the 'read line' routine, though, we still want to allow the caller the option of specifying different end-of-line characters. In RdLine() if the caller only wants one character to signify end-of-line, they can still do so... just make the extra parm be the same char, it won't hurt anything. When sending, however, that would cause problems. So, we'll determine how many chars to add for end-of-line based on how many parms are passed, instead of trying to use both like we did in RdLine().

I think that pretty much sums up the requirements. Here's the routine I actually came up with, check it out:

(This should be added to the member SOCKUTILR4 in the file QRPGLESRC)

         P WrLine          B                   Export
         D WrLine          PI            10I 0
         D  peSock                       10I 0 value
         D  peLine                      256A   const
         D  peLength                     10I 0 value options(*nopass)
         D  peXLate                       1A   const options(*nopass)
         D  peEOL1                        1A   const options(*nopass)
         D  peEOL2                        1A   const options(*nopass)
         D wwLine          S            256A
         D wwLen           S             10I 0
         D wwXlate         S              1A
         D wwEOL           S              2A
         D wwEOLlen        S             10I 0
         D rc              S             10I 0
         C* Allow this procedure to figure out the
         C*  length automatically if not passed,
         C*  or if -1 is passed.
         c                   if        %parms > 2 and peLength <> -1
         c                   eval      wwLen = peLength
         c                   else
         c                   eval      wwLen = %len(%trim(peLine))
         c                   endif
         C* Default 'translate' to *ON.  Usually
         C*  you want to type the data to send
         C*  in EBCDIC, so this makes more sense:
         c                   if        %parms > 3
         c                   eval      wwXLate = peXLate
         c                   else
         c                   eval      wwXLate = *On
         c                   endif
         C* End-Of-Line chars:
         C*   1) If caller passed only one, set
         C*         that one with length = 1
         C*   2) If caller passed two, then use
         C*         them both with length = 2
         C*   3) If caller didn't pass either,
         C*         use both CR & LF with length = 2
         c                   eval      wwEOL = *blanks
         c                   eval      wwEOLlen = 0
         c                   if        %parms > 4
         c                   eval      %subst(wwEOL:1:1) = peEOL1
         c                   eval      wwEOLLen = 1
         c                   endif
         c                   if        %parms > 5
         c                   eval      %subst(wwEOL:2:1) = peEOL2
         c                   eval      wwEOLLen = 2
         c                   endif
         c                   if        wwEOLLen = 0
         c                   eval      wwEOL = x'0D0A'
         c                   eval      wwEOLLen = 2
         c                   endif
         C* Do translation if required:
         c                   eval      wwLine = peLine
         c                   if        wwXLate = *On and wwLen > 0
         c                   callp     Translate(wwLen: wwLine: 'QTCPASC')
         c                   endif
         C* Send the data, followed by the end-of-line:
         C* and return the length of data sent:
         c                   if        wwLen > 0
         c                   eval      rc = send(peSock: %addr(wwLine): wwLen:0)
         c                   if        rc < wwLen
         c                   return    rc
         c                   endif
         c                   endif
         c                   eval      rc = send(peSock:%addr(wwEOL):wwEOLLen:0)
         c                   if        rc < 0
         c                   return    rc
         c                   endif
         c                   return    (rc + wwLen)
         P                 E

Personally, I learn things better by typing them in, rather than reading them. Therefore, I recommend that you type the code examples in this tutorial in yours elf. However, if you'd like, you can download my copy of SOCKUTILR4 here: http://www.scottklement.com/rpg/socktut/qrpglesrc.sockutilr4