Q L   H A C K E R ' S   J O U R N A L
      ===========================================
           Supporting  All  QL  Programmers
      ===========================================
         #28                       July 1998

    The QL Hacker's Journal (QHJ) is published by Tim
Swenson as a service to the QL Community.  The QHJ is
freely distributable.  Past issues are available on disk,
via e-mail, or via the Anon-FTP server, garbo.uwasa.fi.
The QHJ is always on the look out for article submissions.

  QL Hacker's Journal
     c/o Tim Swenson
     2455 Medallion Dr.
     Union City, CA 94587
     swensontc@geocities.com
     http://www.geocities.com/SilconValley/Pines/5865/

****** Note New Mailing Address ********

EDITORS' FORUMN


I have not received much feedback on the Qliberator Source Book
idea I mentioned in the last issue, and I have received basically
no "helpfull hints".  So, my plan is to write what I can, put it
out on the Net, and see what feedback comes from it.  If I make
any mistakes, I'm sure there are many willing to point out my
failings :-).  Sometimes the best way to get an answer to a
question is to give a wrong answer, then many will pop up to give
the real answer.  I'll probably put in questions that I have in
the hope that they too will get answered.

A part of the material going into the Source Book is coming from
stuff I'm writing for this newsletter.  I have taken the article
on adding Config Blocks to Qlib program and expanded it quite a
bit.  A couple of articles from this issue will also go in.  I
also plan to have sections that touch on the various SuperBasic
extentions and programming aids available to the SuperBasic
programmers.

In other news, besides having past issues of the QHJ available on
my web page, I've added a number of articles that I've written
for other newsletters.  Check the link from the main page.


STRUCTURED SUPERBASIC 2.6

Structured SuperBasic is a utility that has been printed a couple
of times in this newsletter.  I have recently dusted the program
off and completed a new version.  SSB 2.6 has all of the
functionality of SSB 2.5, but I have added a lot of error
checking, fixed a few bugs, added Config Block support, Command
Line support, Environment Variable support, and compiled it with
Qliberator.  The manual has been expanded from 5 pages to 16
pages.  The whole package has been zipped and should be available
your local QL BBS and the Internet.  It can be downloaded off my
web page at http://www.geocities.com/SiliconValley/Pines/5865/.

For those that don't remember what SSB is, it is a filter program
that takes SuperBasic code, written in the form for SSB and
converts it to full SuperBasic code.  SSB allows for no line
numbers, blank lines between code segments, conditional
compilation, Include files, and other features.  SSB allows for
more readable and maintainable code.  Download the package, unzip
it, and give it a try.


REVISION CONTROL SYSTEM (RCS)

Whether it's source code or system configuration files, it's nice
to be able to keep track of changes made to files over time.  The
Revision Control System is a collection of programs that keep
track of different versions (revisions) of text files.  A special
file is created by RCS that contains information about the
changes in a file and allow the user to get back to any revision
of the file.

RCS was designed primarily for programmers to keep track of what
changes were made to various source files.  It was derived from
an earlier Source Code Control System (SCCS) and was first
developed for the UNIX operating system.

Since RCS came from a UNIX background, it understands the
concepts of different users accessing the same files and allows
"checking out" files and locking them from being changed by other
users.  RCS can be used by a number of programmers all working on
the same code.  It keeps track of who has what file "checked out"
and who makes what changes.

Although most QL programmers program by themselves, RCS can help
the QL programmer bring a form of discipline to their
programming.  This is especially usefull for what can be called
"full production" programs; commercial or freeware.  Given the
long life of QL programs (I'm still using programs written more
than 10 years ago), RCS can keep track of what changes were made
from revision to revision and why.  A programmer can keep make a
rule that each RCS revision be a bug fix and the reasons for the
fix can be logged as part of the revision history.

Enough blathering on, now to jump right into what RCS is and how
it works.

Original File - This is the text or source code file that you
want to keep track of.  It can be any text file.

Revision File - This is a single file that contains the original
text/source file and all of the revision history.  It has the
same name as the original file except that a ',v' is appended to
the end.  If the original file is 'test_c', then the revision
file is called 'test_c,v'.  A revision file keeps track of only
one original test/source file.  If you have 3 source code files
that make up a single executable, then RCS will have three
revision fills.

Check-In - All revisions to a file are put into the revision file
by checking them in.  The program 'ci' is used to do this.  'ci'
is used to either create a new revision file or to check-in a new
revision into an existing revision file.  Once a file has been
checked into the revision file, it is deleted.

Check-Out - When you want to edit a text/source file that has
been checked in to the revision file, you use 'co' (check-out) to
extract the text/source file from the revision file.  

The whole process of using RCS is basically checking-in and
checking-out the same file.  RCS is not aware of time, so there
is no need to check-in a text file when you have finished a
programming session.  You really only need to check-in a file
when you have made all of the edits you need.  If you are fixing
a bug in a program, you would only check-in the file when you
have fixed it.  No need to keep track of working copies of the
files.

To show you how RCS is used, I've written a short Structured
SuperBasic program:

     OPEN #3,con_100x100a100x100
     CLS #3
     INPUT "Enter The Name of a File :";file$
     OPEN_IN #4,file$
     REPeat loop
        INPUT #4,in$
        IF EOF(#4) THEN EXIT loop
        PRINT #3,in$
     END REPeat loop
     CLOSE #4
     CLOSE #3

I stored this program in the file 'readfile_ssb'.  I then checked
it in to RCS using the following command:

     exec ci;"readfile_ssb"

'ci'  then asks me for  my initials.  Since the  QL does not have
the concept of multiple  users  (and  therefore  user  names),  a
persons  initials are used to keep track of who has checked out a
file  and who has made what changes to that file.  'ci' then asks
for a Description.   The Description  is where  the "why"  of the
file change is kept track of.  If you are using RCS to keep track
of  bug fixs for a program, the Description section would be used
to mention  the bug, what caused it, and  how it was fixed.  I do
not  believe there is a limit on how long the description can be.
The Description is ended by a line with just a period on it.

'readfile_ssb'  is now checked-in to the RCS revision file and is
deleted.

Now to get the file back to edit it, I ran the following command:

     exec co;"-l readfile_ssb"

I need to  tell 'co' what  file I want,  but I also  have to tell
'co' that  I want to  edit the file.   If I ran  'co' without the
"-l" option, 'readfile_ssb' would have been created, but it would
not have  been "checked-out" from  the revision file,  and when I
went  to check it in, RCS would not have allowed it.  Under UNIX,
'co' would create a 'read-only' copy of the file.

'co' asked for my initials.  Since the same person who checks out
a file is  the only person  the can check  in a file,  be sure to
remember what initials you used.

'readfile_ssb' is  now  extracted  from  the  RCS  file  and  the
revision file is changed to show that the file is checked out.

Once I  am done editing  the file, I  can then check  in the file
using  'ci'.  Again I am asked for  my initials.  The file is now
checked  back into the revision file  and deleted.  If you copied
the file  before checking  it back  in and  you have  edited this
file, you can not incorporate those changes into the revsion file
since the file is 'officially' checked in and can not be edited.

RCS uses  revsion numbers to keep track  of the different times a
file is  checked-in.  The first  revision is 1.1,  then 1.2, then
1.3 and so on.  Let's say that I have edited my file 4 times, I'm
now at revision 1.5 and want to recall revision 1.3.  I would run
the following command:

     exec co;"-r 1.3 readfile_ssb"

'co' would then dig though  the  revision  history  and  generate
version 1.3 of 'readfile_ssb'.

RCS comes with  more  than  just  the  'ci'  and  'co'  programs.
'rcsdiff' is  used to compare a  working "checked-out"  file with
the version  still in the revision file.   'rlog' is used to view
the revision file.

Below is the output of 'rlog' from my example session:

RCS file: readfile_ssb,v
Working file: readfile_ssb
head: 1.2
branch:
locks: strict
access list:
symbolic names:
comment leader: "# "
keyword substitution: kv
total revisions: 2;selected revisions: 2
description:
Creation of RCS Archive
----------------------------
revision 1.2
date:  1998/06/11 20:41:01;  author: tcs;  state: Exp;  lines: +2
-2
This is a second version of this program.
----------------------------
revision 1.1
date: 1998/06/11 20:19:34;  author: tcs;  state: Exp;
Initial revision
============================================================
 
RCS  is a lot more complicated that I've mentioned here.  It, and
SCCS,  is fully documented in the book "Applying RCS and SCCS" by
Bolinger  & Bronson, published by O'Reilly & Associates.  RCS for
the QL is available from  most  freeware  sources.   The  QL  RCS
distribution does not come  with  the  'diff'  program  which  it
requires.  'diff' is available as part of the C68 distribution.

RCS  can not be used with  regular SuperBasic programming, as RCS
would see  any new line numbers as a  change in the file.  If you
loaded a  file into SuperBasic,  did only a  line renumber, saved
it,  and then check it back into RCS, RCS would any line with new
line  number as having  changed.  RCS works  well with Structured
SuperBasic as it has no line numbers.  It also works well with C,
Forth, Pascal, etc.


ENVIRONMENT VARIABLES

The concept  of Environment Variables comes  from the Unix world.
They are used  slightly in  MS-DOS, but  not at  all to  the same
extent  as UNIX.  For the QDOS world, the file ENV_BIN provides a
number of extensions that allow the use of Environment Variables.

Essentially an  environment variable  is a  variable that  can be
"seen"  by exectuable programs.  In SuperBasic  we can set up all
kinds  of variables, but if we execute a program from SuperBasic,
these programs  can not "see"  these variables and  get data from
them.

The   purpose  of   environment  variables   is  to   change  the
configuration of a  program.  They  function like  Config Blocks,but don't require running a program to make the change.

Let's take a  quick look  at how  we can  change the  behavior of
programs.  There are five different ways of doing this:

1] User  Intervention.  This  is where  the user  uses a  menu or
answers queries from the program.

2]  Config Blocks.  This feature is pretty unique to QDOS, but it
allows  the user to change default options without having to know
how to edit a binary file.

3]  Configuration File.  This is a separate file that the program
reads to determine how to set defaults and run.

4] Command  line Arguments.  Instead of  the program querying the
user  for information, the user types it in when they execute the
program.

5]  Environment Variables.  The user sets a variable that is then
read by the program and changes its default settings.

Each of  the options have their own  place and their own benefits
and faults.  Some are more  permanent,  like  Config  Blocks  and
Config  files,  while  some  are  very  short  lived,  like  User
Intervention  and Command  line Arguments.   Enviroment Variables
are in between as they can be set in a BOOT program, but they can
be changed by just typing in a new command.

The ENV_BIN file comes with 4 extensions.  They are:

SETENV     - Defines an environment variable.
ENV_LIST   - Lists all defined environment variables.
ENV_DEL    - Deletes an environment variable. 
GETENV$    - Gets the value of an environment variable.

The two key commands are SETENV and GETENV$.  SETENV is used like
this:

   SETENV "VARIABLE=value"

SETENV  takes a string  argument of the  type "XXXXX=YYYYY" where
XXXXX  and YYYYY are two strings separated by an equal sign.  Any
space before the equal sign is treated as a part of XXXXX and any
space  after the equal sign  is treated as a  part of YYYYY.  The
case  of each string is important as "VARIABLE" is different from
"variable".  By convention, upper case is used for variables.

The SETENV  is done either in  a BOOT or setup  program or by the
user.  The command  for an executable  to get the  contents of an
environment variable is GETENV$.  The command is used like this:

    a$ = GETENV$("VARIABLE")

In this  case a$  will be  assigned the  value of  "value".  This
comes from our previous SETENV command above.  If the Environment
Variable  "VARIABLE" is not set (does not exist) then the GETENV$
would return a Null string ( "" ).

Now I did  say that  executables use  GETENV$ and  not SuperBasic
programs.   Since variables  are already  used in  SuperBasic, we
would  not gain much  in using environment  variables.  There the
commands  are use is  in compiled SuperBasic  programs, which are
executables.

I see the purpose of using Environment Variables as adding to the
flexibility of programs that  use  Config  Blocks.   Both  Config
Blocks and Environment  Variables are  really designed  to change
default program settings.  User  Intervention  and  Command  Line
Arguments  are designed  to tell  the program  what the  data is.
Using environment variables allows the user the ability of making
a  temporary change to the default  options of a program, without
having  to go through the trouble of using "config".  Environment
variables  would be used to change a setting for a single session
and that's it.

Getting  Config Blocks and Environment Variables to work together
is not difficult.  The  program  would  first  get  it's  default
settings from the  Config Block.  It  would then check  to see if
there  are any Environment Variables set.  If there are, then the
Environment Variable settings would  override  the  Config  Block
settings.


WORKING DIRECTORY

As we all know, QDOS did not come with the concept of directories
and subdirectories.  A number of extensions to QDOS are available
to help  create the working concepts  of directories.  When using
ED drives, I found the Path (PTH) extensions usefull because they
would  search a list  of subdirectories looking  for a particular
exectuable.

Now, I've never used a QL with  a  hard  disk,  so  I'm  not  too
experienced  with using the tools  for handling directories, plus
my  copies of some of the good articles dealing with this subject
are not available  to me.  So,  I've been doing  some thinking on
the matter, especially since  I  have  been  writing  a  freeware
program and I want to make it as versatile as possible.

The one issue  that I've been pondering over is telling a program
where  there  data  file  are  at.   This  is  called  a  Working
Directory.  It is a directory where the data files are at and any
newly created files will also go.

I  know that DATA_USE is designed for  this purpose, but I do not
want  to type in a new DATA_USE before each program I execute.  I
could set  up  a  short  little  SuperBasic  program  to  set  up
everything, but I want to EXEC a program not LRUN it.  Of course,
using a front end  like QASCADE,  this point  is mute,  since the
user  is removed from the details of executing the program.  With
PTH_  setup, I don't need to set PROG_USE, because PTH_ finds the
executable.  I  was looking a  way of having  the executable know
where the Working Directory is.

What I  came up with is  this:  Have an item  in the Config Block
for Working Directory.  The  contents  would  be  something  like
this, "WIN1_PROG_DATA_".  This  string would  be appended  to the
front of each file name used in the program.  This does mean that
the  user would not be able to type in a new device name, such as
"FLP1_FILE_EXT".  Initally,  the Config  Block entry  for Working
Directory  would be the null string (empty), so that a user would
enter "FLP1_FILE_EXT".  Once  the  user  has  created  a  working
environment, the Config  Block can  be changed  to what  the user
wants.

If this feature is linked  to  Environment  Variables,  it  would
become more powerfull.   By setting  an Environment  Variable for
the program, the user could temporarily override the Config Block
and change the  Working  Directory.   If  the  Config  Block  was
changed to a Working  Directory and  the user  needed to  use the
floppy for a few  times,  the  user  would  set  the  Environment
Variable to "flp1_" or to the  null  string  ("")  and  then  set
DATA_USE to flp1_.   If they did  not want to  change the default
Config Block,  a BOOT program could  set the Environment Variable
and it  would be active  during the whole  computing session.  If
different  programs used different Environment Variable names for
their Working Directory,  then all  Working Directories  could be
set up  in  the  BOOT  program.   Something  that  could  not  be
accomplished with just DATA_USE.

Since my experience in this area  is  kind  of  light,  the  more
experienced QLers may want to take this all with a grain of salt.
I  prefer to think of it as a way that I would prefer to organize
things and others may have a different way.

The reason I am bringing up  this  idea  is  for  programmers  to
consider the concept and add it to their programs.  The option is
not very  difficult to implement, but it  would a nice feature to
the  program.  The whole feature would take up no more than 10-20
extra lines  of code to a program.   By setting the default value
of Working Directory  to  nothing,  the  feature  is  essentially
turned off and it does not impede the user that does not want use
a Working Directory.


CASE STATEMENT IMPLEMTATION

When  I recently was working on SSB 2.6, I was using a SuperBasic
implemtation of a  CASE structure  for the  core of  the program.
The deeper the structure got, the  harder  it  was  to  read  and
understand.    I  starting  thinking  of  using  another  way  of
implementing a CASE structure.

For  those that don't know what a CASE structure or statement is,
it is  essentually the same  structure as a  SuperBasic SELECT ON
statement, but  it is not  limited to numbers.   Plus the general
idea  of a CASE structure  is to have a  number of possible logic
statements  with an action for  all cases that do  not fit one of
the logic statements.  In a generalized CASE structure not all of
the  comparisons must be based on  the same data (string, number,
etc), but can vary.

Traditionaly, a  way of creating the CASE  structure is to have a
number  of nested IF..THEN..ELSE statements,  with the final ELSE
handling the  default value.  An example  would be something like
this:  you are reading in  text from a file.   You want to handle
the  following cases:  a line starts with  a ##, or a line starts
with a  **, a line  starts with a  period (.).  If  none of these
cases, then the line is passed to the output file.

Using nested IF..THEN..ELSE statements the pseudo code would look
something like this:

   IF line starts with ## THEN
     ......
   ELSE
      IF line starts with ** THEN
      ......
      ELSE
         IF line starts with a period THEN
         .......
         ELSE
            pass to output file
         END IF
      END IF
   END IF

If you start having more than just a hand full of possible cases,
the structure can get long and difficult to read.

SuperBasic allows  the use of  NEXT in conjunction  with a REPEAT
statement.  This means that when the NEXT is reached, the rest of
the  REPEAT loop is skipped and the processing goes onto the next
interation  of the REPEAT loop.  Using NEXT in this manner allows
for  the  creation  of  a  different  implementation  of  a  CASE
structure.   Below is an example of the two implementatios listed
side-by-side.

    REPeat loop                 REPeat loop
      IF .... THEN                IF .... THEN
         ....                        ....
      ELSE                           NEXT loop
         IF .... THEN             END IF
            ....                  IF .... THEN
         ELSE                        ....
            IF .... THEN             NEXT loop
               ....               END IF
            ELSE                  IF .... THEN
               default               ....
            END IF                   NEXT loop
         END IF                   END IF
      END IF                      default
   END REPeat loop             END REPeat loop

Using  the REPEAT..NEXT..END version may not seem as clean as the
"classical"  IF..THEN..ELSE structure, but I  think it is cleaner
when it come to  reading and  debugging.  What  I am  looking for
from readers is to ponder  over  any  downsides  from  using  the
REPEAT..NEXT structure.  I  can't think  of any  logical problems
with using this structure over the classical one.  If you know of
any,  please send me a note.  I'll add the productive comments in
the next issue.


CREATING LOADABLE EXTENSIONS USING QLIB

One  of the things that has always amazed me about the QL was the
ability  to load a binary file and  have a bunch of new keyboards
available  in SuperBasic.  In most computers that had Basic built
in, the  language was  static and  had no  way to  extend itself.
Other  languages (like C,  Fortran, or Pascal)  used libraries of
functions and procedures to extend the capability of the library.

The  first major loadable extention to  the QL was ToolKit.  From
then on  the term toolkit has been  used in reference to loadable
extensions.   Popular toolkits are ToolKitII (TKII), DIY ToolKit,
and  DJToolKit.

I knew that the first of these toolkits were written in Assembly,
but  I did not know that they could be created by Qliberator.  It
seems that QDOS executables and  extensions  are  real  close  in
format and when compiled right, they can be interchangable.  This
means that an executable can also be loaded as an extension.

To figure out how  to  create  a  toolkit,  I  grabbed  a  simplefunction, Qliberator,  and  gave  it  a  try.   The  function  is
included below:

10 REMark $$external
100 DEFine FuNction upper$(up$)
110     LOCal x, temp
120     FOR x = 1 TO LEN(up$)
130        temp = CODE(up$(x))
140        IF temp > 96 AND temp < 123 THEN
               up$(x)=CHR$(temp-32)
150     NEXT x
160     RETurn up$
170 END DEFine 

The function  takes any string and converts  it to all upper case
letters.  The $$external  is a  compiler directive  to Qliberator
that tells it that  the next  function or  procedure needs  to be
available outside of  the  executable.   For  each  procedure  or
function  that you want to turn into an extension, you would have
to put the $$external directive in front of it.

If I was to put an additional line in the program,

     180 PRINT upper$("This is a test")

then when I executed the program, line 180 would be executed.  If
I  LRESPRed  the  program,  the  keyword  upper$  would  be  made
available.

When  compiling the program it is a  good idea to turn WINDS off,
since the extension will have  no  channels  open.   Otherwise  3
channels  will be opened for it, wasting them.  To lower the size
of  the binary file, turning off NAMES  and LINES might be a good
idea.  Note that  the case of  the function or  procedure will be
maintained  in the extension.  In my  example, the name that will
show  up when entering EXTRAS is "upper$" (all lower case).  If I
defined the function a UPPER$, then "UPPER$" would show up in the
EXTRAS  command.  By convention, extensions should be done in all
upper case.

If you will be running the extension on a system that already has
the Qlib  runtimes  loaded,  then  compile  the  program  without
runtimes.  If you  don't  know  if  the  Qlib  runtimes  will  be
available,  compile it with the runtimes  included.  It is a good
idea  to compile both ways and let the user decide which one they
need.   The  example  program  when  compiled  without  the  Qlib
runtimes was 594  bytes.  With  the Qlib  runtimes it  was 11,146
bytes.  The runtimes take up a fair bit of space.

If  you load an extension that does not have the Qlib runtimes on
a system where the Qlib runtimes are not loaded, you will not get
an error  message when you  LRESPR the extension.   When you call
the  extention is  when the  error will  occur.  The  exact error
message is:

     Error "Runtimes Missing !"

Once you have compiled  an extension,  all that  is needed  is to
LREPSR  it and test it out.  Remember that you can't LRESPR while
any jobs, other than Job 0 (SuperBasic), is running.

    Source: geocities.com/svenqhj