         Q L   H A C K E R ' S   J O U R N A L
      ===========================================
           Supporting  All  QL  Programmers
      ===========================================
         #6                        November 1991
      
    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
     5615 Botkins Rd 
     Huber Heights, OH 45424 USA
     (513) 233-2178
     swensontc@mail.serve.com
     http://www.serve.com/swensont/

The Editor's Forum

   This issue of the QHJ is a little late.  It's been three
months since the last issue.  I try to get an issue out
every two months.  The reason for the delay is the arrival
of my daughter, Caitlan Anne.  The Tech Specs are: born 27
Sept, 5 lbs 2 oz, blonde hair and blue eyes.  We have had
family staying with us helping with the baby since we brought
Caitlan home.  Between the baby and family, I've found
little time for computers.

   I'll try to still put out an issue every two months.  To
help in this I could use articles from readers.  I'm also
having a problem finding topics for articles.  If you have
an article or a topic, send me a note.  A little help is
nice to get.


Italian Software
    By Timothy Swenson  
  
  While looking at the archives for the comp.sources.misc
newsgroup on Usenet I noticed that there had been a posting
on tools for the QL.  I was able to download the posting and
get the tools.

  The programs came from Giuseppe "Beppe" Zanetti of Italy. 
Giuseppe has written a number of programs, including the
ones posted to comp.sources.misc.  Below is a list of the
programs in the posting:

   BOOT_BFP, INDENTER_BAS, CODER_BAS, TOKEN_BAS,
TOBORLAN_BAS, TEST1_PAS, TEST2_PAS.
   A SuperBasic to Pascal converter.
   COLORS_ASM - Creates affect of 16 colors by alternating
between two screens.
   NEWQL_C - QL screen reader for MS-DOS.
   BUFFIN_ASM, SERBUFFE_ASM - Multitasking serial buffer and
buffin() function.
   TEXTPROC_BAS - Split Quill files in columns and justify text.
   
      I have only tried out the Pascal convertor and the
text processor.  The text processor works as advertised.  It
takes a Quill document as input and outputs it to the screen
or a file, formatted to the column width you specify.  It
also allows you to control justification and line spacing. 
The program seems to strip out all Quill formating
characters and finds just the pure text in the document. 
Kind of a neat little program to use and to see how it
handles Quill files.

     I tried to get the Pascal converter to work.  The boot
program expects the other programs as compiled tasks.  I got
around this by calling each program in turn.  The Tokeniser
works, but the program stopped with an "invalid expression"
error about half way through the coder.  I'll be contacting
Giuseppe about fixing this and/or giving it try myself.  The
program does have some promise.  One caveot:  the output
file is designed for TurboPascal and not QL Pascal.  With a
little effort one can change the TurboPascal program so it
will compile under QL Pascal.  It would take some work, but
the program could be adapted to output directly to QL Pascal.

   One stumbling block with the above programs is that all
the prompts are in Italian (big surprise :-) ).  With some
guessing and looking at the code, I was able to get around
this limitation.  You might find a Italian/English
dictionary helpfull.

    Giuseppe also has a number of other programs that are
available directly from him.  The programs are for both the
QL and MS-DOS.  Most programs are public domain (a
contribution is up to you) and one is commercial (but cheap).

IMPRESS DTP   Compiled SB     $15
  Desktop Publishing for 128K QL
IMPRESS CLIP                  Shareware
HTPAINTER                     Shareware
  Simple but efficent painter
SOCCER MANAGER 2              Shareware
BIG COPIER                    Shareware
  File copier for single drive system
TIFFS                         Shareware
  Images scanned with ScanMan Plus
SCR_OFF                       Shareware
CUT2QL  (MS-DOS)              Shareware
  Converts CUT files to QL Sbytes.

   Giuseppe can be reached at:
      via Vergani, 11-35031 Abano Terme (Padova) Italy
      Tel ++39-49-638225 (please call at 8 pm in Italy)
      beppe@alessia.dei.unipd.it

   The programs that come in the QL Tools posting are
available from the QHJ.


Dutch Connection 2
   By Timothy Swenson
   
   Last issue Mark Martin reported on a Dutch BBS that was
being run on a QL.  I forgot to mention that the same person
that wrote the QL BBS also wrote a number of other programs,
that Mark has sent me.

   The three major programs on the disk are: UNZIP96, QVIEW,
and QBOX.  UNZIP96 is a program that will unzip programs
zipped with PKZIP, the popular MS-DOS utility.  I have tried
this and it really does work.  QVIEW is a terminal program. 
Since the documentation is in Dutch, I have yet to try it. 
QBOX is the QL BBS.  It's not a plug 'n play system.  The
designer found some problems with the QL serial ports and
basically had to design his own serial port.  It's all
detailed in the QBOX documentation, including any other
problems running a BBS on a QL.

   Of the above programs, I find UNZIP96 the most usefull. 
With this utility, you can download a Zipped MS-DOS file
(using xmodem) and unzip it on the QL and use the files.  In
the past, one would have to use an MS-DOS system (or
emulator) to unzip the files before they could be used on
the QL.

   All of the files that I have recieved from Mark Martin
are available from the QHJ.


RPN Calculator
     by Timothy Swenson
    
   Reverse Polish Notation (RPN) is mathmatical convention
for handling expressions.  RPN is most commonly found in
Hewlett-Packard calculators.  RPN, like Forth and
Postscript, is stack oriented.  Operators only handle data
stored on the stack.  The infix (or normal) expression 3 +
5 would be expressed as 3 5 +.  Note the operator goes at the
end of the expression.

   What really happens is that the 3 is pushed on the stack,
then the 5 is pushed, and finally, when the + is
encountered, it takes (pops) two numbers off the stack, adds them
and puts the results back on the stack.

   For the expression (1 + 2) * 3, you would enter 1 2 + 3
*.  You don't need ()'s to control what operation takes
place first.  RPN executes each operator as it comes across it.

   Below is a Structured SuperBasic program that implements
a simple RPN calculator.  The left part of the screen is
used for entry and results.  The right part of the screen is
used to show the current stack.  With RPN, you can put a lot
of numbers on the stack, then enter the operators for them.

** Reverse Polish Notation Calculator

T = 0 : F = 0

CLS #1 : CLS #2
num = 0
sx = 0
DIM stack(30)
stackp = 1
num_entered = F

REPeat loop

    in$=INKEY$(-1)
    IF (in$ INSTR "0123456789") THEN
       num = (10*num) + in$
       PRINT #2,in$;
       num_entered = T
    END IF
    
    IF in$ = "+" THEN
       IF num_entered = T THEN
            push(num)
            num_entered = F
       END IF
       plus : num = 0
    END IF
    IF in$ = "-" THEN
        IF num_entered = T THEN
            push(num)
            num_entered = F
        END IF
        minus : num = 0
    END IF
    IF in$ = "*" THEN
        IF num_entered = T THEN
            push(num)
            num_entered = F
        END IF
        times : num = 0
    END IF
    IF in$ = "/" THEN
        IF num_entered = T THEN
            push(num)
            num_entered = F
        END IF
        divide : num = 0
    END IF
    IF in$ = "^" THEN
        IF num_entered = T THEN
            push(num)
            num_entered = F
        END IF
        power : num = 0
    END IF

    IF in$ = CHR$(10) THEN push(num) : num = 0 : PRINT #2

END REPeat loop

DEFine PROCedure push(x)
    sx = sx + 1
    AT sx,0 : PRINT x
    stackp = stackp + 1
    stack(stackp) = x
END DEFine push

DEFine FuNction pop
  LOCal temp
    IF stackp = 0 THEN
        PRINT #2,"Stack Error"
        STOP
    ELSE
        temp = stack(stackp)
        AT sx,0 : PRINT "            "
        sx = sx - 1
        IF sx = -1 THEN sx = 0
        stackp = stackp - 1
        RETURN temp
    END IF
END DEFine pop 
          
DEFine PROCedure plus
  LOCal temp1, temp2
    PRINT #2 : PRINT #2,"   +"
    temp1 = pop
    temp2 = pop
    temp1 = temp1 + temp2
    push(temp1)
    PRINT #2,"   ="
    PRINT #2,temp1
END DEFine plus

DEFine PROCedure minus
  LOCal temp1, temp2
    PRINT #2 : PRINT #2,"   -"
    temp1 = pop
    temp2 = pop
    temp1 = temp1 - temp2
    push(temp1)
    PRINT #2,"   ="
    PRINT #2,temp1
END DEFine minus

DEFine PROCedure times
  LOCal temp1, temp2
    PRINT #2 : PRINT #2,"   *"
    temp1 = pop
    temp2 = pop
    temp1 = temp1 * temp2
    push(temp1)
    PRINT #2,"   ="
    PRINT #2,temp1
END DEFine times

DEFine PROCedure divide
  LOCal temp1, temp2
    PRINT #2 : PRINT #2,"   /"
     temp1 = pop
    temp2 = pop
    IF temp2 = 0 THEN
       PRINT #2,"Divide by Zero Error"
    ELSE
        temp1 = temp1 / temp2
        push(temp1)
        PRINT #2,"   ="
        PRINT #2,temp1
    END IF
END DEFine divide

DEFine PROCedure power
  LOCal temp1, temp2
    PRINT #2 : PRINT #2,"   ^"
    temp1 = pop
    temp2 = pop
    IF temp2 = 0 THEN
        PRINT #2,"Negative Power Error"
    ELSE
       temp1 = temp1 ^ temp2
       push(temp1)
       PRINT #2,"   ="
       PRINT #2,temp1
    END IF
END DEFine power


Substring Searching in C
    By Timothy Swenson
    
   The December 1988 issue of "Computer Language" had an
article on substring searching in C, based on an APL
algorithm.  One interesting feature of the substring search
function is that it returned a array that pointed to all
occurances of the substring in the string.  Some algorithms
only return the first occurance of the substring.

  The short article does not go into much detail, so I will
just present the code.  The program has been successfully
compiled using C68.  It should compile fine under Lattice
C and with some work it can be converted to Small-C.

STRSRCH_C:

/* From December 1988 Computer Language */

/* This C program is based on the follwoing APL program: */

/*  $
[0] VEC #is STRING STRSRCH SUBSTG:#io:ARY
[1] | returns all postitions of SUBSTG witin STRING
[2] #io #is 0
[3] STRING #is.STRING & SUBSTG #is.SUBSTG
[4] ARY #is #and/(#iota #rho SUBSTG)#theta STRING #outer
    =SUBSTG
[5] VEC #is ARY/#iota #rho STRING
$ */
    
/* STRSRCH returns a pointer to an integer vector that
terminates with -1. any postitive number preceding the -1
is a position of the substg within the string.  A negative
number preceding the -1 represents an error. */
      
typedef char * char_ptr;
#define sizvec 15
#define NOMEM ( char_ptr )0

int *strsrch( string, substg )
char_ptr string, substg;
{
  register int x, y;
  static int vec[ sizvec + 1 ];  /* leave room for -1 */
  unsigned int sizstg, sizsub, endsiz, *ary;
  char_ptr startmem, malloc( );
  int strlen( );
  
  sizstg = strlen( string );
  sizsub = strlen( substg );
  endsiz = sizstg - sizsub + 1;
  
/* check boundary conditions: */

  if ( ( sizstg == 0 ) && ( sizsub == 0 ) )
  {
    vec[ 0 ] = -5;
    vec[ 1 ] = -1;
    return( vec );
  }
  
  if ( sizsub == 0 )
  {
    vec[ 0 ] = -3;
    vec[ 1 ] = -1;
    return( vec );
  }
  
  if ( sizstg == 0 )
  {
    vec[ 0 ] = -2;
    vec[ 1 ] = -1;
    return( vec );
  }
  
  if ( sizsub > sizstg )
  {
    vec[ 0 ] = -6;
    vec[ 1 ] = -1;
    return( vec );
  }
  
  if ( NOMEM == ( ary = startmem = malloc( endsiz*sizeof(
int))))
  {
    vec[ 0 ] = -9;
    vec[ 1 ] = -1;
    return( vec );
  }
  
  /* Start of Algorithm */
  
  for( x = 0; x < endsiz; x++ )
     *ary++ = string[ x ] == substg[ 0 ];
       
  for( y = 1, ary = startmem; y < sizsub; y++, ary=startmem
)
     for( x = y; x < (endsiz + y); x++ )
        *ary++ &= string[ x ] == substg[ y ];
	 
  for ( y = 0, x = 0; (x < endsiz) && (y < sizvec); x++ )
     if ( *ary++ )
        vec[ y++ ] = x;
	  
  vec[ y ] = -1;
  free( startmem );
  return( vec );
}
 
STRTEST_C:

/* This function displays: */
/* The positions are: 4   29  */

#include <stdio_h>
#include "strsrch_c"

int main( )
{
  static char *stg1 = "The files are located in the file
cabinet.";
  static char *stg2 = "file";
  int *ptr;
  ptr = strsrch( stg1, stg2 );
  printf("The postitions are: ");
  while( -1 != *ptr )
     printf("%d   ",*ptr++ );
  printf("\n");
  return( 0 );
}


Levenstein Distance
     By Timothy Swenson
     
   The Levenstein Distance is a measure of how close two
strings are to each other.  The Levenstein Algoritm is used
to calculate the Levenstein Distance.

   The Levenstein Algorithm takes two strings and
determines what it would take to transform one string into
the other, using deletions, additions, and changing
characters.  The more that must be done, the less alike the
two strings are.

   In QHJ #1 the Ratcliff/Obershelp algorithm for inexact
pattern matching was discussed.  This algorithm determines
how close two strings are by recursively finding the largest
equal substrings in the two strings.  The larger substrings
found, the closer the two strings are.

   This program comes from the May 1991 issue of the C Users
Journal.  The text accompanying the article explains the
program better than I do.  If you have questions, you should
refer back to the original article.

/*
 ldistance()  Determine to what extent two charcter
               strings are (un)equal using the 
	       'Levenstein distance' algorithm.

*/

#include <stdlib_h>
#include <string_h>
#include <stdio_h>

int    addition = 1;
int    change   = 3;
int    deletion = 5;

#define COMP_LEN     20
#define ARR_SIZE     COMP_LEN + 1
#define SMALLEST_OF(x,y,z) ( (x<y) ? min(x,z) : min(y,z) )
#define ZERO_IF_EQUAL(x,y) (requested[x-1] == found[y-1] ? 0 : change)

min( a, b)
int a, b;
{
    if ( a < b )
       return(a);
    else
       return(b);
}

int    distance[ARR_SIZE][ARR_SIZE];

ldistance( requested, found)
char *requested, *found;
{
   register int i,j;
   int r_len, f_len;
   
   r_len = (strlen(requested)>COMP_LEN ? COMP_LEN : strlen(requested));
   f_len = (strlen(found)>COMP_LEN ? COMP_LEN : strlen(found));
   
   distance[0][0] = 0;
   for (j = 1; j <= ARR_SIZE; j++)
      distance[0][j] = distance[0][j-1] + addition;
   for (j = 1; j <= ARR_SIZE; j++)
      distance[j][0] = distance[j-1][0] + deletion;

   for (i = 1; i <= r_len; i++)
      for (j = 1; j <= f_len; j++)
         distance[i][j] = SMALLEST_OF(
	     (distance[i-1][j-1] + ZERO_IF_EQUAL(i,j)),
	     (distance[i][j-1]   + addition),
	     (distance[i-1][j]   + deletion) );

    return( distance[r_len][f_len] );
 }
 
int main()
{
   int result;
   
   printf("Comparing '%s' and '%s' : \n","pennsylvania",
            "pencilvaneya");
   result = ldistance("pennsylvania","pencilvaneya");
   printf("  Result = %d\n",result);
}
   	    

QDOS Rights
     By Timothy Swenson

   A new item I learned this month is that the QDOS rights
to North America is held by Mechanical Affinity.  I wrote
Frank Davis trying to learn more about this.  I specif-
ically asked him if the rights to QDOS include creating a
new QL.  He responded by saying that he was not too sure.

   The uniqueness of the QL is not in its hardware but lies
in its operating system, QDOS.  If one could implement QDOS
(Including SuperBasic with QDOS calls) on another hardware
platform, most of us would gladly support the new machine
($costs excluded).

   This has been done with QL emulators for the Atari ST
and the Amiga.  If one had the know-how and the ambition,
QDOS could be implemented on a new 68000 platform in a
language like C.  It could be disk-based and not ROM based
(I don't know what this will do to QDOS calls.  I guess
QDOS could be loaded from disk into low memory where it
resided now) making it easier for updates and cheaper to
produce.

   Changes could be done to QDOS to make it more "standard"
with such things as command line arguements.  As it stands,
QDOS and SuperBasic are really one thing.  QDOS could be
separated from SuperBasic, yet still allow SuperBasic to
use QDOS commands.  This would be similar to MS-DOS Batch
files and UNIX shell scripts.

   The possiblity and opportunity is there, it's just
a matter of finding someone to do it.  It would take a lot
of effort to do this.  The English QLAW group (working on a
"SuperQL") might have the know-how, but not the
opportunity.  As far as I understand it, Amstrad still has
the rights to QDOS in the rest of the world.

   Oh well, it's nice to ponder on it.  Sometimes I wish I
had the know-how, time, and (yes) ambition to do it.  I
confess that I'm a bit lazy.  The initial writing of code
is the hardest part.  Sometimes it takes me a while to work
up the energy to do it.


C68 Compiler Benchmarks
     By Timothy Swenson
   
   In QHJ #3, I reported on some benchmarks that I ran on
Lattice C and Small-C.  Now that I had C68, I decided to run
the benchmarks against it to see how it compares with the
other two compilers.

   Here is how all three compilers compare:
   
Prime Numbers        Small-C      Lattice C        C68
  29000-32767         5 Sec         3 Sec         6 Sec
   1000-32767        32 Sec        20 Sec        42 Sec

Recursion           189 Sec       193 Sec       227 Sec

  As it turns out, C68 seems to be the slowest compiler on
all of the benchmarks.  Since C68 was written for a variety
of systems, it's optimzer may not be tweaked for the QL.

  If you have any applications that are speed dependent you
might be better off using Lattice C.  For porting software
from MS-DOS or UNIX to the QL, C68 is the way to go.  Also
C68 provides full QDOS support, whereas Lattice C provides
only access to traps (you have to write your own functions
to utilise the traps).

  Not having the time to really get into C, I still find
Small-C good enough for my uses.  Small-C's compiler is
hands-down the fastest of the three.
 
