Aros/Developer/Docs/Rexx

From Wikibooks, open books for an open world
Jump to navigation Jump to search
Navbar for the Aros wikibook
Aros User
Aros User Docs
Aros User FAQs
Aros User Applications
Aros User DOS Shell
Aros/User/AmigaLegacy
Aros Dev Docs
Aros Developer Docs
Porting Software from AmigaOS/SDL
For Zune Beginners
Zune .MUI Classes
For SDL Beginners
Aros Developer BuildSystem
Specific platforms
Aros x86 Complete System HCL
Aros x86 Audio/Video Support
Aros x86 Network Support
Aros Intel AMD x86 Installing
Aros Storage Support IDE SATA etc
Aros Poseidon USB Support
x86-64 Support
Motorola 68k Amiga Support
Linux and FreeBSD Support
Windows Mingw and MacOSX Support
Android Support
Arm Raspberry Pi Support
PPC Power Architecture
misc
Aros Public License

Arexx[edit | edit source]

A scripting language for processing text and files.

A script is just a plain text file.

rx script.rexx

Copyrighted (William S. Hawes) extension to Rexx from IBM

ARexx is a scripting language and although you can use it to launch applications, you're better off using a regular DOS script for that, if it's required.

ARexx is far more powerful than DOS, but isn't really intended as a replacement. ARexx's principal strength is that it allows you to write scripts that can talk to "hosts"; that is, any application that has an ARexx port. This allows ARexx to control the application (within the limits of what features it exposes to ARexx). As a single script can communicate with multiple hosts, it can coordinate activity between applications. Any command it doesn't understand, it forwards to the currently selected host. By default, the host is the shell itself.

You invoke CALL searching for a function from a library (according to the documentation in this search order): 1) Internal functions 2) Built-in functions 3) Function libraries and Function Hosts 4) External AREXX programs - forward the Msg to the AREXX process itself and scan for files loaded

There are three different 'modes': - ARexx command host - function host - function library

Tutorial[edit | edit source]

  /* A simple program */
  say 'Hello World'

Looping[edit | edit source]

The DO control structure always begins with a DO and ends with an END.

DO UNTIL:

   DO UNTIL [condition]
   [instructions]
   END

DO WHILE:

   DO WHILE [condition is true]
   [instructions]
   END

Stepping through a variable:

   DO i = x TO y BY z
   [instructions]
   END

Looping forever until exiting with LEAVE - BREAK has other possibilities, but you probably want LEAVE:

   DO FOREVER
     IF [condition] THEN LEAVE
   END

Looping a fixed number of times

   DO i = x TO y BY z FOR a
   [instructions]
   END

Conditionals[edit | edit source]

   IF [condition] THEN
     [instruction]
   ELSE
     [instruction]

Testing for multiple conditions

   SELECT
     WHEN [condition] THEN
     DO
     [instruction]
     END
   OTHERWISE
     DO
     [instruction] or NOP
     END

NOP indicates no instruction is to be executed.

Variables[edit | edit source]

There is a command that looks if a var is a integer or not... DATATYPE(WHOLE)

Command and Function[edit | edit source]

Rexx makes difference between commands and functions. With ADDRESS you indicate a place to send a command to. Commands are always one line. Functions have to be in a library with ARexx support and be added with addlib. You seem to want function syntax but are using command port.

Changes the way that arguments passed on the command line are made available to the called Rexx program. With this switch each parameter on the command line is available as a separate argument, rather then the normal behaviour of only making the combined command line arguments available as one internal argument.

seems indeed that 'command'-mode is not to receive more then 1 argument (arg[0]).

PARSE[edit | edit source]

The PARSE instruction is particularly powerful; it combines some useful string-handling functions

parse [upper] origin template

where origin specifies the source:

  • arg (command line variable)
  • linein (keyboard)
  • pull (REXX data queue)
  • value (a literal or a function) with required to indicate where the literal ends
  • var (a variable)
  • version (version/release number)

and template can be:

  • list of variables
  • column number delimiters
  • literal delimiters

upper is optional; it you specify it, data will be converted to upper case. use 'PARSE PULL var' if you don't want the input to be uppercased. 'PULL var' is the same as 'PARSE UPPER PULL var'

Examples:

Using a list of variables as template

   myVar = "John Smith"
   parse var MyVar firstName lastName
   say "First name is:" firstName
   say "Last name is:"  lastName

displays the following

   First name is: John
   Last name is: Smith

Using a delimiter as template:

   myVar = "Smith, John"
   parse var MyVar LastName "," FirstName
   say "First name is:" firstName
   say "Last name is:"  lastName

also displays the following

   First name is: John
   Last name is: Smith

Using column number delimiters:

   myVar = "(202) 123-1234"
   parse var MyVar 2 AreaCode 5  7 SubNumber
   say "Area code is:" AreaCode
   say "Subscriber number is:" SubNumber

displays the following

   Area code is: 202
   Subscriber number is: 123-1234

A template can use a combination of variables, literal delimiters, and column number delimiters.

Advanced Data Structures[edit | edit source]

array(var, var)
record.field1
record.field2
/* check if a library function is available */
SHOW (L, 'rexxsupport.library')

/* if function not available - add library*/
ADDLIB ('rexxsupport.library',0,-30,0)

/* SHOWDIR (directory, {mode), [pad]) */
SHOWDIR ('sys:')

/* if function  - remove library*/
REMLIB ('rexxsupport.library')
parse arg height, width, depth
PROCEDURE EXPOSE WORD

You can pass a whole stem to a function declared in THE SAME script via the "PROCEDURE EXPOSE" statement, e.g.

...
test: procedure expose stem.
say stem.1 stem.a stem.a.ciao
...

(take care to put the '.' or just the stem var will be visible to the function 'test', not the all stem. Any modificiation to 'stem.' will be global. There is no way to pass a stem to an external function

ARexx will also search for the function in a file with the same name as it and as <name>.rexx), both in current dir and REXX:. Don't put .rexx on my files, as ~(*:*) matches functions, *.rexx ordinary scripts, *.ced ...

File Manipulation[edit | edit source]

/* Create a file */
if open('file', 'ram:test', 'w') then
  call writeln('file' 'data')
  call close('file')
else
  say 'unable to open'
/* Read a file */
if open('file', 'ram:test', 'r') then
  call readch('file' var) /* read first var characters */
  do while ~eof('file')
    say line
    line=readln('file') /* read next line */
  end
  call close('file')
/* Read a file */
if open('file', 'ram:test', 'a') then
  say 'it is here'
else 
  say 'it is not here'
position = seek ('file', var, string) /* var is +/- bytes from start and string can be b, c or e */

IBM EOL (end of line) = <CR><LF> or (^M^J)
Amiga/UNIX EOL = <LF> or (^J)
Macintosh EOL = <CR> or (^M)
IBM EOF = <EOL><CTRL-Z> or (^M^J^Z)
Amiga EOF = <NULL> or (), optionally <LF> or (^J)

Null, of course, is the absence of any data.

IBM text editors do a <CR><LF> at each <RETURN>. Most Amiga software, AFAIK, only does <LF> and the end of the file is simply indicated when there are no more bytes to be read.

The ARexx EOF() indicates when you've run out of stuff to read from a file. It does not (to my knowledge) look around for whatever the IBM thinks signifies the end of a file on top of simply noticing when the file has reached its end of capacity (say you have a 1024-byte file, after reading 1024 bytes you will have reached the end of the file, or EOF).

do UNTILEF("FILE") /* Do some reading here or something */ end

Change "call readch('rfile',1)" into "say c2d(readch('rfile',1))" to display the "ascii" code. (Note that only charcodes < 127 is ASCII)

Debugging[edit | edit source]

Syntax, loop errors, etc.

Trace

Signal

Program IPC[edit | edit source]

rx "address command Dir"

Talking to DOS,

address "program-name"

You can get the ARexx commands of any MUI program with the HELP command. Here is an example for IBrowse.

/* CheckIB.rexx*/

address IBrowse

'HELP vd0:IB.txt'

The drive specification after the HELP command is the drive and file name for the output. Of course, the program you are checking must be running so the port is available. The output will show the standard ARexx commands that every MUI program has followed by the commands specific to the particular program being checked.

Hooks[edit | edit source]

Normally you would provide a commandlist with your application and those command would 'automatically' be processed by the hooks installed.

It's when you use commands that does not get 'processed automatically' (because they are not in the commandlist) you need to make use of the functionality you presented (like MUIA_Application_RexxHook).

Since those attributes does not seem to be in place, you would need to fallback to the ordinairy way of doing things, meaning you would somehow 'invent' how to 'sync' your normal commands with the arexx commands

References[edit | edit source]

Regina 3.00

Instructions[edit | edit source]

CALL {symbol | string} [expression] [,expression,...]
DO [var=exp] [To exp] [BY exp]] [FOR exp] [FOREVER] [WHILE exp | UNTIL exp]
DROP variable [variable...]
ELSE [;] [conditional statement]
END [variable]
EXIT [expression]
IF expression [THEN] [;] [conditional statement]
INTERPRET expression
ITERATE [variable]
LEAVE [variable]
NOP
NUMERIC {DIGITS | FUZZ} expression  or: NUMERIC FORM {SCIENTIFIC | ENGINEERING}
OPTIONS [FAILAT expression]  or: OPTIONS [PROMPT expression]   or: OPTIONS [RESULTS]
OTHERWISE [;] [conditional statement]
PARSE [UPPER] inputsorce [template] [,template...]
PROCEDURE [EXPOSE variable [variable...]]
PULL [template] [,template...]
PUSH [expression]
QUEUE [expression]
RETURN
SAY [expression]
SELECT
SHELL [symbol | string] [expression]
SIGNAL {ON |OFF} condition   or: SIGNAL [VALUE] expression
THEN[;] [conditional statement]
TRACE [symbol|string|[[VALUE] expression]]
UPPER variable [variable...]
WHEN expression [THEN [;] [conditional statement]]

Functions[edit | edit source]

ABBREV
ABS
ADDLIB
ADDRESS [Symbol|string|VALUE] [expression]]
ALLOCATED
ARG [template] [,template...]
B2C
B2X
BEEP
BITAND
BITCHG
BITCLR
BITCOMP
BITOR
BITSET
BITTST
BITXOR
BUFTYPE
C2B
C2D
C2X
CD
CENTER
CENTRE
CHANGESTR
CHARIN
CHAROUT
CHARS
CHDIR
CLOSE
COMPARE
COMPRESS
CONDITION
COPIES
COUNTSTR
CRYPT
D2C
D2X
DATATYPE
DATE
DELSTR
DELWORD
DESBUF
DIGITS
DIRECTORY
DROPBUF
DUMPFILES
DUMPTREE
DUMPVARS
EOF
ERRORTEXT
EXISTS
EXPORT
FREELISTS
FREESPACE
FUZZ
GETCLIP
GETENV
GETPATH
GETPID
GETSPACE
GETTID
HASH
IMPORT
INDEX
INSERT
JUSTIFY
LASTPOS
LEFT
LENGTH
LINEIN
LINEOUT
LINES
LISTLEAKED
MAKEBUF
MAX
MEMORYSTATS
MIN
OPEN
OVERLAY
POPEN
POS
PRAGMA
QUALIFY
QUEUED
RANDOM
RANDU
READCH
READLN
REMLIB
REVERSE
RIGHT
RXFUNCADD
RXFUNCDROP
RXFUNCERRMSG
RXFUNCQUERY
RXQUEUE
SEEK
SETCLIP
SHOW
SIGN
SLEEP
SOURCELINE
SPACE
STATE
STREAM
STRIP
STORAGE
SUBSTR
SUBWORD
SYMBOL
TIME
TRACE
TRIM
TRACEBACK
TRANSLATE
TRUNC
UNAME
UNIXERROR
UPPER
USERID
VALUE
VERIFY
WORD
WORDINDEX
WORDLENGTH
WORDPOS
WORDS
WRITECH
WRITELN
X2B
X2C
X2D
XRANGE

Operators[edit | edit source]

OPERATOR		PRIORITY		OPERATOR DEFINITION

~			8			Logical NOT
+			8			Prefix Conversion
-			8			Prefix Negation
**			7			Exponentiation
*			6			Multiplication
/			6			Division
%			6			Integer Division
//			6			Remainder
+			5			Addition
-			5			Subtraction
||			4			Concatenation
(blank)			4			Blank Concatenation
==			3			Exact Equality
~==			3			Exact Inequality
=			3			Equality
~=			3			Inequality
>			3			Greater Than
>=,~<			3			Greater Than or Equal To
<			3			Less Than
<=,~>			3			Less Than or Equal To
&			2			Logical AND
|			1			Logical Inclusive OR
^,&&			1			Logical Exclusive OR

RexxSupport[edit | edit source]

LONG rxsupp_allocmem(struct Library *, struct RexxMsg *, UBYTE **);
LONG rxsupp_baddr(struct Library *, struct RexxMsg *, UBYTE **);
LONG rxsupp_closeport(struct Library *, struct RexxMsg *, UBYTE **);
LONG rxsupp_delay(struct Library *, struct RexxMsg *, UBYTE **);
LONG rxsupp_delete(struct Library *, struct RexxMsg *, UBYTE **);
LONG rxsupp_forbid(struct Library *, struct RexxMsg *, UBYTE **);
LONG rxsupp_freemem(struct Library *, struct RexxMsg *, UBYTE **);
LONG rxsupp_getarg(struct Library *, struct RexxMsg *, UBYTE **);
LONG rxsupp_getpkt(struct Library *, struct RexxMsg *, UBYTE **);
LONG rxsupp_makedir(struct Library *, struct RexxMsg *, UBYTE **);
LONG rxsupp_next(struct Library *, struct RexxMsg *, UBYTE **);
LONG rxsupp_null(struct Library *, struct RexxMsg *, UBYTE **);
LONG rxsupp_offset(struct Library *, struct RexxMsg *, UBYTE **);
LONG rxsupp_openport(struct Library *, struct RexxMsg *, UBYTE **);
LONG rxsupp_permit(struct Library *, struct RexxMsg *, UBYTE **);
LONG rxsupp_rename(struct Library *, struct RexxMsg *, UBYTE **);
LONG rxsupp_reply(struct Library *, struct RexxMsg *, UBYTE **);
LONG rxsupp_showdir(struct Library *, struct RexxMsg *, UBYTE **);
LONG rxsupp_showlist(struct Library *, struct RexxMsg *, UBYTE **);
LONG rxsupp_statef(struct Library *, struct RexxMsg *, UBYTE **);
LONG rxsupp_typepkt(struct Library *, struct RexxMsg *, UBYTE **);
LONG rxsupp_waitpkt(struct Library *, struct RexxMsg *, UBYTE **);

RexxSyslib[edit | edit source]

UBYTE *CreateArgstring(UBYTE *string, ULONG length) (A0, D0)
void DeleteArgstring(UBYTE *argstring) (A0)
ULONG LengthArgstring(UBYTE *argstring) (A0)
struct RexxMsg *CreateRexxMsg(struct MsgPort *port, UBYTE *extension, UBYTE *host) (A0, A1, D0)
void DeleteRexxMsg(struct RexxMsg *packet) (A0)
void ClearRexxMsg(struct RexxMsg *msgptr, ULONG count) (A0, D0)
BOOL FillRexxMsg(struct RexxMsg *msgptr, ULONG count, ULONG mask) (A0, D0, D1)
BOOL IsRexxMsg(struct RexxMsg *msgptr) (A0)
void LockRexxBase(ULONG resource) (D0)
void UnlockRexxBase(ULONG resource) (D0)

Adding Regina/Rexx to Programs[edit | edit source]

http://aminet.net/search?query=Minrexx http://www.pcguru.plus.com/tutorial/introduction.html

It's really easy to set up your own Rexx port. It's just a standard MsgPort. If you are just going to exit on any message and not parse commands sent to the port then you don't even need to open rexxsyslib.library...

struct MsgPort *CreatePubPort (UBYTE *name, LONG pri)
/* create a port if port name is not already used */
{
struct MsgPort *pubport;

port = NULL;
Forbid ();
if (FindPort (name) == NULL)
pubport = CreateMsgPort (name, pri);
Permit ();
return (pubport);
}

Now that the port is created we just need to wait for a message.

static void PubPortWait (struct MsgPort *pubport)
/* wait for a message or signal on our pubport */
{
ULONG port_mask, signals;
struct RexxMsg *msg;

port_mask = (1L << RexxPort->mp_SigBit);
while (1)
	{
	signals = Wait (port_mask | SIGBREAKF_CTRL_C);
	if (signals & SIGBREAKF_CTRL_C)
	Quit("Aborting");
	if (signals & port_mask)
		{
		while ((msg = (struct RexxMsg *) GetMsg (pubport)) != NULL)
			{
			ProcessRexxMsg (msg); /* routine parses the REXX message */
			ReplyMsg ((struct Message *) msg);
			}
		}
	}
}

ProcessRexxMsg() can print your message and exit but remember to ReplyMsg(). If you want to parse the Arexx commands, it's not much more work. The above is pulled from a longer printed code example and uses some REXX includes from "arexx.h" and "arexxsyslib.h" but it should NOT be necessary to open rexxsyslib.library if you will exit on any message received at your port.

http://utilitybase.com/forum/index.php?action=vthread&forum=15&topic=1915

/*
#include <proto/exec.h>
#include <proto/alib.h>
#include <proto/rexxsyslib.h>

#include <exec/ports.h>
#include <rexx/errors.h>
#include <rexx/storage.h>
#include <stdio.h>
#include <string.h>

int main(void)
{
    struct MsgPort *port;
    struct RexxMsg *msg;
    struct Library *RexxSysBase;
    char *value;
    
    RexxSysBase = OpenLibrary("rexxsyslib.library", 0);
    if (RexxSysBase == NULL)
    {
	puts("Error opening rexxsyslib.library");
	return 20;
    }
    
    port = CreatePort("VARTEST", 1);
    if (port == NULL)
    {
	puts("Error creating port");
	CloseLibrary(RexxSysBase);
	return 20;
    }

    printf("Port created %x, waiting for message\n", port);
    WaitPort(port);
    msg = (struct RexxMsg *)GetMsg(port);
    puts("Got a message");
    if (!IsRexxMsg(msg))
    {
	puts("Message is not a rexxmsg");
	ReplyMsg((struct Message *)msg);
	DeletePort(port);
	CloseLibrary(RexxSysBase);
	return 20;
    }

    puts("Is a rexx message");
    if (!CheckRexxMsg(msg))
    {
	puts("Message is not from rexx interpreter");
	msg->rm_Result1 = RC_ERROR;
	ReplyMsg((struct Message *)msg);
	DeletePort(port);
	CloseLibrary(RexxSysBase);
	return 20;
    }

    puts("Message is from the rexx interpreter");
    if (!GetRexxVar(msg, "A", &value))
    {
	puts("Error during retrieval of value");
	return 20;
    }
    printf("Length string: %d\n", strlen(value));
    printf("Value of A: %s\n", value);
    
    SetRexxVar(msg, "A", "2", 1);
    msg->rm_Result1 = RC_OK;
    msg->rm_Result2 = NULL;
    ReplyMsg((struct Message *)msg);
    DeletePort(port);
    CloseLibrary(RexxSysBase);
    
    return 0;
}

You can now send a message to this little program with the following script:

/* Testing the variable interface */
a = 1
say 'a before call' a
ADDRESS 'VARTEST' test
say 'a after call' a

Save it as (example) 'vartest.rexx' and run it with:

rx vartest.rexx

Example script[edit | edit source]

For More Examples

  /* Calculate some squares and cubes      */
  do i = 1 to 10      /* 10 interations    */
     say i i**2 i**3  /* calculations      */
     end
  say ' all done '
  /* Even or odd? */
  do i = 1 to 10
     if i//2 = 0 then type = 'even'
                 else type = 'odd'
     say i 'is' type
     end
  /* Defining and calling a function     */
  do i = 1 to 5
     say i square(i)     /* call square  */
     end
  exit            /* all done            */
  square:		/* function name       */
  arg x		/* get the "argument"  */
  return x**2     /* square it and return*/
  /* Demonstrate "results" tracing */
  trace results
  sum=0;sumsq=0;
  do i = 1 to 5
     sum = sum + i
     sumsq = sumsq + i**2
     end
  say 'sum=' sum 'sumsq=' sumsq
  /* Calculate age in days */
  say 'Please enter your age'
  pull age
  say 'You are about' age*365 'days old'
DO .... ; you have the code already
  line = READLN(textfile)
  IF line = marker line THEN DO
    target = READLN(textfile)
  END
END
/* File to read */
IF ~OPEN("in", "Path:yourTextFile", "READ") THEN DO
SAY "Can't open 'Path:yourTextFile' for reading."
CALL CLOSE('in')
EXIT
END

/* File to write */
IF ~OPEN("out", "Ram:filename", "WRITE") THEN DO
SAY "Can't open 'Ram:filename' for writing."
CALL CLOSE('out')
EXIT
END

/* Write lines that start with "(" to Ram:filename */
DO UNTIL EOF('in')
line = READLN('in')
temp = STRIP(line, 'L')
IF LEFT(temp, 1) = '(' THEN CALL WRITELN('out', line)
END
/* */
options results

arg sourcefile

if open(input, sourcefile, R) then do
if ~open(output, "ram:output", W) then call error

Do until eof(input)
line=readln(input)
if pos('(',line)=1 then writeln(output,line)    /* line begins with '(' at POS 1 */
end
close(input)
close(output)
end
EXIT

/* function */
ERROR:
SAY "ERROR"
EXIT
/*Bin2AHex*/
/* Convert a binary file to a ASCII representation of Hex.*/

if ~open('bufbin', 'RAM:buffer.bin', 'READ') then do
say "Can't open 'RAM:buffer.bin' for reading."
exit
end

if ~open('buftxt', 'RAM:buffer.txt','WRITE') then do
say "Can't open 'RAM:buffer.txt' for writing."
exit
end

/* 500 seems do be the max for c2x() */
max = 500
in = readch('bufbin', max)
DO until eof('bufbin')
call writech('buftxt',c2x(in))
in = readch('bufbin', max)
END

call close('bufbin')
call close('buftxt')

/* That's All Folks! */
/* Here's a more visible way to enter the control codes */
CSI='9b'x /* control sequence introducer */
boldOn=CSI'1m'
boldOff=CSI'22m'
IF OPEN("Env", "Env:File", "R") THEN DO
filename = READLN("Env")
CALL CLOSE("Env")
END
/* OPEN alone will pop up a filerequester */
OPEN NAME <filename>
/* Displays the data in the source string */
parse source source   /* type results called resolved extension host */
words=words(source)
shell command 'RequestChoice >Nil: Source',
'"Invocation type:' word(source,1)'*n',
||'Result requested:' word(source,2)'*n',
||'Called name:' word(source,3)'*n',
||'Resolved name:' DelWord(trim(DelWord(source,words-1)),1,3)'*n',
||'Search extension:' word(source,words-1)'*n',
||'Initial command host:' word(source,words)'"',
'OK'
/* Grabs the Users Hz thus the tick rate per second of the persons machine
(either 50 or 60 generally) amd pouts it into variable 'f' */
PARSE UPPER VERSION f
f=SUBSTR(WORD(f,6),1,2)

DO UNTIL <expression is valid> | i=<value ya want>
/* Call a delay and do nothing for value times(*) variable 'f' */
Call Delay(1*f)
i=i+1
End

You can do it so it makes a value true or tries indefinitely. otherwise you can make it leave the loop using a 'Leave' command at any stage of the DO UNTIL part.

So the below loading libs works well...

libs = "rexxreqtools.library rexxdossupport.library rexxkuang11.library
rexxsupport.library rexxtricks.library"
DO UNTIL libs='';PARSE VAR libs lib libs
IF EXISTS('libs:'lib)|EXISTS('Libs/'lib)|EXISTS(lib) THEN DO
IF ~show('L',lib) THEN call addlib(lib,0,-30,0);END;ELSE DO
/* error output line whether say or echo or defined */
cECHO('Cannot load 'lib);END;END

find what number every key has, this can be in ARexx...

rx <RAW: "
do while 1;
z=readch(stdin,1);
c=c2d(z);
if c2d(bitand('7f'x,z)) < 33 then
z='<'c'>';
call writech(stdout,z);
end

(All on one line)

CTRL-C will stop this thing. Note here that the above and RAW-KEY Intui-messages (OpenWindow()/intuition, IDCMP flags) are about the only easy and *system legal* way to obtain information about keys pressed.

This will extract the filename extension (such as .lha or .8svx) along with its length. It can be useful, when renaming files. Used it in a conversion script, that can convert different file- types, so archive.lha gets archive.lzx and pic.gif gets pic.png.

STRING = ExtPart(filename)
PARSE VAR string extlen ext
/* ExtPart(filename)
** Returns filename extension and length of extension
** freely usable, freely distributable, intellectual property:
** Andreas Mixich <humpty@...>
*/

PARSE ARG filename

posi = LastPos('.',filename)
ext = SubStr(filename,(posi+1))
extlen = Length(SubStr(filename,posi))-1
extpartinfo = extlen||' '||ext
RETURN(extpartinfo)

This one makes a valid path, which is better to use. No more RAM:Envfilename mistake, but RAM:Env/filename Additional it makes RAM: out of Ram Disk:

Nothing special, though, you can achieve the same result (except for RAM:<-Ram Disk:) by using rexxdossupport.library/AddPart(pathname,"") - Do not forget the "".

STRING = ValPath("sys:Tools")
/* ValPath()
** make a vaild path out of path (in case it lacks '/')
** Concentrates "Ram Disk:" to "Ram:", so we get rid of this annoying space
** Freely usabel, freely distributable, intellectual property of
** Andreas Mixich <humpty@...>
*/

valpath:
PARSE ARG path

IF (LastPos('/',path) ~= Length(path) & Pos('/',path) ~= 0) THEN
path = path||'/'
ELSE
IF (LastPos(':',path) ~= Length(path) & Pos('/',path) = 0) THEN
path = path||'/'

IF Left(path,9) = "Ram Disk:" THEN
path = Insert("Ram",DelStr(path,1,8))

RETURN(path)

this little sub-routine used in all my ARexx scripts, that need external ports to be available. (WaitForPort) Since not liking to write the same lines in any script, just select this routine from a GoldED list requester and it is appended to my source. The function is simple:

BOOL = IfPort(portname, prog, mode)

portname is the ARexx-port name of the application, prog is the full path to the application, in case it needs to be launched mode can be either empty or WB. If WB is used it will be started with WBRun. Sometimes this is quite useful.

The function results in a BOOL (either TRUE or FALSE) so it is easy to handle flow control.

In this routine you will find another function I use:

myERR()

You can replace it by anything you want or just take it away. Use such general routines, because they are more powerful, so could have some logging in myERR() or WB requesters or whatever.

/*///------------------------------ "IfPort(portname,prog,mode)" ------------------------------ */

/*
** IfPort(portname, prog, mode)
** Shows if portname is available, attempts to launch program if not
** Freely usable, freely distributable, intellectual properties:
** Andreas Mixich <humpty@...>
*/

ifport:

PARSE ARG portname, prog, mode

IF ~Show(P,portname) THEN
DO
IF mode = WB THEN
ADDRESS COMMAND wbrun||' >NIL: '||prog
ELSE
ADDRESS COMMAND 'run >NIL: '||prog

ADDRESS COMMAND 'WaitForPort '||portname
IF RC ~= 0 THEN
DO
Call myERR('Error: Could not start '||prog)
RETURN(FALSE)
END
ELSE
RETURN(TRUE)
END
RETURN(TRUE)
/*\\\*/

Here ya go, the fastest SQRT function in Rexx I have seen yet, found in c.l.rexx

/* */
parse arg N
if N <= 0 then exit
s = time('e')
call MethodB(N)
say time('e') - s
exit
MethodB:
x1 = 1
x0 = N
if N ~= 0 then
do until x1 = x0
x0 = x1
x1 = ((x0 * x0) + N) / (2 * x0)
end
else x1 = 0
say x1
return

a very simple one, seldomly used, but given as some example of the Translate() function to newbies and to make the function set a bit more complete. (Since there is an Upper() why shouldn't be there also a Lower())

/*
** Lower()
** Like Upper(), but this one makes a string lowercase.
** Considers german unmlauts and some accents.
** TODO: Add more national characters. Do you know of any missing
** (and where to find them on the keyboard) ?
*/
Lower: PROCEDURE
PARSE ARG string
res =
Translate(string,'abcdefghijklmnopqrstuvwxyzäöüáéíóú','ABCDEFGHIJKLMNOPQRSTUVWXY\
ZÄÖÜÁÉÍÓÚ')
RETURN(res)
/* convert a file into sentences */
arg fileid .
input=''
do until LINES(fileid)=0
in=LINEIN(fileid)
input=input in
end
t=LINEOUT("sentence.txt","--- "fileid" ---")
out=''
do i=1 to LENGTH(input)
char=SUBSTR(input,i,1)
out=out||char
if char="." then do
out=STRIP(SPACE(out))
t=LINEOUT("sentence.txt",out)
out=''
end
end
out=STRIP(SPACE(out))
t=LINEOUT("sentence.txt",out)
exit

Suggest that when you open files you do something more like this to check that it was opened successfully:

if open(tempfile,'t:ms_scenes',w) then do
call writeln(tempfile,'SCENE1')
/* blah, blah... */
call close(file)
end
else do
say 'Unable to open temp file!'
exit
end

Get the file size

/* */

parse arg file

if file="" then do
say "no file given"
exit
end

s=statef(file)
if s="" then do
say "can't find file '"file"'"
exit
end

parse var s type size d d d d d comment
if type~="FILE" then do
say "'"file"' is not a valid file name"
exit
end
say "file '"file"':" size