Rexx: No Dog Here

Some might say I'm a little Rexx-happy, or that I must be a programmer to be so interested in programming OS/2. I'm not, though, and that's what I've found so handy about Rexx. The day I started writing little programs was the day my computer started working for me rather than just with me. You can only get so much mileage from the latter, and once I'd learned to use OS/2's GUI I saw that there had to be a way that I didn't have to be there all the time.

Along came Rexx. It's not C, or Lisp or Perl, but it's the most performance for the (comprehensible) buck that I had, and best of all I had already paid for it when I bought OS/2.

Want so see some? Select one of these entries to view a few Rexx bits that OS/2 installs for you in the E editor. To leave E, press [F3].

c:\os2\HELP.CMD
 * A simple batch-like wrapper for the HELP command. This will look familiar, and isn't Rexx, but it shows you that you can still do batch files.

c:\os2\REXXTRY.CMD
 * This is Rexx, as denoted by the /* comment on line 1. A single-step program for you to try Rexx commands from the command line. For now, just browse through the code and see what you can make of it.

c:\mmos2\PLAY.CMD, c:\mmos2\RECORD.CMD
 * Only if you have MultiMedia installed. Manipulate WAV files (and others) from the command line or other Rexx programs. Demonstrates use of other function libraries.

HELP.CMD
 @echo off rem SCCSID = @(#)help.cmd	6.4 91/08/05 rem * rem * Process HELP requests: verify specification of 'ON' or 'OFF' rem * if '%1' == ''   goto msg if '%1' == 'on' goto yes if '%1' == 'off' goto no if '%1' == 'ON' goto yes if '%1' == 'OFF' goto no if '%1' == 'On' goto yes if '%1' == 'oN' goto yes if '%1' == 'OFf' goto no if '%1' == 'OfF' goto no if '%1' == 'Off' goto no if '%1' == 'oFF' goto no if '%1' == 'oFf' goto no if '%1' == 'ofF' goto no helpmsg %1 %2 goto exit
 * msg

helpmsg goto exit
 * yes

prompt $i[$p] goto exit
 * no

cls prompt
 * exit



REXXTRY.CMD
 /* SAA-portable REXXTRY procedure    11/08/91  version 1.05 Owned by IBM REXX Development. Loosely derived from an ancient formulation of Mike Cowlishaw.

This procedure lets you interactively try REXX statements. If you run it with no parameter, or with a question mark as a parameter, it will briefly describe itself. You may also enter a REXX statement directly on the command line for immediate execution and exit. Example: rexxtry call show

Enter 'call show' to see user variables provided by REXXTRY. Enter '=' to repeat your previous statement. Enter '?' to invoke system-provided online help for REXX. The subroutine named 'sub' can be CALLed or invoked as 'sub'. REXXTRY can be run recursively with CALL.

Except for the signal instructions after a syntax error, this procedure is an example of structured programming. The 'clear' routine illustrates system-specific SAA-portable coding. parse arg argrx                     /* Get user's arg string. */ call house                           /* Go do some housekeeping. */ select                               /* 3 modes of operation... */   when argrx = '?' then call tell    /*   1. Tell user how. */   when argrx = ''  then do           /*   2. Interactive mode. */     call intro ; call main ; end otherwise push argrx ; call main  /*   3. One-liner and exit. */ end done: exit                            /* The only exit. */

house:                                /* Housekeeping. */ parse version version                /* Fill-in 2 user variables. */ parse source source                  /*                           */ parse source sysrx. procrx. /* Get system & proc names. */ remindrx = "Enter 'exit' to end." /* How to escape rexxtry. */ helprx=''                            /* Null if not CMS or OS/2. */ if sysrx = 'CMS' | sysrx = 'OS/2'    /*   Extra reminder for CMS  */ then helprx = '  ',               /*     or OS/2. */   "  Or '?' for online REXX help." /*  Not used in intro. */ promptrx=''                          /* Null if not one-liner. */ if argrx<>'' then promptrx=procrx' ' /*   Name part of user line. */ if sysrx = 'OS/2' then do            /* OS/2-specific... */   posrx = lastpos('\',procrx)        /*   Find name separator. */   procrx = substr(procrx,posrx+1)    /*   Pick up the proc name. */   end temprx = ' 'procrx' on 'sysrx       /* Make border... */   posrx = 69-length(temprx)          /*   where to overlay name,  */ bordrx = copies('.',68)           /*   background of periods,  */ bordrx =,                         /*   name right-adjusted. */     overlay(temprx,bordrx,posrx) save = ''                           /* Don't save user input. */ trace = 'Off'                        /* Init user trace variable. */ return result                        /* Preserve result contents. */

tell: call clear ; do irx = 1 to 20   /* Tell about rexxtry by     */ say sourceline(irx) ; end           /*   displaying the prolog. */ return result                        /* Preserve result contents. */

clear: select                         /* SAA-portable code. */ when sysrx = 'CMS'  then 'VMFCLEAR'  /* No such command on        */ when sysrx = 'OS/2' then 'CLS'      /*   OS/400 or TSO. */ otherwise nop ; end ; say return result                       /* Preserve result contents. */

intro:                                /* Display brief             */ say ' 'procrx' lets you',           /*   introductory            */ 'interactively try REXX',         /*   remarks for             */ 'statements.'                     /*   interactive mode. */ say '    Each string is executed', 'when you hit Enter.' say "     Enter 'call tell' for",   /* How to see description. */   "a description of the features." say ' Go on - try a few... ',   '         'remindrx return result                       /* Preserve result contents. */

sub: say '  ...test subroutine',      /* User can CALL this        */ "'sub' ...returning 1234..." /*  subroutine or           */ return 1234                         /*   invoke with 'sub'. */

main: signal on syntax                /* Enable syntax trap. */ do foreverrx = 1                     /* Loop forever. */   prev = inputrx                     /* User can repeat previous. */   parse pull inputrx                 /* Input keyboard or queue. */   current = inputrx                  /* Current line for 'show'. */   if save <> '' then call save       /* Save before interpreting. */   if inputrx = '=' then inputrx=prev /* '=' means repeat previous */ select when inputrx = '' then say ' ', /* If null line, remind      */ procrx': 'remindrx helprx     /*   user how to escape. */     when inputrx='?' then call help  /* Request for online help. */     otherwise rc = 'X'                      /* Make rc change visible. */       call set2 ; trace (trace)      /* Need these on same line. */       interpret inputrx              /* Try the user's input. */       trace 'Off'                    /* Don't trace rexxtry. */       call border                    /* Go write the border. */   end if argrx <> '' & queued = 0     /* For one-liner, loop until */ then leave                      /*   queue is empty. */ end ; return result                  /* Preserve result contents. */

set1: siglrx1 = sigl                  /* Save pointer to lineout. */ return result                        /* Preserve result contents. */

set2: siglrx2 = sigl                  /* Save pointer to trace. */ return result                        /* Preserve result contents. */

save:                                 /* Save before interpreting. */ call set1;rcrx=lineout(save,inputrx) /* Need on same line. */ if rcrx <> 0 then                    /* Catch non-syntax error    */ say " Error on save="save         /*   from lineout. */ return result                        /* Preserve result contents. */

help: select                          /* Request for online help. */ when sysrx = 'CMS' then              /* Invoke CMS help menu for  */ address CMS 'HELP REXX MENU'      /*   for REXX. */ when sysrx = 'OS/2' then             /* Invoke OS/2 online REXX   */ 'view rexx.inf'                   /*   Reference. */ otherwise say '  'sysrx' has',       /* Todate, only CMS and OS/2 */ 'no online help for REXX.'        /*   provide online help     */ rc = 'Sorry !' ; end              /*   for REXX. */ call border ; return result          /* Preserve result contents. */

border: if rc = 'X' then              /* Display border. */   say '  'bordrx else say ' ',                       /* Show return code if it    */ overlay('rc = 'rc' ',bordrx)      /*   has changed. */ return result                        /* Preserve result contents. */

syntax: trace 'Off'                   /* Stop any tracing. */ select when sigl = siglrx1 then do       /* User's 'save' value bad. */     say "  Invalid 'save' value", "'"save"', resetting to ''." save='' ; end when sigl = siglrx2 then do       /* User's 'trace' value bad. */     say "  Invalid 'trace' value", "'"trace"', resetting to", "'Off'." ; trace='Off' ; end otherwise                         /* Some other syntax error. */                                      /* Show the error msg text. */        say '  Oooops ! ... try again. 'errortext(rc)

parse version. rxlevel. if rxlevel > 4.00 then do       /* get the secondary message */ secondary = condition('o')~message if .nil <> secondary then     /* get a real one? */                                      /* display it also           */ say '                             'secondary; end

if sysrx='CMS' then call cmstax /* More syntax stuff for CMS */ end ; call border                   /* Go write the border. */ if argrx <> '' & queued = 0 then   /* One-liner not finished    */ signal done                       /*   until queue is empty. */ signal main                          /* Resume main loop. */

cmstax: rcrx = rc                     /* More syntax stuff for CMS */ if exist('PROMPT MODULE') then      /* User's input to cmd line. */   'PROMPT' promptrx||'13'x||inputrx  /*   '13'x places cursor. */ if exist('REXX EXEC') & argrx=''     /* Not for one-liners. */ then do; more='EXEC REXX' rcrx+20000 /* CMS rc is 20,000 higher. */   say "  Enter 'more' to see",       /* Prepare 'more' to access  */ 'information about this',       /*  REXX IOSLIB information. */     'syntax error.' ; end            /* Tell user what to do. */ rc = rcrx ; return result            /* Preserve result contents. */

exist: arg inrx ; outrx = 0           /* Assume file is missing. */ address command 'ESTATE' inrx        /* Does the file exist ? */ if rc = 0 then outrx = 1             /* estate says it exists. */ return outrx                         /* 1 makes condition true. */

show: trace 'Off' ; call clear        /* Display user variables    */ say ' 'procrx' provides',           /*   provided by rexxtry. */   'these user variables.' say ' The current values are...'    /* Show current values. */ say say "   'version'   = '"version"'"  /* What level of REXX. */ say "    'source'    = '"source"'"   /* What oper system etc.     */ say "   'result'    = '"result"'"   /* REXX special variable. */ say say '    Previous line entered by', 'user. Initial value=INPUTRX.' say "   'prev'      = '"prev"'"     /* Previous user statement. */ say "    'current'   = '"current"'"  /* Compare curr with prev. */ say say '    Save your input with', 'save=filespec.', " Stop saving with save=''." say "   'save'      = '"save"'"     /* Filespec for input keep. */ say say '    Enter trace=i, trace=o', ' etc. to control tracing.' say "   'trace'     = '"trace"'"    /* Trace user statements. */ return result                        /* Preserve result contents. */ 

PLAY.CMD
 /*-

Name:                  play.cmd Date Created:   12/27/92 Copyright (c) IBM Corporation 1992, 1993 All Rights Reserved

OS/2 REXX command file that uses MultiMedia REXX functions to play a file.

-*/

address cmd     /* Send commands to OS/2 command processor. */ signal on error  /* When commands fail, call "error" routine. */ signal on halt   /* When user does a ctrl break               */

trace off /*trace ?r*/

/* initialize variables */ FILE='' FROM='' TO='' DEV='' TIMEFMT=''

/* Setup keyword string array */ kwd.0 = 5 kwd.1 = 'FILE' kwd.2 = 'DEV' kwd.3 = 'TO' kwd.4 = 'FROM' kwd.5 = 'TIMEFMT'

/* Clear out argx variables */ do i = 1 to 5 junk = value('arg'i,'') end

arg inline                     /* Get the command line parms */ if (inline='' | inline='?') then do    call Help exit 0 end

/* * Check for each keyword * If "FILE" is found, look for quotes which signify possible embedded *  blanks. * Set argx (x is 1 to 5) to the entire keyword string, which ends in *  either a blank or a quote. */ do i = 1 to kwd.0

kwdpos = pos(kwd.i, inline)  /* Position of matching keyword */ if kwdpos > 0 then           /* Found a keyword */ do

if kwd.i = 'FILE' then     /* Check for quote after FILE= */ do     endchar = substr(inline, kwdpos+length(kwd.i)+1,1) if endchar <> '"' then endchar = ' '   end

else endchar = ' '         /* Not FILE=, use blank as delimiter */

/* Find delimiter (either next quote or blank) */ fnend = pos(endchar, inline, kwdpos+length(kwd.i)+2) if fnend = 0 then          /* Ending quote/blank not found */ do     if endchar = '"' then        say 'Missing ending quote mark for' kwd.i 'keyword'      fnend = length(inline)    /* Assume it's just end of line */    end

/* Set argx to the keyword=data */ junk = value('arg'i, substr(inline, kwdpos, fnend-kwdpos+1))

end                          /* End if a keyword was found */

end                            /* End do i = 1 to num of keywords */

/* display values of the argx variables */ /* do i = 1 to 5 say 'arg'i 'is' value('arg'i)  end        */

parse var arg1 arg1a'='arg1b parse var arg2 arg2a'='arg2b parse var arg3 arg3a'='arg3b parse var arg4 arg4a'='arg4b parse var arg5 arg5a'='arg5b

/* Set the variables. */ call keyword arg1a, arg1b call keyword arg2a, arg2b call keyword arg3a, arg3b call keyword arg4a, arg4b call keyword arg5a, arg5b

/* Load the DLL, initialize MCI REXX support */ rc = RXFUNCADD('mciRxInit','MCIAPI','mciRxInit') InitRC = mciRxInit MciCmd = 'open'

/*    if FILE<>'' then do         if DEV<>'' then MciCmd = MciCmd FILE 'type' DEV else MciCmd = MciCmd FILE end else if DEV<>'' then MciCmd = MciCmd DEV else do            call Help exit 0 end
 * Check to see if the FILE && DEV variables are valid.

/*   MciCmd = MciCmd 'alias rexxalias wait'
 * Append the rest of the command line.

/*   MacRC = SendString(MciCmd) if MacRC <> 0 then signal ErrExit else do      if DEV='' then    /* device not specified */ do    /* determine the device type */ MacRC = SendString("capability rexxalias device type wait") if MacRC <> 0 then do                junk = SendString("close rexxalias wait") signal ErrExit end end else  /* set the device specified as the device type */ RetSt = DEV
 * Issue the open command.

/* If a wave file is to be played then do a status length */ /* to determine if the wave file exists. A wave file is */ /* the only type of device that if it doesn't exist and  */ /* you play it, it won't come back as file not found     */ if TRANSLATE(RetSt) = 'WAVEAUDIO' then do           MacRC = SendString("status rexxalias length wait")      /* If length is 0 no file exists */ if MacRC <> 0 then do                junk = SendString("close rexxalias wait") signal ErrExit end if RetSt = 0 then do                junk = SendString("close rexxalias wait") ErrRC = 70555 MacRC = mciRxGetErrorString(ErrRC, 'ErrStVar') say 'mciRxGetErrorString('ErrRC') =' ErrStVar signal ErrExit end end end

/* DeviceID = mciRxGetDeviceID(""rexxalias"")
 * Exercise mciRxGetDeviceID function

/* if TIMEFMT <> '' then do MciCmd = 'set rexxalias time format' TIMEFMT 'wait' MacRC = SendString(MciCmd) if MacRC <> 0 then do        junk = SendString("close rexxalias wait") signal ErrExit end end
 * Check to see if a time format was given.

/* MciCmd = 'play rexxalias'
 * Formulate the play command.

/* if FROM<>'' then MciCmd = MciCmd 'from' FROM
 * check to see if an origin was set.

/* if TO<>'' then MciCmd = MciCmd 'to' TO
 * check to see if a terminating point was given.

/* MciCmd = MciCmd 'wait'
 * append a wait onto the end of the play string.

/* MacRC = SendString(MciCmd) if MacRC <> 0 then do        junk = SendString("close rexxalias wait") signal ErrExit end
 * actually send the play string.

/* MacRC = SendString("close rexxalias wait") if MacRC <> 0 then signal ErrExit
 * close the instance.

/* exit 0
 * Exit, return code = 0.

/*  --- SendString -- SendString: arg CmndTxt /* Last two parameters are reserved, must be set to 0          */ /* Future use of last two parms are for notify window handle   */ /* and userparm. */  MacRC = mciRxSendString(CmndTxt, 'RetSt', '0', '0') if MacRC<>0 then do     ErrRC = MacRC say 'MciCmd=' CmndTxt say 'Err:mciRxSendString RC=' ErrRC RetSt MacRC = mciRxGetErrorString(ErrRC, 'ErrStVar') say 'mciRxGetErrorString('ErrRC') =' ErrStVar MacRC = ErrRC /* return the error rc */ end return MacRC
 * Call DLL function. Pass the command to process and the
 * name of a REXX variable that will receive textual return
 * information.

/* -- keywords -- keyword: arg key, value if key='FILE' then FILE=value else if key='DEV' then DEV=value else if key='FROM' then FROM=value else if key='TO' then TO=value else if key='TIMEFMT' then TIMEFMT=value
 * Parse the arguments according to the keywords.
 * Parse the arguments according to the keywords.

return

/* -- help -- Help: say say 'This command file plays a file or device using the MultiMedia' say 'REXX string interface.' say say 'play [FILE="filename"] [DEV=device] [TIMEFMT=timefmt]' say '        [FROM=from_position] [TO=to_position]' return
 * Display help text

/* --- ErrExit -- ErrExit: MacRC = mciRxExit  /* Tell the DLL we're going away        */ exit 1;              /* exit, tell caller things went poorly */
 * Common routine for error clean up/program exit.
 * Gets called when commands to DLL fail.

/*   error -- error: ErrRC = rc  say 'Error' ErrRC 'at line' sigl ', sourceline:' sourceline(sigl) MacRC = mciRxExit      /* Tell the DLL we're going away */ exit ErrRC               /* exit, tell caller things went poorly */
 * Routine gets control when any command to the external
 * environment (usually OS/2) returns a non-zero RC.
 * This routine does not get called when the macapi.dll
 * returns non-zero as it is a function provider rather
 * than a command environment.

/*   halt -- halt: say 'Halting...' MacRC = SendString("close rexxalias wait") if MacRC <> 0 then signal ErrExit exit 0 
 * Routine gets control when user hits ctrl break to end

RECORD.CMD
 /*-

Name:                  record.cmd Date Created:   12/27/92 Copyright (c) IBM Corporation 1992, 1993 All Rights Reserved

OS/2 REXX command file that uses MultiMedia REXX API's to record a wave file with the default settings of the audio card from the microphone in jack.

-*/

address cmd     /* Send commands to OS/2 command processor. */ signal on error  /* When commands fail, call "error" routine. */

trace off /*trace ?r */

parse arg WaveFileName             /* Fetch command line parms */

if WaveFileName='' | WaveFileName='?' then do    call Help exit 0 end

/* Load the DLL, initialize MCI REXX support */ rc = RXFUNCADD('mciRxInit','MCIAPI','mciRxInit') InitRC = mciRxInit

/* MciCmd = 'open' WaveFileName 'alias rexxalias type waveaudio wait' MacRC = SendString(MciCmd)      /* Put double single quotes around the command */ if MacRC <> 0 then signal ErrExit
 * Open the instance.

MacRC = SendString("delete rexxalias from 0 wait") if MacRC <> 0 then signal ErrExit

/* DeviceID = mciRxGetDeviceID(""rexxalias"")
 * Exercise mciRxGetDeviceID function

/* MacRC = SendString("connection rexxalias query type wave stream alias rexxmic wait") if MacRC <> 0 then signal ErrExit
 * Get the connector.

/* MacRC = SendString("set rexxmic monitor off wait") if MacRC <> 0 then signal ErrExit
 * Set the monitor attribute to off.

/* MacRC = SendString("connector rexxmic enable type microphone wait") if MacRC <> 0 then signal ErrExit
 * Get the microphone connection.

/* MacRC = SendString("record rexxalias") if MacRC <> 0 then signal ErrExit /* say 'Record operation is now in progress.' say ''
 * Start the record operation.
 * Notify users that recording is in progress.

/* */  pull trash
 * Wait for a key to be pressed:

/* MacRC = SendString("stop rexxalias wait") if MacRC <> 0 then signal ErrExit
 * Stop recording

/* MacRC = SendString("save rexxalias wait") if MacRC <> 0 then signal ErrExit
 * Save the file

/* MacRC = SendString("close rexxalias wait") if MacRC <> 0 then signal ErrExit
 * Close the instance

/* exit 0
 * Exit, return code = 0.

/*  --- SendString -- SendString: arg CmndTxt /* Last two parameters are reserved, must be set to 0          */ /* Future use of last two parms are for notify window handle   */ /* and userparm. */  MacRC = mciRxSendString(CmndTxt, 'RetSt', '0', '0') if MacRC<>0 then do     ErrRC = MacRC say 'MciCmd=' CmndTxt say 'Err:mciRxSendString RC=' ErrRC RetSt MacRC = mciRxGetErrorString(ErrRC, 'ErrStVar') say 'mciRxGetErrorString('ErrRC') =' ErrStVar MacRC = ErrRC /* return the error rc */ end return MacRC
 * Call DLL function. Pass the command to process and the
 * name of a REXX variable that will recieve textual return
 * information.

/* -- help -- Help: say say 'This command file records a wave file using the MultiMedia' say 'REXX string interface.' say say 'record filename' say return
 * Display help text

/* --- ErrExit -- ErrExit: MacRC = mciRxExit  /* Tell the DLL we're going away        */ exit 1;              /* exit, tell caller things went poorly */
 * Common routine for error clean up/program exit.
 * Gets called when commands to DLL fail.

/*   error -- error: ErrRC = rc  say 'Error' ErrRC 'at line' sigl ', sourceline:' sourceline(sigl) MacRC = mciRxExit      /* Tell the DLL we're going away */ exit ErrRC               /* exit, tell caller things went poorly */ 
 * Routine gets control when any command to the external
 * environment (usually OS/2) returns a non-zero RC.
 * This routine does not get called when the macapi.dll
 * returns non-zero as it is a function provider rather
 * than a command environment.