The NetRexx Tutorial
- Operations on files
NetRexx Tutorial - Operations on files


Operations on files

Introduction

File I/O has always been a 'problem' in REXX. In fact, REXX was born as a platform-independent language. There were no (at least not in the first specifications of the language) direct ways to access informations stored into a file. This led to (quoting some examples) EXECIO in VM/CMS, CMS/PIPES in later VM/CMS, STREAMS in OS/2, etc.

In NetRexx we use the file access routines that Java provides. In this chapter we will discuss how to access file information using NetRexx. You will learn how to:

We will perform this task using an OOP approach.

Thanks to Massimiliano (Max) Marsiglietti, a NetRexx porting of the functions STREAM, CHARIN, CHAROUT etc. has been done. Those routines will be discussed in the latest section of this chapter.

Basic Concepts

The Stream I/O model

In NetRexx (like in Java) all the I/O mechanism is built on Streams. The idea of Streams is similar (but not related) to the STREAMS in the Unix Kernel.

The Stream class gives you a mechanism "to get from" or "to put in" data into "somewhere". This "somewhere" can be a file, a screen or keyboard, the network, an audio device, etc. Your program will not have to care about the "details" in the implementation. It will just read() or write() to the Input or Output Stream.

 
                            _____________
                           (             )
   read()   <---(bytes)---( Input  Stream )
                           (_____________)

                            _____________
                           (             )
   write()  ----(bytes)-->( Output Stream )
                           (_____________)

 

Read/Write in Blocks

Before going into the details concerning the file I/O operations, let me clarify a point which is, in my opinion, extremely important.

I have always found the approach in which one tries to "confine" the operations on files inside subroutines more elegant, and easier to port and maintain. This is in contrast with the usual practice of intermixing file operations with the normal program flow. Let me make this clearer. The 'standard' approach is the following:

 
  -- "standard" approach to file
  -- Read/Write
  open INPUT_FILE
  open OUTPUT_FILE
 
  do while THERE_ARE_RECORDS
    read INPUT_FILE
    process RECORD
    write OUTPUT_FILE
  end
 
  close INPUT_FILE
  close OUTPUT_FILE

 

what I prefer is the following:

 
   -- "alternative" and "preferred"     *** BETTER ***
   -- approach to FILE I/O

   -- opening/reading/closing
   -- of the input file is done inside the rd_file
   -- method
   read_file INPUT_FILE
 
   do for RECORDS
     process RECORD
   end
 
   -- opening/writing/closing
   -- of the output file is done inside the wr_file
   -- method
   write_file OUTPUT_FILE

 

All the 'dirty' jobs (checking file existence, opening, closing, transferring data to and from an array, etc.) are reduced by this approach to ONLY two methods (the read_file and the write_file). There are cases where ( as we shall see ) there is no choice other than to take the first approach, but these are rare. I have personally used the second in 95% of programs and, again, it is easier to read, easier to port, simpler to maintain. You might ask yourself why I stress the benefits of code porting. An example is the code on VM/CMS written some years ago. In the early versions of Rexx there were NO instructions for file I/O, so someone was obliged to use the "infamous" EXECIO instruction. If all the instructions are 'confined' in two subroutines (now we call'm methods) , the changes are minimal when the code is ported. Otherwise you will need to change it in hundreds of places (if the program is big). And the more changes you make, the more bugs that can slip in. Summary: use simple methods for file I/O operations as much as you can; some of those methods you can see later in this chapter.

Checking file existence

The first thing you might want to do on a file is to check that it really exists. You can use the xfile built-in function 'state':

 
rc = xfile.state(file_id)
 

Alternatively, you could use the HEP/VM function fexist (file exist):

 
rc = xfile.fexist(file_id)
 

The output variable (rc in this example) has the following meaning for both functions:

 
   rc   = 0  : file does NOT exist
   rc   = 1  : file exists
 

Here a small example of the function:

 
+----------------------------------------------------------------------+
| -- (...)                                                             |01
| if \xfile.state('xstring.nrx')                                       |02
|   then say 'File does not exist.'                                    |03
|                                                                      |04
| if \xfile.fexist('/usr/local/bin/tcsh')                              |05
|   then say 'TCSH not present.'                                       |06
|                                                                      |07
| exit 0                                                               |08
+----------------------------------------------------------------------+
                                                               fexa1.nrx
Download the source for the fexa1.nrx example

The implementation of those functions is trivial:

 
+----------------------------------------------------------------------+
| -- method......: state                                               |39
| -- purpose.....: check file existence                                |40
| --                                                                   |41
|   method state(fid=Rexx) public static                               |42
|     in = File(fid)                                                   |43
|     fl = in.exists()                                                 |44
|     return fl                                                        |45
|                                                                      |46
+----------------------------------------------------------------------+
                                                 xfile.nrx(Method:state)
Download the complete source for the xfile.nrx library

Basic File operations in Java.

This entire section should be regarded as "reference" only. You will find, in fact, the basic I/O operations that you perform on a file. In my humble opinion it is nice to know these functions exist, but it is much better to use higher level subroutines that do all the work for you. Therefore, you should skip this section if you are not really interested in the detail.

Java classes for File access

 
 *** This section is: 
  
 *** and will be available in next releases

Writing an extension to the Java File class

In the previous chapter we showed the advantages of the OOP. We now even more clarify those advantages building our own extensions to the java.lang.Object class File.

This new class ( that we'll call xfile ) will allow us to:

Reading and Writing a whole file.

It is sometimes desirable to read or write an ENTIRE file (i.e. from the first to the last line) with a single operation. This approach has the obvious advantage of giving 'somebody else' all the bother of opening, read/write and closing a file. That 'somebody' is merely the code that performs the function. The only drawback to such an operation is that, especially if the file is big, it uses a lot of system resources. Therefore, as a rule of thumb, use the ENTIRE file approach only for files < 1MB in size when you already know you are using ALL the records.

Implementation of read() and write() in xfile.

 
 *** This section is: 
  
 *** and will be available in next releases

 
+----------------------------------------------------------------------+
| -- method......: read                                                |95
| -- purpose.....: read a full file into an array                      |96
| --                                                                   |97
|   method read() public                                               |98
|     rc = 0                                                           |99
|     do                                                               |00
|       in = DataInputStream(FileInputStream(File(name)))              |01
|     catch er=ioException                                             |02
|       rc = 3                                                         |03
|       return rc                                                      |04
|     end                                                              |05
|                                                                      |06
|     i = 0                                                            |07
|     loop while in.available <> 0                                     |08
|       i = i+1                                                        |09
|       line[i] = in.readLine                                          |10
|     catch er=ioException                                             |11
|       say 'Problem reading file "'name'".'                           |12
|       say 'Message is "'er'".'                                       |13
|       rc = 1                                                         |14
|       return rc                                                      |15
|     end                                                              |16
|     line[0] = i                                                      |17
|     lines = line[0]                                                  |18
|     return rc                                                        |19
|                                                                      |20
+----------------------------------------------------------------------+
                                                  xfile.nrx(Method:read)
Download the complete source for the xfile.nrx library

 
+----------------------------------------------------------------------+
| -- method......: write                                               |80
| -- purpose.....: ARRAY -> disk file operation                        |81
| --                                                                   |82
|   method write() public                                              |83
|     rc = 0                                                           |84
|     do                                                               |85
|       out = PrintStream(FileOutputStream(File(name)))                |86
|     catch er=ioException                                             |87
|       say 'Problem opening file "'name'".'                           |88
|       say 'Message is "'er'".'                                       |89
|       rc = 3                                                         |90
|       return rc                                                      |91
|     end                                                              |92
|                                                                      |93
|     loop i = 1 to line[0]                                            |94
|       linew = line[i]                                                |95
|       if recfm = 'F' then           -- is recfm = F ?                |96
|         do                          -- Yup, insert the right amount  |97
|           linew = linew.left(lrecl) --      of spaces (or truncate   |98
|         end                         --      if necessary)            |99
|       out.println(linew)                                             |00
|     end                                                              |01
|                                                                      |02
|     -- we're done. but do not forget to close                        |03
|     -- and flush the printstream                                     |04
|     --                                                               |05
|     out.close()                                                      |06
|     if out.checkError() then                                         |07
|       do                                                             |08
|         say 'ERROR in writing "'name'".'                             |09
|         rc = 1                                                       |10
|       end                                                            |11
|     return rc                                                        |12
|                                                                      |13
+----------------------------------------------------------------------+
                                                 xfile.nrx(Method:write)
Download the complete source for the xfile.nrx library

How to use the new methods.

You use the methods in the following way:

 
 
   infid = xfile('test.input')

   rc = infid.read()
   --   ----- ------  
    |      |    |   
    |      |    +------------< read operation
    |      +-----------------< file object
    +------------------------< ==0 : OK
                               <>0 : problem

   (...)

   oufid = xfile('test.output')

   rc = oufid.write()
   --   ----- ------  
    |      |    |   
    |      |    +------------< read operation
    |      +-----------------< file object
    +------------------------< ==0 : OK
                               <>0 : problem

 

Example of reading of an entire file.

If you need to read an entire file and put its contents into the ARRAY variable, you use the .read() method. Let's follow a complete example. Suppose your input file is test.data, and it looks like:

 
+----------------------------------------------------+
| data info 1                                        |
| data info 2                                        |
| (...)                                              |
| data info N                                        |
+----------------------------------------------------+
file: test.data

You read the ENTIRE file by calling

 
+------------------------------------------------------------------+
| (...)                                                            |
| infid = xfile('test.file')                                       |
| (...)                                                            |
| rc = infid.read()                                                |
| if rc <> 0 then      /* action on READ fail */                   |
| (...)                                                            |
+------------------------------------------------------------------+
                                                Example: read a file

AFTER the call, if rc was == 0, you get the values

 
  infid.line[0]  :   N
  infid.line[1]  :   'data info 1'
  infid.line[2]  :   'data info 2'
  (...)
  infid.line[N]  :   'data info N'
 

You can now process the lines with a loop, such as

 
+------------------------------------------------------------------+
| (...)                                                            |
| loop i = 1 to infid.line[0]                                      |
|   parse infid.line[i] (...)                                      |
|   (...)                                                          |
| end                                                              |
| (...)                                                            |
+------------------------------------------------------------------+
                                       Example: post read processing

Writing a whole file

Now consider the opposite situation, where we accumulate information into an ARRAY and we want to write a file with it (for example test.output).

 
+------------------------------------------------------------------+
| (...)                                                            |
| oufid = xfile('test.output')                                     |
| (...)                                                            |
| loop i = 1 to 30                                                 |
|   (...)                                                          |
|   oufid.addline('Output line' i)                                 |
| end                                                              |
| (...)                                                            |
| rc = oufid.write()                                               |
| if rc <> 0 then      /* action on WRITE fail */                  |
| (...)                                                            |
+------------------------------------------------------------------+
                                                Example: read a file

Read/Write access to a file (line by line)

Reading a file line by line

It is sometimes more desirable to read a file line by line and perform certain tasks within the reading loop. A typical case is when the input file is REALLY big ; for example, a 200MB tape or database. Another instance is when you really do not need to read all the records of the file, but only certain selected ones ; for example, all the accounting cards for a certain user. The logic is the following:

 

open(file)
do while NOT(EOF)
  record = readline(file)
  --
  -- processing
  --
end
close(file)

 

The following code is an example of this approach. You will notice that it is far more expensive in terms of instructions and complexity than the read() example.

 
 *** This section is: 
  
 *** and will be available in next releases

Writing a file line-by-line

It is also interesting to consider writing a file line-by-line. This is again a case where the file being produced is big, or where you do not want to store it inside an ARRAY variable. The logic is

 

open(file)               
do for all records               
  --
  -- processing
  --
  writeline(file,record)
end                              
close(file)                      

 

Here is a complete example:

 
 *** This section is: 
  
 *** and will be available in next releases

Read/Write access to a fixed-format record file

Unlike the VM/CMS and MVS systems, UNIX and Windows systems have no concept of RECORDS in files, so there is not much point in referring to LRECL and RECFM. However, using the xfile class you can access for read and for write a 'pseudo' fixed length file such as you are used on VM or on MVS. The advantage of these files is that you can access them on a record basis and use the record number as the index.

Suppose, for example, you have a TAPE database containing 300 000 records. To access the 283 954th one, where the records are all of the same length, you simply need to position yourself at the 283.954*RECL byte, and operate over a RECL quantity. And that is what the following functions will do. A 'pseudo' RECFM F LRECL 16 file will appear like this on your system:

 
.........    --------------------------------------------------
record 01    t  h  i  s     i  s     a     t  e  s  t
  (hex)      74 68 69 73 20 69 73 20 61 20 74 65 73 74 20 20 0A
record 02    a  n  o  t  h  e  r     l  i  n  e
  (hex)      61 6E 6F 74 68 65 72 20 6C 69 6E 65 20 20 20 20 0A
record 03    l  a  s  t     o  n  e
  (hex)      6C 61 73 74 20 6F 6E 65 20 20 20 20 20 20 20 20 0A
.........    --------------------------------------------------

This file does have three records of 16 (actually 17 with the '0A'x character) characters, so it occupies 51 bytes of disk space. Note that the '0A'x character is not mandatory. You could rewrite the routines presented herein in order to avoid it. I prefer having it so that I can look at the produced files with an editor or a browser. The format of the function to access a RECFM F file is the following:

 
  fid = xfile('test.FIXED')
  fid.options('recfm=F,lrecl=80')

  -- write record 120
  fid.recwrite(120,'Test')

  -- read record 133
  parse fid.recread(133) rc line
 

The methods are the following:

 
+----------------------------------------------------------------------+
| -- method......: recio                                               |53
| -- purpose.....: RANDOM access file record read                      |54
| --                                                                   |55
|   method recio(oper=Rexx,recno=Rexx,out=Rexx) public                 |56
|     rc = 0                                                           |57
|                                                                      |58
|     -- checks & initialization                                       |59
|     --                                                               |60
|     oper = oper.upper()                                              |61
|     if recfm <> 'F' then                                             |62
|       do                                                             |63
|         rc = 10                                                      |64
|         return rc 'ERROR: not a RECFM=F file.'                       |65
|       end                                                            |66
|     raff = File(name)                                                |67
|     size = int raff.length()                                         |68
|     skip = (recno-1)*(lrecl+1)                                       |69
|     skip = int skip                                                  |70
|     if size <= skip then                                             |71
|       do                                                             |72
|         rc = 11                                                      |73
|         return rc 'ERROR: past file end.'                            |74
|       end                                                            |75
|                                                                      |76
|     -- access as a Random File                                       |77
|     -- and skip till the beginning of record                         |78
|     do                                                               |79
|       raf = RandomAccessFile(name,"rw")                              |80
|     catch er=ioException                                             |81
|       say 'Problem opening file "'name'".'                           |82
|       say 'Message is "'er'".'                                       |83
|       rc = 3                                                         |84
|       return rc                                                      |85
|     end                                                              |86
|     do                                                               |87
|       raf.skipBytes(skip)                                            |88
|     catch er=ioException                                             |89
|       rc = 4                                                         |90
|       return rc                                                      |91
|     end                                                              |92
|                                                                      |93
|     if oper = 'READ' then                                            |94
|       do                                                             |95
|         do                                                           |96
|           line = raf.readLine()                                      |97
|         catch er=ioException                                         |98
|           say 'Problem reading file "'name'".'                       |99
|           say 'Message is "'er'".'                                   |00
|           rc = 3                                                     |01
|           return rc                                                  |02
|         end                                                          |03
|         return rc line                                               |04
|       end                                                            |05
|                                                                      |06
|     -- is it a WRITE operation?                                      |07
|     --                                                               |08
|     if oper = 'WRITE' then                                           |09
|       do                                                             |10
|         do                                                           |11
|           linew = out.left(lrecl)                                    |12
|           buf = linew'\x0A'                                          |13
|           raf.writebytes(buf)                                        |14
|         catch er=ioException                                         |15
|           say 'Problem reading file "'name'".'                       |16
|           say 'Message is "'er'".'                                   |17
|           rc = 3                                                     |18
|           return rc                                                  |19
|         end                                                          |20
|         return 0                                                     |21
|       end                                                            |22
|     return 11                                                        |23
|                                                                      |24
+----------------------------------------------------------------------+
                                                 xfile.nrx(Method:recio)
Download the complete source for the xfile.nrx library

 
+----------------------------------------------------------------------+
| -- method......: recwrite                                            |32
| -- purpose.....: RANDOM access file record write                     |33
| --                                                                   |34
|   method recwrite(recno=Rexx,rec=Rexx) public                        |35
|     out = recio('WRITE',recno,rec)                                   |36
|     return out                                                       |37
|                                                                      |38
+----------------------------------------------------------------------+
                                              xfile.nrx(Method:recwrite)
Download the complete source for the xfile.nrx library

The following program makes use of the above methods, showing all the possibilities:

 
+----------------------------------------------------------------------+
| -- test the xfile fixed record feature                               |01
| --                                                                   |02
| parse arg what                                                       |03
| what = what                                                          |04
|                                                                      |05
| fname = 'test.FIXED'                                                 |06
| fid = xfile(fname)                                                   |07
| fid.options('recfm=F,lrecl=16')                                      |08
|                                                                      |09
| say 'Accessing file "'fid.name'".'                                   |10
| fid.addline('this is a test')                                        |11
| fid.addline('another line')                                          |12
| fid.addline('last one')                                              |13
|                                                                      |14
| rc = fid.write()                                                     |15
| say 'RC:' rc' writing "'fid.name'".'                                 |16
|                                                                      |17
| /* access a record                                                   |18
|  */                                                                  |19
| say fid.recread(2)                                                   |20
| say fid.recwrite(2,'New line 2')                                     |21
| say fid.recread(2)                                                   |22
|                                                                      |23
| exit                                                                 |24
+----------------------------------------------------------------------+
                                                                tfix.nrx
Download the source for the tfix.nrx example

Some explication: In line '08' we write a file, RECFM F LRECL 16 ,using the contents of the stem list.. The file will look like this:

 
.........    --------------------------------------------------
record 01    t  h  i  s     i  s     a     t  e  s  t
  (hex)      74 68 69 73 20 69 73 20 61 20 74 65 73 74 20 20 0A
record 02    a  n  o  t  h  e  r     l  i  n  e
  (hex)      61 6E 6F 74 68 65 72 20 6C 69 6E 65 20 20 20 20 0A
record 03    l  a  s  t     o  n  e
  (hex)      6C 61 73 74 20 6F 6E 65 20 20 20 20 20 20 20 20 0A
.........    --------------------------------------------------

In line '11', we 'zap' the contents of the record 2, so our file will look like this:

 
.........    --------------------------------------------------
record 01    t  h  i  s     i  s     a     t  e  s  t
  (hex)      74 68 69 73 20 69 73 20 61 20 74 65 73 74 20 20 0A
record 02    N  e  w     l  i  n  e     2
  (hex)      4E 65 77 20 6C 69 6E 65 20 32 20 20 20 20 20 20 0A
record 03    l  a  s  t     o  n  e
  (hex)      6C 61 73 74 20 6F 6E 65 20 20 20 20 20 20 20 20 0A
.........    --------------------------------------------------

In line '14' we read the record we just zapped in an indexed way Ñ i.e. we access JUST the 2nd record of the file. If you run it, this is what you get:

 
....................................................................
rsl3pm1 (521) java tfix
0 another line
0
0 New line 2
rsl3pm1 (522) 
....................................................................
                                                             rwf.out

You can look at the file with your preferred editor, and check that it's really like I said.

Indexed files

What we discussed about RECFM F files is also true for RECFM V files. On VM/CMS and MVS systems, you can say: "get the record NNN of this file", and you get it in a really fast way. In UNIX, this is not possible. If you want the NNNth record of a file, and the file is NOT fixed length, you MUST read all the file till line NNNth (in the assumption that a record corresponds to a line). In this chapter we will analyse a method for overcoming this limitation, so you can at least partially have the benefits of a RECFM V file on VM/CMS. We will write a routine that (without you doing anything) will build an index file, and use it when you access the file itself. The idea is the following: Whenever you build a variable record length file (ex. test), an index table for it is built automatically, containing for each record the displacement (in bytes) from the beginning of the file itself. As the table is RECFM F, it is easy to find the NNNth record, and, from its contents, to identify the REAL contents of record NNN. Pictorially:

 
 
    +-------+       +-------------+
  1 |       |       |RRRRRRRR     |
  2 |       |       |RRRRRRRRRRR  |
  3 |ptr r3 |------>|RRRRRRRRRR   |
  4 |       |       |R            |
  5 |       |       |RRRRRRRRRRR  |
    | (...) |       |  (...)      |
  N |       |       |RRRRRRR      |
    +-------+       +-------------+
    test.IDX        test
    (RECFM F)       (RECFM V)
 
 

When should you use Indexed files?

The kind of applications that are well suited for indexed files are those where you read many times, RANDOMLY, a big file that you produce or refresh infrequently. An example is the 'phone book' of a company with hundreds of thousand of records, hashed in some particular form. Another example is a tape database, where the Volume ID of the tape is de-facto the key to accessing the file.

pro and cons for Indexed files.

Clearly, if the file is big, the indexed method makes a search for a random record as much as order of magnitudes faster. The drawback is that EVERY time you change the file, you need to refresh the index and make sure that no access to the file is made while the index is being built. In addition, the index itself uses space Ñ a 1 million record file requires an index file as large as 8MB. Of course if your records are big, this will be just a small percentage of the total disk space, but if the records are small, you risk the index file becoming bigger than the file itself.

The 'rw_filev' routine.

The 'rw_filev' routine is the 'kernel' of our discussion. It has three subfunctions: one for writing a file and, at the same time the file's index, a second for building an index for an existing file, and a third for reading a random record.

 
 *** This section is: 
  
 *** and will be available in next releases

Handling of binary files.

It is sometimes useful to handle binary files.

The xfile routines

The two xfile routines readbuf and writebuf help you dealing with byte quantities.

 
                 readbuf()
  ( FILE )      ----------->    fid.buffer
                                byte[size]
                <-----------
                 writebuf()

 

 
+----------------------------------------------------------------------+
| -- method......: readbuf                                             |21
| -- purpose.....: read an entire file into a buffer                   |22
| --                                                                   |23
|   method readbuf() public                                            |24
|     rc = 0                                                           |25
|     do                                                               |26
|       fd   = File(name)                                              |27
|       size = int fd.length()                                         |28
|       off  = int 0                                                   |29
|       fis  = FileInputStream(fd)                                     |30
|       in   = DataInputStream(fis)                                    |31
|       buf  = byte[size]                                              |32
|       oprc = in.read(buf,off,size)                                   |33
|     catch er=ioException                                             |34
|       rc = 3                                                         |35
|       say '(readbuf) ERROR:' er'.'                                   |36
|       return rc                                                      |37
|     end                                                              |38
|     if oprc = size                                                   |39
|       then rc = 0                                                    |40
|       else rc = 1                                                    |41
|     buffer = buf                                                     |42
|     return rc                                                        |43
|                                                                      |44
+----------------------------------------------------------------------+
                                               xfile.nrx(Method:readbuf)
Download the complete source for the xfile.nrx library

The key instruction is:

 
   oprc = in.read(buf,off,size)
 

where we read from the input stream size bytes, and we place them in a byte array called buffer.

 
+----------------------------------------------------------------------+
| -- method......: writebuf                                            |45
| -- purpose.....: write an entire buffer onto a file                  |46
| --                                                                   |47
|   method writebuf() public                                           |48
|     rc = 0                                                           |49
|     do                                                               |50
|       fd   = File(name)                                              |51
|       size = int buffer.length                                       |52
|       off  = int 0                                                   |53
|       fos  = FileOutputStream(fd)                                    |54
|       out  = DataoutputStream(fos)                                   |55
|       out.write(buffer,off,size)                                     |56
|       out.flush()                                                    |57
|       oprc = out.size()                                              |58
|       out.close()                                                    |59
|     catch er=ioException                                             |60
|       rc = 3                                                         |61
|       say '(writebuf) ERROR:' er'.'                                  |62
|       return rc                                                      |63
|     end                                                              |64
|     if oprc = size                                                   |65
|       then rc = 0                                                    |66
|       else rc = 1                                                    |67
|     return rc                                                        |68
|                                                                      |69
+----------------------------------------------------------------------+
                                              xfile.nrx(Method:writebuf)
Download the complete source for the xfile.nrx library

Examples

Let's look to some real examples.

 
+----------------------------------------------------------------------+
| -- test WRITE buffer                                                 |01
| --                                                                   |02
|                                                                      |03
| -- init a buffer, please                                             |04
| --                                                                   |05
| buf = byte[126]                                                      |06
| loop i = 1 to buf.length-1                                           |07
|   buf[i] = i                                                         |08
| end                                                                  |09
|                                                                      |10
| -- declare the output file                                           |11
| --                                                                   |12
| fn = 'twf.out'                                                       |13
| of = xfile(fn)                                                       |14
|                                                                      |15
| -- point to the buffer space                                         |16
| --                                                                   |17
| of.buffer = buf                                                      |18
|                                                                      |19
| -- OK, do the write                                                  |20
| --                                                                   |21
| rc = of.writebuf()                                                   |22
| say 'Write of "'fn'" got RC:' rc'.'                                  |23
|                                                                      |24
| exit                                                                 |25
+----------------------------------------------------------------------+
                                                                 twb.nrx
Download the source for the twb.nrx example

This is how your output file will look like, looking it using hedit (see next section).

 
......................................................................
rsl3pm1 (1) > java hedit twf.out
Welcome to hedit.
d0000000 - 0001 0203 0405 0607 0809 0A0B 0C0D 0E0F  "................"
d0000016 - 1011 1213 1415 1617 1819 1A1B 1C1D 1E1F  "................"
d0000032 - 2021 2223 2425 2627 2829 2A2B 2C2D 2E2F  ".!"#$%&'()*+,-./"
d0000048 - 3031 3233 3435 3637 3839 3A3B 3C3D 3E3F  "0123456789:;<=>?"
d0000064 - 4041 4243 4445 4647 4849 4A4B 4C4D 4E4F  ".ABCDEFGHIJKLMNO"
d0000080 - 5051 5253 5455 5657 5859 5A5B 5C5D 5E5F  "PQRSTUVWXYZ[\]^_"
d0000096 - 6061 6263 6465 6667 6869 6A6B 6C6D 6E6F  "`abcdefghijklmno"
d0000112 - 7071 7273 7475 7677 7879 7A7B 7C7D 0000  "pqrstuvwxyz.|..."
<<< EOF >>>
cmd -> quit
All done.
rsl3pm1 (2) >
......................................................................

Case study: hedit, a file dump/edit in HEX

Let's look at a program that allows us to dump and edit binary (and even text files) in HEX digits. The program, called hedit is available on the WEB source page for the tutorial.

The program does:

 
  - read the full file in storage
  - display the first "page" worth of dump
  - wait for commands
 

Some relevant code

The reading of the input file is issued with a simple call to the readbuf method.

 
fid = xfile(fn)
rc = fid.readbuf()
 

We now can use the array:

 
fid.buffer
 

to get the byte information of the file contents. Again, remember that:

 
fid.buffer.length                -- buffer's length

fid.buffer[0]                    -- BUFFER
(...)                            --
fid.buffer[fid.buffer.length-1]  --
 

The method linedis is used to prepare the line that needs to be displayed.

 
+----------------------------------------------------------------------+
| -- method......: linedis                                             |78
| -- purpose.....: prepare a line                                      |79
| --                                                                   |80
|   method linedis(bs=rexx,buf=byte[]) public static                   |81
|     obh = ''                                                         |82
|     obc = ''                                                         |83
|                                                                      |84
|     -- this logic is not perfect, might require rewriting            |85
|     -- get 2 bytes a time, in HEX and in CHAR                        |86
|     --                                                               |87
|     loop j = 0 to 14 by 2                                            |88
|       p1 = bs*16+j                                                   |89
|       p2 = bs*16+j+1                                                 |90
|       if p1 > buf.length - 1    -- past end of buffer                |91
|         then c1 = '00'          --                                   |92
|         else c1 = rexx buf[p1]                                       |93
|       if p2 > buf.length - 1                                         |94
|         then c2 = '00'                                               |95
|         else c2 = rexx buf[p2]                                       |96
|       obh = obh||c1.d2x(2)||c2.d2x(2)' '                             |97
|       if c1 > 32 & c1 < 127     -- only char we can see              |98
|         then c1 = c1.d2c()      -- please                            |99
|         else c1 = '.'                                                |00
|       if c2 > 32 & c2 < 127     -- ditto                             |01
|         then c2 = c2.d2c()      --                                   |02
|         else c2 = '.'                                                |03
|       obc = obc||c1||c2                                              |04
|     end                                                              |05
|                                                                      |06
|     -- that's the full line                                          |07
|     --                                                               |08
|     ptr = bs*16                                                      |09
|     if dtyp = 'D'                                                    |10
|       then ptr = 'd'ptr.right(7,'0')                                 |11
|       else ptr = 'd'ptr.right(7,'0')                                 |12
|     l = ptr '-' obh '"'obc'"'                                        |13
|     return l                                                         |14
|                                                                      |15
+----------------------------------------------------------------------+
                                               hedit.nrx(Method:linedis)
Download the complete source for the hedit.nrx library

The change routine is used to perform a change over a subsequent set of bytes. You perform a change typing:

 
change START byte_string
 

like:

 
change 5 CAFE000067
 

 
+----------------------------------------------------------------------+
| -- method......: change                                              |58
| -- purpose.....: change a set of bytes                               |59
| --                                                                   |60
|   method change(bs=rexx,up=rexx,buf=byte[]) public static            |61
|     -- some checks                                                   |62
|     --                                                               |63
|     if bs < 0 | bs > buf.length-1 then                               |64
|       do                                                             |65
|         say 'Invalid start byte.'                                    |66
|         return                                                       |67
|       end                                                            |68
|     list = up                                                        |69
|     i = bs                                                           |70
|     loop while list <> ''                                            |71
|       parse list nb +2 list                                          |72
|       say nb                                                         |73
|       buf[i] = nb.x2d(2)                                             |74
|       i = i+1                                                        |75
|     end                                                              |76
|                                                                      |77
+----------------------------------------------------------------------+
                                                hedit.nrx(Method:change)
Download the complete source for the hedit.nrx library

The actual saving is performed by the method save, and the real kernel code is:

 
ofid = xfile(ofn)     -- define OUTPUT file
ofid.buffer = buf     -- point to buffer
rc = ofid.writebuf()  -- WRITE it!
 

 
+----------------------------------------------------------------------+
| -- method......: save                                                |30
| -- purpose.....: saves a buffer                                      |31
| --                                                                   |32
|   method save(sargs=rexx,buf=byte[]) public static                   |33
|     parse sargs ofn .                                                |34
|                                                                      |35
|     -- check if we have a filename and if it is not                  |36
|     -- already there                                                 |37
|     --                                                               |38
|     if ofn = '' then                                                 |39
|       do                                                             |40
|         say 'Missing filename.'                                      |41
|         return                                                       |42
|       end                                                            |43
|     if xfile.fexist(ofn) then                                        |44
|       do                                                             |45
|         say 'File "'ofn'" already exists. OK to overwrite?\-'        |46
|         parse ask.upper() answ                                       |47
|         if answ <> 'Y' then return                                   |48
|       end                                                            |49
|                                                                      |50
|     -- OK, go head                                                   |51
|     --                                                               |52
|     ofid = xfile(ofn)                                                |53
|     ofid.buffer = buf                                                |54
|     rc = ofid.writebuf()                                             |55
|     if rc = 0                                                        |56
|       then say 'Buffer written OK to "'ofn'".'                       |57
|       else say 'Problems writing "'ofn'".'                           |58
|                                                                      |59
+----------------------------------------------------------------------+
                                                  hedit.nrx(Method:save)
Download the complete source for the hedit.nrx library

Sample session

 
.....................................................................
rsl3pm01 (1) > java hedit rwf.out
Welcome to hedit.
d0000000 - 0001 0203 0405 0607 0809 0A0B 0C0D 0E0F  "................"
d0000016 - 1011 1213 1415 1617 1819 1A1B 1C1D 1E1F  "................"
d0000032 - 2021 2223 2425 2627 2829 2A2B 2C2D 2E2F  ".!"#$%&'()*+,-./"
d0000048 - 3031 3233 3435 3637 3839 3A3B 3C3D 3E3F  "0123456789:;<=>?"
d0000064 - 4041 4243 4445 4647 4849 4A4B 4C4D 4E4F  ".ABCDEFGHIJKLMNO"
d0000080 - 5051 5253 5455 5657 5859 5A5B 5C5D 5E5F  "PQRSTUVWXYZ[\]^_"
d0000096 - 6061 6263 6465 6667 6869 6A6B 6C6D 6E6F  "`abcdefghijklmno"
d0000112 - 7071 7273 7475 7677 7879 7A7B 7C7D 0000  "pqrstuvwxyz.|..."
<<< EOF >>>

cmd ->help
Available commands are:
DOWN                - move down one page.
UP                  - move up one page.
QUIT                - exit program.
VERSION             - show program version.
GO nnnn             - go to location NNNN (DECimal).
TOP                 - go to top.
SAVE fn             - save buffer as "fn".
CHANGE start hexstr - change bytes from "start" with "hexstr".

cmd ->CHANGE 0 CAFEBABECAFEBABE
d0000000 - CAFE BABE CAFE BABE 0809 0A0B 0C0D 0E0F  "................"
d0000016 - 1011 1213 1415 1617 1819 1A1B 1C1D 1E1F  "................"
d0000032 - 2021 2223 2425 2627 2829 2A2B 2C2D 2E2F  ".!"#$%&'()*+,-./"
d0000048 - 3031 3233 3435 3637 3839 3A3B 3C3D 3E3F  "0123456789:;<=>?"
d0000064 - 4041 4243 4445 4647 4849 4A4B 4C4D 4E4F  ".ABCDEFGHIJKLMNO"
d0000080 - 5051 5253 5455 5657 5859 5A5B 5C5D 5E5F  "PQRSTUVWXYZ[\]^_"
d0000096 - 6061 6263 6465 6667 6869 6A6B 6C6D 6E6F  "`abcdefghijklmno"
d0000112 - 7071 7273 7475 7677 7879 7A7B 7C7D 0000  "pqrstuvwxyz.|..."
<<< EOF >>>
cmd -> quit

..................................................................

The rxfile package.

Availability

The rxfile package is available directly from the author, at the following URL:

 
http://www.geocities.com/SiliconValley/Park/4218/RXFILE.HTML
for rxfile
and
http://www.geocities.com/SiliconValley/Park/4218/
Marsiglietti's home page
 
 
 *** This section is: 
  
 *** and will be available in next releases

Summary

A resume' of what we have seen in this chapter:

 
 
 
 *** This section is: 
  
 *** and will be available in next releases


File: nr_13.html.

The contents of this WEB page are Copyright © 1997 by Pierantonio Marchesini / ETH Zurich.

Last update was done on 18 May 1998 21:47:48(GMT +2).