We know what a line of text looks like now. We also know how to write lines to disk. What we don't know, yet, is how to read them.
The big difference between reading lines and writing lines is that when you write the data, you already know how long the line needs to be. But when you read, you don't. You won't know how long it is until you've found the CRLF sequence in the text!
Now, we could solve that by reading one byte from disk at a time, until one of them turned out to be the new line sequence, but that would not run efficiently, because disk hardware is designed to read from disk in larger chunks. So, what we'll do is read a whole buffer of data, and then parse that data looking for our new line sequence.
We'll save any characters in the buffer that occur after the new line, so that we can use them as the start of our next line of text.
The RPG code that I came up with looks like this:
P readline B export D readline PI 10I 0 D fd 10I 0 value D text * value D maxlen 10I 0 value D rdbuf S 1024A static D rdpos S 10I 0 static D rdlen S 10I 0 static D p_retstr S * D RetStr S 32766A based(p_retstr) D len S 10I 0 c eval len = 0 c eval p_retstr = text c eval %subst(RetStr:1:MaxLen) = *blanks c dow 1 = 1 C* Load the buffer c if rdpos>=rdlen c eval rdpos = 0 c eval rdlen=read(fd:%addr(rdbuf):%size(rdbuf)) c if rdlen < 1 c return -1 c endif c endif C* Is this the end of the line? c eval rdpos = rdpos + 1 c if %subst(rdbuf:rdpos:1) = x'25' c return len c endif C* Otherwise, add it to the text string. c if %subst(rdbuf:rdpos:1) <> x'0d' c and len<>maxlen c eval len = len + 1 c eval %subst(retstr:len:1) = c %subst(rdbuf:rdpos:1) c endif c enddo c return len P E
Add that routine to the IFSTEXTR4 service program, and then add this code to the prototypes in the IFSTEXT_H member:
D readline PR 10I 0 D fd 10I 0 value D text * value D maxlen 10I 0 value
One more thing: Because we're writing a service program, and we want our code to be as useful as possible, we're going to write binder source that tells the CRTSRVPGM how other programs can bind to us.
If you haven't done this before, don't worry about it. It's really, really simple. It's just a list of what gets exported. Here's the entire code of our binding source:
STRPGMEXP EXPORT SYMBOL(WRITELINE) EXPORT SYMBOL(READLINE) ENDPGMEXP
See? Very simple. Put that code into a member called "IFSTEXTR4" in a source file called QSRVSRC. Now let's compile it:
CRTRPGMOD IFSTEXTR4 SRCFILE(IFSEBOOK/QRPGLESRC) DBGVIEW(*LIST) CRTSRVPGM IFSTEXTR4 EXPORT(*SRCFILE) SRCFILE(IFSEBOOK/QSRVSRC) TEXT('IFS Text file service program')
Finally, to make it easy to compile the programs that use this service program, we'll create a binding directory. This only involves running two commands:
CRTBNDDIR IFSEBOOK/IFSTEXT TEXT('IFS Text binding directory') ADDBNDDIRE BNDDIR(IFSEBOOK/IFSTEXT) OBJ((IFSTEXTR4))