Hands On: Wilf’s Programmers’ Workshop
Wilf’s
Programmers’ Workshop
Wilf Hey completes the Qbasic file selection box project begun last month and
introduces several useful new utility subroutines.
ast month we stopped I also stated that we can get by The first five of these routines use a
L to consider several
DOS interrupt
routines that were
needed to aid us in building a file
without functions &HE (change
current drive) and &H3B
(change current directory) but
there are useful side-effects
mixture of DOS interrupts; the sixth uses
the fast algorithm T that we have featured
several times in the past. A full description
of how to use these routines from your
selection box. These included the following available to us if we employ them. See the own programs will be given later on.
tentative list (these are all interrupt &H21): panel below: Full path information. I took the opportunity to make a few
In the process of creating our file minor corrections and improvements to
Function &H8 – check whether device is selection box routine several of the tasks the existing routines. These are included in
removable we do may be useful in other programs. a new version of [Link], included
Function &HE – change current drive The thoughtful (and time-saving) action in on the SuperCD/Disk along with the .OVL
Function &H19 – get current drive such a case is to make each of those tasks support files. Among the enhancements
Function &H1C – get disk data into subroutines as well, perhaps tidying that result is the fact that you can now use
Function &H2F – get Data Transfer Area them up a little by generalising them. The the solid black colour (number 0) when
(DTA) address routines that I created in the course of the using ziPublish. Before now, colour 0
Function &H3B – change current directory project included:
Function &H47 – get current directory
Function &H4E – find first match ● Get or change
Function &H4F – find next match current drive
● Get or change
Further details of these functions were current directory
published in last month’s Workshop. ● Create an array of
After experimenting further I found that directories matching
I was able to get by without the first a pattern
function because function &H1C (get disk ● Create an array of
data) is sufficient to find out whether a filenames matching a
drive letter is valid and whether a disk is pattern
present. If we wished to indicate (for ● Validate that a path
example) that drive A: was a removable (possibly including
disk drive and that no disk was currently a drive letter) is valid
present, we would need both these and exists
interrupts. But we can simply ignore the ● Sort the strings in an A file selection box to the kind used in the Windows environment
drive if no disk is present. array can make the choice of input files smooth and efficient.
Full path information
ne unfortunate omission from the available where the DTA is; this requires function &H2F. directory string (used in the previous step) did.
O DOS interrupt routines is one to parse ● Check whether there is a drive letter ● Now we must undo the damage: first use
pathnames. In particular, it would be ideal if you mentioned in the path; this is also easy, as the function &H3B to get back to the normal current
could pass a routine a path – which may even second character would be a colon [:]. directory for this drive.
include the ‘.’ or ‘..’ shortcuts, and may or may ● If a drive is specified, find out the current drive ● Now check whether we saved a drive letter
not entail the drive letter – and the output would (function &H19) and save this information. Then from the third step; if so, use function &H0E
be a string containing the full path. With a change to the specified drive (function &H0E). again to get back to the original drive.
knowledge of the available interrupt routines, I ● Now (whether or not you executed the
managed to work out a way of accomplishing previous step) find out the current directory Now we are back where we started, armed
this which consists of several steps: (function &H47) and save it. with the full path string, even if we were only
● Next, change to the specified directory handed an abbreviated version. This routine will
● Verify that the input path is a valid one: the (function &H3B). probably be useful in other circumstances, so I
“Find First” routine (function &H4E) will do this ● Here is where all the preceding steps pay off: have made it a new subroutine in [Link]:
admirably. If it finds a match, the path is valid. use function &H47 again – this time to find out it is called zzValidate. It includes a few handy
Remember that if we want to look at the results the new current directory. This result will not wrinkles, such as allowing a ‘\’ on the end of the
(and believe me we will) we will have to know contain any short form, even if the specified string you pass it for validation.
308 PC Plus November 1996
Hands On: Wilf’s Programmers’ Workshop
When a string is not a string
ach computer language has its share of do a little more work to get that information. The whenever you change its length (by modifying the
E peculiarities, and Qbasic seems to have the VARPTR of an ordinary string variable is the value) you actually change its location too. This
share of two or three languages. One of these is address of two integers: the first is the current doesn’t happen with a DIM string.
the format, or rather two different formats, of a length of the string (remember that it can change In each of the newly built subroutines there is
string variable. You will need to know this format at any moment); the other is the pointer to the a DIM string variable called Str, defined as:
if you use some interrupt routines that require first byte of the field.
you to pass the address of a string constant. This means that you must bring a few tricky DIM str AS STRING * 64
There are several examples of this, and the ‘get commands into play in order to pass the actual
current directory’ function (which appears in this address to the DOS interrupt routine: When you want to point to the start of Str you
project) is one of them. The technical description can do it directly with VARPTR(str).
of this function is: I = VARPTR(X$) There is, however, a penalty. If you want to
ADDR$=CHR$(PEEK(I + 2))+CHR$(PEEK(I + 3)) manipulate the contents of Str (like adding bytes
Get current Directory - function &H47 [Link] = CVI( ADDR$ ) on to the end of its current value) you must
(DOS remember that it has a constant length, filled on
interrupt &H21) This would pass the true address of X$ to the the right with spaces. If you wanted to put an X
Input: [Link] = &H47xx routine via the SI register. on the end of the value currently in Str you have
[Link] = number (1 is drive A, 2 is However, there is an overall simpler way: use a to do something like this:
drive B, and so on) DIM string. A string defined with a DIM statement
[Link] = segment address of a 64-byte is kept in an altogether different and simpler str = RTRIM$( str ) + “X”
string variable within your program format. Its length is fixed so its address can be
[Link] = offset address of the same fixed as well. Qbasic goes to great lengths to keep or you will be adding the X on to the end of the
string variable things efficient when handling a string variable; spaces and it will be lost.
Output: ([Link] AND 356) = 0 if DOS interrupt routines,
successful along with C and C++, often use a
completely different method of
This is not difficult to program in Qbasic, using dealing with strings: the end of a
our pre-written Piecrust code: string is signalled by a null byte – a
character with the value CHR$(0).
[Link] = &H4700 You will notice that many of the
[Link] = 3 routines we use in this project
[Link] = VARSEG (stringvariable) employ this standard. Fortunately,
[Link] = VARPTR (stringvariable) we can mix the two methods by
CALL zzBasicInt (&H21) putting a CHR$(0) into the Qbasic
string where we want DOS to think
But does this really work? The answer depends on it ends. Remember that we have to
the way that the string variable is defined. If it is do the reverse in order to convert a
of the ordinary type – usually a variable with a DOS string back to Qbasic format:
name ending in ‘$’ – then the answer is no, it will
not work as intended. i = INSTR( str, CHR$(0) )
Indeed, the VARSEG of such a string variable is X$ = MID$( str, 1, i - 1)
If you must pass pointers to a DOS interrupt, or
the segment in which the string exists: that is sometimes to other language subroutines, you must know
what should be in [Link]. But its VARPTR is not a little more about how strings are defined than the This method will put the returned
the area where the string exists. You will have to Qbasic manual tells you. value of Str – without the null byte
meant ‘the original foreground colour’ – a may wish to build a counterpart for output the mouse cursor, the graphics cursor and,
limitation because of the way in which of files: it would look very much the same, of course, the text cursor.
PSET and PAINT commands work. but would permit keying a new filename, You may also want to include periodic
Of course, there is one final, all- assigning it to the program’s output. It need testing of removable drives (usually A: and
important new routine that makes use of not necessarily use the pattern idea that B:, but also your CD-ROM) so that they
most of these others: zzFileSelectBox is we have incorporated with this input file are detected as valid even after the
the fruit of our labours. It’s a menu-style file selection version. zzFileSelectBox routine has started. It
selection box that enables you to traverse Another good enhancement would be seems to me that you will need to
the whole of any disks available on your to add mouse processing for the menus; incorporate the ‘test removable’ DOS
system, choosing a file that already exists. this is not as difficult as it might initially interrupt that we have discarded in this
This will be a boon for many programs even seem. However, remember that in such a particular implementation.
as it is, but consider for a few moments case you have to take care of three
how you can modify and improve [Link] separate cursors and their compatibility: New Piecrust routines
In the course of this month’s project we
have built several new useful subroutines
A minor complication with pathnames into our Piecrust code. If you start with
[Link] you can easily use many
here are some peculiarities we have to filename or subdirectory; the root directory pre-written routines in order to enhance
T recognise when we deal with pathnames. already has the \, and you must not double that your productivity.
A major headache is the fact that the root character, or you will end with a path that is Routines are classified by the first two
directory of a disk is not valid to some of the totally invalid. These problems require some characters of their name: ‘zi’ is used for
interrupt routines – notably change directory tricky exception programming that cope with routines dealing with the font or mouse
(&H3B). A related problem is that all other the incompatibility and make it painless and interface; ‘zs’ is used for routines that
directory names can be followed by \ and a virtually invisible to the user. pertain to handling the screen ➜ p310
November 1996 PC Plus 309
Hands On: Wilf’s Programmers’ Workshop
A Qbasic cursor bar
basic does not furnish any commands for fx = FromCol * 8 - 8 the new location. Notice that the same routine
Q choosing items from a list by moving a fy = FromRow * 8 - 8 both puts the cursor bar in place and removes it
cursor bar up and down a menu. It is not difficult tx = ToCol * 8 - 1 when the subroutine is executed again with the
to read keystrokes (or even read mouse position if ty = ToRow * 8 - 1 same area designated.
you wish, using Piecrust routines) and determine FOR ix = fx TO tx
whether to go up or down a menu. But the FOR iy = fy TO ty
question still remains: how can we include a SELECT CASE POINT(ix, iy)
cursor bar? CASE bg
There is a subroutine within the FileSelectBox PSET (ix, iy), fg
routine – the product of our project last month CASE fg
and this – which is extremely simple and useful; PSET (ix, iy), bg
it presumes that you are using an 8-by-8 font END SELECT
(50 rows and 80 columns), and will reverse the NEXT
background and foreground colours within a NEXT
specified area.
Using this subroutine and keeping track of Remember that when you want
which portion is coloured, we can make a useful to move the cursor bar to a new
cursor bar for any menu we desire. Four variables location, you will usually want to
are needed: row and column numbers (not execute this routine twice: once Using a simple reverse colours subroutine, it is easy to
graphic pixels, you should notice) defining the to remove it from its old location make and manipulate a cursor bar; this method combines
four corners: FromRow, FromCol, ToRow, ToCol. and a second time to place it in the power of both text and graphic processing commands.
mode, including routines to convert – simply make the parameter blank (or just parameter, or a single ? character if the
between graphic and text cursor include the drive letter with a colon). In this parameter is incorrect.
addresses; ‘zz’ is used for general case the current directory is not changed.
routines. zzSearchF: Similar to zzSearchD, except
The routines that are new in this zzChangeDrive: This routine takes one that it searches for filenames that match
version of Piecrust are all classified as ‘zz’: parameter and makes the drive it specifies the parameter specification. The results will
Note that they will not cause a critical stop the current drive. Note that it alters the be sorted and placed in the shared array
(the old ‘Abort, Retry, Fail’ error message) contents of the string variable passed as a FileNames$(), and the shared integer
that demands operator intervention. If you parameter, placing in it the resultant current variable FileNames will be set to the
specify a fictional drive, you will simply be drive letter, followed by a colon. If the drive number of matches found.
returned with a ? in your calling parameter. specified on input was not valid, the single
character ? is subsequently returned in the zzValidate: Takes one parameter and
zzAlphaSort: This routine uses two string variable. verifies whether it specifies an existing
parameters: one is shared – the integer Consequently, you can use directory name. This string can include a
variable SortCount, which should be set to zzChangeDrive to find out the current drive drive and path, either full, or relative to the
the number of items to sort; the other – simply make the parameter blank. In this current directory. If the directory exists, its
parameter is a string array containing all the case the current drive is not changed. full form is passed back in the parameter; if
items to be sorted – it is passed in the it does not exist, a single ? is passed back.
CALL command itself. For example: zzSearchD: This routine takes one
parameter and searches for directory And finally, the moment you’ve all been
SortCount = 44 names that match the parameter waiting for...
CALL zzAlphaSort( NAMES$() ) specification, which is made up of
characters and wildcards (the characters * zzFileSelectBox: Pass it a single string
will alphabetically sort entries 1 to 44 in the and ? used as in DOS). This pattern can parameter which optionally contains the
string array NAMES$. include a drive and path to specify where drive, path and/or pattern (containing * or
the matches should be made; otherwise ?) that determines where the selection
zzChangeDir: This routine takes one the current drive and directory will be the process starts. If the string is empty (or
parameter and makes the directory it one searched. invalid) the selection starts by displaying all
specifies the current directory of the drive. If no pattern is specified (except the files in the current directory of the
If you include the drive letter you can perhaps the drive and/or path) then a current disk.
change the current directory of that drive search for all directories will be made, just The selected file’s full path (including
without making that drive the current one. as if *.* were coded. For example: drive) is passed back in the parameter
This is similar in effect to the Qbasic string, or if none is chosen, a single
command CHDIR except in two important CALL zzSearchD( X$ ) question mark is passed back. The file
respects: the routine alters the contents of selection process uses the full screen, and
the string variable passed as a parameter, when X$ = “V*.*” will find all subdirectories does not save any part of it, so it is your
expanding it to the full current pathname, to the current directory that begin with the responsibility to save the content and
including the drive letter; if the specification letter V. restore it (and the SCREEN mode) when
is impossible (for example the drive or The results will be sorted and placed in you regain control.
directory specified does not exist) the the shared array Directories$(), and the
single character ? is then returned in the shared integer variable Directories will be So there you have it. With the new file
string variable. set to the number of matches found – selection dialog routine you can make your
As a consequence of this, you can use remember it may be zero. The full form of programs easier to use and ensure that
zzChangeDir to find out the current the path (no ‘.’ or ‘..’ shortcuts), including they look more professional. PCP
directory of the current (or any other) drive the drive, will be passed back in the
310 PC Plus November 1996