04 July 2015, 13:45 | #1 |
Registered User
Join Date: Feb 2014
Location: Germany
Posts: 261
|
ARexx scripting: How to
I think section "Coders/Tutorials" is for ASM,C and C++. So i placed it here. Move it if i'm wrong.
Absolute beginners start here Because of some recent uploads to Aminet, i'd like to show users who are interested in learning how to create working ARexx scripts a shorter, faster and much more convinient way to interact with a tool like DirOpus4 While DO4-Version12.rexx from Aminet fills RamDisk with garbage data and is not aware of folders (it copies selected directories to ram drive for no reason), our small script just ignores entries that do not give any valid version information and deletes the output when done. First a simple script that reads version information from selected files and present them as a list. Code:
/* * $VER: VersionLister 1.21 (25.06.15) * * * Small REXX script to get version * information from selected files * and to filter the output. * There is no error checking is made. * * Requirements : * * DirectoryOpus v4.xx * Version command in DOS search path * * Written by BigFan * no copyright claims on basic stuff * and no license idiocracy * */ tempfile = 't:v2' /* to store version information */ outfile = 't:vt2' /* to store filtered information */ numitem = 0 Options RESULTS /* delete remaining files from an earlier interrupted run this is necessary as we redirect version command to append the data, not to overwrite */ If Exists(tempfile) Then Address command 'delete 'tempfile If Exists(outfile) Then Address command 'delete 'outfile DirOpus = Address() Address value DirOpus Busy On Status 3 /* get active pane */ actpane = result Status 9 actpane /* get selected files */ numitems = result If numitems = 0 | numitems = 'RESULT' Then Do Busy Off Exit 5 End Status 13 actpane /* get the current directory */ actdir = result Do i = 1 to numitems /* call version for each selected file */ GetNextSelected item = result Address command 'version >>'tempfile' "'actdir||item'" file' SelectFile item toggle /* deselect current file */ DisplayDir /* refresh current pane */ End /* mow filtering the input from unidentified files*/ Open('infile',tempfile,'r') Open('outfile',outfile,'w') Do While ~EOF('infile') /* or simply Do Forever*/ w = Readln('infile') if w = '' then break /* this will always break (forever or eof)*/ d = Word(w,2) /* use second part of version output */ /* check if result is a numeric value */ if Datatype(Left(d,3)) = NUM Then Do writeln('outfile',w) /* in that case write entire string */ end end Close('outfile') Close('infile') Read outfile /* let DOpus open it */ /* cleaning up*/ Busy Off Delete tempfile Delete outfile Exit 0 Last edited by BigFan; 04 July 2015 at 15:02. Reason: attached files |
04 July 2015, 13:47 | #2 |
Registered User
Join Date: Feb 2014
Location: Germany
Posts: 261
|
Using requesters
As this is much too simple and still uses intermediate files (which we have to
remove from RamDisk to save memory), here is a similar script to show each version string in a requester. This time we send our data to PIPE: instead to a file. This makes the script smaller and leaves no remains in RAM: Code:
/* * $VER: VersionRequester 1.03 (30.06.15) * * * Small REXX script to get version * from selected files and filtering * the output. * There is no error checking. * * Requirements : * * DirectoryOpus v4.xx * Version command in DOS search path * PIPE-handler must be running * * Written by BigFan * no copyright claims on basic stuff * and no license idiocracy */ tempfile = 'pipe:versionstring' numitem = 0 Options RESULTS DirOpus = Address() Address value DirOpus Status 3 /* get active pane */ actpane = RESULT Status 9 actpane /* get selected files */ numitems = RESULT If numitems = 0 | numitems = 'RESULT' Then Exit 5 Status 13 actpane /* get the current directory */ actdir = RESULT Open('temp',tempfile,r) Do i = 1 To numitems /* call version for each selected file */ GetNextSelected item = RESULT Address command 'version >' tempfile ' "'actdir||item'" file' item2 = Readln('temp') /* get the version string*/ vers = Word(item2,2) /* reduce to version number only*/ /* and check if it is a numeric value*/ If Datatype(Left(vers,3)) = NUM Then Do Verify item2 /* call DirOpus requester to display*/ r = RESULT If r=0 Then Do Close('temp') Exit 10 End End SelectFile item 0 /* deselect current file */ DisplayDir End Close('temp') Exit 0 /* leave with no error code */ Last edited by BigFan; 04 July 2015 at 14:12. |
04 July 2015, 13:49 | #3 |
Registered User
Join Date: Feb 2014
Location: Germany
Posts: 261
|
Enhanced version info
This script can be enhanced, of course. Like DOS command version, we could pass
an argument like 'full' to get additional information. Try yourself before peeking the next script. Hints: Arguments are taken with Arg name [name]... name now contains the argument given We need to check if argument is given, find a good place for this Instead of concatinated strings, a line break eases reading. Find a way. Code:
/* * $VER: VersionRequester 1.04 (04.07.15) * * * Small REXX script to get version * from selected files and filtering * the output. * There is no error checking. * * Requirements : * * DirectoryOpus v4.xx * Version command in DOS search path * PIPE-handler must be running * * Written by BigFan * no copyright claims on basic stuff * and no license idiocracy */ Arg fullversion tempfile = 'pipe:versionstring' numitem = 0 item3 = '' Options RESULTS DirOpus = Address() Address value DirOpus Status 3 /* get active pane */ actpane = RESULT Status 9 actpane /* get selected files */ numitems = RESULT If numitems = 0 | numitems = 'RESULT' Then Exit 5 Status 13 actpane /* get the current directory */ actdir = RESULT Open('temp',tempfile,r) Do i = 1 To numitems /* call version for each selected file */ GetNextSelected item = RESULT Address command 'version >' tempfile ' "'actdir||item'" file 'fullversion item2 = Readln('temp') /* get the version string*/ vers = Word(item2,2) /* reduce to version number only*/ /* and check if it is a numeric value*/ If Datatype(Left(vers,3)) = NUM Then Do If fullversion='FULL' Then Do /* check for argument */ item3 = Readln('temp') /* overwrite empty string */ If item3 ~= '' Then item3 = '0a'x item3 /* add a line break */ End Verify item2 item3 /* call DirOpus requester to display*/ r = RESULT If r=0 Then Do Close('temp') Exit 10 End End SelectFile item 0 /* deselect current file */ DisplayDir End Close('temp') Exit 0 /* leave with no error code */ Last edited by BigFan; 04 July 2015 at 14:13. |
04 July 2015, 13:55 | #4 |
Registered User
Join Date: Feb 2014
Location: Germany
Posts: 261
|
Comparison of directories
The next is a small script to compare two folders. It does not require any
selection, it will highlight files in both panes which have the same name. The script is not optimized for speed but yet is 2 to 8 times faster then DO4-Intersect13.rexx. The script can be altered to highlight mismatches instead. Code:
/* * $VER: Comparer 1.00 (03.07.15) * * * Small REXX script to compare files * by name and select identicals. * * Requirements : * * DirectoryOpus v4.xx * * * Written by BigFan * no copyright claims on basic stuff * and no license idiocracy * */ numitem = 0 Options RESULTS DirOpus = Address() Address value DirOpus Busy On All Status 3 /* get active pane */ actpane = result Status 9 actpane /* get selected files */ numitems = result If numitems = 0 | numitems = 'RESULT' Then Do Busy Off Exit 5 End Do i = 1 to numitems GetNextSelected item = result OtherWindow SelectFile item 1 1 /* select file if exists */ OtherWindow SelectFile item 0 1 /* deselect to continue with next selected */ End /* Now corresponding files have been selected lets reselect first pane*/ OtherWindow GetSelectedAll ';' /* get all identical files using ; as devider*/ item = RESULT OtherWindow Do Forever /* and parse them */ n = Pos(';',item) If n = 0 Then Leave /* until all ; found */ Selectfile Left(item,n-1) 1 1 item = DelStr(item,1,n) /* cut processed file from selection */ End Busy Off Exit 0 ugly. If you want to test your scripting skills than alter it to work in "silent" mode Some suggestions on how to achieve this: -use intermediate files to store your results -use Setclip()GetClip() to store them in memory create a simple array using stems. A stem is identified by its name and a trailing number e.g.: /* presume numoffiles has return value '3' and search path is 'libs:' then :*/ this gives: found.1 = icon.library found.2 = rexxsys.library found.3 = workbench.library There are more possible solutions, like allocation of memory and write/read directly to mem (using rexxsupport.library). Piping might be used as well. Last edited by BigFan; 04 July 2015 at 14:11. |
04 July 2015, 14:05 | #5 |
Registered User
Join Date: Feb 2014
Location: Germany
Posts: 261
|
ARexx Bug Killing
When you start with ARexx the first time you'll soon encounter the problem of
finding errors. (erm, no , the errors will find YOU ) The script stops on any error and you won't see anything useful Debug Mode To enable debugging, put Trace('R') right after the first comment in your ARexx script to see result values or use any other option that suits you (e.g. Trace('A') for All) Before starting your script call 'TCO' from command line or command input (right Amiga + e). 'TCO' (short for TraCeOutput) resides in 'sys:rexxc' To close console call 'TCC' (abbr. for TraCeClose). There are 2 more commands and they come very handy when debugging is not need all the time. TS - starts tracing TE - ends tracing While hunting bugs, it might be a good idea to use 'say' command for testing. If you doubt a function to work as expected, put say infront. An output console (or shell) is required. For DOpus you could abuse DOpus Command 'Verify' to pop up a requester. Verify <symbol|string> where <symbol> is your variable and <string> is a text in single quotes like: Verify 'Var item2= ' item2 Have fun with ARexx Last edited by BigFan; 05 July 2015 at 00:06. Reason: darn, i wrote tce instead of tcc . |
04 July 2015, 14:39 | #6 |
Registered User
Join Date: Feb 2014
Location: Germany
Posts: 261
|
Introduction and TOC
Never done anything in ARexx. Absolute newbie ?
Here is a short introduction: ARexx is a scripting language to send commands to a so called 'host'. A host is an application with a message port, that listens to ARexx. So what we do is simply sending a message containing our code. The host has to interpet it and if possible process it. Whether the host answers back with valid return codes or not is up to host. ARexx has 2 special vars to deal with return values. See later Chapter ARexx requires the RexxMaster to be up and running. RexxMast can be found in Sys:System. To create an ARexx script any editor will do. Ed from C: or emacs or your preferred editor. If you don't have an editor at hand, you can still use AmigaDOS commands to create a script. An ARexx script starts with a comment. A comment is one or more lines encapsuled in /* */. ARexx has no variable typing, that means a var has not to be declared as Int, Float, String. You might use any single char as a variable. welcome = 'Hello Folks' /* now welcome contains a string*/ welcome = 7 /* is now overwritten with a number */ welcome = RESULT /* and now contains what the host replied */ A very basic script looks like: Code:
/* * Very, very basic stuff * $VER: Verybasicstuff 1.0 (04.07.15) */ w = 'Hello, world !' Say w /*or simply say 'Hello, world!'*/ Exit Time for a TOC ******************************************** First steps Run from shell Using quotation marks Addressing hosts Symbols and operators introducing functions Analyze strings List of commands List of functions Console input Counting words String searching and replacing Interrupt Signals Checking return codes Tracing Decrypting trace output Special character Function usage Creating functions and commands Parsing arguments Code obfuscation Parsing with variable length Parsing strings from DirOpus4 Loops Adding libraries Create and open a rexx port Last edited by BigFan; 14 July 2015 at 13:11. Reason: Table of content added |
04 July 2015, 14:49 | #7 |
Registered User
Join Date: Feb 2014
Location: Germany
Posts: 261
|
Now if you got too much free time, then do it in AmigaDos:
Open a shell and use 'echo' For example echo >>ram:test.rexx /* test */ echo >>ram:test.rexx /* $VER: test 1.0 */ echo >>ram:test.rexx "welcome = 'Hello, world!'" echo >>ram:test.rexx Say welcome echo >>ram:test.rexx Exit to test your script cd ram: 2.RAM:> rx test the file ending *.rexx is not needed. But using it helps to identify rexx scripts among others. If you are running WinUAE with shared clipboard, then mark the commands and copy (ctrl+c) them to clipboard. Switch to WinUAE , start a shell and paste it by pressing rightAmiga+v, press 'Enter' The prompt will quickly repeat itself. Don't worry. :>cd ram: 2.RAM:>type test.rexx or run it 2.RAM:>rx test You can check version with 2.RAM:> version test.rexx An editor is really, really much more convinient Last edited by BigFan; 04 July 2015 at 15:09. |
04 July 2015, 14:51 | #8 |
Registered User
Join Date: Aug 2004
Location: www.amigakit.com
Posts: 2,083
|
Thank you for taking the time to publish the useful tutorials.
|
04 July 2015, 15:30 | #9 |
Registered User
Join Date: Feb 2014
Location: Germany
Posts: 261
|
Thanks for the flowers
Now after we did what the author said without asking why, it's time to have a look at syntax! Though DOS batchfiles and Rexx scripts look similar there are fundamental differences. Check out quotation marks. Where DOS expects double quotes ", Rexx uses single quotes '. THIS IS VERY IMPORTANT ! DOS will not deal with path names as strings if they contain white spaces, until wrapped in "". ARexx on the other hand uses ' single quotes. If you want to send a " from within a rexx script, wrap it in single quotes which is very hard to read and prone to errors. e.g. path0 = 'Ram Disk:' Address command 'cd 'path0 Though arexx correctly concatenates strings ('cd ' is first string and content of path0 is 2nd) it will throw an error. The path contains white space. The error code is sent by DOS command cd. Cd can't find "Ram". alter it to path0 = 'Ram Disk:' Address command 'cd "'path0'"' to make it work. The command string is now build from 'cd " ' + content of path0 + ' " '= cd " + Ram Disk: + " = cd "Ram Disk:" This is hell. The more you glue together, the more gibberish it looks like. from example above Code:
Address command 'version >' tempfile ' "'actdir||item'" file 'fullversion line in source that is causing trouble, but damn, which one of these flyspecks was set wrong ? Another big difference is the way we set comments. In DOS you just place a semicolon ; in front and everything behind is ignored till end of line like: version >NIL: icon.library version 46 ; check for peters lib In ARexx the semicolon is used to separate commands from each other in one line. f.e.: Address dopus.1;verify 'are you sure?';if rc=1 then exit; A comment in ARexx starts with this pair of characters /* and ends with its counterpart */ All text in between will be ignored, no matter how many lines of text are encapsuled. /* Our first script */ or for a single line Say Show('p',rexx) /* check for rexx port*/ Nested comments are allowed like /* outer /*inner comment*/ comment*/ Beware that this is difficult to read and if you forget to set one of these "slashterixes" ARexx may start to interpret the next word as a command. . Last edited by BigFan; 05 July 2015 at 01:07. |
04 July 2015, 15:34 | #10 | |
Registered User
Join Date: Feb 2008
Location: RNO
Posts: 1,010
|
Quote:
For example here you can find short but very cool AmigaDOS script to view version of the file in a standard system requester. It even compares if there is different version loaded in ram compared to file on the filesystem. |
|
04 July 2015, 15:45 | #11 |
Registered User
Join Date: Feb 2014
Location: Germany
Posts: 261
|
Right now you have learned a new ARexx command.
Address Adress is used to switch between hosts, to send our messages to a new recipient. The first port that is always open when running rexx code is "REXX" where Address Workbench /* now we talk to workbench, prior host (REXX)becomes old host */ Address rexx /* makes REXX to our new port to talk to, and Workbench will be old host */ Address dOpUs.1 /* ha, you don't have to type it upper case , ARexx will change everything to upper case by default */ Address command 'echo "Hello eablings."' /* We send our command to DOS, but we do not change the host.*//* DOPUS is now new host, REXX is again old host */ Address dopus.1 'verify welcome' /* Again we send a command (verify is a DOpus command) Address command can be replaced with Shell commandto a host (dopus here) without changing our current host.*/ Shell command 'cd ram:' is similar to Address command 'cd ram:' Address /* without a host name will switch between current and previous*/ How do i know who is the caller ? Address() this is no command , but builtin function. have a look at the scripts above on where and how it is used Last edited by BigFan; 06 July 2015 at 16:45. Reason: Shell command corrected |
04 July 2015, 15:48 | #12 | |
Registered User
Join Date: Feb 2014
Location: Germany
Posts: 261
|
Quote:
This is why i started this thread. But this is not a tutorial for DOS scripting Placing a button in DirOpus and run version from there is the most easy way i can think of. Enough for today. ... to be continued But you won't get able to compare folders this way and you can't process many files that easy Have a closer look at Severin's script. it has hardcoded path names and messages. Comparing file version in different locations always tell you about RAM: Last edited by BigFan; 04 July 2015 at 16:16. |
|
04 July 2015, 16:10 | #13 |
Zone Friend
Join Date: Sep 2001
Location: Germany
Posts: 814
|
Here's my version of the "Version number in DOpus requester" script. It was published as part of the DOpus4Plus package, so it's a little more complex because like all scripts in the package it has German and English strings included.
I'm posting it, because it illustrates how to change button texts for the DOpus requester (it's changed to "Next" when viewing several files and changed back before the script exits). Code:
/* $VER: WinVersion.dopus 1.1 (18.03.01) C. Gutjahr */ busy on options results reqtext='' lf='0a'x status 26 oldokay=result status 3 actwin=result status 9 actwin files=result status 13 actwin source=result call language() status 26 set nexttxt do files getnextselected actwin filename=result 'selectfile "'filename'" 0 1' if right(filename,5)~=".info" then do fileinfo filename info=substr(result,length(filename)+1) if word(info,3)<0 then do address command 'version "'source||filename'" full file >t:version.tmp' open('file',"t:version.tmp") ver=readln('file') ver=ver||" "||readln('file') close('file') if left(ver,9)="Could not" then reqtext=lf || 'File "'filename'"' || nothave else reqtext=havetxt || '"' || filename || '":' || lf || lf || ver || lf || lf request reqtext if ~result then call abort end end end abort: status 26 set oldokay address command 'delete t:version.tmp' busy off exit language: if open(file,"ENV:Language","R") then do la=upper(readln(file)) call close(file) end else la="" select when la="DEUTSCH" then do nexttxt='_Nächstes' nothave=' Hat keinen Versionsstring!' || lf || lf havetxt=' Versions-Information für ' end otherwise do nexttxt='_Next' nothave=' does not have a version string!' || lf || lf havetxt=' Version information for ' end end return Code:
/* generate a unique name ("req" + my process' id) */ cmdid='req'pragma('id') /* create a clipboard entry named "req<my_process_id>" containing the result of requestchoice (note the backticks around the requestchoice command) */ address command 'rxset' cmdid '`requestchoice "SOME_TITLE" "Are you sure?" "Yes|No"`' /* get the clipboard entry and delete it */ from_shell=getclip(cmdid) call setclip(cmdid,'') Last edited by Korodny; 05 July 2015 at 02:34. |
04 July 2015, 16:29 | #14 |
Registered User
Join Date: Feb 2014
Location: Germany
Posts: 261
|
Very nice. Always good to have additional examples. You excluded *.info files.
In WB3.1 and DOpus4.16 the left requester button has only one char (N underlined), but no text, probably the Ümläüts. |
04 July 2015, 16:36 | #15 |
Registered User
Join Date: May 2011
Location: Poland
Posts: 37
|
Hi,
Nice tutorial. In my opinion the debugging is an issue while scripting with ARexx. Perhaps some more integrated environment needed there. Using PIPE: device is awesome! Both ARexx and PIPE: as well are perfect examples of Amiga "soul" there IMHO... Thanks, |
04 July 2015, 17:16 | #16 |
Registered User
Join Date: Aug 2004
Location: www.amigakit.com
Posts: 2,083
|
Would you be able to compile you tutorials into a PDF or AmigaGuide document and upload to Aminet ?
|
05 July 2015, 00:25 | #17 | |
Registered User
Join Date: Feb 2014
Location: Germany
Posts: 261
|
Quote:
I consider authoring pdf/aguide when this proved to be usefull. Help is appreciated for bug hunting/fixing, spell checking, style etc. Problem is, my explantions sound simple to me, but any other reader might just go |
|
05 July 2015, 01:14 | #18 |
Registered User
Join Date: Feb 2014
Location: Germany
Posts: 261
|
|
05 July 2015, 01:41 | #19 | |
Registered User
Join Date: Feb 2014
Location: Germany
Posts: 261
|
Quote:
Rewriting the code block again and again with lots of - if - then - select -when - constructs isn't fun at all. Code:
pseudo code: lang = 50 select when francais then lang=lang when espanol then lang=lang+25 when italiano then lang=lang+50 when deutsch then lang=lang+75 otherwise lang=lang+100 welcome.msg = sourceline(lang) abort.msg = sourceline(lang + 1) check.msg = sourceline(lang + 2) .... your code runs here ... Exit /* and exits before language section*/ /* line 50 is next line*/ bienvenue oui non /*75 is next*/ bienvenida si no /* 100 is next*/ accoglienza --- --- /* 125*/ willkommen ... ... ... /* 150*/ welcome ... ... |
|
05 July 2015, 02:34 | #20 | ||
Zone Friend
Join Date: Sep 2001
Location: Germany
Posts: 814
|
Quote:
Code:
when la="<language>" then do <list_of_string_definitions> end Quote:
Code:
pseudo code: lang = 50 select when francais then lang=lang when espanol then lang=lang+25 when italiano then lang=lang+50 when deutsch then lang=lang+75 otherwise lang=lang+100 welcome.msg = sourceline(lang) abort.msg = sourceline(lang + 1) check.msg = sourceline(lang + 2) Ouch, sorry - seems I posted a test version of the script, it enforces German strings ('la="DEUTSCH"). I'll edit my first posting. |
||
Currently Active Users Viewing This Thread: 1 (0 members and 1 guests) | |
Thread Tools | |
Similar Threads | ||||
Thread | Thread Starter | Forum | Replies | Last Post |
AmigaDOS scripting resources | Photon | Coders. System | 26 | 19 March 2018 14:51 |
Very Basic Scripting. Confused. | marduk_kurios | Coders. System | 5 | 06 February 2014 11:13 |
UAE Scripting Layer | FrodeSolheim | support.FS-UAE | 15 | 26 January 2014 15:56 |
C= 64 BASIC as a Scripting Language | Charlie | Retrogaming General Discussion | 2 | 17 November 2008 14:23 |
|
|