Aros/Developer/Docs/Rexx

Arexx
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

Looping
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
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
There is a command that looks if a var is a integer or not... DATATYPE(WHOLE)

Command and Function
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
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
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 .rexx), both in current dir and REXX:. Don't put .rexx on my files, as ~(*:*) matches functions, *.rexx ordinary scripts, *.ced ...

File Manipulation
/* 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) =  or (^M^J) Amiga/UNIX EOL =  or (^J) Macintosh EOL =  or (^M)

IBM EOF =  or (^M^J^Z) Amiga EOF =  or, optionally  or (^J)

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

IBM text editors do a  at each . Most Amiga software, AFAIK, only does  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).

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

Debugging
Syntax, loop errors, etc.

Trace

Signal

Program IPC
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
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

Instructions
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
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
OPERATOR		PRIORITY		OPERATOR DEFINITION

~			8			Logical NOT +			8			Prefix Conversion -			8			Prefix Negation /			6			Division %			6			Integer Division //			6			Remainder +			5			Addition -			5			Subtraction (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 Exclusive OR
 * 7			Exponentiation
 * 6			Multiplication
 * 4			Concatenation
 * 1			Logical Inclusive OR

RexxSupport
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
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
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

/*
 * 1) include 
 * 2) include 
 * 3) include 


 * 1) include 
 * 2) include <rexx/errors.h>
 * 3) include <rexx/storage.h>
 * 4) include <stdio.h>
 * 5) 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
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

/* 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', 'OK'
 * '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)'"',

/* 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 | i= /* 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: PROCEDURE PARSE ARG string res = Translate(string,'abcdefghijklmnopqrstuvwxyzäöüáéíóú','ABCDEFGHIJKLMNOPQRSTUVWXY\ ZÄÖÜÁÉÍÓÚ') RETURN(res)
 * 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) ?

/* 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