Making things look pretty with REXX

From EDM2
Jump to: navigation, search

By Richard K. Goran

Formatting data

There are many reasons for being able to format data in a particular way. I won't attempt to rationalize the need versus style arguments for formatted data. Let it suffice to say that the requirement exists for formatting alphabetic and numeric data, each for its own reason. Your config.sys file is a good example of a file that contains data that is difficult to read because it probably contains lines that are wider than the width your editor can show you without your having to scroll the lines left and right. More than likely the LIBPATH= line and the HELP= line contain many more characters that you can see at one time with your editor. If the number of characters exceeds your editor's width by just a few characters, it is quite easy to scroll back and forth. However, if your system is growing as mine is you may have a HELP= line like mine that contains over 200 characters. You can see that aligning the tokens from each long line as I have shown in Figure 1 makes it much easier to see the relative position of the tokens.

Figure 1

   SET HELP=c:\os2addon;

Listing 1 contains a program (9601ls01.cmd) that will create a config.txt file (in the root directory of your OS/2 system drive) from your config.sys file. This program will format all of the config.sys lines which exceed 76 characters into the format shown in Figure 1. The sequence numbers at the end of each line are not necessary and are included for convenient reference here.

Lines 01 - 18 are housekeeping and initialization. The meat of the program is contained in lines 24 - 62 with the actual formatting occurring in lines 38 - 61. The technique used is to capture the number of positions occupied up to, and including, the equal sign. This value is used to create the leading portion of each subsequent line in order to align each of the semicolon terminated tokens.

A further enhancement to this program could add the time stamp of every file and directory referenced in config.sys as a prefix to each line ([#Figure 2 Figure 2]). In the event that a file or directory is referenced in config.sys that does not exist, the time stamp is replaced with a ? for files and 00/00/00 for directories. This enhanced program, [../pub/], can be downloaded.

Figure 2

   94/08/27  07:59p  SET HELP=c:\os2addon;
   95/01/17  10:15p           E:\MMOS2\HELP;
   95/01/17  10:07p           E:\OS2\HELP;
   95/01/17  10:07p           E:\OS2\HELP\TUTORIAL;
   95/03/21  07:15a           E:\VIEWER\HELP;
   94/08/31  09:57p           J:\DEVTECH;
   94/04/07  01:11p           J:\READIBM2;
   95/03/25  04:51p           K:\VISPRORX;
   95/07/04  02:20a           L:\TCPIP\HELP;
   95/07/04  02:21a           L:\TCPIP\UMAIL;
   95/09/24  11:59a           M:\WWW\BIN;
   95/03/26  12:39p           P:\IBMPLI\HELP;
   95/09/13  09:10p           P:\IBMPLID\HELP;
   95/03/26  12:35p           P:\PLITK\HLP;
   95/03/26  03:23p           P:\VXODK\BOOKS;
   95/03/26  02:42p           P:\VXREXX;
   95/03/29  07:51p           P:\WATCOM\BINP\HELP;

These two examples are very simplistic and don't really take advantage of the formatting facilities in REXX but they can be used as a reference for further examples.


Presenting column data is facilitated by the LEFT() and RIGHT() built-in REXX functions. Each allows a string to be returned at the beginning or the end of a string of a specified length and with a padding character if necessary. Since REXX strings that exclusively contain numeric characters are presented without leading zeroes, using the RIGHT() function with both a length and a padding character can be used to force a string to be presented with leading zeroes. For example:

new_field = RIGHT( 123, 5, '0' )

will assign the string '00123' to the variable new_field. If the resulting value was to be placed in a column followed by two blanks,

column_data = LEFT( new_field, 7 )

would result in a 7 character string being assigned to the variable column_data which would contain '00123' ( is used here to indicate a blank space).

Numeric values

The FORMAT() built-in function is used for rounding a string containing a decimal value, specifying the number of integer characters to be used (the digits to the left of the decimal point) along with the number of characters to the right of the decimal point that will be returned. [#Figure 3 Figure 3] shows a comparison of using the FORMAT() function to vary the style of a numeric value's representation.

Figure 3

/* */
number = 123.45
say FORMAT( number, 3, 0 )  => 123
say FORMAT( number, 3, 1 )  => 123.4
say FORMAT( number, 3, 2 )  => 123.45
say FORMAT( number, 3, 3 )  => 123.450
say FORMAT( number, 2, 3 )  => error

Note that the fifth SAY instruction results in the following error:

7 +++   Say FORMAT(number, 2, 3)
REX0040: Error 40 running 9601FG03.CMD,
line 7: Incorrect call to routine

because the number of digits to the left of the decimal point in number exceeds the number of places indicated in FORMAT(number, 2, 3). You always have to be sure that the number of integer digits in the value you try to format with the FORMAT() function doesn't exceed the number of positions specified as the second, optional parameter to the FORMAT() function. If the second parameter is omitted, the value is displayed without leading zeroes, unless the number is less than 1.0 in which case one zero will precede the decimal point. The FORMAT() function also results in the returned value being rounded at one greater than the number of decimal positions specified. Hence, in the second SAY example in Figure 3 the value returned is rounded at the second decimal digit.

In summary, you can present data in a comfortable, readable format with REXX without very much effort. As with all data formatting, a little careful thought and planning will save you a considerable amount of effort.

Object REXX

As I write this column, I have come to the conclusion that Object REXX may be dangling in the breeze. The person who has served as the lead developer of Object REXX for the past couple of years has moved on and is entrenched in his new surroundings - not the IBM Glendale Laboratories in Endicott, NY. I have been told that the new REXXUTIL API that I wrote about in my August, 1995 column will still be released with the next version of Warp - when and if that occurs.

Listing 1

   /* 9601LS01.CMD - Create CONFIG.TXT from CONFIG.SYS */        /*01*/
   call RxFuncAdd 'SysLoadFuncs', 'REXXUTIL', 'SysLoadFuncs'     /*02*/
   call SysLoadFuncs                                             /*03*/
   GBL. =             /* initialize stem */                    /*05*/
   GBL.environment =,                                            /*06*/
      'OS2ENVIRONMENT'                                           /*07*/
   GBL.boot_drive  =,                                            /*08*/
      LEFT( VALUE( 'RUNWORKPLACE',, GBL.environment ), 2 )       /*09*/
   GBL.width       = 76 /* limit for nbr of characters / line */ /*10*/
   GBL.input_file =,                                             /*12*/
      GBL.boot_drive ||,                                         /*13*/
      '\config.sys'                                              /*14*/
   GBL.output_file =,                                            /*15*/
      GBL.boot_drive ||,                                         /*16*/
      '\config.txt'                                              /*17*/
   call SysFileDelete GBL.output_file  /*erase existing file */  /*18*/
   /*------------------------------*\                            /*20*/
   |  Read each line of CONFIG.SYS  |                            /*21*/
   |    & list according to size    |                            /*22*/
   \*------------------------------*/                            /*23*/
   do while LINES( GBL.input_file ) > 0                          /*24*/
      input_line = LINEIN( GBL.input_file )                      /*25*/
      if LENGTH( input_line ) <= GBL.width then                  /*26*/
         do                                                      /*27*/
            /*------------------*\                               /*28*/
            |  Echo short lines  |                               /*29*/
            \*------------------*/                               /*30*/
            call LINEOUT GBL.output_file, input_line             /*31*/
            iterate                                              /*32*/
         end                                                     /*33*/
      /*-------------------------------*\                        /*34*/
      |  Calculate number of leading    |                        /*35*/
      |  blanks for continuation lines  |                        /*36*/
      \*-------------------------------*/                        /*37*/
      continuation_line_blank_count =,                           /*38*/
         POS( '=', input_line )                                  /*39*/
      /*------------------------*\                               /*40*/
      |  Process 1st of ? lines  |                               /*41*/
      \*------------------------*/                               /*42*/
      parse value input_line with,                               /*43*/
         output_line,                                            /*44*/
         ';',                                                    /*45*/
         after_semicolon                                         /*46*/
      call LINEOUT GBL.output_file, output_line || ';'           /*47*/
      /*-------------------------------*\                        /*48*/
      |  Process all remaining tokens,  |                        /*49*/
      |        one per line             |                        /*50*/
      \*-------------------------------*/                        /*51*/
      do while after_semicolon <>                              /*52*/
         parse value after_semicolon with,                       /*53*/
            token,                                               /*54*/
            ';',                                                 /*55*/
            after_semicolon                                      /*56*/
         output_line =,                                          /*57*/
            COPIES( ' ', continuation_line_blank_count ) ||,     /*58*/
            token || ';'                                         /*59*/
         call LINEOUT GBL.output_file, output_line               /*60*/
      end                                                        /*61*/
   end                                                           /*62*/
   call STREAM GBL.input_file,  'C', 'CLOSE'                     /*63*/
   call STREAM GBL.output_file, 'C', 'CLOSE'                     /*64*/
   exit                                                          /*65*/