"Experts arc not bora. They are hewn from the bedrock of endeavour, and the granite of experience."
The Author
MARK SMIDDY is a founding Consulant Editor on Future Publishing’s acclaimed Amiga Shopper and the world’s best known AmigaDOS author. He has worked with and written about, a variety of computers over the last 12 years and thinks intellectual bores should be lined up against a wall and shot: not necessarily in that order. Mark currently lives in a sleepy Cleveland backwater and remains convinced that life is a fatal disease.
Introduction
“Alas poor Yorick, I knew him…”
Poor Shakespeare would turn in his grave if he knew how many times people have misquoted that famous line from Hamlet. The problem is our brains hear the first part of the sentence and fill in the rest - in modern English we wouldn’t think of ending such a sentence with “Horatio” (the listener). It just doesn’t sound right. That’s the thing with scripts: they’re a sequence of instructions written down for actors to follow, and a director to alter at will.
Amiga scripts are like that. No more than a sequence of AmigaDOS commands which define some action or actions. When starting out with this fascinating area, you are the copyist entering the script for the actors to follow. Later, you will direct the course of events by changing the scripts to suit your needs. Eventually you will be the scriptwriter creating scripts to solve your own, distinct problems.
Looked at from another angle, scripts are AmigaDOS programs. No different from the programs that Charles Babbage first conceived of when he first thought up the idea of his Analytical Engine. Babbage’s idea was to create a machine that could follow a set of pre-written instructions that could be changed to fit the job at hand. Of course, in those days, programs only solved mathematical problems. Even Babbage, were he alive today, might find it difficult to conceive of how much effect his idea could have had on our everyday lives.
All this talk of computers and computer programs may have you thinking, "Is all this for me?”
Programming is an art and a science all rolled into one. The scientific part is being able to think of a problem logically and break it into easily achievable steps (commands, if you like) whereas the artistic part is the ability to add flare and polish to a program.
Nothing in this book is beyond you. Even the fact you have managed to read the words here proves you are intelligent enough to learn a language: one of the most complex in the world come to that. To get the most from this book, you should have a basic understanding of the workings of AmigaDOS, perhaps by following on from Mastering AmigaDOS Tutorial: or The AmigaDOS insider Guide. In any case a copy of Mastering AmigaDOS 3 Reference would be very helpful.
Just remember as I have said many times, "Experts are not born. They are hewn from the bedrock of endeavour and the granite of experience”. Here then are the three stages to becoming an expert.
-
Your can enter the programs as they appear at first and use them as described in the text. Copying is the first stage of learning: very few things we do are instinctive: almost everything is learnt by copying other people.
-
You follow the flow of the program as described in the detailed text and modify it to see what happens. Later on you will be able to predict what effect your actions have.
-
Having had some experience with the first two stages, you will begin to see places in your everyday use of the Amiga where a script can streamline work: and where nothing supplied here can fit the bill. You will become the scriptwriter.
AlarmClock: AlarmSet
Synopsis: [EXECUTE] <[Time=]time> [[Message=]"Text"]
Template: Time/a,message/f
Path: S:
Requires: V3+
See also: AlarmClock, AlarmSnooze
Type: Script
Brief: Alarm setting module for the Alarmclock
Description
This module is used to set the AlarmClock’s Alarm from AmigaDOS. The script has a built-in message (that you can change) but it is more normal to supply one. The choice is yours. Typically, you’ll use AlarmSet like this.
1>ALARMSET 15:00
1>ALARMSET 13:22 It’s time for a cuppa!
Line-By-Line
1-3. Defines a simple header. Note the use of a “final” argument to ensure the whole message is collected by the command line.
-
Sets the default message. You can change this to suit yourself.
-
Creates the message variable. Multiple calls to this command will change the message: be wary of this.
-
Clears the Alarm variable.
-
Starts a new resident process which will continue until the required time is reached. Note input and output re-direction to NIL:. This ensures you can close the Shell that called AlarmSet.
-
When the time is reached and the WAIT times-out, this activates the alarm. ■
-
.key time/a,message/f
-
.bra {
-
.ket }
-
.def message You rang, sir?
-
setenv AlarmMsg "{message}"
-
setenv AlarmOn "ON"
-
setenv Alarm ""
-
run <NIL: >NIL: wait until {time}
-
setenv Alarm "NOW"
AlarmSnooze
Synopsis: Internal to AlarmClock
Template:
Path: S:
Requires: V3+
See also: AlarmClock, AlarmSet
Type: Script
Brief: Snooze timer module for the alarm clock
Description
This module uses some clever features of AmigaDOS 2 and 3 to present a requester and calculate a snooze period for the AlarmClock. It is not normally executed on its own.
Line-By-Line
1-3: Construct a standard header.
4. Clears the global alarm variable.
5. Sets the Alarm message variable to itself plus "Snooze”. This is to indicate that the clock has already been snoozed from its original time. On successive runs, the word Snooze is added again and again, so you can see how many times you have snoozed!
6. Displays the requester inquiring how long the user wants to snooze for. Four returns are possible here:
1: 5 minutes.
2: 10 minutes.
3: 15 minutes.
4. Cancel
7. This line does several things in one go.
• Calculates the actual delay time required by multiplying the return from Step 6 by 3. This is done by expanding the return variable inside an inserted EVAL command. The result from:
eval $ret{$ $} *5' is inserted at that point. For a return of 3, AmigaDOS sees the line as:
run >nil: Wait 15 mins
• Starts a new process that will wait for the specified time.
• Does nothing until the next line has been added to the process.
8. Is part of the process started by Step 7. When the WAIT times out, the Alarm variable is set to NOW and the Alarmclock triggers.
Listing
-
.key dummy
-
.bra {
-
.ket }
-
setenv Alarm ""
-
setenv AlarmMsg "Snooze: SAlarmMsg"
-
requestchoice >env:ret{SS} "Alarmclock" "Snooze for how long?" "5 mins" "10 mins" "15 mins" "Cancel"
-
run >nil: Wait eval SretfSS} *5' mins
-
setenv Alarm "NOW"
AlarmClock: WBAIarmSet
Synopsis: Only run from Workbench
Template: Time,message/f
Path: na
Requires: V3+
See also: AlarmClock, AlarmSnooze
Type: Script
Brief: Alarm setting module for the Alarmclock
Description
This module is used to set the AlarmClock’s Alarm from Workbench - it’s really for weanies who can’t be bothered using the AmigaDOS one which is a lot faster in the long run…
Line-By-Line
1-3. Defines a simple header. Note the use of a “final” argument to ensure the whole message is collected by the command line. Also, note that the "time” argument is not required by the Workbench version of this script. 4. Sets the default message. You can change this to suit yourself. 5. Checks if a time has been entered. If not, control continues at Step 6; otherwise it jumps to Step 9. 6. Displays the prompt if a time has not being supplied- as will usually be the case from Workbench. 7. The script now calls itself recursively with interactive mode triggered. Note that the output is re-directed to NIL: to prevent the command line options from being shown. If you change the name of the script you must also change its name here too. 8. When the script unwinds its recursion this jumps to the bailout for speed. 9. Terminates the IF…ENDIF construct from Step 5. 10. Creates the message variable. Multiple calls to this command will change the message- be wary of this. 11. Sets a variable to indicate the alarm is active. 12. Clears the Alarm variable. 13. Starts a new resident process which will continue until the required time is reached. Note input and output re-direction to NIL:. This ensures you can close the Shell that called AlarmSet. 14. When the time is reached and the WAIT times-out, this activates the alarm. 15. Is the bail-out point for the recursive calls. 16. Is some information for WX to use if the script is called from AmigaDOS.
Listing
-
.key time,message/f
-
.bra {
-
.ket }
-
.def message "You rang, sir?"
-
if "{time}" EQ ""
-
echo "Enter a time (and optional message)"
-
execute >NIL: WBAlarmSet ?
-
skip out
-
endif
-
setenv AlarmMsg "{message}"
-
setenv AlarmOn "ON"
-
setenv Alarm ""
-
run <NIL: >NIL: wait until {time}
-
setenv alarm "NOW"
-
lab out
-
;WX:WINDOW=WINDOW=con:0/0/190/60/Memory Gauge/SMART/NOSIZE
AlarmClock
Synopsis: Run from Workbench
Template:
Path: na
Requires: V3+
See also: Snooze, AlarmSet, Clock 2, Clock 3
Type: Script
Brief: Digital alarm clock with snooze facility!
Description
This is a script to amaze your friends with. In the current incarnation it requires Workbench 3 (an A1200, for instance) because it makes use of requesters to provide instant responses. It’s a bit like Pest3 in some respects, but unlike Pest it’s a lot simpler and only supports a single Alarm time. A snooze mode is provided with a programmable Snooze period from 5…15 minutes. A status indicator shows if the Alarm is clear, set or asleep.
Line-By-Line
1-2. Makes some essential commands resident.
3-5. Checks for the global variable “AlarmON”. AlarmClock needs this (even if it contains nothing) to work. If the variable does not exist it is created.
-
Switches the cursor off and positions the print position at the top, left hand corner of the window.
-
Marks the start of a loop.
-
Displays the day, date and time.
-
Checks if AlarmON contains anything. If it does, control continues at Step 10; otherwise it jumps to Step 23.
-
Tests if ClockRtn (set later in the script) was 1: Snooze mode. If it was, control continues at Step 11; otherwise it jumps to Step 12.
-
Displays the snooze “Zzz” message in a highlight colour and re positions the cursor.
-
If control gets here from Step 11, it jumps to Step 14; otherwise it continues…
-
…here where it displays the alarm Set message in a highlight colour.
-
Terminates the IF…ELSE…ENDIF construct opened at Step 10.
-
Tests if the alarm has “timed out” indicated by the variable. Alarm. If Alarm is set to trigger, control continues at Step 15; otherwise it jumps to Step 22.
-
Displays a requester with the alarm message (determined by "AlarmSet”). Two possible returns are possible:
0: OK. Cancel the alarm
s 1: Snooze. Trigger snooze mode.
-
If the return from the alarm request was “1", control continues at Step 18; otherwise it branches to Step 19.
-
Silently creates a new process running the Snooze timer. Actually, AlarmSnooze will create its own sub-process, as you’ll see described there.
-
If control reaches here from Step 18, it jumps to Step 21; otherwise it continues…
-
…here and re sets the alarm variable.
-
Closes the IF…ELSE…ENDIF construct opened at Step 17.
-
Closes the IF…ELSE…ENDIF construct opened at Step 15.
-
If control reaches here from Step 22, it continues at Step 25, otherwise it carries on…
-
…here, and prints the "Alarm off" message before repositioning the cursor ready to display the time.
-
Closes the IF ELSE…ENDIF construct opened at Step 9.
-
Halts the script for about a second. You can increase this period if you want to give more time over to other processes.
-
Re-starts the loop and keeps the clock ticking.
-
Is some information for the WX script.
-
resident c:wait
-
resident c:date
-
if not exists ENV:AlarmON
-
setenv AlarmON ""
-
endif
-
echo "*e[0 p*e[0;0H" noline
-
lab start
-
date
-
if "SAlarmOn" NOT EQ ""
-
if SClockRtn EQ "1"
-
echo "Alarm *e[32mzzz*e[31m*e[0;0H" noline
-
else
-
echo "Alarm *e[32mset*e[31m*e[0;0H" noline
-
endif
-
if "Salarm" EQ "NOW"
-
Requestchoice >env:ClockRtn “Clock" "SAlarmMsg'1 "Snooze" "OK"
-
if SClockRtn EQ "1"
-
run >NIL: execute s:AlarmSnooze
-
else
-
setenv alarmON ""
-
endif
-
endif
-
else
-
echo "Alarm off*e[0;0H" noline
-
endif
-
wait 1 secs
-
skip start back
-
;WX:WINDOW=WINDOW=con:0/0/240/30/CLI_Clock
AskEm
Synopsis: EXECUTE >NIL: Askem <[file=]Answerfile> ? Template: file/a, a,b,c, d.e. f ,g, h, i,j Path: S: Requires: VI.24- See also: Pest 3: GetArg Type: Script Brief: To interactively read input from the user in a script
Description
A programmer once commented: "ASK is fine for simple questions but what happens if 1 need to get a text string inside a program? There’s no way to interactively ask the user a question like 'what’s your name? once the script has started. From the outset this looks a deceptively simple command. Plowever, this is not a stand alone program - it’s a script designed to be executed from another script - possibly called by ICONX.
Line by line
-
This line is the command’s key - which gives some hint as to how this script works. Apart from the file argument the rest of the options look meaningless. In fact, the less meaningful these are, the better! They never actually appear on screen and serve to pick up the user’s input. I’ve provided 10 here which should suffice for questions such as "What is your name" and so on.
2-3. Set bra and ket to { and }.
-
This merely echoes the user’s input back to the file defined in line 1.
This script is not obvious until you see it in use. So here’s a very simple script to show how it works:
1 .key dummy .bra {
2 echo "What is your name?" noline execute >nil: AskEm ram:AnswerfSS} ? echo "Nice to eat you1 " noline
type ram:Answer{SS}
Line by line
-
This is a dummy parameter.
-
ECHO is being used here to ask the question. This has to be done here for reasons which will become clear below.
-
This is the crucial part. AskEm is executed with output redirection sunk to NIL:. This makes sure that it can’t generate any output of its own to the screen. The ? at the end puts the script’s key into interactive mode so it is ready to accept some input. Remember, nothing actually appears because the output is going to NIL: although, what you type belongs to the current console so it does appear.
More important, AskEm has one required argument - the file it will send its output to. A little known feature of interactive mode is that you can send partial command lines - in this case the filename - before the interactive mode starts. When it does, the user’s input is passed to each argument letter in turn, thus allowing about ten words for this example.
Listing
-
. key file / a,a,b,c,d,e,f,g,h,i,j
-
. bra
-
.ket
-
echo >{file} "{a} {b} {c} {d} {e} {f} {g} {h} {i} {j}
AutoHelp
Synopsis: [EXECUTE] AutoHelp
Template: none
Path: S:
Requires: VI.3+
See also:
Type: Script
Brief: Make all disk-loaded commands produce help templates
Description
Here’s a little script for beginners and experts alike who cannot remember how each command behaves. It uses LIST to create a special alias for all commands so they always present their command line templates:
The first line creates a script file in RAM: called “helpme” formatted like this for every file in the C: assignment: ; <Path> ALIAS <command> <path and command> ? i
For instance if C: contained just CD and DIR, "helpme" would look : like this:
;Workbench1.3:C ALIAS DIR Workbenchl.3:C/DIR ? ;Workbench1.3:C ALIAS CD Workbenchl.3:C/CD ?
although, in real terms, the list will be much longer - two lines for every command in C:! When this file is executed, it is no longer necessary (or possible) to supply an argument to each command. Instead you just give the command without parameters and it presents the list of parameters it requires:
1 >DIR NAME.OPT/K,ALL/S,DIRS/S,INTER/S,FILES/S:
All you have to do is enter the parameters as usual and press the <Return> key to activate the command. This is useful if you only have a single disk drive because transient commands are pre loaded so you can swap disks without having the hassle of getting the wrong directory etc.
Listing
-
LIST >RAM:HELPME C :#? LFORMAT " ; °oS* nAL I AS ^S °oS°oS ?"
-
EXECUTE RAM:HELPME
AutoStart 1.3
Synopsis: none
Template: none
Path: S:
Requires: VI.3 - 1.3.3
See also: AutoStart 2
Type: Script
Brief: Auto start multiple application (like WB20+'s WBStartup)
Description
The most logical way to create a boot disk is to custom build a disk that will automatically start applications from a special Workbench drawer. This example applies to all releases of Workbench from 1.3 to 1.3.3 and provides functionality similar to that in Workbench 2.
Installing AutoStart
-
Boot your Workbench disk, make a copy of the Empty drawer and rename it Auto.
-
Open a Shell and enter:
1>ED S:Startup-sequence
-
Move the cursor to the line where EndCLI >NIL: appears, press <Return> to open a blank line and move the cursor into it. Now enter the lines shown in the listing below.
-
Now drag one or more applications (tools) to the Auto drawer and reboot the machine. Typical examples are Clock and NotePad (on 1.3). This patch only works on "tools". If you are unsure what an icon is, select it and choose Info from the menu. The icon’s type must be a tool otherwise it will not work. (The Shell’s icon for instance is a Project.)
Line-By-Line
-
The first line checks for the Auto drawer required by the patch. If the drawer is missing execution passes to step 6 and allows the startup to continue as normal. This allows you to modify one Startup-sequence and copy it to lots of different disks without having to create an Auto drawer on every one.
-
This creates a script (T:AutoTemp) using LIST’S LEORMAT argument. Typically it will look something like this if the Clock and NotePad tools were placed in the Auto drawer:
RUN <NIL: >NIL: .info RUN <NIL: >NIL: Clock.info RUN <NIL: >NIL: NotePad.info RUN <NIL: >NIL: Clock RUN <NIL: >NIL: NotePad
-
Creates a macro (T:Strip) for the EDIT command. There isn’t room here to describe EDIT in detail, but this macro will force EDIT to search for and delete, any lines containing the string “.info". (See Mastering AmigaDos 3 Reference).
-
Creates the final script (T:RunIt) by removing any lines containing the substring “.info”. The new script will typically look something like this:
RUN <NIL: >NIL: Clock RUN <NIL: >NIL: NotePad
As you can see, this script only attempts to RUN tools. The original MakeAuto program tried to run everything, icons and all and this slowed things down. Redirection to and from NIL: (<NIL: >NIL:) is used to stop any tools getting a "lock" on the CL1 window, thus allowing it to close.
-
Executes the script. The reason for using RUN might not be clear, but in some releases EXECUTE complains about the lack of a .KEY statement. This fixes that problem at the expense of an extra [CLI 2] message during startup.
-
This is just the tag for the IF statement at step.
Listing
-
IF exists SYS:Auto
-
LIST >T:AutoTemp SYS:Auto LFORMAT "RUN <NIL: >NIL: %S%S"
-
ECHO >T : Strip " 0 ( F / . inf o /; d ; ) 11
-
EDIT T:AutoTemp TO T:RunIt WITH T:Strip
-
RUN EXECUTE T:RunIt
-
EndlF
AutoStart 2
Synopsis: none
Template: none
Path: S:
Requires: V2+
See also: AutoStart 1.3
Type: Script
Brief: Auto start multiple application (like WBStartup)
Description
The most logical way to create a boot disk is to custom build a disk that will automatically start applications from a special Workbench drawer. This example applies to all releases of Workbench from 1.3 onwards. Although the Workbench already has an auto start drawer (WBStartup) it does not work correctly for applications that don’t exit quickly. This makes it unsuitable for things like EMacs and IconEd for instance.
-
It’s worth noting, that Workbench does have a ToolType: DONOTWAIT that accomplishes this task instantly, but you have to add this manually using Icons…Information.
-
Boot your Workbench disk, and use the New Drawer function to create a drawer called Auto.
-
Append the listing to the end of User-Startup.
-
Now drag one or more applications (tools) to the Auto drawer and reboot the machine. Typical examples are Clock and NotePad (on 1.3). This patch only works on “tools". If you are unsure what an icon is, select it and choose Info from the menu. The icon’s type must be a tool otherwise it will not work. (The Shell’s icon for instance is a Project.)
-
Line-By-Line
It works just like AutoStart 1.3, with the exception that the script is created without the .info files. This is afforded by the new (NOT) wildcard modifier which stops the dot-info files being included here.
Listing
-
IF exists SYS:Auto
-
LIST >T:AutoTemp SYS:Auto/-(#?.info) LFORMAT “RUN <NIL: >NIL: %S°oS"
-
RUN EXECUTE T:AutoTemp
-
EndlF
BACK
Synopsis: BACK <command>
Requires: VI.3+
See also:
Type: Alias
Brief: Run a command in the background
Definition: ALIAS BACK RUN <NIL: >NIL:
Description:
This little tip is for AmigaDOS/ARP versions 1.3 and above. Partly because it uses ALIAS and partly because the NIL: device did not work correctly in earlier versions. The idea for this one came from Charlie (ARP) Heath’s PD utility, RUNBACK; a patch that allows processes to run completely in the background. RUNBACK is not required for AmigaDOS 1.3 and above because the facility is already there. You use BACK like this:
BACK [command] [options]
For instance, to start the PD file viewer ZAP:
1>BACK ZAP
Beginners are probably wondering what all the fuss is about. Indeed, if you try to BACK DIR or something similar, nothing seems to happen. BACK was devised so you can start programs from the Shell then close it down. If you try this, many programs will prevent the Shell window from closing until they exit. Of course, BACK is useless for most AmigaDOS commands, it is only intended for Intuition based applications.
BarClock
Synopsis: Run from Workbench Template: none Path: Requires: V2.0+ See also: Type: Scnpt Brief: Analogue AmigaDOS clock using bar graphics Description This script is a demonstration of "bar" graphics - as a real-time clock. Three bars are used to represent hours, minutes and seconds elapsed. It isn’t really practical to add an alarm to this script: but it is possible. Special EDIT macros are used to separate the hours, minutes and seconds from the date string: and the numbers produced determine the length of the bars.
Line-By-Line
1-3. Form a standard header.
4-8. Make some essential commands resident for speed.
9-11. Create the EDIT macros used to extract the time components. Note these macros are not the most obvious solution to the problem, but were written this way to avoid a bug in EDIT’s DTA command.
-
Creates the “bar" using spaces.
-
Marks the start of the clock loop.
-
Positions the cursor and switches the cursor off.
1 5. Sends the date to a file.
16 18.Extract the hours, minutes and seconds to three global variables.
-
Tests if the current hours variable has increased since the last loop. If not, control passes to Step 23. This block stops the bar "flashing" every loop.
-
Positions the cursor at the start of the HOURS line, clears the
entire line and sets the new background colour.
-
Displays the bar in the current background colour and appends the number of hours at the end for clarity. Note that the LENgth of the second string “$hrs" is always displayed correctly, even though its length is trimmed by LEN.
-
Sets the local HRS to the current value of the global hours#.
-
Terminates the IF…ENDIF construct opened at Step 19.
-
Tests if the current minutes variable has increased since the last loop. If not, control passes to Step 28. This block stops the bar "flashing” every loop.
-
Positions the cursor at the start of the MINS line, clears the entire line and sets the new background colour.
-
Displays the bar in the current background colour and appends the number of minutes at the end for clarity. Note that the length of the second string “Smins" is always displayed correctly, even though its length is trimmed by LEN.
-
Sets the local “mins” to the current value of the global "mins#”.
-
Terminates the 1F…ENDIF construct opened at Step 24.
-
Tests if the current seconds variable has increased since the last loop. If not, control passes to Step 32. This block stops the bar "flashing" every loop.
-
Positions the cursor at the start of the SECS line, clears the entire line and sets the new background colour.
-
Displays the bar in the current background colour. Seconds are not displayed.
-
If control reaches here from Step 31, it is transferred to Step 35; otherwise it continues…
-
…here. Positions the cursor at the start of the SECS line, does not clear the line and sets the new background colour.
-
Displays the bar in the current background colour. Seconds are not displayed.
-
Terminates the IF…ELSE…ENDIF construct opened at Step 29.
-
Updates the local secs counter.
-
Waits a second. This probably isn’t necessary given the average speed of the DOS language, but it gives other processes a look in!
-
Re-starts the loop.
-
Is some information for the WX script.
1 . .key dummy
-
.bra {
3.
-
resident c:avail
-
resident c:wait
-
resident c:eval
-
resident c:date
-
resident c:edit
-
echo >t:ed1{$$} "2pa/:/;sb//*np;d" ; extract SECS
-
echo >t:ed2{$$} "sa/:/*np;d;2>;3#" ; extract MINS
11 . echo >t:ed3{$$} 2dta/ /*n2>;6#" ; extract HRS
-
echo >env:bar{$S} "
-
lab start
-
echo "*e[2;0H*e[0 p" noline
-
date >t:t{SS}
-
edit t:t{SS} to ENV:secs{SS} with t :ed1{$$}
-
edit t:t{SS} to ENV:mins{SS} with t :ed2{$$}
-
edit t:t{SS} to ENV:hrs{SS} with t: ed3{$$}
-
if Shrs{SS} NOT EQ Shrs
-
echo "*e[40m*e[2;7H*e[K*e[2;OH HRS :*e[42m" noline
21 . echo "$bar{S$}" "Shrs{$S}" len=$hrs{$$}
-
set hrs $hrs{$$}
-
endif
-
if $mins{SS} NOT EQ Smins
-
echo "*e[40m*e[3;7H*e[K*e[3;0HMINS :*e[43m" noline
-
echo "$bar{S$}" "$mins{}" len=Smins{}
-
set mins Smins{SS}
-
endif
-
if Ssecs{SS} NOT GT Ssecs
-
echo "*e[40m*e[4;7H*e[K*e[4;0HSECS : * e [ 41 m" noline
31 . echo "$bar{$$}" len=$secs{SS}
-
else
-
echo "*e[40m*e[4;7H*e[4;0HSECS:*e[41m“ noline
-
echo "$bar{$$}" len=$secs{SS}
-
endif
-
set secs $secs{$$}
-
wait 1 secs
-
skip start back
-
;WX:WINDOW=con:0/0/550/60/Bar Clock/SMART/NOSIZE
BarGraph
Synopsis: [EXECUTE] Bargraph <[Data]> [Min=<min>] [Max=<max>] [Xaxis=<xais>] [Header=<text>] [Sub=<text>]
Template: data/a,min/k,max/k,xaxis/k,header/k,sub/k
Path: S:
Requires: V2+
See also:
Type: Script
Brief: Use AmigaDOS to create a bar graph
Description
Why would anyone in their right mind want to use a DOS command language to produce graphics? Back in the days when computers didn’t have proper graphics, everything was done with fixed width characters. As a respectful salute to those pioneering machines 1 present an AmigaDOS screen-based charting program. The example given here produces automatically or manually scaled, horizontally aligned, bar charts for integer data values between -10000 and +10000.
This might appear something of an esoteric problem, but the solution addresses some interesting areas: not least how to handle fixed point arithmetic. FORTH programmers have being doing such things for years, but most of us take floating point for granted in other high-level languages such as BASIC. The theory behind such things is quite involved so the discussion is featured elsewhere in these pages. The program requires AmigaDOS 2 (sorry about that) since it makes extensive use of the new environment handler.
The BarGraph script reads data (and labels) from a text fiie. Typically a data file will look like this:
D1 200 L1 Jan D2 325 L2 Feb
and so on. Data Values are prefixed by Dn and labels by Ln where “n" is the number of the data item or label (1-11 characters) attached to it. Every data item must have a label and all the items must be separated by two spaces. This is slightly more complex than spreadsheet-based graphics, but is necessary for speed; and AmigaDOS isn’t exactly fast at the best of times. Once the data file has been created, the script is called like this:
1>Bargraph T:Data
Using the default settings like this, the script determines the maximum and minimum values for the X axis by taking the highest and lowest values from the data set. This may produce unwanted results, so either or both of these can be set at run-time, for example:
1>Bargraph T:Data Min=50 1>Bargraph T:Data Max=3000 1>Bargraph T:Data Min=-200 Max=200
The number of data items that can be plotted depends on the height of the current CLI window - although the width of the plot assumes a full-width, hi res screen. Using an interlaced screen with a larger window will afford better results.
Finally, a simple XAxis label, header and sub-header can be defined like this:
1>Bargraph T:Data Min=0 Max=5000 Header="Accounts" sub="3/1/93" XAxis="Pounds"
Fixed Point Theory
It is well known that AmigaDOS does not handle numeric data particularly well. Specifically, even the simplest calculation must be carried out by a special command: EVAL. But EVAL is only capable of very simple arithmetic and does not handle decimal fractions at all. For instance: 7/2 gives 3 remainder 1 and even this must be performed in two distinct steps.
(The details following apply to any language, not just AmigaDOS and can get machine code programmers out of some tenuous situations. Unless shown, most of the results are truncated integers as would be returned by EVAL. This should be considered when checking the arithmetic with a calculator.)
Consider the sum “7/2". tJsing traditional methods: “two goes into seven twice, with one left over (the remainder). Pop the remainder (1) over the divisor (2) and you are left with the vulgar fraction 1/2. This is fine for dividing up a cake, but not much use in computer maths. The decimal fraction version of this is:
Dividend 7 -------- = - = 3.50 Divisor 2
Of course, most of us can do that in our heads, but AmigaDOS cannot. Now suppose we change the scale of the figures somewhat by multiplying just the dividend by 10.
(Dividend*Scaler) (7*10) 70 Divisor 2 2
The result is to move the (purely imaginary) decimal point one place to the right. However we have also retained the fractional part - this is the essence of fixed point arithmetic. To show this in more detail let’s take a slightly more complex problem: 2/5 for instance. Using our integer AmigaDOS calculation, we get:
2
Even thought the answer is 0.4. Now use a constant (K) 1000 to get something more realistic:
(Dividend*K) _ (2*1000) _ 2000 _ ^ Divisor 5 5 Result 400 ------ = -------- = 0.4 K 1000
So far so good— but what is the point to all of this? Consider you have a set of values of between 0 and 1000 which must be scaled down to fit on an axis with 70 plottable points. The scaling factor can be calculated thus:
70 Factor = -------- = 0.07 1000
And any value can be plotted by multiplying it by the scaling factor 0.07. Take a value of 500 which is half-way up the scale: Data*Factor = 500*0.07 = 35 points
Since AmigaDOS would lose the fractional part in the original calculation - 0.07 becomes 0 - the scaled value would be useless. By using a constant of 5000, we can calculate the scaling factor thus: Max Data *K 1000*5000 Factor = ---------------- = ------------ = 71428 Plot Width 70
To arrive at a final result we must now divide the data by the scale factor and multiply the result by the constant: Data * K 500*5000 Points = -------- = --------- = 35 Factor 71428
Since no fractions are involved in this calculation, AmigaDOS can cope. Provided the scaler is large enough to cope, fairly complex arithmetic can be performed. In some cases, part of what would have been the decimal fraction is discarded - but this is common in all maths - so it is nothing to be concerned with. You can see this in action by dividing 7 by 6 - a calculation which always results in a recurring fraction 1.16666666. (or rounded up 1.167):
(7*10000) (70000) -------- = ---------- = 11666* 6 6
As a guideline, the size of the scaler determines the accuracy of the calculations: a scaler of magnitude 10000 sets an internal accuracy of one ten thousandth (the last digit is dropped due to rounding errors). Unfortunately, the size of the scaler is finite: an error will occur if, in any calculation, the scaler multiplied by the scaled data exceeds the operational limit of EVAL (in this case).
Line-By-Line
-
Defines the key as described above. Note the data file name is a required argument, all other parameters are keywords and must be supplied with the data.
2-4. Re define the “bra", “ket” and “dollar" characters.
-
Copies the data file to the T: assignment (in RAM) with the filename “DATA”.
6-8. Add EVAL, SEARCH and JOIN to the resident list. While you are entering the program the ADD switch should be omitted to save memory in case the script terminates abnormally.
-
Sets the constant “K” to 100000. This is the scaler described in the detailed description of fixed point arithmetic.
-
Sets “width” to 56 - the usable window width. This value is used to determine the scaling factor for the data. You may experiment with this value, but it must be an even number.
II. Sets scan ON. The chart is plotted in two phases, the first phase scans the data for the upper and lower boundaries - the second plots the chart.
12 14. These three local variables are set to the contents of the axis labels. If any are missing, a single space is set instead.
15 17. These set the default scan values for the lowest, middle and maximum points on the chart. Global variables are used because they can be written directly by AmigaDOS commands.
-
Directly writes the global variable “Middle" to half the value of Width.
-
As 1 9, but stores an adjusted value to centre the XAxis - held
in “MidRoint".
-
This line is not usually used, but is provided here as an
alternative graph style. Using the listing as shown, the bars will be black. By replacing line 21 with this one, the bars appear with a hatched pattern. You can use either of these or design your own - but do not use asterisks (*) or dollars ($) since these have a special meaning to AmigaDOS.
21-22. Set the bar and XAxis styles to spaces.
23-24. Concatenate the strings defined at 21 and 22 and stores them in global variables.
-
The script will have been running for a few seconds now, so this prints a progress message to indicate the start of the scan phase. During this time the script is looking for values outside those determined by Max and Min.
-
Marks the start of the main loop.
-
Sets the global variable ‘'Loop" to 0. This value is used as a data index as you will see later.
-
Marks the start of the scanning loop.
-
Increments “Loop” by 1.
-
Writes the global variable "Number" prefixing the value with “D" and suffixing it with a space. On the first loop therefore, Loop=Dl.
-
As 30 but storing Ln in “Labels”.
-
Searches the data file for the current data item (for the contents of “Number") and stores it in the global “data". The nonum switch suppresses SEARCH’S unwanted line numbering facility. Using the example data file supplied here, if “Number=Dl" then “Data" receives:
D1 2200
Note the entire line is read from the data file - this is corrected later on.
33-35. If the numbered data item could not be found this test forces the script to exit either the scan or display phases. Under normal circumstances this will only happen when all the data has been read and displayed.
-
String slices the numeric data from the string generated at Step 32 and stores the result in the global "NData". Using the FIRST keyword on its own forces ECHO to retrieve the whole string starting from position four and moving right. Using the current example:
D1 2200
-translates to- 2200
37-39. Tests if the current data value is greater than the current maximum chart value, and resets maximum to the data value if it is. Since this test is in a loop, all data points are tested (the scan phase) so the highest point on the X-Axis is always at least equal to the highest value in the data.
40-42. As above, but sets the lowest data point. The function “NOT GE" is IF’s version of “less than".
-
If “Scan" is ON (the scan phase) control skips to Step 56 otherwise it continues at Step 44.
-
Prints the right side of the string made up from a lot of space (SXAxis) plus the X-Axis ($Axis) label. This centres it roughly over the X-Axis.
-
Calculates the scaling value described in the detailed description of fixed point. Assuming Max is 500 and Min is -500, the calculation works like this:
-
Calculates the value of the mid-point and it stores in the global “Mid".
47-50. Print the X-Axis labels. This is completed in several stages.
-
Prints some blank space characters above where the labels will appear.
-
Prints the Minimum scale value followed by some padding space - the amount of which is determined by the value of Midpoint. This is crude, but it works.
-
Does the same as above with the Middle scale value.
-
…and this displays the highest scale value, “Max”.
51-53. Display the X-Axis graticule. Not posh, but functional.
-
Changes the “scan" variable to “SHOW" so the program will enter the display phase (at Step 57).
-
Alters the display colours to white text on a black background. The text foreground colour is changed primarily to show hatched bars (an option described above) but it also makes the labels etc stand out.
-
Closes the IF…ENDIF construct opened at Step 43.
-
Tests if the “scan" variable has been set to “SHOW" (the display phase). It is important to note, since this is an iterative script, this variable is not changed until the scanning phase is completed.
-
Extracts the current label (Fn) from the data file and stores it in the global “Lab".
-
Prints the current label with the correct amount of padding spaces to position the cursor ready to print the bar. The
length of the string printed is always 10 characters regardless of the length of any particular label - longer ones are truncated.
-
Calculates the number of characters required to print the bar. Let’s assume “Ndata" is 350 and 'Min" is -500, the calculation works like this
So the length of the bar representing 350 on a scale of 500 to -500 is 47 characters of 56 possible (as defined in “Width" -see above). Work that out on a calculator and you will find the result is out by a fraction - but such things are outside the limits of the display. Using a proportional font (such as Times) could improve the resolution ten-fold, but the console device cannot cope in Release 2.
-
Changes the background colour to default (slate-grey). This, like many of the other colour changes must be done separately otherwise the string slicing will get in the way with unpredictable consequences.
-
Prints the current label with one extra padding space.
-
Changes the background colour to black.
-
Prints a bar according to the magnitude of the current data. See Step 60 for details of how the variable “Dlen" is calculated.
-
Changes the background to slate-grey…
-
…and prints the actual value of the data displayed. This is an optional feature but has been included here to help overcome the deficiencies with the display resolution.
-
Terminates the 1F…ENDIF construct opened at Step 57.
-
Jumps back to Step 28 and does it all again for the next data point!
-
Marks the escape point. Control jumps here from Step 34 when the last data point has been read or charted and the data table is exhausted.
-
Checks if the scan phase is still active. If the scan has been completed control jumps to Step 74 and exits, otherwise…
-
…"scan" is set to OFF to mark the end of the scan phase and the start of the charting phase and…
-
Control jumps right back to the start and does the whole lot again, this time it’s for real.
-
Terminates the 1F…ENDIF block opened at Step 71.
74-76. Puts everything back to normal, prints the header and sub-headers…
77-80. …and finally removes SEARCH, JOIN and EVAL from the resident list.
Listing
-
.key data/a,min/k,max/k,xaxis/k,header/k,sub/k
-
.bra {
-
.ket }
-
.dollar |
-
copy {data} T:data
-
resident c:eval add
-
resident cisearch add
-
resident c:join add
-
set K 100000
-
set Width 56
-
set scan ON
-
set Header {header|“ "}
-
set Subhead {sub|" "}
-
set Axis {Xaxis|" "}
-
setenv max {max|1}
-
setenv min {min|0}
-
setenv mid 1
-
eval $Width/2 to env:Middle
-
eval SMiddle -2 to env:MidPoint
-
;echo >T:G “/\/\/\/\/\/\/\" noline
-
echo >T:G " “ noline
-
echo >T:H " " noline
-
join T:G T:G T:G T:G T:G T:G T:G T:G T:G as ENV:Bar
-
join T:H T:H T:H T:H T:H T:H T:H T:H T:H as ENV:XAxis
-
echo "Scanning…"
-
lab again
-
setenv loop 0
-
lab Readloop
-
eval Sloop + 1 to env:loop
-
eval Sloop to env:number lformat "D?sn "
-
eval Sloop to env:labels lformat "L?sn "
-
search >env:data Trdata Snumber nonum
-
if warn
-
skip done
-
endif
-
echo "Sdata" first=4 to env:ndata
-
if val Sndata GT SMax
-
setenv Max Sndata
-
endif
-
if val Sndata NOT GE SMin
-
setenv Min Sndata
-
endif
-
if Sscan EQ OFF
-
echo "SXAxis SXAxis SAxis" len=SMiddle
-
eval ((SMax-SMin) * SK)/$Width to env:Scale
-
eval ((SMax-SMin)/2)+SMin to env:Mid
-
echo " " noline
-
echo "SMinSXAxis" first=1 len=SMidPoint noline
-
echo "SMidSXAxis" first=1 len=$MidPoint noline
-
echo "Smax"
-
echo "……….!" noline
-
echo "……………………..!" noline
53 . echo "…………………………! "
-
set scan SHOW
-
echo ”*e[41m*e[32m" noline
-
endif
-
if Sscan EQ SHOW
-
search >env:lab T:data "Slabels" nonum
-
echo >env:labels "SlabSXAxis" first=4 len=10 noline
-
eval ( (Sndata - $min) * SK ) /SScale to env:dlen
61 . echo "*e[44m" noline
-
echo "Slabels " len=10 noline
-
echo " * e [ 41 m" noline
-
echo "SBar" first=1 len=Sdlen noline
-
echo "*e[44m" noline
-
echo "Sndata" len=12
-
endif
-
skip I Readloop back
-
lab done
-
if Sscan EQ ON
-
set scan OFF
-
skip again back
-
endif
-
echo "*e[44m*e[31m"
-
echo "*e[I$Header"
-
echo "*e[I$Subhead"
-
resident c:eval add
-
resident c:search add
-
resident c:join add
Listing
Sample data file (only five items shown). D1 2200 D2 2300 D3 2400 D4 2100 D5 2700 L1 January L2 February L3 March L4 April L5 May
Booty
Synopsis: [EXECUTE] Booty <Drive> Name
Template: DRIVE/A.NAME
Path: S:
Requires: VI.34-
See also:
Type: Script
Brief: Make a boot disk
Description
This is a simple script to interactively create boot disks. It is designed primarily for AmigaDOS 1.3 and 2. Calling the script is a simple matter of supplying the drive number and (optionally) a name for the new disk. The disk is formatted, initialised and all major directories are created and “stuffed". Booty works best with at least two drives or a hard disk. Single drive users especially will benefit by using In tel 1 i Res (detailed later) on this script.
This script is an example only - you should examine it and modify it to suit your own wants and requirements. For instance, Workbench 3 users could use REQUESTCHOICE in place of the ASK commands. Experienced users can modify the script to select the correct handlers, devices and so on. It’s up to you.
Example
1>Booty 0 Works
Line-by-Line
-
Defines the key as having one required argument for the drive number and one optional argument for the volume name.
-
If a volume name is not supplied, this sets the default one.
-
Prints a totally pointless progress message. It’s there because it gives people a sense of achievement.
-
Formats the disk using the normal system command. This line assumes you have the System directory defined in your path setting and will produce some interactive output. It is possible to start the format straight away but that was considered much too volatile a technique to use. You may wish to add DCFS FFS or 1NTL switches here. See MAD 3 Reference
-
Installs (adds boot code) to the disk thus making it a blank
boot disk. You could end the script at this point and copy your own information to it if you prefer.
-
Checks to see if you want the fonts copying from your current system disk. (This test was added because fonts are not required for all applications and they do take up a lot of room.) Capital N serves as a reminder that the default action is NO.
-
The WARN flag is set at Step 6 if you answer Y. In this case control continues at Step 8, otherwise it branches to Step 9.
-
Copies all the fonts from the current boot disk onto the new boot disk.
-
Control reaches here if you answered “N" to the question at Step 7 and continues at Step 10. If you answered “Y” it jumps to Step 11.
-
Creates an empty fonts drawer on the new boot disk. This isn’t absolutely necessary but it should be retained for the sake of keeping things clean.
-
Terminates the IF…ELSE…ENDIF construct opened at 7.
-
Tests if you need a complete System directory (FORMAT, FIXFONTS etc).
-
If you answer Y at Step 12, the WARN flag is set and execution continues at Step 14, otherwise it jumps to Step 16.
-
Copies the entire System directory from the current system disk onto the new boot disk…
-
…and this copies that all-important Shell icon.
-
If execution arrives here from Step 15 it jumps to Step 20, otherwise it continues at Step 17.
1 7. As Step 1 5.
-
Creates the system directory and copies the system directory commands: FastMemFirst and SetMap. These are required by the Startup-sequence/Note changes for 2.1 and higher.
-
Closes the IF…ELSE…ENDIF construct opened at Step 13.
-
Makes the Utilities directory (nothing is placed here and no Workbench icon is created).
21-25. Copies the handler library, libraries, Devices, script and command directories to the new boot disk - generally speaking these lines should be left as they are.
-
Copies the Preferences directory over. This line is really only required for Workbench 2.
2 7. Guess what!
Listing 1.
2. 3. 4. 5. 6. 7. 8. 9. 10. 11 . 12. 13. 14. 15. 16. 17. 18. 19. 20. 21 . 22. 23. 24. 25. 26. 27. .KEY DRIVE/A,NAME .DEF NAME Lazy_Bones Echo "Making a simple boot disk - please wait" FORMAT DRIVE DF<DRIVE>: NAME <NAME> INSTALL DF<DRIVE>: Ask "Do you require fonts y/N?" IF WARN COPY Fonts: DF<DRIVE>:Fonts ALL ELSE MAKEDIR DF<DRIVE>:Fonts ENDIF Ask "Do you require a complete system y/N?" IF WARN COPY SYS:System DF<DRIVE>:System ALL COPY SYS:Shell.info DF<DRIVE>: ELSE COPY SYS:Shell.info DF<DRIVE>: COPY SYS:System/(FastMemFirst|SetMap) DF<DRIVE>:System ENDIF MAKEDIR DF<DRIVE>:Utilities COPY L: DF<DRIVE>:L ALL COPY Libs: DF<DRIVE>:LIBS ALL COPY Devs: DF<DRIVE>:DEVS ALL COPY S: DF<DRIVE>:S ALL COPY C: DF<DRIVE>:C ALL COPY SYS:Prefs DF<DRIVE>:Prefs ALL Echo "Operation complete…"
CALC 1.3
Synopsis: [EXECUTE] CALC [[val=]valuel] [[op=]operator] [[val2=]value2]
Template: val 1 ,op,val2
Path: S:
Requires: VI.3
See also:
Type: Script
Brief: Attempt to patch one of the bugs in EVAL
Description
AmigaDOS 1.3’s EVAL command has a problem - if you get the spaces in the wrong place, it doesn’t work. This script attempts to solve the problem by analysing the input line and deciding if it received enough parameters. This is a highly modified version of an example to be found in Mastering AmigaDOS Reference which relies on /a required arguments to generate an error. This version gives the users some polite help when they stumble…
Line-by-Line
-
The argument key has three arguments. These would normally be required but I’m assuming someone using this is going to need help. The last thing they’re going to need is EXECUTE screaming "args no good for key…” which is what would happen if one of those arguments was required.
2-3. Set the bra and ket characters to { and }.
4-9. If one of the required arguments is missing this code generates some help. Required arguments could have been used in the template (at Step 1) but one feature of this script is to show how interactive help can be used.
-
If control reaches this point when the correct arguments have been supplied, it continues at Step 11; otherwise it jumps to Step 12.
II. The reason EVAL failed in the first place haunts execute too. That is: if two (or more) optional arguments run into each other, they appear just like one argument. And we’re going to use that very feature to catch the bug. Consider a command line which reads:
CALC 1 +2
What actually happens is this: The “1” is assigned to “Vail”;
"+2" is not separated by a space so that gets assigned to "op". This leaves “val2” empty - so we check for a null value for “val2” and, if found, give some help. In practice you can give as much or as little help as you think is required. Be brief: you can always refer to the documentation files if need be. This is only mentioned in passing because you may note the use of “*" in the help string. This is because this example needs to print a literal “” (multiply).
12 This isn’t strictly necessary at the end of a script, but it’s good practice.
Listing
1 . key vail,op,val2
2 . bra {
3 . ket }
4 if {va!2} EQ ""
5 echo "Argument missing - help is:"
6 echo "Usage: CALC <value1> <operator> <value2>
7 echo "All arguments are required."
8 echo "Valuel and value2 use Ox for hex"
9 echo "Operator is one of - etc"
10 else
11 eval {vail} {op} {val2}
12 endif
Monthprint
Synopsis: [EXECUTE] MonthPrint
Template: none
Path: S:
Requires: V2+
See also: Calendar
Type: Scrit
Brief: Month printing module for the Calendar script
Description
One of the most irritating features with AmigaDOS is its low speed - and the calendar script is no exception. Elowever, any operation always seems quicker if you can see something happening. For instance, when Workbench is busy, it displays a sprite (clock or Z’s bubble) to show it’s working; hacks such as Sleepy 3 go further by animating the sprite.
Long operations should have some form of progress indicator and this is the method chosen for Calendar’s display module, Monthprint. The script’s progress is shown in the form of a bar traversing the screen from 0 to 100 per cent completed.
Progress indicators can be implemented in several ways - the choice of which method to use depends on how the script works. In linear scripts you update the indicator at strategic points - after a long copying operation for instance. Looping scripts are easier, you update once every loop. In such cases it is also much easier to determine the length of the progress bar because you can usually determine how many loops will be performed in advance. This is the method used by Monthprint - the length of the progress indicator is calculated from the number of days in the month.
Line-by-Line
-
Prints a simple message to let you know what’s going on.
-
Displays the fixed part of the progress indicator bar using string slicing. For the sake of illustration, let’s imagine the program was displaying a (purely hypothetical) seven day month. The variable “DiM" contains eight loops so the printed result from this step looks like this. (The extra space is picked out with a period and the cursor position with an asterisk.):
Adds the second part of the progress indicator. Note how this
0%. — *
3.
line looks a little strange at first glance. It sends a line feed, adds some spaces and then stops ECHO from printing a line feed. The screen display now looks like this:
o%.----.100%
★
As you can see, the cursor has been moved to the first position in the progress indicator, just below the first bar. This explains the strange use of “*n" and NOLINE switch.
-
Defines a loop label "loop" which is accessed by the backward jump at 20. The loop is defined as early as is practical in the script to help speed things up. When jumping backwards, the SKIP command starts at the beginning of the program and works its way down. If you must jump backwards, keep the labels early on.
-
In BASIC this line reads:
IF DiM > daynum
This tests if the value in “DiM” (Days in Month) has exceeded the value in "daynum” and branches accordingly. The variable daynum is initialised to “1" in the Calendar script.
-
This calculates the value held in the global environment variable “wrap” which is used later to determine when to wrap the display. The variable “wrap" is calculated each loop to contain a value between 0 and 6 - the offset of the current date in the week. The calculation uses a technique which is not available in AmigaDOS 1.3 since it writes directly to the variable being used. In early versions, EVAL opens a file to the variable and keeps it open until the command has completed. Since you cannot write to a file which is open for reading this was not possible. Assuming “wrap” contains 5, AmigaDOS treats this line thus:
EVAL (5 +1) mod 7 to ENV:wrap
The file containing the variable is opened, read and closed, while the line is being parsed. When EVAL gets round to executing it sees the variable just as if it had been typed.
-
In BASIC this line could be written:
IF daynum ⇐ 9
The test determines if the value stored in “daynum" is less than or equal to 9. If the test is positive, control branches to Step 9, otherwise it continues…
-
…here where a single space is added to the print file, “MFile". (Mfile was created by Calendar.) This handles the character alignment by making sure all the numbers line up neatly.
9.
10.
11.
12.
13.
14.
15.
16.
17.
-
19.
Since all the numbers appear a+ regular tab stops (accomplished with the string *e[I), single digits line up over the tens column, viz:
This becomes this
1 ► 1
8 ► 8
15 ► 15
You may exclude Steps 7 to 9 if you wish.
Closes the IF…ENDIF construct opened at Step 7.
Tests if the value of “wrap” is less than or equal to 1. (These values occur when the day is a Saturday or Sunday.) If it is, control continues at Step 11 otherwise it branches to Step 12.
Control reaches here if the date being displayed falls on a weekend. This special exception is highlighted by adding control characters to the output string: "*e[32m" turns the printed output white and “*e[31m" puts it back to normal. See Step 13 for more information.
If control reaches here from Step 11 it branches to Step 14 otherwise it continues…
…here, where it prints the next day number. A couple of things are worth noting here. First the output is sent to file for later display, but second two separate items are being printed. There’s nothing unusual in that, but look at how this
is achieved:
echo >>T:MFile Sdaynum "*e[I" noline
ECHO is receiving two print arguments (Sdaynum and “*e[I") instead of the more usual one. This is a unique feature of AmigaDOS 2 and cannot be used in earlier versions. In fact, you can send as many arguments as you like, switches such as NOLINE should be added to the end for clarity.
Closes the IF…ELSE…ENDIF construct opened at Step 10.
Increments the variable “daynum” using the direct write technique described at Step 6.
Checks if the value held in “wrap” is zero. If it is, control resumes at Step 1 7 otherwise it jumps to Step 18.
Adds a new line to the print file “MFile”.
Closes the IF…ELSE…ENDIF construct opened at Step 16.
This displays the progress meter block for the current loop. Note this is echoed directly to the current console screen and not sent to file.
-
Jumps back to Step 4 for another bite at the cherry.
-
Closes the IF…ELSE…ENDIF construct opened at Step 5. Control reaches here when the entire month has been sent to the print file.
-
Appends the bottom ‘'ruler” to the print file…
-
…which is finally displayed here using MORE. Note that since MORE is not RUN-launched it uses the current Shell window for display. This also clears the progress indicator.
-
Prints a black line.
-
Removes EVAL from the resident list since it is required.. no longer
-
…and closes the Shell process opened by Calendar.
Listing
-
echo "Calendar Working… wait"
-
echo "0% ----------------" first=1 len=$DiM noline
-
echo 100%*n " noline
-
lab loop
-
if val SDIM GT Sdaynum
-
eval ( Swrap +1) mod 7 to env:wrap
-
if val Sdaynum NOT GT 9
-
echo »T:MFile " " NOLINE
-
endif
-
if val Swrap NOT GT 1
-
echo >>T:MFile "*e[32mSdaynum*e[31m" "*e[I" noline
-
else
-
echo >>T:MFile Sdaynum "*e[I" noline
-
endif
-
eval Sdaynum + 1 to env:daynum
-
if Swrap eq 0
-
echo »T:Mfile ""
-
endif
-
echo "*e[41m *e[40m" noline
-
skip loop back
-
endif
-
echo >>T:Mfile "*n===============================~
-
more T:Mfile
-
echo ""
-
resident eval remove
-
endcli
Calendar
Synopsis: [EXECUTE] Calender <[YEAR=] year> [month]
Template: year/a, month
Path: S:
Requires: V2+
See also: MonthPrint
Type: Script
Brief: Main module for the AmigaDOS calendar
Description
Zeller’s congruence is something of a mouthful after, say, a few pints; anything mathematical brings tears to my eyes. Zeller’s congruence is a complex integer-based formula to calculate the day number of the first day in any year from the start of the Georgian calendar (1582) to well into the next millennium; including leap years. It’s just as complex to express as a mathematical formula too. Nevertheless, Zeller’s mathematical prediction is widely used in applications such as perpetual digital calendars.
The first day numbers (there’s seven of them from zero to six) are fixed and it is possible to program say, a watch, with a hundred or so in packed binary (two values per byte) and use them to fix the calendar. However, that approach is a bit feeble so here’s how to program the congruence in AmigaDOS with a complete calendar program to boot. As you will see the maths are quite easy, the hard part is making use of the figures! Calendar 1
First though, here is one way to express Zeller’s congruence in most versions of BASIC:
10 INPUT "Year", Year 20 Century=INT( (Year -1)/100)
30 Decade=Year-1 -100*Century
40 Day=(799+Decade+(Decade/4)+(Century/4)-(2*Century)) MOD 7
50 PRINT “Day number is: ";Day
Looks pretty hair-raising at first glance doesn’t it - but it breaks down quite well. Lines 20 and 30 split the year into two parts - the century number (1800, 1900, 2000 etc) divided by 100; and the decade number minus one. Therefore, 1992 breaks down thus: Century=19 Decade = 91
Line 30 uses these values to calculate the number of the first day in January of any particular year. In 1992 for instance the first day is Wednesday, so the result is 3 (where Sunday=0 and Saturday=6). Essentially this is just a piece of simple arithmetic and even AmigaDOS 2 can handle that without too many problems.
The script programs presented here are not suitable for earlier versions of AmigaDOS because of the advanced maths and variable handling, but if enough of you make a fuss, I will attempt to reprogram this example for AmigaDOS 1.3.2. This sort of problem is not suitable for AmigaDOS 1.3 because the EVAL command did not support multiple arguments. Enthusiastic owners might like to try this as an exercise.
Calendar is divided into two separate scripts for speed. The first is a linear script which does all the necessary calculations, the second displays an entire month. It is quite possible to write this program as a single script, but since the printing side performs a lot of backward loops, it is faster to do it this way. Let’s take a close look at how the main part works.
Line-By-Line
-
Defines the arguments. Calendar only requires a year to work, but you can supply a month number too. The month argument could have been a month name, but this just adds complexity and means you have to type more. .
2-4. Re-defines the bra, ket and dollar symbols. Dollar is changed here to make the script easier to read - you’ll see why later on.
-
Preloads EVAL into memory for speed. Note the ADD argument is supplied here so the command can be safely removed without affecting any other scripts.
6-7. Creates local environmental variables “Y” and “M” containing the year and month (if any) specified from the command line.
-
Subtracts 1 from the year number and stores the result in the global environmental variable, “Date". (You should note here, the dollar symbol is used to signify an environmental variable - it is not affected by the .DOLLAR command used earlier.)
-
Subtracts 1 from the month number and stores the result in the variable, "Month”.
-
This is a natty little trick to remove the century number from the date variable. Assuming the value held in Date was 1991, it works like this:
ECHO "SDate"
is read by AmigaDOS thus: ECHO "1991"
because the local variable is expanded as the command executes. This is then affected by the FIRST and LEN keywords - FlRST=l, tells ECFIO to display the leftmost character on the string. LEN=2, makes ECFIO display just two characters - in other words the century number. In fact, this value is not displayed, instead it is sent to a new global environmental variable, “Cent”.
-
Like step 10, this removes two characters from the “Date” variable. However, since the FIRST keyword is not supplied, ECHO reads the rightmost two characters - the Decade in other words. As before, this value is used to create an environmental variable (Decade).
-
This looks a lot worse than it really is! It uses the BASIC translation of the Zeller’s congruence method described above to calculate the day number of the first day in the required year. A point worth noting here is there must be a space before the dollar symbol used to signify an environmental variable. The result is stored in (yet another) global variable, “Day".
-
It is an interesting fact that you can determine if a year is a leap year (29 days in February) by performing modulo 4 on it. Leap years always return a value of 0. This calculation performs MOD 4 on the year number (supplied at the command line) and stores the result in the global environmental variable, “Leap". Just to aggravate matters though, most centuries are not leap years. A century must be divisible by 4 (1600, 2000, 2400 etc) for it to be leap year.
-
Tests the value of “Leap” and determines what to do next. If the year is not a leap year, execution continues at Step 15: if it is, execution branches to Step 19.
15-16. This two-part step does some string slicing to obtain a value from an array of numbers. Each of the twelve month’s in a year has a particular number of days, you knew that much of course - but the computer does not. In BASIC for instance, you would set up an array like this:
FOR N=1 TO 12
READ DaysInMonth(N)
NEXT N
DATA 31,28,31,30,31,30,31,31,30,31,30,31 and read the array thus:
Days=DaysinMonth(Month)
where the variable “Month” selects the correct element from the array. AmigaDOS cannot handle arrays in this way - but by careful use of string slicing (and some careful typing) this can be achieved quite simply. I’ll explain this step in detail because it occurs several times in this script.
The first job is to construct the array of numbers. This is just the number of days in each month as demonstrated in the BASIC example above. To keep the script easy to read (and debug) the list is constructed with full stops between each value - although this is not strictly necessary. This leaves something like this:
".31.28.31.30.31.30.31.31.30.31.30.31"
Each number is three characters long, therefore you can pick any value by multiplying the offset (the month number) by three. A feature of AmigaDOS means the first character in the string is numbered one. Also, since the months start from zero (determined earlier) we must add two to get the correct offset. If that makes your brain itch, consider this:
Take June - month number five. In the script, the variable "Month” will be holding four. Therefore:
Offset = (4*3)+2 = 14
The 14th and 15th characters in from the start of the data are "31”, the fifth number in the data. Taking this offset as a start value and reading two characters, you can create an environmental variable. Here’s how:
-
Calculates the starting position using the environmental variable Month and sending the offset result to global environemtal variable, “Slice".
-
Starting from the position determined by “Slice" this takes two characters from the data string and saves the result in “DiM” (Days In Month).
-
Creates another offset variable, which is used to read the data at Step 18…
-
…here. This data is the number of days in the year that have elapsed at the start of the current month. Note this table is almost identical to the first one except the numbers are two
or three characters long. To read a data table in this way, it is vital all strings are the same length. Therefore, if a number is composed of just two digits, it must be preceded by a padding space.
-
If control reaches here from Step 18, it branches to Step 24 otherwise it continues at Step 20.
20-23. These lines are essentially the same as 15-18, however these data strings are used for leap year exceptions. The data changes after February which has 29 days in this case.
-
Calculates the offset variable used at Step 21…
-
…which is used to determine the number of days in the selected month. This value is then sent to the variable, “DiM".
-
Calculates the offset variable used at Step 23…
-
…which determines how may days have elapsed up to the current month. It is important to note when you enter this program, all but three of the values change in this data set!
-
Closes the IF…ELSE…ENDIF construct opened at Step 14.
-
Prepares another string slice offset. This one is used at Step 27 to grab the month name.
-
Creates a text (MFile) file in T: with an initial string. Note here, the NOLINE switch is used to suppress the extra line feed. At this stage MFile contains:
Calendar for:
-
Uses ECHO’S string slicing facilities plus the append to file operator (>>) to attach the current month name to the message string, MFile. If month 4 had been requested, MFile now contains
Calendar for: Apr
-
Next, the year is added. This is taken from the local environmental variable (Y) created at Step 7. MFile now looks like this:
Calendar for: Apr 1992
-
This appends a "ruler” to the message file. (Equals signs are used here, but you can used any convenient character.) Note the line feed at the start of the line:
Calendar for: Apr 1992
-
This appends the “day names" heading to the message file. Note how “*e[I” (TAB) escape sequences are used to tabulate the text correctly.
-
Like Step 31, this adds rules to the day names. You can use any characters you prefer here, but you should keep the tab sequences.
32-34. Calculate the initial print position of the first date under the day name rules. Since this is quite an involved topic, I’ll look at it in a bit more detail. The idea is quite simple, the day names appear across the top from Sunday to Saturday like this:
Sun Mon Tue Wed Thu Fri Sat
Now, let’s take January 1992 (January is the simplest month). The 1st is a Wednesday (Day=3) so the program has to start printing 24 characters (1 TAB=8 characters) in from the start, like this:
Sun Mon Tue Wed Thu Fri Sat
12 3 4
5 6 7 8 9 10 11
This is quite simple to produce using the formula:
Space = Day * 8
But what happens later in the year? Take May for instance. The 1st of May 1992 is a Friday so how can we calculate that from the day number returned from Zeller’s congruence? This is where the "Elapsed" variables determined at Steps 21 and 23 come into effect. These determine the number of days elapsed up to the start of the current month. At the first of January, no days have elapsed, but by the first of May, 121 days have passed. By adding this to the initial day number and dividing by seven, the remainder is the offset to the first day in the week. The formula is therefore:
Space = ((Day+Elapsed) MOD 7) * 8
-
This is the AmigaDOS version of the above calculation. “Elapsed" and "Day" are summed first. Then the modulo (remainder after division) is taken and stored in "Day". The calculation is split in two like this because the value of “Day" is required elsewhere.
-
The new value of Day is multiplied by 8 and stored in the new global environmental variable, "Space".
-
Uses ECHO’S string slicing function to produce an effect similar to the STRINGSO function found in most modern BASICS. Note in the listing these are shown as periods (.) but
they should be entered as spaces.
-
Sets the global environmental variable, "daynum” to 1. “Daynum" is used by the display script.
-
Copies the current value of "Day” to a new global environmental variable, "wrap” (used by the display script). It is interesting to note, this operation could be accomplished by COPY. However, EVAL has been used because that command is made resident for the script.
-
Increments the value held in “DiM” by 1.
-
Starts the display script, MonthPrint although it is important to note how this has been achieved. In normal circumstances, the script would be called using either EXECUTE or RUN EXECUTE; the latter being the closest approximation to the final solution. Using NEWSHELL allows you to effectively RUN launch EXECUTE and specify a window size at the same time.
Listing
-
.key year/a, month
-
.bra {
-
.ket }
-
.dollar !
-
resident c:eval add
-
set M {month}
-
set Y {year}
-
eval $Y-1 to env:Date
-
eval SM-1 to env:Month
-
echo "SDate" first=1 len=2 to envrCent
-
echo "SDate" len=2 to env:Decade
-
eval (799+ $Decade+($Decade/4)+($Cent/4)-(2* SCent)) mod 7 to env:Day
-
eval (($Cent+1) + $Y) mod 4 to env:leap
-
if val Sleap NOT EQ 0
-
eval Smonth * 3 +2 to env:slice
-
echo ".31.28.31.30.31.30.31.31.30.31.30.31" first=$slice len=2 to env:DiM
-
eval Smonth * 4 +2 to env:slice
-
echo "..00..31..59..90.120.151.181.212.243.273.304.334" first=$slice len=3 to env:Elapsed
-
else
-
eval Smonth * 3 +2 to env:slice
21 .
22.
23.
24.
25.
26.
27.
28.
29.
30.
31 .
32.
33.
34.
35.
36.
37.
38.
echo ".31.29.31.30.31.30.31.31.30.31.30.31" first=$slice len=2 to env:DiM
eval Smonth * 4 +2 to env:slice
echo "..00..31..60..91.121.152.182.213.244.274.305.335" first=$slice len=3 to env:Elapsed
endif
eval Smonth * 4 +2 to env:slice echo >T:MFile "Calendar for: " noline
echo >>T:MFile " Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec" first=$slice len=3 noline
echo »T:MFile " $Y"
echo >>T:MFile "*n======================================"
echo >>T:Mfile
"Sun*e[IMon*e[ITue*e[IWed*e[IThu*e[IFri*e[ISat" echo >>T:MFile
"===*e[I===*e[I===*e[I===*e[I===*e[I===*e[I===" eval ( SElapsed + SDay ) mod 7 to env:Day eval SDay * 8 to env:Space
echo >>T:MFile "………….." first = 1 len=$space noline
setenv daynum 1
eval Sday to env:wrap
eval SDiM + 1 to env:DiM
newshell from s:MonthPrint con:0/0/480/140/Calendar/Auto
Monthprint2
Synopsis: [EXECUTE] MonthPrint2
Template: none
Path: S:
Requires: V2+
See also: Calendar2, SuperCal
Type: Script
Brief: Month printing module for the Calendar2 script
Description
Although given here as a complete listing, there are only a few differences between this module and the one listed elsewhere in the book. The changes are noted below.
Line-by-Line
The following changes have been made to Monthprint: 1. Cosmetic change to be more informative 11 and 13. Tabs replaced by spaces.
-
Signals to Calendar2 it is safe to continue by breaking the wait
state.
Listing
-
echo "Calendar Working on SMName SY. Please wait"
-
echo "0% » first=1 len=$DiM noline
-
echo 100%*n " noline
-
lab loop
-
if val SDIM GT Sdaynum
-
eval ( Swrap + 1) mod 7 to env:wrap
-
if val Sdaynum NOT GT 9
-
echo >>T:MFile " " noline
-
endif
-
if val Swrap NOT GT 1
11 . echo >>T:MFile "*e[32m$daynum*e[31m noline
-
else
-
echo >>T:MFile Sdaynum " " noline
-
endif
-
eval Sdaynum + 1 to env:daynum
-
if $wrap eq 0
-
echo >>T:Mfile ""
-
endif
-
echo "*e[41m *e[40m" noline
-
skip loop back
21 . endif
-
echo >>T:Mfile "*n—
-
break SBreakMe C
-
endcli
SuperCal
Synopsis:[EXECUTE] Supercal <[YEAR=] year> [Month]
Template:Year/A, Month
Path: S:
Requires: V2+
See also: Calendar2, Monthprint2
Type: Script
Brief: Full year version of the AmigaDOS calendar program
Description
Supercal is the main module for Calendar2, the whole year calendar. For the sake of speed, it operates as a separate module which calls modified versions of Calendar and Monthprint.
Line-By-Line
-
Define the key for this command. You must specify a year for this script and optionally a starting month. For instance you might only want the calendar from August 1992. Note however, both these arguments are numeric.
-
Just in case you don’t supply a month, Supercal assumes you mean January - this will normally be the case since Supercal is designed to display whole year calendars.
-
Checks if the Calendar variable has been set - this is used to check for certain once only configuration. If Calendar exists control branches to Step 7, otherwise execution continues…
-
…here, where the Calendar variable is defined.
-
Adds the EVAL command to the resident list - this used to be done in Calendar, but since that is now a subroutine of this script, it is done here.
-
Creates the print file and defines its heading with the current year.
-
Closes the 1F…ENDIF construct opened at Step 3.
-
Defines a global environmental variable MN and gives it the value of the current month.
-
Prints a simple progress message in the current console window. This is the working window that Supercal was launched from.
-
Executes Calendar2 (listed below) with the correct parameters.
-
Increments the month number.
-
Tests if the whole year (up to December has been done). If it has, execution branches to Step 14 otherwise it continues…
-
…here, which calls Supercal itself recursively. This has been done in preference to using RUN because that would cause more than one occurrence of Calendar2 to execute at once and that cannot happen. Using EXECUTE on its own does not work because this script contains a backward loop and the temporary command file required by the SKIP command is trashed by the second EXECUTE running from the same process. (Phew!) Don’t worry, it just works that way.
-
Closes the IF…ENDIF construct opened at 12.
-
Displays the completed Calendar.
-
Removes EVAL from the resident list since we’ve now finished with it.
-
Makes a copy of the print file in your S: assignment…
-
…and lets you know for future reference. This is the print file. You can make your own calendar by copying this to your printer thus:
COPY S:Calendar 1992 to PRT:
-
Finally, makes sure no recursive copies of the script are left to execute.
Listing
-
.key Year/A,Month
-
.def Month 1
-
if ‘SCalendar EQ SCalendar
-
setenv Calendar ON
-
resident c:eval add
-
echo >T:MFile "Calendar <Year
-
endif
-
setenv MN <Month>
-
echo "Working on $MN/<Year>"
-
execute s:Calendar2 <Year> SMN
11 . eval SMN + 1 to ENV:MN
-
if val SMN NOT GT 12
-
execute s:Supercal <Year> SMN
-
endif
-
more T:MFile
-
resident eval remove
-
copy T:MFile to S:Calendar<Year>
-
echo "Calendar saved to disk as Calendar<Year>…*nSee text for info on how to print this."
-
quit
Calendar2
Synopsis: [EXECUTE] Calendar
Template: none
Path: S:
Requires: V2+
See also: MonthPrint2, SuperCal
Type: Script
Brief: Improved calendar script
Description
The only changes between this version and the one listed elsewhere are as follows (line numbers refer to this version):
Calendar 2
Line-By-Line
-
The month string is now written to a global environmental variable.
26-29. Tabs removed and other cosmetic improvements.
-
$Day is now multiplied by five to account for new spacing.
-
A new variable has been added here to aid the multi tasking. "Breakme” is set to the current process number running Calendar2.
-
NEWSHELL calls a different file and the window has been made smaller.
-
An extra line forces the script to wait until the Monthprint2 process is completed. Redirection to NIL: is used to stop the “***Break" message appearing.
Listing
-
.key year/a,month
-
.bra {
-
.ket }
-
.dollar !
-
set M {month}
-
set Y {year}
-
eval SY-1 to env:Date
-
eval $M-1 to env:month
-
echo "SDate” first=1 len=2 to env:Cent
-
echo "SDate" len=2 to env:decade
-
eval (799+ Sdecade+($decade/4)+($cent/4)-(2* Scent)) mod 7 to env:day
-
eval ((SCent+1)+$Y) mod 4 to env:leap
-
if val Sleap NOT EQ 0
-
eval Smonth * 3 +2 to env:slice
-
echo " 31 28 31 30 31 30 31 31 30 31 30 31" first=Sslice len=2 to env:DIM
-
eval Smonth * 4 +2 to env:slice
-
echo " 00 31 59 90 120 151 181 212 243 273 304 334"
first=Sslice len=3 to env:Elapsed
-
else
-
eval Smonth * 3 +2 to env:slice
-
echo " 31 29 31 30 31 30 31 31 30 31 30 31" first=$slice len=2 to env:DIM
-
eval Smonth * 4 +2 to env:slice
-
echo ” 00 31 60 91 121 152 182 213 244 274 305 335"
first=$slice len=3 to env:Elapsed
-
endif
-
eval Smonth * 4 +2 to env:slice
-
echo >ENV:MName " Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec" first=$slice len=3 noline
-
echo »T:MFile "*n=========== SMName $Y ============"
-
echo »T:MFile "================================="
-
echo »T:Mfile "Sun Mon Tue Wed Thu Fri Sat"
-
echo »T:MFile "=== === === === === === ==="
-
eval Sday to env:wrap
-
eval ( SElapsed + SDay ) mod 7 to env:Day
-
eval Sday * 5 to envrspace
-
echo >>T:MFile " " first=1 len=$space noline
-
setenv daynum 1
-
eval SDiM + 1 to env:DiM
-
eval Sday to env:wrap
-
setenv BreakMe Sprocess
-
newshell from s:MonthPrint2 con:0/0/360/50/Calendar/Auto
-
wait >NIL: 20 mins
CCOPY
Synopsis: [EXECUTE]CCOPY<[ FRO M]=Source>[TO=] Destination [BUF=<buffers] [CLONE] [DATES] [NOPRO] [COM]
Template: FROM/A, TO, ALL/S, QUIET/S, BUF/K, CLONE/S, DATES/S, NOPRO/S, COM/S
Path: S:
Requires: VI.3+
See also: CCOPY Alias
Type: Script
Brief: Intelligently select COPY or COPY dependant on arg chain
Description
The CCOPY Alias is all very well, but you have to remember which version of COPY to use depending on the situation. To get around this, it is necessary to write a small script to make COPY intelligent. If a destination is supplied it works like AmigaDOS; if not it behaves like MS-DOS.
This script mirrors the original COPY command very closely although a few embellishments have been added - displaying the source and destination directories for instance. Also, the destination is no longer a required argument. To use this, simply type it into your favourite editor and save it in S:. Now set the “S” protection flag and it works like the real thing. It relies on an undocumented feature of AmigaDOS 1.3 in that switch (/s) arguments are supported.
Example
1>CD RAM: 1>CC0PY S:Startup - sequence
Line-By-Line
-
Defines the argument template to match that of the existing COPY command.
2-3. Redefine bra and ket to my favourite settings.
-
Sets the default value for "BUF" to 200 (200K in disk buffers for this script. You may alter this as you see fit (or whatever fits your machine).
5.
Sets the default value for the TO (destination) parameter to something silly. You could replace this with some white space or punctuation if you prefer - this value was chosen for clarity.
-
Checks if a value was sent via the TO argument. If an argument was supplied, control transfers to Step 10; otherwise it continues at Step 7.
-
Prints the first part of a progress message to keep you informed as to what’s going on - note use of the NOLINE switch to keep everything on the same line. (You may like to add another command line option to suppress these messages or incorporate the existing QUIET switch here.) Assuming the example above, the script will output something like this
Copying from: S:Startup-sequence TO:
-
Displays the current directory setting thus completing the example above - remember, CD gives the volume name - not the device name:
Copying from: S:Startup-sequence TO Ram Disk
-
Performs the copy operation using the options specified at the command line and copying to the current directory.
-
If execution gets here from Step 9 it jumps to Step 13; otherwise it continues…
-
…here and gives out a pretty obvious progress message based upon the command line arguments.
-
Actually does the copy proper. 1 3. Terminates the IF…ELSE…ENDIF construct opened at Step 6.
Listing
-
.key FR0M/A,T0,ALL/s,QUIET/s,BUF/K,CL0NE/s,DATES/s,
N0PR0/s,COM/s
-
.bra {
-
.ket }
-
.def BUF 200
-
.def TO NOTHING
-
IF {TO} EQ "NOTHING"
-
ECHO "Copying from: {FROM} TO " NOLINE
-
CD
-
COPY {FROM} "" {ALL/s} {QUIET/s} {CLONE/s} {DATES/s} {NOPRO/s} {COM/s} BUF={BUF}
-
ELSE
-
ECHO "Copying from: {FROM} TO {TO}"
-
COPY {FROM} {TO} {ALL/s} {QUIET/s} {CLONE/s} {DATES/s} {NOPRO/s} {COM/s} BUF={BUF}
-
ENDIF
CCOPY
Synopsis: Template: Path: Requires: See also: Type: Brief: Definition:
Synopsis: <source file> [options] Template: see text Path: na Requires: VI.3+ See also: CCOPY Script Type: Alias Brief: Simple version of MS-DOS COPY Definition: ALIAS CCOPY COPY []
Description:
This potboiler started life on CIX late one evening - someone wanted COPY to act like a PC. That is: if a source directory is not specified, COPY duplicates the file in the current directory. For instance:
1>CD RAM: 1>COPY S:SPAT
is not possible in AmigaDOS because COPY requires two arguments.
Either argument can be replaced with but this is messy. The solution therefore is to use an alias. I’ve called this one CCOPY -Current Copy; the name is not important. Add this to your Shell-startup script so it will be available at any time: all the normal COPY options are available too.
Chatter
Synopsis: [EXECUTE] Chatter <[NAMEl=]Namel> <[NAME2]Name2> Template: NAME1/A, NAME2/A Path: S: Requires: VI.3 See also: Chatty Type: Script Brief: Start the pipe messaging system with names Description Although executed from the remote terminal, CHATTER and CHATTY handle all the communication between the two machines. Unlike MS-DOS and UNIX, AmigaDOS does not support unnamed pipes where the output stream of one command can be connected to the input stream of another. Hence the MS-DOS construct: TYPE READ.ME | MORE is not valid in AmigaDOS and has no direct equivalent. (The bar symbol is used in MS-DOS to signify a pipe.) The nearest alternative is to do the command in two steps thus: 1>COPY READ.ME PIPE:A 1>MORE PIPE : A or, alternatively: 1>TYPE >PIPE:A READ.ME 1>MORE <PIPE:A Note: the ability to use unnamed pipes is documented as part of the ARP 1.3 release and is claimed to work with the Shareware Shell replacement, Conman. ARP 1.3 users may want to try this out for themselves.The MORE program exhibits different behaviour depending on whether it is launched directly or as a separate process via RUN. Try this: 1>MORE S:SPAT 1>RUN MORE S:SPAT Notice in the first instance how MORE uses the current Shell window, but when RUN it opens a window of its own. This feature may seem pointless, but it allows MORE to be used over the serial port. Moreover, it also gives rise to a variation on the CHAT theme. The “Chat" system is designed to be launched by the remote system and initialise all the pipes. It cannot set up the CHATTO alias - this can be done in Shell-startup, by adding the following lines: ALIAS ChatTo COPY * TO PIPE:[] ALIAS ChatNow RUN EXECUTE CHATTY To start the chat system, the remote operator enters (for example): 1>CHATN0W MARK BRUCE The host terminal then receives a message (via MORE) like this: Chat system opened as: Host=MARK Remote=BRUCE Mark (using the host Amiga) can now start chatting to Bruce: 1>CHATT0 BRUCE Hello Bruce! and send the message by pressing CTRL+\ as described earlier. Similarly, Bruce can chat back to Mark like this: 1>CHATT0 MARK Hello Mark! wonderful system, huh? Line-By-Line 1. This defines the script’s argument template. Two inputs are required from the user: the name of the host and remote machines. Since the /A template option has been used, both arguments must be supplied or the script will fail to run. 2. Creates a file in the temporary files assignment T: containing the startup message which will be displayed on the host terminal. The arguments surrounded with angle brackets will be replaced by the user’s input. Therefore if the command line is: 1>RUN CHATTER DAVE PAT the file will contain the message: Chat system opened as: Host=DAVE Remote=PAT. The filename is determined by adding the process number to “QWE”. Therefore if CHATTER was running as process 3, the filename would be “QWE3”. 3. This is a trick which relies on the ability of MORE to recognise when it has been RUN-launched. Normally, MORE would display T:QWE on the remote terminal, however since EXECUTE has be RUN-launched, the script is also running as a process and any commands it contains are also running asychronously. The end result is MORE pops up as a window on that the HOST machine (much to the surprise of unwary operators). 4. Not a lot of people know this, but it is quite legal to RUN-launch scripts from within scripts - even those which have been launched with RUN in the first place. That’s what happens here, CHATTY is RUNTaunched from CHATTER. It must be started in this way because, as you will see later, it never returns. CHATTY is passed one parameter, the name of the remote terminal. (The parameter was missing in the original listing.) 5. This label defines the start of a loop which is called endlessly. Like CHATTER, this script never finishes. 6. Copies the contents of the host pipe (if any) to a temporary file. This forces the script to pause until some data appears at the pipe and prevents the script from needlessly looping. 7. Immediately displays the contents of the temporary file. If more was used like this to display the contents of the pipe directly the script would not pause correctly. 8. Forces the script to jump back to the label defined at step 5 completing the endless loop. The result is the program waits until a message appears on the pipe, displays it, and waits for the next one. Listing 1 . .key NAME1/A,NAME2/A 2. Echo >T:qwe<> "Chat system opened as: Host=<NAME1> Remote=<NAME2>" 3. More T:qwe<> 4. Run Execute S:Chatty <NAME2> 5. Lab Start 6. Copy Pipe:<NAME1> T:msg<> 7. More T:Msg<> 8. Skip Start Back
CHATTY
Synopsis: [EXECUTE] Chatty [Name=] Template: Name Path: S: Requires: VI.3+ See also: Chatter Type: Brief: Script Read piped messages from either terminal Description This script is never executed directly, it works as a support script that is called by CHATTY. Typically, scripts of this size can be created by the script that calls them. However, that was not thought necessary for this program. Line-By-Line 1. Defines the argument template for the script. Although the argument would normally be required, it is not necessary to do that here since the correct syntax is assured by the calling script. The argument received by CHATTER is the name allocated to the remote terminal. 2. Defines a label which will be jumped to when the script loops. 3. Waits for data to be sent to the pipe on the remote terminal and prints it. Like COPY, TYPE waits for information to appear on the pipe before doing anything. 4. Loops the script back to Step 1, causing it to execute again. This script never stops, but because it’s attached to an internal Shell (via RUN) it does not affect the machine’s operation. Listing 1. .key NAME 2. Lab start 3. Type pipe:<NAME> 4. Skip start back
Clock 1.3
Synopsis: Run from Workbench Template: none Path: Requires: VI.3-1.3.3 See also: Clock 2, Clock 3 Type: Script Brief: Simple AmigaDOS clock Description The idea of having a real-time clock from AmigaDOS must seem a little preposterous: especially if you consider the same thing is already supplied with Workbench. The difference between this one and the one you get with Workbench is size: this clock program is less than 512 bytes long and fits easily on your Workbench disk even if you’re short of space! This version should be run from Workbench via IconX. Line-By-Line I. Sets a dummy key for IconX. 2-6. Makes some commands resident for the script. This is necessary or the clock will spend most of its time loading commands from disk. 7. Sends some special control codes to the console to switch the cursor off (*e[0 p) and position the cursor at the start of the line (*e[;0H). Note: the punctuation is necessary! 8. Marks the start of the endless clock loop. 9. Displays the current date and time. 10. Re-positions the cursor. This stops the clock wrapping on a line. II. Waits for a second. This controls the speed of update - you can set this wait for a longer period if you prefer: say five seconds. 12. Re-starts the loop again. 13. Is WX information. Not used by this script. 1. .key dummy 2. resident c:wait 3. resident c:date 4. resident c:echo 5. resident c:lab 6. resident c:skip 7. echo "*e[0 p*e[;0H" 8. lab start 9. date 10. echo "*e[;0H" noline 11. wait 1 secs 12. skip start back 13. ;WX:WINDOW=WINDOW=con:0/0/190/60/MemoryGauge
Clock 2
Synopsis: Run from Workbench Template: none Path: Requires: V2+ See also: Clock 1.3, Clock 2 Type: Script Brief: Simple AmigaDOS clock Description This clock program is less than 512 bytes long and fits easily on your Workbench disk even if you’re short of space! This one is written for AmigaDOS 2+ and should be run from Workbench via IconX or placed in WBStartup. The CON: options set for IconX determine how and where the clock appears. The BACKDROP option should only be used if the Workbench is normally operated as a window: with the BACKDROP option OFF. Clock 2 Line-By-Line 1-2. Make WAIT and DATE resident. These commands are ADDed to the resident list and never removed, so this script should only be run once from WBStartup. The ADD option can be removed but this can cause problems if some other program has control of the resident list. 3. Positions and switch the cursor off. 4. Marks the start of the clock loop. 5. Displays the current date and time and positions the cursor back at the start of the line; thus avoiding line wrap and scrolling. 6. Waits for a second. This controls the update time of the loop (and the clock) and may be increased if preferred. 7. Re-starts the loop ready for the next display run. 8. Is some information for WX to use. You don’t need this for the script if it’s run from Workbench. Listing 1. resident c:wait add 2. resident c:date add 3. echo "*e[0 p*e[;0H" noline 4. lab start 5. echo "'date'*e[;0H" noline 6. wait 1 secs 7. skip start back 8. ;WX:WINDOW=WINDOW=con:0/0/190/60/Memory Gauge/SMART/NOSIZE
Clock 3
Synopsis: [EXECUTE] Clock3 []Ticks=]Seconds] Template: ticks Path: S: Requires: V2+ See also: Clock 1.3, Clock 2 Type: Script Brief: imple digital clock with auto window Description This script was created just to prove a big point in a short space. That is: you don’t have to use Workbench if you want to have a program working in its own window. This script creates a completely new script and executes it using NEWSHELL. In this way it creates its own window and starts the program as a process! Clock 3 Clock Alarmed Typically you can use this script without arguments, but you can supply the number of "ticks": that is the number of seconds it leaves other processes before re-executing. Examples: 1>Clock3 1>Clock3 ticks=5 Line-By-Line 1-3. Define a simple, standard header. 4. Sets the default for seconds to wait. One second is used here since this is a simple script, but you could use a longer time and send a shorter delay from the Shell. 5-6. Make the required external commands resident. 7. Creates the program file in T: as "xlclock#”. (Remember that # is the current Shell process number.) This line will appear after translation as: echo "*e[0 p *e[;OH" noline 8. Adds the second line to "Xclock#". This is simply a label. 9. Adds the next line to the “Xclock#” file. Note that this is only the first half of the third line! The reason is we don’t want the ..couplet expanding the date just yet! The NOLINE switch for ECHO ensures the line is not fully terminated just yet. At this stage the second line looks like this: echo ‘‘date 10. Writes the second part of the third line completing it thus: echo *"'date'*e[;0H" 11. Adds the third program line: inserting the wait limit directly into the program. If no arguments were supplied, ticks = 1, so the line looks like this: wait 1 secs 12. Completes the program by adding the loop back. 13. Starts the clock program by executing the xclock# script with NEWSHELL. This isn’t exactly the same as using IconX but it will suffice, thank you. The values shown here place the clock on the Workbench screen. You should remove the NOBORDER and BACKDROP switches if you want to be able to move the clock around. 14. This information is only used by WX - which is really not suitable for this script. Listing 1. .key ticks 2. .bra { 3. .ket } 4. .def ticks 1 5. resident c:wait 6. resident c:date 7. echo >T:xclock{} "echo *"**e[0 p **e[;0H*" noline 8. echo »T:xclock{} "lab start" noline 9. echo >>T:xclock{} "echo *"'date" noline 10 . echo >>T:xclock{$S} "**e[;0H*" noline" 11 . echo >>T:xclock{} "wait {ticks} secs" 12 . echo >>T:xclock{$$} "skip start back" 13 . newshell from t:xclock{S$} window=con:0/5/240/30/Clock/NOBORDER/BACKDROP/SMART 14 . ;WX:WINDOW=WINDOW=con:0/0/240/30/CLI_Clock
Clock
Synopsis: Started from Workbench Template: none Path: Requires: V2.0+ See also: Type: Script Brief: Simple digital AmigaDOS clock Description They don’t get much simpler than this one. This script forms the basis of many of the clock scripts featured here without toO many clever tricks. The great thing about this one is it’s short. It must be run from Workbench via IconX to ensure it runs in its own window though. Line-By-Line 1. Gives IconX something to chew on. It isn’t absolutely necessary for scripts as simple as this one. 2-3. Makes some essential commands resident. 4. Switches the cursor off. 5. Marks the start of a loop. 6. Displays the current date and time. 7. Moves the cursor up a line. 8. Waits for a second… 9 …and starts the whole thing again! Listing 1. .key dummy 2. resident c:wait 3. resident c:date 4. echo "*e[0 p" 5. lab start 6. date 7. echo "*e[A" noline 8. wait 1 secs 9. skip start back
Add Data
Synopsis: [EXECUTE] AddData [a 1…ac] [Data=<data>] Template: al ,a2,a3,a4,a5,a6,a7,a8,a9,aa1ab,ac,data/k Path: S: Requires: VI.3+ See also: Sortdata, DataBase, PrintData etc Type: Brief: Script The add module for the Database Description You can add and delete records using the ED command. While this is fine for the most part, it is a little prone to error. This is the AddData module which will allow you to add one or more records directly from the command prompt. AddData works rather like the FindData module in that you can execute the command followed by a data string: Command: A Mark Smiddy, Mastering AmigaDOS 3, BSB Add another y/N? or just execute it on its own, like this. Command: A Add another y/N? Y Mark Smiddy, Mastering AmigaDOS 3, BSB Add another y/N Note in the second case, AddData asks if you want to add something before you actually do; this is quite normal. Line-By-Line 1. Defines a long key with lots of parameters - as you may recall, this allows for long interactive command lines. In this case, it allows you to enter 12 "words" without having to resort to quotes. 2. Collects the contents of <al>…<ac> into a variable called data. 3. Checks to see if any data has been entered - it can come from the initial command line or interactively from this segment. If data has been supplied control jumps to Step 5; otherwise it continues at Step 4. 4. Transfers control immediately to the end of the script (Step 20 actually). Since no data has been supplied, the script must be executed again in order to gather some. 5. Closes the IF…ENDIF construct opened at Step 3. 6. Tests for the presence of a temporary file in T: (the temporary files assignment). It’s called “Temp” in this example, but any name would do - provided you stick to the same one throughout the script. If the file exists, control passes to Step 7; otherwise it jumps to Step 8. 7. The new record (the contents of the variable “Data") are appended (added) to the temporary file here. By appending each new entry, the file contains the new records - before they are added to the main database. It is important to note, the temporary file is created by this module and removed by the main Database script. 8. If control reaches here from Step 7 it branches to Step 10; otherwise it continues at Step 9. 9. Creates the temporary data file "T:temp” and adds the current record to it. T:Temp is only valid while this program is running, see the description of Step 7 for more details. 10. Closes the IF…ELSE…ENDIF construct opened at Step 6. 11. Prints the prompt “Add another y/N” and pauses for user interaction. (It waits for you to enter something.) Note the ASK command sets the WARN flag if you enter Y and clears it otherwise. 12. Tests for the WARN condition. If you enter, Y at Step 8, control resumes at Step 13; anything else causes a branch to Step 14. 13. Transfers control to Step 20 where the script will start again. You may notice, this label is also used by Step 4 to achieve the same effect. 14. Control can only arrive here from Step 12 (Step 13 is an unconditional branch) and it continues directly at Step 15 because Y was not entered. 15. Displays a short message (split over two lines by the use of *n) just to keep the user informed. 16. Adds the new records to the start of the data file using the JOIN command. The AS keyword is used here to keep the syntax clear. Note the new file is also stored in T: because it isn’t legal to write to a file you are reading from. You may wonder at this stage why the records are kept in a temporary file. The reason is twofold. First, since T: is in RAM, a temporary list is faster to update than it would be if the write went directly to disk every time. Second, this also offers the chance to insert a “Get out” clause; such as “Are you sure?". This is demonstrated in the DelBlock module described below but not incorporated here. 17. Replaces the existing database with the one just created at Step 16. This command can fail if the disk is full and in this case, you should copy the complete database from RAM to a disk with enough room. As an exercise you might like to devise a fix for this eventuality. 18. The module completes here and calls the main program again. 19. Closes the IF…ELSE…ENDIF construct opened at Step 12. 20. This marks the restart point called at Steps 4 and 13. 21. Displays the entry prompt without a line feed - to give the illusion of an interactive prompt. 22. Calls the AddData module recursively in its interactive mode. Redirection to NIL: (>NIL) is used to suppress the messy command line. Listing 1. .key a1,a2,a3,a4,a5,a6,a7,a8,a9,aa,ab,ac,data/k 2. .def data "<a1> <a2> <a3> <a4> <a5> <a6> <a7> <a8> <a9> <aa> <ab> <ac>“ 3. if "<data>" EQ "" 4. skip AddOne 5. endif 6. if exists T:temp 7. echo »T:temp "<data>" 8. else 9. echo > T:temp "<data>" 10. endif 11. ask "*nAdd another y/N" 12. if warn 13. skip AddOne 14. else 15. echo "Adding new records*nPlease wait…" 16. join T:Temp S:Data AS T:tempdata 17. copy T:TempData S:Data 18. execute S:database 19. endif 20. LAB AddOne 21. echo "data: " noline 22. execute >NIL: s:AddData ?
DelBlock
Synopsis: [EXECUTE] DelBlock [Start=] [Number=] Template: start,number Path: S: Requires: VI.3+ See also: Database, Findata, AddData etc. Type: Script Brief: The delete data module for the Database Description The DelBlock module is used to remove specific records from the database and can be accessed from the command line in one of two ways. For instance, to delete record number three you would enter: Command: D 3 3 Fred Bloggs, 1 The Marketplace, Newton Abbott Delete records? However, you can delete a range of records by specifying the start and ending record numbers, viz: Command: D 2 4 2 Amiga Shopper, Future Publishing, BATH, Avon 3 Fred Bloggs, 1 The Marketplace, Newton Abbott 4 Dave Smith, Behind the Bike Sheds. Delete records? If you do not supply either value, DelBlock will prompt you for them automatically. Line-By-Line 1. This module only takes two parameters: START, the record number to be deleted; and NUMBER and optional parameter which will be the last record to be deleted. 2. This defaults the NUMBER variable to 1 if no value is supplied. 3. Similarly, if a starting record number is not supplied, this variable is initialised to the string SKIP. 4-5. Redefine < and > as { and }. 6. Test the start variable to see if some number was supplied. (A test is made for a string supplied as a default value at Step 3 to make the meaning clearer.) If the test passes, control continues at Step 7; otherwise it jumps to Step 8. 7. Control arrives here if no values were supplied and is sent directly to the re-start code, beginning at Step 39. 8. Close the IF…ENDIF construct opened at 6. (Control arrives here if a START value was supplied.) 9. Calculates how many records (lines actually) are going to be removed from the database and stores the result in the environmental variable “ThisMany". Note a LFORMAT string is used to suppress the LF character. 10. Adds 1 to the value ThisMany and stores it in a temporary variable. Note that interactive mode must be used for this example because one of the values is being retrieved from a file. This must be done to retain downward compatibility with AmigaDOS 1.3 11. Copies the temporary variable back to the proper variable. 12. Test if the value “ThisMany” is not less than or equal to zero. ("VAL NOT GT" is akin to BASIC’s ⇐.) If it is less than 1, control continues at Step 13. Note: Interactive mode is used again here because the value is being retrieved from a file. 13. Negative or zero values are not allowed for this value, so this line resets ThisMany to its lowest possible value. 14. Closes the IF…ENDIF construct opened at Step 12. 15. Raises the fail level slightly to prevent minor complaints from EDIT stopping the script in its tracks. 16. This is where it starts to get a little hirsute - so to make things a little simpler, let’s assume some values. Set START as 5 and TF1ISMANY as 4. Using these values, this line creates a file containing: 5n;p; and saves that as DelMacl 17. Similarly, this creates DelMac2 which looks like this: (?;n;) STOP 18. Now the module joins the two macro segments to the environmental variable and the macro takes shape like this: 5n;p; 4 (?; n;) STOP Translation 5n Go down five lines p Move back one line 4(?;n;) Display this line then move to the next one (four times) STOP Quit and return to the caller. This example also illustrates why it was necessary to suppress the line feed in "ThisMany". Otherwise, the macro would read: 5n;p; 4 (?;n;) STOP which is a subtle error and not easy to trace; pretty hairy stuff too, as 1 said. 19. Uses the EDIT macro on the database and creates a temporary file (T:DelRec) which contains a list of the records about to be deleted. The story does not end there though… 20. …because, if the record(s) were not found, EDIT returns with an ERROR condition - remember Step 15. This is tested for here, and if found control resumes at Step 21; otherwise it branches to Step 23. 21. Displays the error message… 22. …and jumps to the code at Step 37. 23. Control only gets here from Step 20 when the requested records are found. It continues at Step 24… 24. …which changes the text colour to the highlight… 25. …displays the list of records marked for the chop… 26. …and switches the highlight off again. 27. Closes the IF…ELSE…ENDIE opened at Step 20. 28. Pauses the script and gives the user a last chance to decide whether or not to delete the records. 29. Tests for the WARN condition - returned by ASK if “Y” is entered. If WARN is found control continues at Step 30; otherwise it jumps to Step 36. 30. Displays an information message. The start and end numbers are filled in when the script runs of course. 31. We’ve already met something like this before - at Step 16. This creates the same edit macro once more (although the original would probably do just as well). Assuming the previous values, DelMacl reads: 5n;p; 32. DelMac2 is simpler though, this just adds a single letter. d 33. Creates a new version of DelMac which now looks like this: 5n;p; 4d Which finds the first line (records) we are interested in and deletes that and the next three… 34. …here as temporary file… 35. …which replaces the original database here. 36. Closes the IF…END1F construct opened at Step 29. 37. Marks the skip point which is used by the error handler. Note, when EXECUTE is called, the failure level is reset back to 10 (crash on ERROR). This line is ignored if execution reaches here normally from Step 36. 38. Calls the main database module. 39. Marks the skip point called when the user has not entered any values - see Steps 6-7. 40. Displays the single line prompt… 41. …and executes the module in interactive mode so the values can be retrieved correctly. Listing 1. .key start.number 2. .def number 1 3. .def start SKIP 4. .bra { 5. .ket } 6. if "{start}" EQ "SKIP" 7. skip restart 8. endif 9. eval {number} - {start} to ENV:ThisMany lformat "%n" 10. eval >NIL: <env:ThisMany op "%n" ? =+ value2=1 to T:Tmp lformat 11 . copy T:Tmp to ENV:ThisMany 12. if >NIL: <ENV:ThisMany VAL NOT GT 1 ? 13. setenv ThisMany 1 14. 15. 16. 17. 18. 19. 20. 21 . 22. 23. 24. 25. 26. 27. 28. 29. 30. 31 . 32. 33. 34. 35. 36. 37. 38. 39. 40. 41 . endif failat 11 echo >T:DelMac1 "{start}n;p;" echo >T:DelMac2 "(?;n;)*nST0P" join T:DelMac1 ENV:ThisMany T:DelMac2 AS T:DelMac edit s:Data with T:DelMac ver=T:DelRec if error echo "Record(s) not found1' SKIP ReRun else echo "*e[33m" type T:DelRec echo "*e[31m" endif Ask "‘nDelete record(s) y/N?" if warn echo "Removing Records from: {start} to {number} *nPlease wait…" echo >T:DelMac1 "{start}n;p;" echo >T:DelMac2 "d“ join T:DelMad ENV:ThisMany T:DelMac2 AS T:DelMac edit s:data to T:DelData with T:DelMac copy T:DelData to S:Data endif LAB ReRun execute s:database LAB restart echo "Delete record #: " noline execute >NIL: s:DelBlock ?
DELF
Synopsis: DELF <file|pattern> Template: As DELETE Path: na Requires: V2+ See also: DELQ "type: Alias Brief: Short form for DELETE Definition: ALIAS DEL DELETE >NIL: [] FORCE Description: This alias deletes a list of files like DELETE, but does not report anything back to the console and will also delete protected files. Use this with extreme caution! Example: 1>DELF #? ALL
DEL
Synopsis: DEL <name or pattern> Template: na Path: na Requires: VI.3+ See also: Type: DELQ, DELF Alias Brief: Short name for DELETE Definition: Description: ALIAS DEL DELETE This alias is not included for padding (as it might seem to be) it has a very serious us. DEL is the MS-DOS command for DELETE and MS-DOS users will feel much more at home with AmigaDOS if the command works like this.
DOCTOR
Synopsis: Requires: See also: Type: Brief: Definition: DOCTOR VI.3 DISKDOC script Alias Open a new CLI window with DiskDoctor NEWCLI WINDOW CON :0/3/500/1 00/DiskDoc FROM S:DiskDoc Description: This command is listed here for the sake of completeness. It does nothing on its own apart from launching the DISKDOC script with a window. The clever bit is this: NEWCLI WINDOW CON:013/500/100/DiskDoc FROM S:DiskDoc This command performs several functions at once. • It opens a new CLI independent of the current Shell so DISKDOCTOR can be run from here. • It defines a new window for the CLI. In practice this is tucked away in the top left of the screen with enough room for most messages to be displayed. The idea is to stop it getting in the way -but you can position it to your own liking. For the sake of beginners only, here’s a brief explanation of what it means: WINDOW Device:X/Y/Width/Height/Name Device: CON: or NEWC0N: X: X position. Range 0 to 639 (Topaz 80) Y: Y position. Range 0 to 255 (PAL) or 0 to 199 (NTSC) Width: Width of window in pixels - practical range 50 to 639 Height: Height of window in pixels - practical range 50 to 255 • It starts DISKDOCTOR. The command is run from the script explained under DISKDOC using the FROM argument.
DRS
Synopsis: Template: Path: DRS Requires: V2.0+ See also: Type: VLS, DVS Alias Brief: Definition: Description: Check an assignment without removing it ALIAS DRS ASSIGN DIRS DRS is a quick way to list all the current directory assignments. This alias uses ASSIGN in such a way that the device and volumes lists are suppressed. DVS Synopsis: DVS Template: na Path: na Requires: V2+ See also: VLS, DLS Type: Alias Brief: Definition: Description: Check an assignment without removing it ALIAS DVS ASSIGN DEVS DVS displays the names of the current devices attached to the system using ASSIGN. The volumes and directory listings are suppressed. Find Data Synopsis: Template: Path: Requires: See also: Type: Brief: [EXECUTE] FindData [a 1 ]. . . [ad] [data=search string] al ,a2,a3,a4,a5,a6,a7,a8,a9,aa,ab,ac,ad,data/k S: VI.3+
Database
Script Find data module for the Database suite Line-By-Line 1. This key can collect up to 13 items of data, although it will usually only pick one or two. The first item found is dumped in <al>, the next in <a2> and so on. 2. Collects the data together in a single variable - <data>. 3. Tests for the presence of an argument string. You may wonder why the collected variable, <data> wasn’t used here and true enough the reason is not immediately apparent. In fact, <data> is a collection of variables separated by spaces. If no data was supplied, <data> will still contain spaces, thus fooling IF into thinking some data has in fact been supplied. This is not true of <al> because all the excess spaces are removed by EXECUTE’S command line parser. Or in other words, don’t worry, it just works that way. 4. Control reaches here if a blank command line was specified and immediately jumps to Step 19. 5. Closes the IF…END1F construct opened at Step 3. Control only reaches here when a command line search string was supplied. 6. This is a bit of extra redundancy. It prevents the script from crashing if the data file is missing for some reason. In this case control jumps to Step 1 1; ordinarily it proceeds to Step 7. 7. Using the search command, this attempts to locate the search string within the data file. (Note: the search string is surrounded by quotes to prevent a blank string confusing the parser.) If matching data is found, it is displayed with a corresponding record number and the WARN flag is cleared. If no matches are found, the WARN flag is set, which is tested… 8. …here. If the search string was found control jumps to Step 10. Otherwise it continues at Step 9 and… 9. …prints a short message to the effect the search string could not be found. The search string is enclosed in escaped quotes to show the exact contents of the search. 10. Closes the IF…END1F construct opened at Step 8. 11. Closes the IF…ENDIF construct opened at Step 6. 12. Is the jump point used when an empty command string is found at Step 3. If control reaches here from Step 11, the command is ignored. 13. Inserts a blank line (n) and waits for the user to enter Y or press Return. Entering Y <Return> sets the WARN flag; it is cleared otherwise. 14. If the user entered “Y”, control resumes at Step 15, otherwise it jumps to Step 16. 15. The user wants to execute another search, so control is passed to Step 19. 16. If control gets here from Step 15, it branches to Step 18; otherwise it continues at Step 17. 17. Control only reaches here if the user did not enter Y at step 14. In other words, they want to return back to the main program. 18. Closes the IF…ELSE…ENDIF construct opened at Step 14. 19. Marks the jump from Step 1 5. 20. Displays the command prompt. This is only displayed when a search string was not specified OR a successive search has been requested. 21. Calls the FindData script again recursively and places it in interactive mode. The command line argument string is sunk to NIL and not displayed. Listing 1. .key a1,a2,a3,a4,a5,a6,a7,a8,a9,aa,ab,ac,ad,data/k 2. ,def data "<a1> <a2> <a3> <a4> <a5> <a6> <a7> <a8> <a9> <aa> <ab> <ac> <ad>" 3. if "<a1>" EQ "" 4. skip FindOne 5. endif 6. if exists S:Data 7. search S:Data <data> 8. if warn 9. echo ""<data>*" not found" 10. endif 11. endif 12. LAB again 13. ask "*nSearch again y/N" 14. if warn 15. skip FindOne 16. else 17. execute S:database 18. endif 19. LAB FindOne 20. echo "search string: " noline 21. execute >NIL: srFindData ? Database Synopsis: Template: Path: Requires: See also: Type: Brief: [EXECUTE] Database S: V Findata, AddData, SortData etc Script The main menu module for the Database Description In the last few years digital diaries have sold in ever-increasing numbers (the idea for this script hit me late one night when my diary’s battery expired). Since you already have a computer you may as well use it, but “real" database programs are rarely cheap and usually too powerful for simple applications - such as a telephone book. The flat-file database described here is crude by commercial standards but boasts the following features: • Completely menu driven • Sort records on any record column • Search and display a record on any sub string • Database can be edited directly with any text editor (or ED) • Delete any record group • View any group of records • View entire database • Outputs to printer • Compatible with AmigaDOS 2 • Imports and exports to and from Superbase Quite impressive for a program written entirely in the machine’s DOS batch language 1 think you will agree. But even if you have a database, this application will show you how to control many parts of AmigaDOS in previously unexplored avenues. The entire program (actually it’s a series of modules) is much too large to explain in one chunk; instead, I’ve divided it over the several scripts as each module is added. All the scripts are compatible with AmigaDOS 2 and probably AmigaDOS 3, but they do not take advantage of extra facilities in the new systems: such as the /f find argument The Menu System The main part of AmigaDOS Database is its front-end menu system. This allows anyone with little or no knowledge of AmigaDOS to operate the system without fuss. All commands can be operated by selecting the first (highlighted) letter and pressing return “P" for print; “S" for sort and so on. However, many commands also take parameters. View for instance takes one or two parameters as the starting and finishing numbers. These can be supplied as part of the command line, for instance: Command: V 25 displays records 2 to 5 inclusive. However if a parameter is not supplied the module will prompt for it automatically. For instance: Command V View record #: The task of deciding which parameters are required is handled partly by the main menu module and partly by the view module. Database Menu Line-By-Line 1. This is the key to the main menu, although under normal circumstances you won’t even see it. Database is normally started without parameters so it displays its menu. Experienced users have the option of giving a command directly like this: 1>DATABASE P ; print database now -or- 1>DATABASE S 20 ; Sort database from column 20 The key breaks down into three sections. Command: The single letter command. d1…d9:Nine data elements (or arguments) Option: A reserved variable 2-3. Set the BRA and KET characters to { and ) respectively (because l happen to like them like that). 4. Sets the Option variable to the contents of dl …d9. This is reserved for the FindData and AddData modules described later. 5-7. This is provided as a time saver. Under normal circumstances when Database is launched, the Command variable will be empty, that is equal to "" and this causes the program to skip immediately to the menu starting at Step 41. 8. If the command variable equals “A" (add) control continues at Step 9, otherwise it continues at Step 11. 9. Control only reaches here if the Add selection has been made. This line deletes a temporary file created by the AddData module. Re-direction to NIL: is used to suppress the error message when the file does not exist. (This could be tested with IF EXISTS… but that’s just overkill.) 10. Calls the AddData module directly passing any automatic data entered by the user. (Automatic data is collected in the Option variable at Step 4). 11. Marks the end of the AddData control block. Control only reaches here if the test at Step 8 was not successful. 12. Opens the DelData block and tests for the delete option. If the key has been pressed, control resumes at Step 13: otherwise it branches to Step 14. 13. Calls the DelData module passing up to two parameters collected in Dl and D2. 14. Control only reaches here if the test at Step 12 was unsuccessful and continues directly at Step 15. 15-17. Open the ViewData block. This works just like DelData described above. 18-20. Open the FindData block. These work like AddData, passing any automatic data in the Option variable. 21-23. Open the ED block. The ED screen editor is opened. Note that ED is called directly within the script unlike most commands which execute a new script. 24-26. Open the SortData module. One optional automatic parameter is passed in Dl 27. Open the List by number module if “L” was passed as a command parameter. 28. Copies the entire database to a file in the Ram disk, but adds line numbers to each record. This is a good way to view and edit blank records. 29. Displays the temporary data file a screen at a time. 30. Terminates the List block. Control will continue from here when MORE exits. 31-33. Operate the PrintData block. Control does not resume at Step 33 unless the test at Step 31 is unsuccessful. 34-40. Looks after the Quit section, terminating the program nicely. The block 35-39 just stops users from accidentally exiting. If they enter N at the prompt, execution falls off the end of the program and the script runs itself again. 41-51. Display the menu. Several escape sequences are used here, viz: *e[I Tabulate *e[7m Inverse video *e[0m Normal video *e[33mForeground orange (blue in AmigaDOS 2) *e[31mForeground white 52. Displays the command prompt. Note the use of the NOLINE switch to suppress the automatic line feed character. 53. More trickery. EXECUTE is used to call the Database module and display its command line in interactive mode; which is also suppressed by the re-direction to NIL:. The result is you can enter commands at an invisible prompt! Listing 1. .key Command,d1,d2,d3,d4,d5,d6,d7,d8,d9,option/k 2. .bra { 3. .ket } 4. .def option "{d1} {d2} {d3} {d4} {d5> {d6} {d7} {d8} { d9 }" 5. if "{command}" EQ "" 6. SKIP menu 7. Endif 8. if "{command}" EQ "A" 9. delete >NIL: T:Temp 10. execute S:AddData {option} 11. endif 12. if "{command}" EQ "D" 13. execute S:DelBlock {d1} {d2} 14. endif 15. if "{command}" EQ "V" 16. execute S:ViewBlock {d1} {d2} 17. endif 18. if "{command}" EQ "F" 19. execute s:FindData {option} 20. endif 21. if “{command}'' EQ “E" 22. ED s:Data 23. endif 24. if "{command}" EQ "S" 25. execute S:SortData {d1} 26. endif 27. if "{command}11 EQ "L" 28. TYPE >RAM:data{} S:DATA OPT N 29. MORE RAM:data{} 30. endif 31. if “{command}" EQ "P" 32. execute S:PrintData 33. endif 34. if "{command}" EQ "Q" 35. ASK "Are you sure y/N?" 36. if warn 37. echo "Thanks for using AmigaDOS DataBase*nPlease come again…" 38. QUIT 39. 40. 41 . 42. 43. 44. 45. 46. 47. 48. 49. 50. 51 . 52. 53. endif endif LAB Menu echo "*e[33mAmigaD0S Database*n*e[I(c) 1992 Mark Smiddy*e[31m*n" echo "*e[7m*e[I(A)*e[0mdd a record" echo "*e[7m*e[I(D)*e[0melete records [Start @ #][End @ #)]" echo "*e[7m*e[I(V)*e[0miew records [Start @ #][End @ #] echo "*e[7m*e[I(F)*e[0mind a record [Search string]'' echo "*e[7m*e[I(E)*e[0mdit database directly" echo "*e[7m*e[I(S)*e[Omort database [Column #]" echo "*e[7m*e[I(L)*e[0mist entries by number" echo "*e[7m*e[I(P)*e[0mrint database" echo "*e[7m*e[I(Q)*e[0mUIT" echo "Command: " noline execute >NIL: s:database ?
PrintData
Synopsis: [EXECUTE] PrintData Template: None Path: S: Requires: VI.34- See also: Database, FindData, AddData, SortData etc Type: Script Brief: The printing module for the Database Description This script supports the main Database engine described under DATABASE and provides simple print facilities. It should not be executed directly. Line-by-line 1. Pauses the script and waits for you to press return. The WARN/OK return from ASK is not used at this point, but the command provides a useful pause. 2. Gives an opportunity to set the Printer preferences. If "Y” is entered before pressing <Return> a WARN condition is returned to AmigaDOS. 3. Checks the WARN condition set at Step 2. If the user wants to set the printer preferences, the WARN condition is active and control continues at Step 4; otherwise it jumps to Step 5. 4. Calls the System preferences. Note that if you are using AmigaDOS 2 or above, you should set the path to activate the PRINTER tool: usually “Prefs/Printer”. 5. Terminates the 1F…END1F construct opened at Step 3. 6. Creates a new file in the Ram disk starting with an opening line as follows: “AmigaDOS Database on:”. Note use of the NOLINE switch to prevent ECHO sending a newline character. The filename created depends on the current Shell number: appended automatically by the use of “{}”. 7. Adds the current date and time to the headline string in the “Title" file. 8. Creates a new file by joining the contents of the header string to the data file and copies the whole lot to a new file in RAM, “Printfile#". 9. Raises the default failure code to 11. This is support for Step 10 below. 10. Copies information to the printer device (PRT:) and prints it using the default settings. Note that PRT: is used in preference to PAR: to ensure the correct printer is used. This also ensures that the correct margins and other preferred modes are used. If the command fails because the printer is busy (or not connected) it returns an ERROR condition and error message. The message is absorbed to NIL: and never appears and the script is not halted because the failure level has been raised to 11. 11. Tests if the COPY command (in effect, print) failed. If this is the case, control continues at Step 12. If everything worked according to plan, control jumps to Step 13 - lucky for us. 12. Prints a helpful message. This technique (even though it’s stating the obvious) is preferable to just ignoring the fact, or simply crashing the script. 13. Terminates the IF...ENDIF construct opened at Step 11. 14. Re-sets the failure level back to its default, 10. 15. Re-starts the DATABASE main menu program. Listing 1. ASK "Position paper, ready printer and press Return" 2. ASK "Do you want to check/alter your printer setup?" 3. if warn 4. SYS:Prefs/preferences printer ; AmigaDOS 2 use Prefs/Printer 5. endif 6. ECHO >RAM:title{} "AmigaDOS Database on:" noline 7. DATE »RAM:title{} 8. JOIN RAM:title{} S:Data AS RAM:Printfile{} 9. FAILAT 11 10. COPY >NIL: RAM:PrintFile{} to PRT: 11. if error 12. echo "Your printer is not responding!nPlease check *"0n-Line" is on; paper loaded; and cable connected" 13. endif 14. FAILAT 10 15. Execute S:DataBase
SortData
Synopsis: [EXECUTE] SortData [Col = Column Template: Col Path: S: Requires: VI.3+ See also: Database, AddData, FindData etc Type: Script Brief: The sort module for the Database Description If this database were a commercial application (which it isn’t) you would expect to find indexes removing the need to sort data. However, since this is a database writen purely in AmigaDOS the need to sort data may arise, and therefore, that’s what this module is all about. Line-By-Line 1. Defines the key for this script. Only one argument is required - the start column for the sort. This is provided as an option: those of you setting up fixed width fields will be able to sort on a field using this. 2-3. Re define bra and ket to { and }. 4. Sets a default value of 1 to the starting column number. This is not absolutely necessary, but provided for example. 5. Displays the progress message - this is necessary to show something is going on when the module is executed from the main menu. 6. Raises the failure level above ERROR (10) in anticipation of the next command. 7. Temporarily raises the current stack to 16000 bytes. Redirection to NIL: is used to suppress any messages. There are a couple of things you might want to consider here: • The stack size determines how large a file can be sorted (the sort function is recursive and requires a lot of stack space). Stack overflow will result in the machine vanishing into the land of the Guru and this is unpleasant, although it will not affect the database’s security. • The amount of stack given should be OK on 512K or 1M machines, if you find the command fails (see below) reduce the amount or, if you need more stack, increase it. • It is possible to add another option to the command line here: STACK/K. You might want to do this as an experiment and optionally define the stack space when the sort option is selected from the main menu. 8. If the command at Step 7 fails because of low memory (“Insufficient free store” in AmigaSpeke) the ERROR flag is returned and control continues at Step 9, otherwise it jumps to Step 10. (This would normally stop the script in its tracks, but since the FailAt level has been increased to 1 1 this command can execute.) 9. Lets you know what’s happened and why the sort operation cannot progress. 10. If execution reaches here from Step 9 it jumps to Step 13; otherwise it continues below. 11. The sort is carried out by AmigaDOS using the options defined by the user. The sorted file is sent to data{} in the Ram disk. This could, just as easily, have been T:data{). 12. Assuming there were no cock-ups in the sort, the sorted file is copied from RAM to disk. (You may wish to add some extra safety measures here - such as viewing the sorted file with MORE before committing yourself). 13. Closes the 1F…ELSE…END1F construct opened at Step 8. 14. Resets the FailAt level back to its default setting of 10. 15. Resets the stack back to 4000 bytes. (You may want to change this if you usually work with a larger stack.) 16. Calls the main database module. Listing 1. . key Col 2. .bra { 3. .ket } 4. .def col 1 5. ECHO "Sorting*nPlease wait…" 6. FAILAT 11 7. STACK >NIL: 16000 8. if error 9. echo "Out of memory…* nCan ’ t sort, sorry" 10. else 11. SORT S:data RAM:data{$S} COLSTART={col} 12. COPY RAM:data{$$} S:data 13. endif 14. FAILAT 10 15. STACK 4000 16. execute s:database
ViewBlock
Synopsis: [EXECUTE] ViewBlock [start=] [number=] Template: start,number Path: S: Requires: VI. 34- See also: Database, DelBlock, FindData Type: Brief: Script The view data module for the Database Description This module is an integral part of the AmigaDOS database and is not usually called from Shell. Line-by-Line 1. The key for this script takes the two arguments representing the start record number and the number of records to display. 2. Ensures the module always displays at least one record by defaulting Number to 1. 3. Makes sure that if the module is called without correct arguments, the Start number contains something. 4-5: Re-set the bracket characters to { and }. 6. Tests if the Start variable contained something when the module was called. If it did, control jumps to Step 7; otherwise it jumps to Step 8. 7. Jumps to Step 30 and terminates the script normally. 8. Terminates the IF…ENDIF construct opened at Step 6. 9. Determines how many records will be printed and stores the result in the global variable “ThisMany”. 10. This 1.3 compatible operation increments the value in ThisMany by 1 and stores the result in T:Temp. AmigaDOS 2 users can replace this calculation and line 1 1 with the following: EVAL SThisMany + 1 TO ENV:ThisMany 11. Re sets the value in ThisMany. This line is not required if you have made the release 2 modification noted at Step 10. 12. In a 1.3 compatible fashion, this checks if the value in ThisMany is less than 1. Control continues at Step 13 if it is and Step 14 otherwise. The release 2 version of this line is as follows: if VAL SThisMany NOT GT 1 13. Sets the global variable ThisMany to 1. 14. Terminates the IF…ENDIF construct opened at Step 12. 15. Raises the failure level to 11. 16. Creates the first part of an EDIT macro which will step Start records in. If Start=3, this macro file will read: 3n;p; 17. Creates the second part of the EDIT macro. This file expands as: (?;n;) STOP 18. Joins the two macro files with the contents of ThisMany sandwiched in between. The final macro, ViewMac looks like this: 3n;p; 4 (?; n;) STOP 19. Extracts the required records from the data file sending the result to a file. You could send the result straight to screen if you prefer. 20. EDIT produces an ERROR condition if the line numbers -records in this application - did not exist in the file. ERROR is trapped by the raised fail level (Step 15). If this test proves false, control jumps to Step 23: otherwise it continues at 21. 21. Displays a useful error message. Hopefully, this was because you entered a dodgy Start or Number value. 22. Jumps straight to Step 28 and exits back to the main menu. 23. Control only gets here when the EDIT command has produced a workable file. 24. Changes the current console screen colours… 25. Displays the file. You might prefer to use MORE here. 26. Re sets the console colours. 27. Terminates the 1F…ELSE…ENDIF construct opened at Step 20. 28. Marks an entry point for error handling. 29. Re calls the main DATABASE program. 30. Marks an entry point for special handling. 31. Displays a prompt for the user to enter a number and… 32. …the module calls itself. This allows a number to be entered interactively. Listing 1. .key start,number 2. .def number 1 3. .def start SKIP 4. .bra { 5. .ket } 6. if "{start}" EQ "SKIP" 7. skip restart 8. endif 9. eval {number} - {start} to ENV:ThisMany lformat "%n" 10. eval >NIL: <env:ThisMany op=+ value2=1 to T:Tmp lformat "hn“ ? 11. copy T:Tmp to ENV:ThisMany 12. if >NIL: <ENV:ThisMany VAL NOT GT 1 ? 13. setenv ThisMany 1 14. endif 15. failat 11 16. echo >T:ViewMac1 "{start}n;p;" 17. echo >T:ViewMac2 "(?;n;)*nST0P" 18. join T:ViewMac1 ENV:ThisMany T:ViewMac2 AS T:ViewMac 19. edit s:Data with T:ViewMac ver=T:ViewRec 20. if error 21. echo "Record(s) not found" 22. SKIP ReRun 23. else 24. echo "*e[33m" 25. type T:ViewRec 26. echo "*e[31m" 27. endif 28. LAB ReRun 29. execute s:DATABASE 30. LAB restart 31. echo “View record #: " noline 32. execute >NIL: s:ViewBlock ?
DCopy
Synopsis: [EXECUTE] DCOPY<[pat=]dir|pattern> [SINCE=<date>] [UPTO=<date>] Template: .key pat/a,dest/a,opt 1 ,since/k,upto/k Path: S: Requires: VI.3+ See also: CCOPY Type: Script Brief: To copy files (optionally by date) without failing Description This is a modified version of the DEL script. It will not overwrite “delete protected" files in the destination directory, but it will not stop either. An extra couple of lines have been added to ensure that the destination directory exists - if not, the directory is created -then it calls itself recursively. Recursion is not actually required for this example but this shows simply how the method works. Line-by-Line 1-3. Sets the key and other script parameters. Note that source and destination parameters are required here. 4. Sets the default “since" date to the earliest supported by the Amiga’s RTC. 5. Sets the default "upto” date to the current date. This should be used with care if you don’t have a RTC (or it has not been programmed correctly). A very late date such as Ol-Jan-99 might be a better idea. 6. This tests to see if the destination directory actually exists for this invocation. If it does the script carries on as normal, if not it jumps to Step 10. 7. Creates a file "copy" using the parameters for date and time defined by the user. This file is a script which contains the path of all files listed and a copy command for each one. Typically, a few lines of the script might look like this: COPY "Workbenchl.3:C/Dir" TO RAM: Clone COPY "Workbenchl.3:C/Copy" TO RAM: Clone COPY "Workbenchl.3:C/List" TO RAM: Clone 8. Raises the failure level to above that returned by all AmigaDOS commands. The script is now unstoppable. DCopy 9. Executes the list of commands and effectively performs the copy operation. You might like to make COPY resident before this point and remove it afterwards to speed the operation. 10. If control reached this point from Step 9, it jumps to the end of the script; otherwise it continues at Step 11. During a recursive phase this provides the way out. 11. This sets the fail level to WARN. There’s a reason for this… 12. …because if the destination directory cannot be created the script must exit. If MAKED1R cannot create the directory because “object not found” it returns ERROR (vl.3) or WARN (v2). This could be tested but it’s faster to crash the script. 13. This calls the script a second time recursively with the original parameters. There are other ways to do this (loops for instance) but this one best serves the purpose of the demonstration. (Incidentally, recursion is the only way to jump backwards in AmigaDOS 1.2!) 14. Closes the IF…ELSE…ENDIF construct opened at Step 6. ENDIF is required for correct recursion. Listing 1. .key pat/a,dest/a,opt1,since/k,upto/k 2. .bra { 3. .ket } 4. .def since 01-Jan-78 5. .def upto Today 6. if exists {dest} 7. list >T:copy{$$} {pat} since={since} upto={upto} FILES LFORMAT "COPY "%s%s" TO {dest} {opt1}" 8. failat 21 9. execute T:copy{S$} 10. else 11. failat 5 12. makedir {dest} 13. execute DCOPY {pat} {dest} {opt1} since={since} upto={upto} 14. endif
DEL
Synopsis: Template: Path: Requires: See also: Type: Brief:
pat/a,optl ,opt2,opt3,upto/k,since/k S: VI.3+ Script Delete files without failing. Optionally window to a date. Description This is a very simple script which gets around one of those annoying little problems - pattern matched deletes which fail when they encounter a protected file or a directory. Much the same effect is possible using SPAT incidentally, but this script is designed for the job and does it better. Also, this script adds the option of a “date windowed” delete. Refer to LIST to see how to use the SINCE and UPTO options. With little modification this script could be used to copy files by date in the same way that they are deleted. Line by line 1: The key used here is a simple one because this is, in essence, a simple script. The pattern is required and can be any valid AmigaDOS wildcard. Of the other three switches, only “optl” is usable in vl.3 and 1.3.2. This is the QUIET switch. Note: The order of the arguments is important it could be fixed later in the script - but this is a simple example. UPTO and SINCE work just like normal keywords allowing you to window the delete. 2-3. Re define < and > to { and }. 4. Sets the default starting date to 1st January 1978 - no files can exist before this date. This keyword must have a default value since it is a keyword in LIST. 5. Sets the other part of the window to Today’s date. Like SINCE, this is also treated as a keyword by LIST so it must have some value. It is possible for files to exist in the future (if you don’t have a real time clock) these should either be deleted manually or the option removed from the script. Better still, get a RAM expansion with a clock. 6. This is the crucial part in this script. LIST creates a file in T: called DELETE<n> where n is the current CLI number, making it unique to this invocation. The pattern used for this list is retrieved from the pattern the user entered. SINCE and UPTO provide a date window for the files. If not supplied, all files are listed. Note: The files switch prevents LIST from displaying directories. The escaped quotes surrounding %S%S allow spaces and other odd characters in filenames and stops them interfering with the command line. The output from this command could look something like this: DELETE "RAM:Tempi" DELETE "RAM:Test" DELETE "RAM:Space file" DELETE "RAM:Work" • In AmigaDOS 2 and above, the ALL and FORCE switches come into effect here. These must be used with great care - you have been warned! 7. Sets the failure level to 21. Generally a bit risky - but it’s OK. 8. Executes the newly created script - deleting the files one-by-one. If the files are protected against deletion the failure level of 21 prevents the script from stopping. With little modification this script could be used to delete empty directories or copy files by date in the same way that they are deleted. The copy script needs some extra work -which we’ll see later on, for now, here is the amended line to delete directories: Listing 1. .key pat/a,opt1,opt2,opt3,upto/k,since/k 2. .bra { 3. .ket } 4. .def since 01-Jan-78 5. .def upto Today 6. LIST >T:delete{} {pat} {opt2} since={since} upto={upto}files lformat "DELETE *"%s%s*" {opt1} {opt3}" 7. FAILAT 21 8. EXECUTE T:delete{}
DIRS
Synopsis: [EXECUTE] DIRS Template: none Path: S: Requires: VI.3+ See also: VOLS Type: Brief: Script List just the current directory assignments Line-By-Line 1. Sends the current assignment list to a temporary file. Note the use of <> to prevent multi-tasking clashes. 2. Prints a heading of what is about to happen... 3. ...and displays (by searching for) all those names with two spaces. This is only true for the current logical directory assignments, FONTS:, ENVARC: and so on. Listing 1. ASSIGN >T:temp<> 2. ECHO "Directories:” 3. SEARCH T:temp<$$> " " nonum ; note "two spaces" no DiskDoc Synopsis: [EXECUTE] DISKDOC Template: none Path: S: Requires: VI.3 - 1.3.3 See also: DOCTOR (Alias) Type: Script/Alias combined Brief: Multi-task DISKDOCTOR in the background Description Diskdoctor cannot normally be run in the background, but there is more than one way to skin a command. This solution uses two techniques - an alias and a script. The script will do the work of running DISKDOCTOR and the alias will run the script. Add this line to the Shell-startup script (using ED S:Shell-startup). A detailed explanation appears in the definition of this ALIAS proper. ALIAS DOCTOR NEWCLI WINDOW C0N:0/3/500/100/DiskDoc FROM S:DiskDoc Now close the Shell and re-open it to ensure the alias is defined and type DOCTOR to get started. Line-By-Line 1 Raises the failure level to 21 - beyond anything generated by AmigaDOS commands. In other words, this script cannot be stopped by any errors! 2 Executes DISKDOCTOR and starts processing drive 0 - you can change this to any drive you require. This script cannot take parameters because it is executed specially from the alias. 3 Checks if DISKDOCTOR generated a serious error. (For instance, if there is no disk in the target drive - dfO: in this case.) Normally the script would grind to a halt at this point and leave you at the CLI prompt but this has already been prevented at line 1. Since we have turned normal error handling off, we must deal with this and that’s what this does. If DISKDOCTOR exits normally, control skips to line 6, if not it passes to 4… 4 …where the error message is printed. Note the ASK command is used here: it prints the error message and waits for the user to react - giving them time to study what has happened. 5 This line shuts the CL1 down and closes its window. This is the reason for pausing at line 4 - if an error had occurred you might not get to see it. 6 Terminates the IF…ENDIF construct opened at 3. This is used as a marker by the IF command but it must be present for the script to handle errors correctly. Control only gets here if DISKDOCTOR terminates normally. 7 This behaves like line 4, giving the user chance to react to any warnings or messages generated by DISKDOCTOR before the CLI window is finally closed… 8 …here. Listing 1 FAILAT 21 2 DISKDOCTOR dfO: 3 IF fail 4 ASK "A serious error occurred! Press Return to exit'' 5 ENDCLI 6 ENDIF 7 ASK "Press Return to exit" 8 ENDCLI
DRIVES
Synopsis: [EXECUTE] DRIVES Template: none Path: S: Requires: VI.2+ See also: VOLS Type: Script Brief: Show drives with mounted disks Description AmigaDOS’s INFO command generates a lot of information. There are times when much of this is redundant as you just need to know about the mounted disks and how much space you have left on them. This script selectively locates the drives with mounted disks and lists them. Extraneous information on mounted volumes and empty drives is ignored. Line-By-Line 1. This is a dummy key it does not affect the function of the script from a user’s point of view. It must be provided although we are going to use {} (the current CLI number) -this allows the script to multi-task correctly. When EXECUTE reads a .KEY argument it copies the script into a temporary directory either :T (T on the current disk) or T: (the temporary assignment - usually RAM:T). Then it expands variables enclosed by bra "<” and ket “>" characters. That is, it replaces the variables with what the user entered. <> is a special case - it expands to the calling CLI number. 2-3. Change the bracket characters to { and }. 4. Asks AmigaDOS to give information (see INFO) on all the current drives. That includes the external disks, hard drive partitions and mounted disks. The output from this command is sent to a file: RAM:t{} using output re-direction. 5: This is the all important line. Used here to give experts a clue as to what’s coming and beginners a reason for the command. The INFO command spits out a lot of information, but every drive with a disk present is listed with nn% full. SEARCH homes in on this and, therefore, only lists the drives with a disk inserted. Similar code can be used to list mounted disks. All you have to do is replace with This causes SEARCH to list the disks showing [Mounted]. Similarly, you could add a line 6 to perform this function. If you are using AmigaDOS1.3 or higher, you can add the NONUM switch to the end of the line. This suppresses the line numbers and makes the output more usable. Listing 1 .key dummy 2 .bra { 3 .ket } 4 info >ram:t{} 5 search ram:t{$$} "V Synopsis: EDS Template: na Path: na Requires: VI.3+ See also: FRED Type: Alias Brief: Edit the startup-sequence Definition: ALIAS EDS ED S:Startup-sequence Description: How many times have you mis-typed ED S:Startup-sequence? This has to be one of the most difficult sequences of letters to get your pinkies round yet invented, so here’s an ALIAS to get you going quickly. Example: 1>EDS ; edit the startup-sequence
EDU
Synopsis: EDU Template: na Path: na Requires: V2+ See also: FREUD Type: Alias Brief: Edit the startup-sequence Definition: ALIAS EDU ED S:User-Startup Description: This little alias is a modification of the EDS and is used to call ED for the User-startup. Example: 1>EDU ; edit the user-startup EggTimer Synopsis: [EXECUTE] EggTimer [SOFT | MEDIUM | HARD]
Template: soft/s,med/s,hard/s,time/k Path: S: Requires: V2+ See also: Pest Type: Brief: Script Time a hard-boiled egg! Description This script was devised as a bit of light-relief in a weak moment. As it turns out, it demonstrates some interesting problems: particularly how to use ”/S” in scripts. The program forms the basis for the Pest idea (although the code is very different). No checking is performed to see if you have entered more than one switch (say SOFT and HARD) or that the time is some ridiculous value. You might like to try this for yourself. Line-By-Line 1. Defines the argument template. Notice how this looks just like a template for a real AmigaDOS command - it’s processed in a very similar way too. In the synopsis described above the switch options: Soft, Med and Hard are shown as a combined argument - but only one of these should be supplied at any time. It is important to note the AmigaDOS parser will not check the presence of too many or too few switches. Such error checking will usually be performed in the script - it has not been implemented here to keep the listing simple. 2. Changes the default opening angle bracket character to “{“. 3. As Step 2 for the closing bracket. 4-6. Adds EVAL, WAIT and TYPE to the resident list. This pre-loads the commands from disk and makes them available in system RAM where they can be executed faster. This technique is also handy when a disk-based command is used more than once in a script. 7. This checks if the user has entered a time via the time keyword. The exact position of this conditional test is not crucial although it should be placed early in the script. The exact workings of this line are a little complex, so let’s examine them. Assume you had entered a command line thus: 1>EggTimer Time="3 mins" The keyword Time absorbs the argument ‘3 mins’ (quotes are required to ensure all the text is taken in). This process “sets" the internal script variable, time to ‘3 mins’. This can be picked up at any time by enclosing the name in special brackets - set as { and } in this script. AmigaDOS reads this line as: IF "3 mins" NOT EQ "" Similarly, if you do not enter a time keyword, AmigaDOS reads this: IF "" NOT EQ "" This statement checks if the expression on the left does not match the expression on the right - it seems a little backward at first, but it will all become clear shortly. If the test passes (first example - "3 mins” does not equal “") the script continues at the next line. If the test fails, it jumps to the closest ENDIF - at Step 9 in this case. 8. Sets a local environmental variable to the value defined by the keyword. Remember, this line is only called if a keyword and argument for “time” is supplied. Variables are like temporary containers. Local variables are held in system memory making them convenient for private storage. It is not possible to alter a local variable directly though and this must be borne in mind when deciding which type to use. 9. Closes the IF…ENDIF construct opened at Step 7. Put simply, this "command" acts as a place marker to inform AmigaDOS where to jump to when the "IF" test fails. 10-13. These lines check for the presence of the soft option on the command line (no pun intended). The position of this test is crucial in case you supply more than one switch. As programmed the switches have priority over the keyword and of those, the hard option is preferred. If soft has been supplied the variable "time" is set to three minutes - you can set this lower if you like runnier eggs or higher if you have oversize ones. An ostrich egg for instance, will take a lot longer and a much larger pan. 14-17: Sets the time for an average cooked egg. Typically this should be enough for a nicely done size three egg with a slightly hardened yolk. Adjust this timing to your own taste. 18-21: Like the previous brace of options, this sets the timing for a hard boiled egg - probably enough to kill off any trace of Salmonella. This switch overrides all others if it is supplied. 22. This pauses the script and waits for the <Return> or <Enter> key to be pressed. This command is normally used to check for a yes or no answer, but it does this job just as easily. The “*n" enclosed in the text forces the Amiga to print a line break so the text appears split over two lines. 23. Waits for the time determined by the contents of the variable time. If, for instance, you had asked for a soft boiled egg, AmigaDOS reads this as: WAIT 3 mins You can insert the contents of any user-defined (environmental) variable by prefixing its name with a dollar symbol as shown here. The dollar symbol is a special variable operator and is not affected by the “.DOLLAR” operator. Note: if a badly formed command line is used, the WAIT statement will kill the script dead in its tracks. This can be avoided - but is too bothersome to warrant inclusion here. 24. This line is actually simpler than it looks and uses one of those little tricks of the trade. EVAL is generally thought of as being a mere calculator, although it is capable of much more than that. This line splits into two distinct parts: EVAL >env:bleepz 7 This calls the command and makes it write a global environmental variable. The variable’s name is taken from the text “bleep” plus the number of the Shell process executing the script. If the process was say 2, AmigaDOS reads the line as: EVAL>env:bleep 27. In this form, this would usually send a text string to the variable - just like ECHO. However, the second part of the command does something special: lformat "Dinner’s up… %c" This defines the output string as a message plus a nonprinting character code - 7. In ASCII this code is called “BELL" and is used to flash the screen, or more usually, sound the terminal bell. (The screen flash is a peculiarity of the Amiga: under Workbench 3 you can change the simple bleep to a sampled sound.) In other words, when this variable is displayed the message will appear and the screen will flash. 25. Sets a global variable “count” to 10. Note how “{}” is used to attach the process number? This makes the name unique so avoiding clashes if it is executed from several Shells at once. The actual value determines how many loops will be made later on. 26. Marks the current position in the script. 27. Prints the message described at Step 24 and flashes the screen. 28. Decrements the counter variable, count. Expanded this line might read: eval 10 -1 to env:count2 therefore, the variable “count2" receives the result of 9. 29. Checks if the value of “count2" is equal to zero. If it is (TRUE) control continues at Step 30 otherwise it jumps to the next ENDIF at Step 31. 30. The script reaches this point when the counter has reached zero and jumps to Step 33. 31. Terminates the IF...ENDIF construct formed at Step 29. 32. Jumps backwards to Step 26 - the label “loop". Backward jumps are quite slow because the script starts from the beginning and works down looking for the label. Generally these are placed at the start of a script wherever possible, but it makes little difference here. 33. Marks the bail out point for the SKIP command defined at Step 29. 34-36. Removes the resident commands from the system list and frees up memory. This must be done otherwise each successive invocation of the script will add more copies of the commands and waste memory. Listings 1. .key soft/s,med/s,hard/s,time/k 2. .bra { 3. .ket } 4. Resident c:Eval add 5. Resident c:Wait add 6. Resident c:Type add 7. if time NOT EQ "" 8. set time "{time}" 9. endif 10. if {soft} EQ "soft" 11. set time "3 mins" 12. echo "Computing for soft boiled egg" 13. endif 14. if {med} EQ "med" 15. set time “4 mins 30 secs" 16. echo "Computing for medium boiled egg” 17. endif 18. if {hard} EQ "hard" 19. set time "6 mins" 20. echo "Computing for hard boiled egg" 21. endif 22. ask "Place egg in boling water*nThen press <Return>" 23. wait $time 24. eval 7 lformat "Dinner1s up... %c" T0=t:bleep{} 25. setenv count 10 26. lab loop 27. type t:bleep{$$} 28. eval >NIL: $count-1 to env:count 29. if val Scount eq 0 30. skip end 31. endif 32. skip loop back 33. lab end 34. Resident c:Eval remove 35. Resident c:Wait remove 36. Resident c:Type remove
EMove
Synopsis: [EXECUTE] EMove<[from=] sou rce> <[to=]destination> Template: from/a,to/a Path: S: Requires: VI.2+ See also: Type: Script Brief: The Eclectic MOVE script Description RENAME cannot be used to move files or directories across disks -but COPY can. However, using copy it is necessary to remove the source file after copying, so RENAME is better used on the same disk. This script solves the problems of remembering which command(s) to use by doing it all for you automatically! You use EMove as you might use COPY/DELETE or RENAME. However the script does the hard work of deciding which to use for you. In fact, it attempts to use RENAME first, then if that doesn’t work goes on to use the more longwinded version for moving between devices. The PROTECT part is optional in this script, it just makes doubly sure the source file is removed. This example moves a file in RAM:C to DFO:C-Backups 1>EM0VE RAM:C/Myfile.C DFO:C-Backups Under AmigaDOS 1.3+ you can use pattern matching for this function - the S bit must be set in the MOVE script for this to work: 1>SPAT EMOVE RAM:#? DFO:RAM-Backups In AmigaDOS 2 there is no need to use SPAT since all the functions can use pattern matching anyway. The example above becomes: 1>EMOVE RAM:#? DFO:RAM-Backups Line-By-Line 1-3. Defines the key and sets bra and ket to { and }. Note that source and destination arguments are required here. 4. Provides a simple progress message confirming what the script is up to. 5. Sets the fail level to indestructible. 6. Attempts to move the object using RENAME. This may or may not work depending on where the destination is. RENAME returns EAR. if you attempt a rename across devices; returns OK otherwise. 7. Tests if RENAME failed. If it did, control continues at Step 8; otherwise it jumps to Step 12 (and out of the script since the operation was successful). 8. Resets the failure level back to its default. 9. Performs a simple COPY operation. Redirection to NIL: is used here for the sake of users with 1.2. Later versions can make use of the QUIET switch instead which is more appropriate, in case errors are reported: copy "{from}" ''{to}" QUIET 10. Makes the source object deletable. This isn’t strictly necessary, but it might be a good idea. 11. Deletes the source object. From AmigaDOS 2, you can omit Step 10 and re-write this line as follows: DELETE "{from}" FORCE 12. Marks the exit point for this script. Listing 1 . .key from/a,to/a 2. .bra { 3. .ket } 4. echo "Moving from {from} to {to}" 5. failat 21 6. rename >NIL: {from} TO {to} 7. if fail 8. failat 10 9. copy >NIL: "{from}" "{to}" 10. protect >NIL: {from} +d ; This is optional 11. delete >NIL: "{from}" 12. endif
ENABLE
Synopsis: [EXECUTE] ENABLE Template: none Path: S: Requires: VI.3+ See also: Li st De l Type: Script Brief: Enable access ListDel (a script presented later) Description This is just about as short as scripts get - so much so, it could have been an alias. However, it does have a use - as a support routine for a script later in this book. The “jammer” variable prevents access to a potentially dangerous script. Unless this command has been issued, the script refuses to run… Line-by-Line 1. Sets the environmental variable "jammer” to the string OFF. Listing 1. SETENV JAMMER OFF EX Synopsis: Template: Path: EX <Directory> na na Requires: See also: Type: Alias Check an assignment without removing it VI.3+ Brief: Definition: ALIAS EX ASSIGN []: EXISTS Description: This short alias is a very useful (and quick way) to check an assignment. Given the assigned name without the it checks if the assignment exists and displays it. Example: 1>EX FONTS FONTS: Workbench3.0:Fonts FACTOR Synopsis: [EXECUTE] FACTOR <[n=]n> [[result=]private] Template: n,result Path: S: Requires: VI.3.2+ See also: Type: Script Brief: To calculate factorials in the range 1..12 Description This is one of those scripts which you don’t really need. The reason that it’s here is because it introduces some more of those clever little tricks AmigaDOS is capable of - with a little imagination almost anything is possible. It’s also an excellent excuse to use recursion to solve a tricky little problem. Factorials, for those who didn’t do too well in mathematics, are a sequence of numbers. The factorial of a number is calculated by multiplying together all the whole numbers from one up to the number concerned. Factorials can only be obtained for positive, whole numbers. The exclamation mark is used by mathematicians to indicate a factorial - probably because they result in huge numbers! For instance, factorial 8: 8! = 8*7*6*5*4*3*2*1 = 40320 You can calculate this directly using the EVAL command in vl.3.2+: 1>EVAL 8*7*6*5*4*3*2*1 40320 it’s much easier to type: 1>FACT0R 8 40320 This script has not been designed to multi-task to keep things clearer: this script uses some tough concepts to get the effect. Once these are understood, they act as a gateway to some very exciting script programming… Line-By-Line 1. The key for this script is an unusual one because, although the user only enters a single parameter, a second parameter is available. This is used during recursion as an internal variable; and actually ends being the result when the script “unwinds”! 2-3. Guess what, sets the bracket characters to { and }. 4. This sets the default value of the result to the value entered by the user. This has two effects: first, if the user requested factorial 1 (1!) the script exits immediately. Since 1! is 1, this forces the correct result. More importantly, for values of three and above the starting value of the result required by this script is the initial value of the required factorial. You’ll see why at Step 6. 5. Recursive scripts must have an exit point - otherwise they will keep on looping until something interrupts them or the machine crashes. The latter is far more likely! This tests for a factorial value of N = 1 and forces an exit if this condition has occurred. Unless the user has entered 1, this must be calculated in the script… 6. …here. One is subtracted from the current value of N and sent to the file T:N. A LFORMAT has been used to create a special format for this file. This is required later on in the script when the recursion takes place and will be covered in more detail then. All you need to know now is if the result of the calculation was five, the output file would read: N=5. 7. This is where things start to get a little hairy - so we’ll break this line into bite sized chunks. (Or should that be “byte sized Hunks…") eval {result} * ({n}-1) to t:fact Takes the current expanded value of "result” and multiplies it by "n-1". Imagine - result=60 and n=3. This is equivalent to: eval 60 * (3-1) to t:fact The result of this calculation (120 in this case) is directed to the file T:FACT using the LFORMAT string which follows… lformat ".k i*n.bra (*n.ket )*n At this point you may have noticed what is about to happen -then again you may not. Don’t worry too much if this seems complex - it is! That’s what 1 mean when 1 say “Experts are not born, they are hewn from the bedrock of effort.” This line writes a standard script header in the form: .k i . bra ( . ket ) You’ve already met something like this in an earlier example (ListDel) so if you don’t remember go back to it now - you’ll need to understand that example if you are going to understand this one! This header is being generated by EVAL, which is unusual but necessary. EXECUTE <t:n >nil: factor result=%n ?*n" This is the second part of the LFORMAT. It generates the script main part. The header is just for support. To understand how the resultant script is going to work, it’s necessary to examine a possible case. We’ll maintain the assumption that the result of the calculation was 120. The completed file will look like this: .k i . bra ( . ket ) EXECUTE <t:n >nil: factor result=120 ? The key “i” is just a dummy, but it must be there for the script to work. The reason for ( and ) brackets has already been explained. The clever bit is in the EXECUTE. This calls the script which generated it - double-recursion. Adding to the confusion only one value "result” is passed directly. The other value is retrieved from the file "t:n". That’s the reason why "t:n” was generated with the N=%n format. In AmigaDOS 2 this is far easier to achieve, as we’ll see later. 8. This executes the file just created by EVAL in 7, and starts the recursion process. 9. If the value passed to “n” from the main command line equals 1 control passes here… 10. …and the final result - also passed through the command line - is printed. The script unwinds itself after this point. Changes for AmigaDOS 2 If that lot got your brain waving the white flag don’t worry -imagine what it was like to write! In fact - it’s easier than it looks once you get a hang of the basics. This script can be made a lot simpler if you have AmigaDOS 2 because there’s no need to use interactive mode in the EVAL created script. Here are the amended lines and how they work: 4. The only changes to this line are the use of a real environmental variable rather than the T: assignment. Also the lformat string has been removed because it is not required: eval {n} - 1 to env:n 5. This line is also far simpler. It still creates a script, but now the “n" variable which had to be expanded interactively, is expanded automatically by AmigaDOS. This removes the need for mucking around with dummy keys and bracket characters: eval {result} * ({n}-1) to t:fact lformat “execute factor2 n=$n result=%n*n" Listing 1. .key n,result 2. . bra { 3. . ket } 4. .def result {n} 5. if val "{n}“ not EQ "1“ 6. eval {n} - 1 to t:n lformat "n=ssn*n" 7. eval {result} * (*n.ket)*nEXECUTE ({n}-1) to t:fact lformat ".k i*n.bra <t:n >nil: factor result=%n ?*n" 8. execute t:fact 9. else 10. echo "{result}" 11 . endif Synopsis: Template: Path: Requires: See also: Type: Brief: Description [EXECUTE] FANCYLIST <[dir=]dir> [pat=<pattern>] dir/a,pat/k S: VI.3+ Script To enhance LIST and ListAll by adding pattern matching In essence, this is an upgrade of the ListAll script shown elsewhere - but this has a far more attractive display. It searches all the directories on a disk just like the other examples, but adds the possibility of matching directories and files to different patterns. If no files match, this script tells you. This script does not do anything new, so I haven’t provided a description. You may like to discover its workings for yourself -refer back to the previous examples if you need any guidance. As an exercise you may like to add some of the extra features supported by LIST too. Listing 1. .key dir/a,pat/k 2. .bra { 3. .ket } 4. .def pat #? 5. list "{dir}" pat={pat} files to ram:Fancy{} 6. echo "*nDirectory:*e[33m{dir}*e[31m" noline 7. search ram:Fancy{} nonum 8. if warn 9. echo "No files match pattern {pat}" 10. endif 11. list >T:L{} {dir} dirs lformat "execute s:FancyList *"%s%s*" pat={pat}“ 12. execute T:L{}
FCD
Synopsis: [EXECUTE] FCD [[number=] # | dir | pat] Template: number Path: S: Requires: V2+ See also: RCD Type: Brief: Script Store recent directory changes and make them menus Description This command is very useful if you have a hard disk. It stores a list of the last ten directory changes to disk and allows you to pick one by selecting it from a numbered menu. Every path feature available to CD, including patterns, may be used. The command line is sensitive to arguments so that the script can completely replace CD (using an ALIAS) if you prefer. This command is very similar to RCD and is fully compatible with it. Several modes are available: • • Called without arguments. The script shows the current list and prompts you to interactively select an existing entry, load or save the list or enter a new directory. Note: you can enter LOAD or SAVE at this prompt. Example: 1 >FCD 1. "Workbench3.0" 2. "Workbench3.0:Fonts" 3. "Apps:" 4. "Workbench3.0:Fonts" 5. "Workbench3.0:Devs/Keymaps" Enter directory or pick a number, any number: • Called with a new directory path: FCD selects the directory (if available) and adds its full path to the menu. (The oldest directory is removed.) Example: 1>FCD SYS: • Called with a number from the directory menu. The directory is selected from the list and changed. Example: 1>FCD 3 FCD Menu Line-by-Line 1-3. Defines the template as “number" and the angle brackets as braces. It’s important you don’t change the template name since it is used in a recursive call. 4. Checks if some argument has been supplied. If not, control continues at Step 5; otherwise it jumps to Step 14. 5. Checks if the FCD preferences file (CDS) exists in the current S: assignment. If not, control moves to Step 10; otherwise it continues at Step 6 where… 6. The contents of the preferences file is listed with line numbers: this generates the menu. 7. Displays the main part of the interactive prompt. 8. Calls FCD recursively with interactive mode. This finishes the prompt with “number:". Note also, if you change the name of this script, you must also change this call. 9. Provides an easy exit for the script when it unwinds the recursion. Control transfers to Step 33. 10. If control gets here from Step 5, it continues at Step 11; otherwise it continues to Step 13. 11. Displays a progress message/warning. 12. Creates the CDS file by echoing the current directory. Note that automatic insertion ( command ) is used here so the directory can be enclosed in quotes. This protects CD against spaces in the directory name. 13. Terminates the IF…ELSE…ENDIF construct opened at Step 5. 14. Terminates the IF…ENDIF construct opened at Step 4. 15. Checks if the value of the entry made for number was 0. This is the case if a text entry - a directory path - was made. If text was entered, control continues at Step 16; otherwise it jumps to Step 22. 16. Attempts to set the new directory. If this command fails because the directory cannot be found (or more than one directory matches, for patterns) the script stops. Normally, the directory is made current. 17. Creates a temporary file with the new current directory name enclosed in quotes. 18. Joins the new current directory to the existing list and saves the resulting file as t:CD0#. 19. Creates a simple edit macro thus: 9n Move down nine lines (to line 10). d Delete the current line. 20. Uses the macro created at Step 19 to hack off the last entry in the file. Note if there are less than ten entries (directory paths) in the file, this macro has no effect. This macro therefore, only trims off the oldest entries. Changing the line count at Step 19 affects how many lines are stored in history. More than about 25 is getting silly and less than 3 is pointless. (If you increase this number, you will have to make changes later in the script too.) 21. Replaces the old directory list with the new one. 22. If control reaches here from Step 21, it branches to Step 32; otherwise it continues at Step 23. 23. Subtracts 1 from the menu entry and stores the result in the global, Usr#. 24. Tests if the value of Usr# is less than 1 and if it is, control continues at Step 25; otherwise control jumps to Step 25. 25. Writes a simple macro to skip the first line of a file (n) and delete the next 9 lines (9d). 26. If control gets here from Step 25 if jumps to Step 28; otherwise it continues at Step 27. 27. Writes a simple macro to delete the first “Usr#” lines of a file. 28. Closes the IF…ELSE…ENDIF construct opened at Step 24. 29. Edits the history file with the macro created at Step 25 or 27 and creates a global, CD# using that information. Note that the contents of this variable can be 2 or more lines, but only the first line will be read by $CD#. 30. Changes to the selected directory. 31. Terminates the 1F…ELSE…ENDIE construct opened at Step 15. 32. Marks the bail-out point for the recursion. 1. .key number 2. .bra { 3. .ket } 4. if "{number}" EQ 5. if exists s:cds 6. type s:cds number 7. echo "Enter directory or pick a number, any " 8. execute s:fcd ? 9. skip number 10. else 11. echo "No entries in file - I’ll create one!" 12. echo >s:cds ""'cd'"“ 13. endif 14. endif 15. if VAL "{number}" EQ 0 16. cd "{number}" 17. echo >t:cd{} "‘"'cd'*"" 18. join t:cd{} s:cds AS t:cdO{} 19. echo >t:ed{} “9n;d" 20. edit t:cdO{} with t:ed{} ver=nil: 21. copy t:cdO{} s:cds QUIET 22. else 23. eval >env:usr{} {number} -1 24. if val $usr{} NOT GE 1 25. echo >t:ed{} "n;9d" 26. else 27. echo >t:ed{} "$usr{} d" 28. endif 29. edit S:cds with t:ed{} to env:cd{} ver=nil 30. cd $cd{$$} 31 . endif 32. lab number noline
FFIND
Synopsis: Template: Path: FFIND <file|pattern> <start directory> na na Requires: See also: Type: 1.3+ PFIND Alias Brief: Find a file Definition: ALIAS FFIND SEARCH SEARCH=[] FILE ALL Description: How often have you found yourself wondering where some file went? You know you saved it somewhere, but it seems to have disappeared into the depths of your data disk. This problem is especially nasty on a hard disk. FFIND is a solution to that problem and will easily allow you to locate any file on a disk. Typically you’ll use this from the root directory, but the search can start anywhere. Example: 1>FFIND StartPest SYS: ; search for StartPest Workbench3.O/WBStartup/StartPest FRED Synopsis: <drive number> Template: na Path: Requires: See also: Type: Brief: Definition: na VI.3+ EDS Alias Edit the floppy disk startup-sequence on any disk ALIAS FRED ED DF[]:S/Startup-sequence Description: FRED is very similar to EDS, but is useful for editing the startup on other disks. To use it just type FRED and the number of the drive whose startup you want to edit, viz: 1>FRED 1 ; edit startup on drive one
FREUD
Synopsis: <drive number> Template: na Path: na Requires: V2+ See also: EDU, FRED Type: Alias Brief: Edit the floppy disk user-startup on any disk Definition: ALIAS FREUD ED DF[]:S/User-Startup Description: FREUD is very similar to FRED, but this version edits the User-startup. To use it just type FREUD and the number of the drive whose user-startup you want to edit, viz: 1>FREUD 1 ; edit user-startup on drive one FTEXT Synopsis: Template: Path: FTEXT <file|pattern> <start directory> [ALL] Requires: 2.0+ See also: FFIND Type: Alias Brief: Find ASCII text within a file or group Definition: ALIAS FTEXT SEARCH [] SEARCH=[a NONUM z] PATTERN Description: Hands up, this one is a little weird - but I can imagine some folk will find a use for it. (Looking for passwords in protected files comes to mind.) This alias will search any file, group of files or entire tree for some files containing strings of text. No guarantee is offered for the reliability of this alias, but it actually works better than you might imagine on things like intermediate object (.0) files from C compilers and the like! • Warning: this alias can generate a lot of output!
GetEm
Synopsis: Template: Path: Requires: Type: Brief: [EXECUTE] GETEM [[pat=]name|pattern] pat S: vl.3-1.3.3 only Script To list the environmental variables currently defined Description This script is intended for use with the earlier versions of AmigaDOS because environmental variables were not fully supported. The only Amiga command to use them is the text viewer "MORE”. The GETENV command can only get an environmental variable by name. This script remedies that by listing and displaying the variables by name and value. As an extra freebie, this approach allows the implementation of pattern matching. If the script is called without a pattern, it displays all the variables in use by using This is what you’d normally do. Line-by-Line 1. Sets the simple header for this script. A single argument is used: and it’s optional too. 2-3. Set the bracket characters to { and ). 4. Sets the default pattern to “#?” - everything. 5. Lists the files in the ENV: assignment by the specified pattern - and once again the ubiquitous LFORMAT string comes into play. This script utilises a slightly unusual use of the "%s” substitution. It’s obvious this line creates a simple script, so let’s take a peek at what this script would look like. For the purposes of example I’ve only defined one variable - Editor -the script repeats for every file. ; env: How? As you may recall, if more than one "%s” expansion is used in LIST’S LFORMAT, the first %s expands to the complete path minus the filename. Why? We want to throw this bit away! Who needs to know that the environment variables are stored in ENV:? You should already know that - and if you didn’t, you should not need to worry about it. In this way we can lose the path description safely and no one is any the wiser! ECHO "Editor = " noline This is reasonably obvious. It just prints the name of the variable… OK, it does a bit more than that. We sneaked in a little one here. The spaces are generated by I "e[I" this makes echo produce a tab - helping the output line up better. GETENV "Editor" Displays the current value held by the variable. This is because the 3rd %s expansion, produces the name of the file. You could use TYPE by modifying the line end: TYPE *"%s%s" Not as daft as it looks - you’re more likely to have TYPE resident than GETENV after all. 5. Executes the script created at line 5: and displays the requested variables. Listing 1. . key pat 2. .bra { 3. .ket } 4. .def pat #? 5. list >t:getem{} env:{pat} files lformat ";%s*nECH0 *"%s *e[I=*e[I*" NOLINE*nGETENV *"%s*"" 6. execute t:getem{}
GetEm 2
Synopsis: [EXECUTE] GETEM [Pat=name|pattern] Template: Pat Path: S: Requires: v2 See also: GetEm 1.3 Type: Brief: Script To list the environmental variables currently defined Description The original GetEm script would have worked under AmigaDOS 2 if it wasn’t for Commodore fiddling around adding all those new (private) environmental variables about prefs and so on. Just kidding, those new variables are very necessary and in the proper place - the fault was with the original script. There’s nothing actually wrong with the original, apart from it pre-supposing the presence of SETENV created variables. You may even wonder why this script is here at all - doesn’t SETENV have the same effect? Actually it doesn’t, but I didn’t want you to feel left out and besides this has pattern matching! Line-by-Line 1. Sets the simple header for this script. A single argument is used: and it’s optional too. 2-3. Set the bracket characters to { and }. 4. Sets the default pattern to “#?” - everything. 5. This line comes under scrutiny again - but for an extra reason now. We’ve used this example to show how to use NOT pattern matching to great effect - if you haven’t noticed yet, the tilde (~) symbol introduces a negative pattern. Let’s take a closer look at the main group: #?.prefs: Ignore all private "preferences" files #?.pat Ignore any “pattern” files (Workbench background) #?.info Ignore all Workbench pictures and other info Now let’s take a look at the script this file generates using the same example… Ram Disk:env/EditorRam Disk:env/ This bit is being thrown away. Unlike the previous example though, we are throwing away three “%s” substitutions… echo "Editor = SEditor" …because here we are using %s again to expand the name twice. This is a new feature of AmigaDOS 2. If %s is used more than four times, remaining substitutions are the filename. This also relies on the ability of AmigaDOS 2 to expand an environmental variable directly in a string using $. The remainder of the line is the same as the 1.3 implementation of this script - you should refer back to that example for further clarification. 5. Executes the script created at line 5: and displays the requested variables. Listing 1. . key pat 2. . bra { 3. .ket } 4. . def pat #? 5. list >t:getem{} env:-(#?.prefs|#?.pat|#?.info) files lformat "; %s%s%s*"*necho *"%s*e[I = $%s*"" 6. execute t:getem{} Halt Synopsis: Template: Path: Requires: See also: Type: Brief: [EXECUTE] HALT <[command=]command> command/a S: VI.3-2.1 Stop Script To stop multiple processes with the same name Description This is just a modified version of the STOP script described elsewhere. The difference is that this script can stop more than one process of the same name in a single stroke. Once again, a backwards loop could have been used to get the effect - however using recursion requires less commands and tends to be more reliable. This script is meant for emergencies only. Line-by-Line 1. Sets the argument template. Note that the command name is required. 2-3. Redefine bra and ket characters to { and }. 4. Find the first command in the current process list with the name defined by “Command”. Note this will not work with AmigaDOS 3, because it lists all current processes of the same name. 5. This is exactly the same as the original Stopper script. However, under certain circumstances BREAK may fail with a FAULT code (Return Code=10). You should trap against this by setting the fail level to 11 with FAILAT - no higher. The script exits (rather dramatically) when there are no processes to stop and BREAK fails RC=20. This is deliberate - HALT is only meant to be used as a last resort. In very early releases of AmigaDOS 2, BREAK does not fail correctly, and this script will cause the machine to grind to a halt! 6. This line forces the script to run itself again and again until BREAK stops it. Listing 1. .key command/a 2. .bra { 3. .ket } 4. status >env:stopper{} com={command} 5. break <env:stopper{} >nil: all ? 6. execute halt {command} Host-Chat Synopsis: [EXECUTE] HOST-CHAT Template: none Path: S: Requires: VI.3+ See also: REMT-CHAT Type: Script Brief: Read piped messages from remote terminal Description Implementing and experimenting with a dual-user system is fun. Elsewhere I described some file-based messaging programs and in this part, I’ll take that a step further. You may recall those programs waited for a specific time and checked for any pending messages. However, it would be much nicer if messages could be instantly display - like the chat system found in better BBSs; CIX for instance. Although this is possible using pipes, there are few minor limitations which are due to AmigaDOS’s scripting language. Users with one machine can try these examples by running two shells at once. In these examples, the “host" Shell’s prompt is “1>” and the “remote" Shell’s prompt is “2>”. Enter the two scripts HOST-CHAT and REMT-CHAT and try the following. If you are trying this on a single machine, you will probably find it easier to open two Shells before proceeding. Also, make sure you enter the commands in the correct Shell window. 1>RUN HOST-CHAT 2>RUN REMT-CHAT 1>ALIAS CHAT COPY * TO PIPE:B 2>ALIAS CHAT COPY * TO PIPE:A The first two lines start the pipe-based chat system as an asynchronous process. Once activated it cannot be turned off, but the operation is completely transparent. Note also, both programs must be run before an inter-shell conversation can take place. The last two lines might seem a little strange, but rely on a feature of ALIAS whereby aliases are local to the Shell process in which they were created. In other words, “CHAT" in Shell 1 will execute the command: COPY * TO PIPE:B and “CHAT" in Shell 2 will execute: COPY * TO PIPE:A This version of COPY may seem unfamiliar because the source file is an asterisk - normally used as a wildcard character in other systems. Under AmigaDOS, the * used in this way refers to Shell window; specifically, take input directly from the keyboard and copy it to the named pipe. When this command is executed, all keyboard input is directed to the pipe. To return to the Shell, you must enter the EOF (End of File) sequence by holding down CTRL and pressing \. This forms the basis of the chat system. Assuming you have started the chat scripts in two Shells as detailed above, you can now start chatting. Try this: 1>CHAT Hello World Is anyone out there?
1> All being well, that message will appear instantly in the other Shell window: 2>Hello World Is anyone out there? The same command can be repeated from the other Shell to reply or send another message. Note the prompt (2>) will not appear in the receiving Shell after the message. This is quite normal and does not affect the Shell’s operation. Pressing return in the receiving Shell will return a prompt. An interesting feature of AmigaDOS is once you start typing, the chat system is disabled - it won’t interrupt half-way through entering a command line. Line-by-Line 1. Defines an arbitrary label for the script to jump to when it loops. 2. Serves two functions. First, if a message is waiting in the pipe, it is displayed (typed) immediately. Second, if no messages are waiting, TYPE halts until one appears. This is an action of the pipe device - not the command. 3. The script only reaches this point after a message has been posted to the receiving pipe (above) and displayed. After this happens the script is sent back (via the label at Step 1) and waits for the next message at Step 2. Listing 1. lab start 2. type pipe:A 3. skip start back
HostRead
Synopsis: [EXECUTE] HostRead [time] Template: time Path: S: Requires: V See also: RemoteRead, Mail-2-Host, Mail-2-Remote Type: Script Brief: Read mail messages from the host terminal Description This system for reading your messages employed by Mail-2-Host and Mail-2-Remote works - but it would be nicer to get the machine to read them for you. HostRead and RemoteRead were devised to do just that. They work in a similar fashion to the others, but take more advantage of the Amiga’s multi-tasking properties. This script is an unusual one because it is designed to multi-task -even though it starts its own tasks too. There are two ways of doing this, the obvious way: 1>RUN EXECUTE HostRead and the less obvious way: 1>NEWSHELL 1>RUN EXECUTE HostRead In the second case, you start a new Shell process before starting HostRead. This allows you to work as normal without the messages suddenly appearing in the middle of your screen. Note however, the second technique cannot be used on the remote terminal because the new Shell window will still appear on the host’s terminal -phew. Also, once this script has been started, it can only be stopped by setting the StopItNow environmental variable. You can do this thus: 1>SETENV StopItNow ON • AmigaDOS 2 users only should enter: 1>ECH0 >ENV:StopItNow "ON" The actual value is arbitrary, but once this has been done, the program will halt during its next loop. You may want to write an alias to perform this function. Line-by-Line 1. This defines the argument template for this script. Only one argument can be supplied here, the time delay in minutes. Unless you have a fast machine and a hard disk do not set this below 10 minutes. If no time limit is supplied, the script will check messages every 30 minutes (defined at Step 4). 2 . 3 Redefine the bracket characters as before. 4. Sets the variable for the time limit if none is supplied to 30. 5. This label is supplied so the script has somewhere to return to during looping. In fact, this script has been designed to loop continuously until stopped; more of that shortly. 6. This line is identical to the one used in the MaiT2-Remote program. It displays and removes the current mail messages. 7 . 9 Check for the existence of the StopItNow environment variable. Actually, this could have been a temporary file placed anywhere, but it is more convenient here. Note this line is identical in both versions of this program - so once the variable is set, both users will cease to get update messages. 10. Here the script is executed as a new process with the RUN command. Now' this might seem a little strange, but a minor bug in AmigaDOS EXECUTE causes the SKIP at Step 12 to fail if this is not done! 11. This puts the script to sleep for the predetermined time -default of 30 minutes in this case. You might want to change this to seconds (by substituting SECS for the MINS switch) but don’t forget to change the default time value. If the delay is too short the machine tends to get very tied up attempting to read messages which simply aren’t there. 12. After the WAIT at Stepll times out. this forces the script to go back and do it all again. Listing 1. .key time 2. .bra { 3. .ket } 4. .def time 30 5. Lab Start 6. list >T:ItsForMe{} T:#?.rmt lformat "TYPE %ssoS*nDELETE %s%s*n" 7. if exists env: StopItNow 8. quit 9. endif 10. run execute T:ItsForMe{SS} 11. wait {time} mins 12. skip Start BACK InterDel Synopsis: [EXECUTE] InterDEF <[pat=]dir|pattern> [AFF] Template: pat/a,op Path: S: Requires: VI.3+ See also: Type: Script Brief: Interactive delete - asks before deleting each file Description The idea for this script is borrowed from the *WIPE command on BBC DFS. (DFS was Acorn’s original Disk Filing System or DOS). It works in very much the same way: it allows you to get a list of the files one-by-one according to your pattern and just delete the ones you want to. It is possible to add date windowing as in DEF but this tends to clutter line #5 - FIST (which is complex enough as it is). You might like to add this yourself however. Line-by-Line 1. This line gets the user options. Only a pattern is required. If you're lucky enough to have Workbench, the AFF switch can be used. 2-3. Redefine bra and ket to { and }. 4. This line is not required for this example but is here because some possible versions of this script do require it and it’s best explained sooner rather than later. It is possible that you might want to use command expansion and/or re-direction in the script which FIST is about to create. This means you need a standard .key header, plus .bra and .ket directives. This line will write the necessary lines for you. In this example the result looks like this: . key i . bra ( .ket ) I’ve used ( and ) in this standard header because { and } are already being used by the main script. 5. The crux of this script is here. I’ve already shown how it’s possible to create a script using FIST. Before explaining the ins and outs of the line let’s just take a short look at the output generated by this command: ASK "RAM:TempFile - delete y/N" IF WARN DELETE "RAM:TempFile" ECHO "Deleted" ENDIF There’s nothing unusual about this script. Unless you consider these five lines are generated for every single file matching the pattern! This has the effect of generating a very long linear (that’s top to bottom) script. Also there is another slight flaw in this - if the file is protected against deletion, the script still says it was deleted; AmigaDOS will disagree and complain.** AmigaDOS is right - the file wasn’t removed after all. But let’s take a look at how the LFORMAT part of this line works - the remainder is quite standard. This discussion is quite complex so don’t worry if you have to read it a couple of times. First, we can break it down by splitting it at every “*N” combination; remember this is where LFORMAT will break its output on a newline - just like ECFIO. Already the script begins to take shape: 5.1 "ASK *"%s%s - delete y/N*"*n 5.2 IF WARN*n 5.3 DELETE *"9ss%S*"*n 5.4 ECHO *"Deleted*"*n 5.5 ENDIF" 5.1. The first quote (“) marks the start of the LFORMAT - this will be thrown away. Next, the command outputs ASK and a quote is escaped in with * so it is included in the output; this will become the opening quote of the ASK statement. Now the %s%s is expanded to the file and path of the current file. Next, we add the message “ - delete y/N”. N is capitalised because it’s default. That is: assumed if you press Return. Finally, the closing quote for ASK is escaped in with a *” and the linefeed added with *n. This must be present on every newline in the script. 5.2 Just adds IF WARN to the output file. “Note: The way to cure this slight malady is to call another script which does all this for you correctly. The problem with that approach is speed - the line could have read: list »T:dele{} {pat} {op} FILES LFORMAT "EXECUTE IDEL-2 ',?6S%S" " 5.3 First adds DELETE then escapes a quote character so the %s%s will be interpreted as a literal string; just in case someone has used a space in a filename; also happens with files in Ram Disk! Now %s%s is expanded again to the complete path and filename. Finally the closing quote is escaped in with ” 5.4. This uses the techniques already described to add the "DELETED" message. 5.5. The closing END1F. This must be included or the script will not have anywhere to branch to. The closing quote is only required by LFORMAT and not included in the output file. This is fine on a 68030 based machine like the 3000, but it tends to slow things down quite a bit on the humble 7Mhz A500s. Why? Because for every file listed, EXECUTE has to expand a new miniscript (called 1DEL-2 in this theoretical example) and that does take time. 6. Raises the failure level to the indestructible limit! 7. Executes the interactive delete script. 8. Assuming everything has gone according to plan, this removes the script. You might want to omit this line and read the example scripts for yourself. Listing 1 .key pat/a,op 2 .bra { 3 .ket } 4 echo >T:dele{} ".key i*n.bra (*n.ket )*n" 5 list »T:dele{} {pat} {op} FILES LFORMAT "ASK *"%s%s -delete y/N"n IF WARN*n DELETE"%s%s*"nECH0 *"Deleted"n ENDIF" 6 failat 21 7 execute T:dele{} 8 delete T:dele{} quiet IntelliRes Synopsis: [Execute] IntelliRes <[Script=]Scriptname> Template: Script/a Path: S: Requires: VI.3+ See also: Type: Brief: Script Determine which commands should be made resident Description Even with the latest release, a large number of AmigaDOS commands are stored on disk. (Workbench 3.1 at the time of writing). However, the RESIDENT command can be used to store transient (disk-based) commands in RAM and share them as if they were ROM based. If you use scripts a lot, making the required commands resident at the start of a script is a real chore, especially during development. This program takes the hard work out of RESIDENT by working out which commands we’re using and create a simple script automatically. It works on all versions from AmigaDOS 1.3 upwards (probably not ARP though). This script is deceivingly simple because it writes a script, which itself writes another script; almost like a friendly virus. Understanding this program completely requires a good knowledge of AmigaDOS, so for the sake of those who are more interested in what it does, here is a short explanation. You can start IntelliRes in the following manner: 1>Execute IntelliRes S:ScriptName Searching S:Scriptname Please wait… There will now be a long delay and a fair amount of disk thrashing for 1.3 users (more on that shortly). During its first phase, IntelliRes creates a script similar to that shown in Listing 2. The pause will continue as IntelliRes runs Listing 2 and creates Listing 3 - the list of commands you need to make resident. All you have to do is insert the result of Listing 3 at the head of your script. Strictly speaking you should also add a set of RESIDENT… REMOVES at the end to clean up. IntelliRes is not perfect and the human angle is required to a certain extent. It is intelligent enough to work out which commands are present in the C directory so it will work on most disks. But, it cannot determine the difference between pure commands and dirty commands (those which cannot be made resident). This much is up to you. It will occasionally become confused when it finds a sub string which appears to be a command. This is fixed partly by including a space after the name in Step 7e of Listing 1 (every command must be followed by a space but it is not perfect. One solution is to write your scripts so every command starts at the beginning of every line (I usually use indents). In this case you can modify Line 7 thus: 7. list >T:AutoRes c: lformat ";%s*nsearch >NIL: T:SearchMe *"n%s *" *nif not warn*necho »T:ResIt *”Resident %s%s"nendif" Did you spot the difference? Here it is in detail: search >NIL: T:SearchMe *”n%s *" *n The extra part is the “*n” just before %S. This causes SEARCH to look for a carriage return character just before the command’s name. Use whatever suits you best. Line-By-Line 1. Defines a single required command argument which will be the name of the script to process. 2-3. Change the default AmigaDOS bracket characters from < and > to { and }. There are two reasons for this: partly because this saves clashing with re-direction operators and partly because I like them that way! 4. Copies the script to be processed to the Ram Disk - T: being a logical assignment which usually resides in RAM. You should also note the name of the destination file is forced as “SearchMe". The reason for this will be explained shortly. 5. Creates the first line of Listing 3, overwriting any previously created files with the same name. This also is necessary to fix a feature (bug) in the » (append) operator in the 1.3 Shell. Note: RESIDENT should be moved for 2.0+ machines - it is already in ROM. 6. Displays a short progress message to let the user know the script is operating normally. The message contains the source script’s full path and filename specified in the command line -not the copy of it being processed. 7. This is the heart of this program - this one line creates the program-writing, program Listing 2. Here’s how it works. Let’s assume the command being processed is C:D1R: 7a. LIST >T:AutoRes Calls the LIST command and informs it to send all its output to a file called AutoRes; Listing 2 in other words. 7b. C: Is the directory to be LISTed. Every command in the C: assignment (usually SYS:C) will be listed using the format string explained below. 7c. LFORMAT introduces the quoted format string. Every file displayed by LIST is processed using this string like this: 7d. ";%S*n The resultant script requires the filename, then the path and filename, so the first pathname must be discarded and that’s what this does. When %S is used once in LIST’S LFORMAT mode, it is replaced by the name of the file in the final output. Used, twice the first occurrence displays the path, the second displays the filename and so on. The is a comment, which is ignored by AmigaDOS. Finally the "n” part creates a new line. At this stage the program consists of one line: ;C: 7e. search >NIL: T:SearchMe *"%s *" *n This creates the next line of the program. The command’s name appears at %S and *” is used to force the inclusion of quotes in the output string without confusing AmigaDOS. You should notice there is a single space after the %S and this is very important. “*n” adds a new line and the program now looks like this: ;C: search >NIL: T:SearchMe "DIR " 7f. if not warn*n Adds a conditional branch to the script which now looks like this: ;C: search >NIL: T:SearchMe "Dir " if not warn 7g echo >>T:ResIt ‘"Resident %s%s"n Adds the next line, which is notable for escaped quotes and the inclusion of the forced “PATH/Filename” combination. This was the reason for adding ;%S earlier. If this had not been done %S would be out of step and the line would have received “filename/PATH”. (This fix is for the sake of AmigaDOS 1.3; it could have been achieved differently in AmigaDOS 2.) The program is now all but complete and looks like this: ;c: search>NIL: T: SearchMe “Dir " if not warn echo >> T:ResIT " Resident C:Dir" 7h. endif" Completes the conditional branch and ties the whole thing together. What you may not have realised yet is this program is generated for every single command in the C directory! I’ll explain what it does shortly. 8. Runs the script created at Step 8 and creates the final output ready for inclusion in another program. 9-10.Ties the whole thing together so you know something happened! This displays a short message and displays the script fragment created at Step 8. Line-By-Line: AutoRes 1. Does nothing! This is some detritus from LIST that got thrown away. 2. Searches the copy of the source script for any occurrences of the command name - DIR in this case. (See how it works, yet?) If the command is found, search CLEARS the WARN flag. 3. Tests if the WARN condition is not set. If it was, execution branches to Step 6. If the search string was successful, the command is in the script and needs to be made resident, which is taken care of… 4. …here. Just refresh your memory and look back to Step 5 in Listing 1. That created a file which this command is going to append (») its output to; and its output consists of RESIDENT and the path/filename combination of the command just searched for. A complete path is required by RESIDENT, by the way. 6. Terminates the IF…ENDIF construct. 7. This listing repeats for every command in the C: assignment. Listing 1: IntelliRes 1. . key script/a 2. . bra { 3. . ket } 4. copy "{script}" T:SearchMe 5. echo >T:ResIt " Resident c:Resident" 6. echo "Searching {script}*nPlease wait.. 7. list >T:AutoRes c: lformat ";%s*nsearch >NIL: TrSearchMe *"%s *" *nif not warn*necho »T:ResIt ‘"Resident %s%s ADD"*nendif" 8. execute T:AutoRes 9. echo "Command file now available in T: as the following:" 10. type T:resit