ScriptBasic Users Guide

by Peter Verhas

Table of Contents

[Contents]

1. Introduction

ScriptBasic is a BASIC language interpreter with several features that makes it unique. First of all ScriptBasic itself is free and is supported by the GNU LGPL licence. Note that the GNU LGPL licence applies only to ScriptBasic itself, while the modules interfacing 3rd party software may apply different licences. For example the module bdb is under LGPL, but the library it uses, namely the Berkeley DB is NOT LGPL! It runs on Windows NT, Windows95, Linux, Tru64 UNIX and probably on many other platforms. ScriptBasic the ideal tool to write small scripts that gurus write in Perl. But this is not the only situation to consider ScriptBasic. ScriptBasic can be a valuable tool for experts as well as a language interpreter ready to be built into their application. Read the list of ScriptBasic features and decide how you can use it.

This documentation is the User’s Guide for the so-called STANDARD variation of the interpreter. This code runs on the command line, accepts command line arguments and runs a single program in a single process. Other variations may exist in the future, which will be based on the same code but exhibit different interfaces to the system. Some features for those variations may be different, but most language features probably remain the same.

This document describes how to use the interpreter and the programming language. The first few chapters describe how to install the interpreter. If you are not the system manager of your machione and somebody has already installed ScriptBasic, you may want to skip these sections and jump directly to section

[Contents]

2. Using ScriptBasic

This chapter is about the STANDARD variation of the interpreter. Starting and using other variations whenever they become available in the future will probably be different.

[Contents]

2.1. Running from command line

There are several ways to start a ScriptBasic program. The different ways also depend on the installation. Windows NT may associate the extension .bas with the ScriptBasic interpreter. In this case you can start a ScriptBasic program double clicking on its name in the explorer. You can type the name of the text file containing your basic program and UNIX will start it for you automatically if the first line of the program contains the starting information like

#! /usr/bin/scriba

and the file itself is executable. The simplest way among all is to type the name of the executable. First try to start it without any command line argument:

$ scriba
Usage: scriba [options] program.bas

options: -o file_name save binary format to file but dont execute -b file_name load binary format from file and execute -n do not use cache (no save, no load) -e execute after binary format was saved -v print version info and stop -c inform scriba that this is a CGI script -C save C program output -p preprocessor specify preprocessor.

The program does not insist on its name, the person installing can name it basic, scriba or any other name. However scriba is the preferred name for the executable. When you start the program without arguments it tells you the different options. If the program does not start check that the executable is in the path or specify the full path to the executable. Under UNIX you may need to start ScriptBasic after you have compiled as ./scriba

The compilation and execution of the code can be altered and driven by command line options. There are not too many and usually you do not need any. If you want to start a basic program you can type:

$ scriba hello.bas
Hello word!
$ _

The program will execute the basic code and after executing the final statement exists the process. ScriptBasic STANDARD variation is able to dump the compiled code to a file and later use this image to execute the basic program. To do this you can type

$ scriba –o hello.bbf hello.bas

This command will compile the program hello.bas into the file hello.bbf. The code itself will not be executed. The extension bbf stands for basic binary format, but you can use any extension. ScriptBasic does not assume any default extensions. The saved binary format can be executed by the interpreter issuing the command

$ scriba –b hello.bbf

The switch `–b' tells the interpreter that the file is already a compiled format and it needs only execution. However ScriptBasic STANDARD variation is intelligent enough to recognize the binary format and it will not try to interpret it as basic text even if you miss the switch. The command line

$ scriba hello.bbf

should also work. The switch `–b' is available because recognizing the binary format applies some heuristics and using the `–b' switch to execute a compiled basic program is the safe way.

IMPORTANT

Note that the compiled format is not compiled in the sense of usual compilers. The compiled format is NOT machine executable. This is the internal format that the interpreter interprets and executes. This format is created and stored in memory each time a basic program is executed. When the –o switch is used this internal format is saved and can be reloaded later to execute again the basic program.

This binary format is proprietary and is NOT portable. This binary format was designed to load the programs fast and allow the interpreter speedy startup for codes executed many times. You have to "compile" your basic code on the architecture you want to run it. It needs not be physically the same machine, but it should be the same type of operating system, the same compiler used to compile ScriptBasic executable and the same ScriptBasic version and build number. The different variations may accept each others binary format. For example the WIN32DLL variation accepts the binary format created by the STANDARD variation. We recognized that the STANDARD variation running on Linux compiled with gcc can execute the binary format generated by the Windows NT version of ScriptBasic, but this is only the fact of life and it is not a guaranteed feature.

In certain situations you may want to save the binary format and execute the code the same time. In this situation you can issue the command line:

$ scriba –o hello.bbf -e hello.bas

The switch `–e' tells ScriptBasic to execute the code. This is not the normal behavior when binary format is saved to file. Note that you need the switch `–e' only when the switch `–o' is also on the command line. To execute a program without saving the binary format to a file you need no switches at all.

The binary format of the code is usually saved into a directory without user intervention. The cache directory is defined in the configuration file using the configuration key cache. In certain situation you want to avoid caching of the code. If you use the command line option –n neither cache checking for newer version nor cache writing will be performed.

You should use the `-c' option in programs that are executed as CGI scripts. This helps the error reporting system to cooperate with the web server and send properly formatted HTML formatted errors whenever and error occurs.

The option `-C' asks the interpreter to generate C language code. For more about this option read in Compiling BASIC programs to C.

The option `-p' should be used to specify an external preprocessor. This preprocessor has to be configured in the configuration file of ScriptBasic. Ask the system manager responsible for your installation about the available preprocessors. If this person is you read the section Using external preprocessor. You can specify more than one preprocessors on the line. These are executed one after the other in the order they are specified on the command line. If no preprocessor is defined on the command line the interpreter will execute the preprocessor or preprocessors that are assigned to the extensions of the source file name. The assignments should be configured in the ScriptBasic configuration file.

The last switch is `–v', which can be used to print version information.

$ scriba –v
ScriptBasic v1.0
Variation >>STANDARD<< build 19
Magic value 859001650
Node size is 16
Extension interface version is 5
Compilation: May 22 2000 01:55:12

When you use this switch ScriptBasic does not execute any program. Instead it prints out information on itself and stops. The information lines printed are:

Note that any command line option should be specified before the name of the basic executable. Any command line option or argument specified following the name of the file containing the basic program in text or binary format will be passed to the basic program itself. The basic program can use the function command() to access this part of the command line.

There is a rarely used command line option -d. This should be used in case some modules can not be loaded by ScriptBasic and you want to know what the problem is. If this option is given on the command line the low level UNIX function dlopen prints the system error message on the standard error when a module is not loadable.

[Contents]

2.2. Running CGI programs

Although this topic should have been out of the scope of this documentation it is described here in detail because of its importance. Several users want to run ScriptBasic programs as CGI scripts.

Running CGI programs is just the same as running command line programs if you are experienced configuring your web server. The major difference is that it is the web server that starts the command line program and not the shell (or command.exe).

Under UNIX you can start a ScriptBasic program the same way as any other CGI scripts. The very first line of BASIC program should be

#! /usr/bin/scriba

assuming that the executable code is called scriba and it is placed in the directory /usr/bin. The text file containing the code should also be executable for the user who runs the CGI scripts. This user is usually called nobody. You may say

$ chmod a+x hello.bas

to give all users execute permission. This may impose security questions that we do not discuss here. You should really know what you are doing.

On Windows NT using Internet Information Server the situation is different. Here you have to associate the extension with the executable of ScriptBasic. Note that this is not the same association as the one that allows the explorer to run the program when you double click on it. You have to configure the association in the Internet Service Manager configuration program.

You have two different possibilities. One is to run your BASIC programs as real CGI programs. In this case the Web server IIS starts a new process for each execution of your programs. The other possibility is to use the ISAPI version of ScriptBasic. In this case the extension has to be associated with the DLL code of the interpreter. When running the IIS version of the interpreter you can gain much faster execution. This is because the ISAPI version runs in the process of IIS and does not start a separate process for each interpreter. Further the ISAPI version not only uses the precompiled cache files that all variations of ScriptBasic uses but also keeps the precompiled code in memory.

To use the ISAPI variation of the program you have to install and configure it. For further information how to it please read the document winisapi.

[Contents]

2.3. Writing CGI programs

There are two ways to write CGI programs in ScriptBasic. The old way is just like in any other language: the basic program can access the environment, the standard input and standard output. This is all needed to write a CGI program, decode the CGI parameters and create the http response.

The better way is to use the CGI module delivered with ScriptBasic that automatically handles CGI input, CGI environment variables, creates the uploaded files and even supports some security settings. For more details on the CGI module read the separate documentation of the module named cgimodu.

[Contents]

3. Installation Instructions

Installation of ScriptBasic is easy. Although there is no SETUP.EXE to install the program, it does not require you to be rocket scientists to install under Windows NT. Under Linux you can use the Debian or the RedHat packages to install the program. Under other unices you should build the and install the program from source that should be as complex and sophisticated as typing make install.

[Contents]

3.1. Installation under Windows

Download the ZIP file containing the compiled executable files and the documentation. You have probably have done this as you are reading this document. If you instead downloaded some other file that included this document then download the latest ScriptBasic binaries ZIP file and extract it to the directory

C:\ScriptBasic

In case you want to install it on a different disk or in a different directory feel free to use the file explorer and move the files. After you have done this you may want to alter the system PATH to include the directory where the executable is. In case you use the default installation directory this is

C:\ScriptBasic\bin

On Windows 2000 to do it click on the start button, control panel, system. Choose the tab Advanced and then the middle button with the text Environment Variables...

Select the variable Path either from User variables of from System variables. Press the button Edit..., press "Home" to get to the start of the line and type the directory of the ScriptBasic binaries, like C:\ScriptBasic\bin;. Do not delete any of the characters already in the edit box, and do not forget the separating semicolon.

You can find a file `scriba.conf' in this directory. This is the configuration file of ScriptBasic. Do NOT try to edit this file using notepad. This is a binary format file created using the program `cftc.exe'. If you want to change any configuration options edit the file `scriba.conf.lsp' and generate the new `scriba.conf' file typing the command line:

C:\ScriptBasic\bin> cftc scriba.conf.lsp scriba.conf

When ScriptBasic starts it searches for the configuration file in the same directory where the executable is (this is a Windows only feature). If you want to store the configuration file in a different directory then use the registry editor and set the key

HKEY_LOCAL_MACHINE\Software\ScriptBasic\config

to hold the full path file name of the configuration file (not the text version but the converted binary).

After the successful installation run some test programs.

[Contents]

3.2. Installation under Linux

To install ScriptBasic under Debian Linux is as simple as typing

dpkg --install scriba-v10b27-1_i386.deb

If you happen to install ScriptBasic on a RedHat Linux then you can download the RPM version of the program and say

rpm -i scriba-1.0b27-1.i386.rpm
This will install and configure ScriptBasic form most of the systems. In case the directory structure of the standard installation does not fit your needs then you can edit the configuration file scriba.conf.unix.lsp and convert it to binary version using the command line:

#/usr/bin/cftc scriba.conf.unix.lsp /etc/scriba/basic.conf

This is the default configuration file that ScriptBasic reads and all other directories and locations are taken from this file.

In case you want to create a version of ScriptBasic that links some external modules statically or want to play around with the source then you have to recompile the source. Because this is the standard installation mode for any operating system that is not Windows nor Linux, read on in the next section how to do it.

[Contents]

3.3. Installation under other Unices

On Windows NT and Linux (Debian or RedHat) the installation is simple. On other operating system the installation is a bit more sophisticated. You have to extract the tar.gz source file into a directory and have to type:

make install

Cross your fingers and in case you are lucky all went fine. If there are some errors that prevent the compilation of the programs then try to find out what the issue is.

On Tru64 UNIX the Makefile has to be edited to change the line

LD=ld

to

LD=

because this UNIX links the dynamic library handling routines by default.

Some unices need the `-lm' in the linker options, thus

LD-ld -lm

may be the appropriate.

Note that the Makefile starts `install.sh' that tries to create the Debian package. That may not work on unices not Debian.

If you have other issues, drop an eMail on the mailing list and we are ready to help you. This is especially important because in this case we can incorporate the change into the next version and this way you help others as well.

[Contents]

3.4. Configuration

To reach advanced features, like using system include files from predefined directories or using dynamic modules you have to create a configuration file. This configuration file is probably different for most installation and reflects the directory structure, networking environment and other specialties of your setup.

The format of the configuration file is binary. This is to speed up configuration information reading. To create this binary format, you have to edit the text format of the configuration file usually named scriba.conf.lsp and have to convert it to binary format using the command line

cftc scriba.conf.lsp scriba.conf

The configuration compilation tool cftc is shipped with ScriptBasic. There is also a counter program named cftd that dumps binary configuration files into text format. Note that this dump program will not restore the comments of the original configuration file nor the line breaks or spaces (except those in strings).

The format of the text file version of the configuration information is simple. It contains the keys and the corresponding values separated by one or more spaces and new lines. Usually a key and the assigned value is written on a line. Lines starting with the character ; is comment.

The values can be integer numbers, real numbers, strings and sub-configurations. Strings can either be single line or multi-line strings starting and ending with three """ characters, just like in the language ScriptBasic or in the language Python.

Sub-configurations start with the character ( and are closed with the character ). The list between the parentheses are keys and corresponding values.

The keywords of the current version are:

ScriptBasic ignores all lines containing a key that it does not understand. This is dangerous on one hand because it makes typing errors less recognizable. On the other hand it allows different variations share common configuration file. The external modules implemented as dynamic load library functions can also access any configuration data.

An example configuration file from a Windows NT installation:

; scriba.conf
; ScriptBasic sample configuration file
;
; Note that this configuration file format is from v1.0b19 or later and has to be compiled
; to internal binary format before starting ScriptBasic

; this is the extension of the dynamic load libraries on this system dll ".dll"

; where the modules are to be loaded from module "d:\\MyProjects\\sb\\modules\\" module "d:\\MyProjects\\sb\\sysmodules\\" module "c:\\ScriptBasic\\modules\\"

; where to search system and module include files ; trailing / or \\ is needed include "d:\\MyProjects\\sb\\source\\include\\" include "c:\\ScriptBasic\\source\\include\\"

; ; define external preprocessors ; preproc ( ; extensions that preprocessors are to be applied on extensions ( ; here the key is the extension and the value is the symbolic name of the external ; preprocessor heb "heb" ) ; the external preprocessors external ( heb ( executable "D:\\MyProjects\\sb\\Release\\scriba.exe d:\\MyProjects\\sb\\source\\heber.bas" directory "d:\\MyProjects\\sb\\hebtemp\\" ) ) )

; ; LIMIT VALUES TO STOP INIFINITE LOOP ;

; the maximal number of steps allowed for a program to run ; comment it out or set to zero to have no limit maxstep 30000

; the maximal number of steps allowed for a program to run ; inside a function. ; comment it out or set to zero to have no limit maxlocalstep 0

; the maximal number of recursive function call deepness ; essentially this is the "stack" size maxlevel 300

; the maximal memory in bytes that a basic program is allowed to use ; for its variables maxmem 1000000

; ; ScriptBasic loads the modules before starting the code ; in the order they are specified here ; ;preload "ext_trial"

; ; This is the directory where we store the compiled code ; to automatically avoid recompilation ; cache "d:\\MyProjects\\sb\\cache\\"

isapi ( ; ; where to write error messages when running the isapi interpreter ; report "d:\\MyProjects\\sb\\error.log"

; ; When an error happens this file is sent to the client after the ; listed error messages. Error messages are recorded in the error log file ; if there is configured any and are also sent to the client browser. ; after the list of error messages this file is sent. This is actually the tail ; of an HTML file. It has to contain </TT></FONT></BODY></HTML> tags in this ; order with optional extra text between. You probably want to place the webmaster ; eMail address in this text. ; errmesfile "d:\\MyProjects\\sb\\source\\error.html"

; ; If this config key exists scribais.dll will cache the compiled code ; into memory. To avoid memory caching during development comment this ; config line out. ; ; Note that if there is a code in memory cache it is used even if the code on ; disk has changed! This results fast execution but may be inconvinient during ; development. ; ; memcache

; ; This parameter is used by the CGI module when the interface is ISAPI ; This parameter gives the size of the buffer that the module uses to handle ; POST parameters above 48K (usually uploads). The larger the buffer is the ; faster upload handling can be. ; buffer "655360" )

cgi ( ; ; These are the keys used by the CGI module ; debugfile "d:\\MyProjects\\sb\\cgidebug.txt" )

Although Windows NT programs are usually configured using the registry or active directory services ScriptBasic uses a text file to ease and maintain compatibility with UNIX versions.

When the program starts one of its first action is to search the configuration file. The search for the configuration file is different on Windows NT and under UNIX operating system.

[Contents]

3.5. Configuration file location under Windows NT

Win32 installation first tries to open the file scriba.conf if it exists in the same directory as the executable file. This is a convenient place to store the configuration file and does not require registry editing to install ScriptBasic. This is also the ultimate place for CD ROM products utilizing ScriptBasic that need running it without any installation.

If the file `scriba.conf' does not exist in the directory of the executable the configuration manager tries to open the file, which is specified in the string value, named config under the registry key HKEY_LOCAL_MACHINE\Software\ScriptBasic. If there is no name specified in this registry value ScriptBasic tries to locate the configuration file `scriba.ini' in the system directory. The system directory is determined reading the environment variable windir. If this environment variable does not exits ScriptBasic tries systemroot. It should usually exist on normal Windows installation. If ScriptBasic can not find even this environment variable it tries `C:\WINDOWS' as final try. If no configuration file can be found ScriptBasic tries to execute the program without configuration information.

Note that if for example `C:\WINNT\scriba.ini' exists and is valid, but the registry defines a different and a non-existent or invalid file ScriptBasic will fail to load the configuration file. It will try to read the file specified in the registry. The other file options are searched when the registry key does not exist or is empty.

[Contents]

3.6. Configuration file location under UNIX

UNIX installations try to locate the configuration file from the environment variable SCRIBACONF. This environment variable should contain the configuration file name. If this environment variable does not exist ScriptBasic tries to load the configuration data from the file `/etc/scriba/basic.conf'.

Note that if the file defined in the environment variable SCRIBACONF is non-existent or is invalid ScriptBasic does not try to load the configuration from the default configuration file.

If you can not configure ScriptBasic configuration file name using environment variables or the system registry you can modify the source file `configurer.c'. You have to modify the value of the macro DEFAULTCONF.

[Contents]

3.7. Overriding the default configuration file

On both UNIX and Windows NT you can specify a configuration file on the command line. If you specify the file name of the configuration file on the command line following the option `-f' then the specified configuration file is going to be used instead of the default.

[Contents]

3.8. Trouble shooting installation

Although installation is quite simple there can be some pitfalls that we and our users have experienced during installing ScriptBasic. To help you we list the possible error.

[Contents]

4. Code caching

The interpreter has to do several things before actually starting the BASIC code. It has to read the file, split it into tokens, analyze the program and build the internal executable format. This takes a lot of time especially for programs longer than a few lines. This can be skipped if the code built up is saved into a file and the file is loaded and run without the recompilation.

To save the binary format file after the BASIC program was analyzed and build the `–o' option can be used. To load and run the code the binary file can be used instead of the text file containing the BASIC code. The interpreter automatically recognizes the binary format and does not try to interpret it as BASIC program source. To help the interpreter you can also supply the option –b on the command-line that tells the interpreter to believe without checking that the file is binary format of the basic program. This is one solution to speed up execution of the ScriptBasic programs and to avoid recompilation of the code several times. The other method is caching.

The configuration file (see section Installation Instructions) may contain a value for the key cache. The value for this key should specify a readily existing directory (do not forget the trailing / or \) where the BASIC interpreter can store the binary format of the code. If there is a valid value specified as cache directory the interpreter checks the cache for an already existing binary format file and if that is newer than the BASIC source it neglects the BASIC source and loads the binary format from the cache.

On the other hand whenever a BASIC source code was analyzed and built the resulting binary code is saved into the cache. This method is transparent for the user and significantly speeds up program execution especially for CGI programs that run many times.

To avoid cache using you can use the command-line option –n.

Caching has some limitation that can result unwanted behavior. Before switching on program code cache, please read the following sections that describe the shortages and the security issues that should be considered.

[Contents]

4.1. How cache file name is calculated

The cache file name is calculated from the source file name. The applied algorithm calculated the MD5 digest of the source file name and converts it to a 32-character string. The digest itself is 16 bytes long. The conversion to ASCII takes the 32 half-bytes. Each half byte can have a value from zero to 15. Zero is converted to A, 1 to B and so on. This method was introduced in ScriptBasic v1.0build18 and eliminates some shortages of the algorithm of former versions.

[Contents]

4.2. Cache handling shortages

The cache directory contains the cached binary format program codes for the BASIC programs executed. For each BASIC program a new file is generated when the BASIC source code is analyzed and built.

The file name for the cache file is calculated using the file name. This file name includes the full path to the file under Windows NT but is the bare file name as supplied on the command line under UNIX.

This means that if you have two different programs named hello.bas in two different directories and you run one after the other from their directory issuing the command line under UNIX

scriba hello.bas

the second execution will erroneously think that the cache file created when the first one was running belong to this program and will execute the binary code.

On the other hand if you execute the program hello.bas from two different directories and the supplied paths are different the caching mechanism will not realize that the two execution refer to the same file.

Also note that cache handling does not compare the modification time of the files that basic program includes. If the program itself does not change, but an included file did the caching will still run the older version.

Caching usually works and in case you experience mysterious problems you can try to avoid caching using the option `–n' or commenting out the configuration line. Web servers usually specify full path to the executed code and in that case cache file name conversion is correct. Especially Apache does start CGI programs passing the full path to ScriptBasic as argument and thus caching works well with this web server.

It is advised to switch the caching totally off during development and switch on at the start of the test phase. On test environment delete the cache frequently whenever you experience mysterious errors that you think you have already corrected.

[Contents]

4.3. Cache security

Caching is great, but there are some security issues that you have to understand before allowing caching. The ScriptBasic interpreter is usually available to all users in an installation. The cache directory should be created for the interpreter and the security of the directory should be set so that each user can read and write the directory. This will impose security risks in a shared environment.

[Contents]

5. Compiling BASIC programs

BASIC programs can be compiled to a tokenized form. This format is the internal format of ScriptBasic and this is always generated before executing a BASIC program. To speed up execution ScriptBasic is capable saving this code into cache files automatically. However in some situation you may want to save this format into a separate file. To do this you have to issue the command

scriba -no myprogram.bbf myprogram.bas

This will save the code into the file `myprogram.bbf'. After having this file you can execute the BASIC program issuing the command

$ scriba -b myprogram.bbf

or

$ scriba myprogram.bbf

On UNIX you can make the compiled file to executable setting the permissions

$ chmod u+x myprogram.bbf

and after that you can just write1:

$ myprogram.bbf

Please note that binary format files may not be executed on different versions or builds of ScriptBasic and are not movable from one platform to another. You may be lucky to execute Windows NT and Linux version bbf files on the other platform, but this is not a guaranteed feature. You surely can not run the binary format generated on a DEC OSF/1 (ooops, sorry Tru64 UNIX) on a 32 bit system. I tried it.

[Contents]

5.1. Binary format of the BASIC code

In this section we describe some features of the binary format of the BASIC programs saved by ScriptBasic automatically into the cache directory or to a file using the option -o. There is absolutely no need to know or understand the binary format of ScriptBasic for those who want to program in ScriptBasic. However advanced users may want to perform some modification on binary format BASIC programs, like changing the interpreter location in the start line. For those this section describes the details.

The binary format is binary. It starts with some constant leading code to ensure that no erroneous execution starts and to avoid memory corruption. This leading code helps ScriptBasic to recognize the binary format even if the interpreter was started without specifying the option -b.

The binary format may, however start with a textual line specifying the interpreter. This is needed to make the binary format file executable on UNIX, for example to run a CGI program. This line is interpreted by the UNIX operating system and is ignored by the interpreter. Because this line has no meaning on Windows NT the line HAS TO be terminated by a line feed and not CR/LF. You need not worry about it, because this is created this way on Windows NT as well as on UNIX. This first line is copied from the source BAS file when the binary format is created.

You may want to change this line when you want to run the code on a machine having the interpreter located at a different location. To do this you can use the following sample ScriptBasic code:

cmdlin = command()
split cmdlin by " " to FileName,Interpreter
open FileName for input as 1

binmode 1 File$ = input(lof(1),1) close 1 if left(File$,1) = "#" then i = 1 while i < len(File$) and mid(File$,i,1) <> "\n" i = i+1 wend if mid(File$,i,1) = "\n" then File$ = "#!" & Interpreter & mid(File$,i,len(File$)) end if

open FileName for output as 1 binmode 1 print#1,File$ close 1 end if

This leading line is optional. The ISAPI version of the interpreter, for example, does not put this optional line into the cache files.

The first byte of the binary code (following the optional command line) is the ASCII number of the size of a long on the actual platform. This is currently 4 on Linux and Windows NT, Windows 95/8 and it is 8 for Tru64 UNIX.

This is followed a magic code. This is 0x1A534142. On DOS platforms this is printed as BAS^Z and prevents dumping the code to the screen if one attempts. This magic code is saved as a long. This means that the order of the bytes follows the order of the bytes in a long in memory on the machine the code was saved. This is followed by six long values. These are

The following eight bytes contain the name of the variation that created the binary code. Because some variations do not differ in binary format from the STANDARD variation they create binary file saying it being STANDARD.

To successfully load a binary format file to run in ScriptBasic the long size, the magic code, version information including the build and the variation string should match. Date may be different.

Following this header the real information of the executable code is placed. The following four long numbers present in order the

Following this the nodes come one after the other. ScriptBasic prints the size of a single node in bytes when the option `-v' is used. This is usually 16bytes on 32bit systems and 24bytes on 64bit systems. If you get smaller node size printed then you will face alignment problems. If you get bigger node check your compiler options. ScriptBasic was not tested on 128bit systems up to now.

The string table follows the nodes. This contains all the strings zero character terminated that present in the code. There is at least a zero valued byte in the string table even if the program does not contain any string constant.

There can be arbitrary data following the string table, they are ignored.

[Contents]

6. Compiling BASIC programs to C

Although ScriptBasic is an interpreted language it is possible to create stand-alone executables from BASIC programs. To get an executable standalone program first you have to compile your BASIC program to C and the C code has to be compiled to get the executable.

To get the C version of the BASIC program you should use the command line option C, for example:

scriba –nCo myprogram.c myprogram.bas

This command will compile the BASIC program myprogram.bas and save the code in C language format into the text file `myprogram.c'. You have to compile this file using a C compiler and link it with the library file `basicc.lib' on Windows and with `basicc.a' under UNIX. The UNIX installation puts `basicc.a' in the directory `/usr/lib'. If you compile it under Windows NT or Windows 95/98 you may also need to link it with the system library `ws2_32.lib'. After having this done you should get an executable, binary file running your BASIC program.

When compiling BASIC to C do not forget to specify the output file using the option `-o'. Without this option ScriptBasic does not perform the compilation, there is no default file name for the output.

Note that this is not a real compilation. This methodology only creates the intermediary code that the ScriptBasic execution module uses and puts it into a C program. The final executable will contain the binary code of the BASIC program and will execute the same way as it would execute running the original file.

The achievements you can get compiling the code and generating standalone executable file are:

Also note that you have to have a properly created configuration file and the dll or so modules on the system your executable is going to run in case you need some features that rely on configuration settings.

You may also consider recompiling the ScriptBasic interpreter to statically link some of the external modules that your BASIC program uses.

[Contents]

7. Compiling ScriptBasic with modules

This section is rather technical and not for the BASIC programmer. This section details how to compile the ScriptBasic interpreter so that it already includes some of the external modules. This requires knowledge how to compile C programs and some programming practice of C is also helpful. If you just want to program in BASIC do not read this section and get distracted by this, skip to the next section.

ScriptBasic supports external modules that are usually delivered as DLL or SO files and using them do not require the recompilation of the interpreter. However in some cases there is a need to compile the ScriptBasic interpreter and some of the external modules into a single executable image. This may be the case when you deliver a solution to a customer and want to build a special version of the interpreter lessening the possibility of misconfiguration or you just simply want to protect your intellectual property implemented in the external module and do not want to deliver it as DLL or SO file. Linking some modules static into the interpreter also provides some speed improvement.

To link some modules into the interpreter you have to create a file named lmt_XXX.def If you are a hacker minded person you may use a different file name, however we decided this file name convention and you better stick to it. This file should list the names of the modules that you want to statically link. There can be empty lines and lines starting with the character # as comment. You can use the characters XXX (not necessarily three character, but try to avoid the word httpd and none as we have already have those) to distinguish your compilation.

Use the command line:

perl lmt_make.pl lmt_XXX.def

This will create the file lmt_XXX.c. Now check that the modules that you want to link are statically linked. Some modules, like the cio, cgi and mt modules are prepared to be able to be linked statically, but others are not yet. Do not get distracted, you can prepare them. Find the interface source program, like `bdinterf.c' or `gdinterf.c' or `hash.c' and create a table at the end of the file named ZZZ_SLFST where ZZZ is the name of the module as it was listed in the file `lmt_XXX.def' but in all caps. The table is a C syntax table that should look like:

SLFST CGI_SLFST[] ={

{ "versmodu" , versmodu }, { "bootmodu" , bootmodu },

some lines are deleted for documentation readability

{ "defsfile" , defsfile },
{ "getfile" , getfile },
{ NULL , NULL }
  };

If you look into the file `lmt_XXX.c' you will see that this table will be referenced by the global table StaticallyLinkedModules. You have to give the name of the function as a string as well as the function as well. This table will be searched by ScriptBasic when the module is "loaded". When a module is loaded from a DLL or SO file such a table is provided by the operating system and is searched by operating system functions.

The next step is to compile the modules for the static linking. This is a bit different from compiling for DLL or SO file as result. When the module is compiled to be linked into a DLL or SO file all interface functions should be exported so that the operating system can access their name and entry point. When the module is compiled to be linked together with the interpreter all functions should be static. If we compiled the module the same way as for targeting DLL or SO file we were not able to link more than one module into the interpreter because the linker would complain about multiple defined versmodu and other functions that are defined in each module.

To compile a module for static linking you should use the compilation option `-DSTATIC_LINK=1' that tells the C preprocessor to define some macros differently than normal. If you look into the `Makefile' or `Makefile.nt' you will see that these compilation options are already there and all module interface files can be compiled using the tool make to its static version which is named `s_module.o[bj]'. The prefix s_ is used to denote that this version of the object file was compiled from the same C source using the options that are required for static linking of the interpreter.

Choose an appropriate executable name XXX for your version and edit the Makefile. Create the rule that compiles your version. Your version will eventually use `lmt_XXX.c' and `lmt_XXX.o[bj]' instead of `lmt_none.*' and the executable will depend on the source and should be linked with the compiled object files and libraries of the modules. As an example you can look at the variation sbhttpd that includes the CGI and the MT module linked into it.

Type

make XXX

And you should get your executable.

[Contents]

8. General Language Format

[Contents]

8.1. Hello Word

ScriptBasic is a BASIC language. As such it is line oriented. The commands follow each other in the source file lines. The simplest example, the usual "hello word" example is a one-liner in basic:

print "HELLO WORD"

You can write the commands upper or lower case or even mixed case. Therefore the following lines are equivalent:

Print "HELLO WORD"
print "HELLO WORD"
PRINT "HELLO WORD"
PrInt "HELLO WORD"

[Contents]

8.2. Strings

Strings are most frequently used entities in a BASIC program. As you could already see in the very first example strings are quoted with double quote. But they can be more than just characters between double quotes. String as a type is a basic data type of ScriptBasic. A variable having string value contains several bytes. There is no length limit for a string other than the virtual memory of the machine. A string may contain character of any code even characters that have the ASCII code zero. In other words a string is a collection of bytes of certain length. You can treat strings as arbitrary length of binary data if you need.

Strings can be concatenated, you can cut off a part of a string and several other functions and operators can handle strings. ScriptBasic automatically allocates the space required to store the string and releases the unused space when the string is not needed anymore. For more information on string handling operators and functions see the chapter 24.2.

The simplest form of a string is a strings constant appearing in the source file. This is the form like in the example

print "Hello Word\n"

Here you can note that there is a special character at the end of the string denoted by two characters. This may be familiar from other languages. The characters \n denote a new line character in a readable form. The \ (backslash) character is the escape character in a string altering the meaning of the character that follows it. The special characters that ScriptBasic handles are:

All other characters remain the same after a backslash. This means that you can write \\ to have a string containing a backslash character, or \" to have a string containing a double quote character.

There is another way to include special characters into string constants. The usual way in basic is to split the string into sub strings and concatenate the parts during run time, like in the code fragment:

St = "This is a special string, containing a bell character at the end" & chr(7)

You can do this in ScriptBasic, but you can do it easier and more effective:

St = "This is a special string, containing a bell character at the end\7" 

The last character is a number preceded by a backslash. Whenever numbers follow a backslash character in a string ScriptBasic calculates the value of the numbers and uses the character of the code. If the first character after the backslash is zero then the number is calculated as octal number, otherwise it is calculated as a decimal number.

Strings should not contain the new line character. In other words

St = "this is
a multi line
string of three lines."

is not legal in ScriptBasic. It was legal in former versions before v1.0build15 to write multi-line strings, but it caused problem to programmers forgetting the closing double quote character on a line. Instead a new string constant format was introduced that starts and ends with three double quote characters2. For example:

St = """this is
a multi line
string of three lines."""

is perfectly correct. Strings starting and ending with three " characters can, but need not span multiple lines. There is another difference between single-line and multi-line strings. A multi-line string may contain a double quote character without escaping with backslash. You can write:

St = """this is " a double quote character """

or you can write

St = """this is \" a double quote character """

which is also correct. The only situation where you should escape a double quote character in a multi-line string is when you want to have three or more " characters following each other in a string. For example

St = """this is """ three double quote characters """ THIS IS WRONG

is wrong. You have to type instead:

St = """this is \""" three double quote characters """

or

St = """this is "\"" three double quote characters """

or

St = """this is ""\" three double quote characters """

or

St = """this is \""\" three double quote characters """

These solutions are equally correct. The simple rule is that a multi-line string should not contain three consecutive " character so that none of those three is quoted with backslash.

There is another constrain regarding multi-line strings. The very first character of a multi-line string should not be the character & or it has to be escaped with the backslash character. Thus:

St = """\& is a 
multi-line string starting with an & character """

is the correct format. If you use the & character without escaping it as the very first character of a multi-line string it will be treated as a binary multi-line string. A binary multi-line string is a very special beast that only hard code users need to specify binary data inside a BASIC program. In such strings all new-line characters are ignored unless escaped with the back-slash character. Thus the following two strings

"""&is a 
binary multi-line\n string starting with an & character """

"is a binary multi-line\n string starting with an & character "

are equivalent. Single-line strings can not be binary, and in case the first character of a single-line string is the & character that is just treated as a normal character.

We have to admit that disallowing single quote string to be multi-line is incompatible with former versions. On the other hand we have removed the constant string length limitation in v1.0build15. Since this build there is no limit on the length of string constants other than the virtual memory available to the program.

[Contents]

8.3. Numbers

There are two type of numbers in ScriptBasic:

The integer numbers can be used to represent integral values, while real numbers can be used to represent number that have fractional part or are too large to store as integer. The integer numbers are stored in a memory location of size equivalent to a long of the programming language C. The real numbers are stored internally as C double.

Number constants can be used in the basic program in the usual format. Integer numbers are represented in either decimal or hexadecimal format. Decimal numbers contain only digits. Hexadecimal numbers start with the characters 0x or 0X and are followed by hexadecimal digits. The format that many basic implementation follows using the &H characters to start a hexadecimal number is also allowed. The following numbers are valid integer constants in ScriptBasic:

There is no difference between decimal and hexadecimal numbers for ScriptBasic. The lexical analyzer converts both format to internal representation and stores the value of the number. In other words wherever you are allowed to use a decimal number you are allowed to use a hexadecimal number as well. You should decide whether to use decimal or hexadecimal number for your convenience.

Real number constants can only be decimal and may contain fractional and exponential part. The followings are valid real number constants:

3.14
1.0
1.
2.3E-7

2.3e7 2.3E+7

Note that the exponent part is preceded by the character e or E and is followed optionally be a sign. The number 1. is a real number and is stored in a C double internally although it could be integer. ScriptBasic will store this number as a real number. However real numbers are automatically converted to integer values whenever ScriptBasic needs an integer value.

[Contents]

8.4. Variables

Variables are core entities of ScriptBasic. Variables are used to store string, real or integer values. Variable names start with alpha characters, underscore, dollar sign or colon, and from the second character they may contain digit characters in addition. The only restriction is that the last character should not be colon.

Whenever you need a variable choose a meaningful name. If you are a real old BASIC programmer use trailing $ to denote string variables. However ScriptBasic can store any value in any variable, one at a time of course.

The colon as a name character is allowed to help name space management, and you should use it only for the purpose.

The following are valid and invalid variable name examples:

myvariable	This is a perfectly legal variable name.
main::var	This is OK. This variable is in the name space main.
chr$	This is invalid. Chr$ is a reserved word, this is a built-in function
apple$	This is OK. You will use it probably to store strings.
b:2	This is valid, but it is recommended not to use : inside variable names, because it is unreadable. Use colon only to separate hierarchical name space and variable name.
_mother	If it is your taste to start a variable with underscore you can.
Beee$bop	Valid, but it is not recommended. The dollar sign is allowed in variable names to allow the usual BASIC string variable notation. System or application specific extensions may use predefined global variable names that contain a $ sign inside. Using such variable names you may get into conflict.
::boo	This is valid. This variable is explicitly noted to be in the current name space.
_::baa	This is valid. The variable is in the parent name space.

For more information on name spaces read the chapter 10.

Variables can contain any data in ScriptBasic. There is nothing like integer or string variable. A variable may contain integer value at a time a real value another time and string value later. You can use a variable name to use real, integer or string value at a time; later you may use the variable as an array; later as a real again. You can change it any time.

Variables can be local or global. Global variables are those that are not declared but used. Any variable is global unless it is declared to be local. Local variables are local to the function or subroutine in which they are declared to be local. To declare local variables you should use the command local.

REM This is a sample program to demonstrate local variables

'A is a global variable A=13 Call MySUB Print A,"\n",B

Sub MySUB Local A A=9 B=55 End Sub

The output of the program is

13
55

This is because the variable B inside the sub is global, but the variable A is local and as such it does not alter the value of the global variable A. You can define one or more variables to be local in a local statement. If you declare more than one variable to be local then you have to separate the variable names with commas.

[Contents]

8.5. Constants

Constants are values that the program can not alter. Unlike variables constants have a single value and this value does not change during program execution. The strings and numbers that are used in a program are naturally constants. They are the simplest forms of constants without a name. However you may want to name constant values. You could store the value in a named location, in a variable. But that consumes an extra variable and slows down execution.

ScriptBasic allows you to define constants using the format:

const name=value

or

global const name=value

where name is an identifier and value is a string or a number. You can not specify an expression as a value, only constant string or number. Later you can use the name of the constant in any expression where you could use the value of the constant. If you have the constant declaration:

Const nl = "\n"

then the following two lines are identical, they create the same executed code and therefore execute the same speed and need the same size of memory:

print 6+6,nl
print 6+6,"\n"

ScriptBasic evaluates named constants during syntax analysis and the name of the constant is replaced by their defined value in the expression where the constant is used.

Named constants are local unless you use the global keyword. If you declare a constant in a module (see chapter 10 for more on modules and name spaces) the named constant becomes part of the name space and you can refer to the constant only with full name space specification from outside of the module. If you declare a named constant inside a function or a subroutine the constant becomes local, and can not be used outside of the subroutine or constant.

If you use the keyword global the constant is declared global. This means that the constant is not part of any name space and can be used anywhere in the program after the line it was declared.

When ScriptBasic finds an identifier in an expression, which is not a built-in function or reserved word it checks the followings until a check succeeds:

Constants can be redefined. When a named constant is defined in a const statement ScriptBasic does not check if the constant has already be defined. It is legal to change the actual value of a constant. But note that this does not make a constant to be a variable. The change of the value is performed during syntax analysis and not during execution. Const statements do not generate any executable code and therefore are never executed.

When an identifier is declared as a constant the identifier can not be used as variable in expressions following the program line that defines the constant. Sometimes you want to delete a defined constant and use the identifier as variable again. To use an identifier as a variable that was already defined as constant you can use the command

var name

where the name is the identifier. This declaration tells ScriptBasic that the identifier is not a constant anymore. This declaration is similar to the normal, non-global const declaration. If you write a var statement inside a function or subroutine the identifier becomes a local or module specific global variable in the function or subroutine only. If you write a var statement outside any function or subroutine the identifier becomes a variable in the module only.

To explain the behavior let us see an example:

module TEST
a = "VARIABLE"
Global Const a = "a "

Const b = "b "

sub TestSub const c = "c " print " values in TestSub=",a,b,c,"\n" var a print "a in sub=",a,"\n" end sub

print "values in the module=",a,b,c,"\n" print "values called from within the module:\n" TestSub var a print "a in module=",a,"\n" end module print "values outside the module=",a,b,c,"\n" print "values called from outside the module:\n" TEST::TestSub var a print "a in global=",a,"\n"

The output is:

values in the module=a b undef
values called from within the module:
  values in TestSub=a b c
a in sub=VARIABLE
a in module=VARIABLE
values outside the module=a undefundef

a is a global constant, b is module specific, but c is undefined here, because c can only be used as a constant only inside the sub TestSub. c here is a variable and is undefined. Note that a is a global constant here, although there was a var statement before the line printing this text and the line defining a as global constant. This is true because the var statement is inside the subroutine TestSub, and has effect only inside the subroutine.

a is a global constant, b is module specific and c is local constant inside the sub TestSub. This is true if the sub is called from inside the module or if it called from outside.

A var statement altered the behavior of a. Now this is a variable that holds the string "VARIABLE". Note that a was altered to be variable locally, but still a is a module specific global variable.

In this printout a is a variable, in fact a is the same module specific global variable as in the previous printout. But the same var statement did not cause its being variable. To have a as a variable here we needed another var statement outside the sub TestSub. The var statement inside TestSub altered the behavior of a only inside the sub, as you could see in the very first printout line.

a is a global constant again. The var statement defined it to be a variable only inside the module. b and c were local constants and therefore here these identifiers are variables that happen to hold the value undef.

values called from outside the module:
  values in TestSub=a b c
a in sub=VARIABLE

a in global=undef

We should not get a different printout from the subroutine, no matter where it was started. from Const and var statements are declarations that do not generate executable code.

Another var statement alters the identifier a to be a module specific constant in the default module, which is implicitly named main. And it is undefined, because the variable holding the string "VARIABLE" belongs to the module TEST.

To help understand even deeper the behavior of local, global, module specific constants and variables here we present a short description how the syntax analyzer handles the constants. You need read this only if you are curious.

There are several symbol tables during syntax analysis. ScriptBasic maintains a symbol table for the labels, one for global variables, one for local variables and a separate one for constants. When a constant is defined the name and the corresponding value gets into this symbol table. If the const statement is global the name is not altered. If the const statement is not global the name is modified to include the name space. This modification is the same as the modification for variables. After this modification an apostrophe character is appended to the constant name and the name of the actual function or subroutine if the constant is defined inside one. This is the same name decoration mechanism, which is performed for the labels.

When an identifier is found in an expression the syntax analyzer searches for the name module::constname’function in the symbol table if the expression is inside a function or subroutine. If the expression is in a global area – out of any subroutine or function – this search is not performed. If there is no such entry in the symbol table the analyzer searches for the symbol module::constname’. If this symbol is still not defined in the symbol table the analyzer searches constname.

If any of the names can be found in the symbol table a C pointer will point to the lexical element that is the replacement for the constant. If none of the names can be found in the symbol table the pointer is going to be NULL.

After this searches the lexical analyzer starts to analyze the lexical element, which is an identifier. If the pointer set by the search for the named constant is NULL then the actual lexical element is taken, which is an identifier and is treated as a variable by the lexical analyzer. If the pointer is not NULL the lexical element pointed by the pointer is used instead of the current lexical element. Thus the identifier is replaced by the value of the constant during syntactical analysis.

When a var statement declares an identifier to be constant it actually does a weird action inside the interpreter. It redefines the constant name module::constname’ or module::constname’function to be associated with the value NULL. In other words, when the syntax analyzer finds the constant name in the symbol table and retrieves the pointer that is supposed to point to the constant replacement lexical element it gets a pointer with NULL value. But it does find the constant name in the symbol table and the search finishes successfully. On the other hand the next step in the syntax analysis sees only the NULL value in the constant replacement pointer and thinks that no constant was found.

This means that if the basic program has a global constant and we declare the identifier to be a variable using a var statement the constant search stops when it finds it to be a local (either function or subroutine local or module local) constant. A local constant that happens to have a replacement pointer pointing to NULL. And it does not search for the global constant, because it did find a symbol table entry.

The same situation happens if a module constant is redefined using the var statement inside a subroutine or function.

ScriptBasic defines some named constants before it starts analyzing the program. These named constants always start with the letters sb, and their purpose is to help the programmer to set various options in option statements.

[Contents]

8.6. Arrays

Arrays are memory locations that store many values at the same time. While normal variables store a single value at a time, an array variable can store many values. The values are accessed via the name of the variable and the appropriate indices. The index or indices follow the name of the variable between [ and ]. This is the usual notation for most programming languages, like C, Perl, PASCAL, Pithon. Some BASIC implementations use ( and ) instead, but that confuses array access and function call.

In the following subsections we tell you how to use arrays.

[Contents]

8.6.1. Creating arrays

Any variable can become an array. In ScriptBasic arrays are automatically created. There is no statement like DIM. All you have to do is to use the variable like an array. Array subscripts are written using square brackets. This is usually the convention in PASCAL and in C. Other BASIC languages use normal parentheses to index arrays. That confuses the reader as well as the parser, because function call looks the same as array access. Therefore ScriptBasic uses the square brackets.

There is no limit on the number of indices. You can use as many as you like. Also there is no limit on the index values. Positive, negative or zero integers can play the role of an index. Whenever you access an array element or assign a value to that ScriptBasic automatically checks if the referenced array element exists or not and adjusts the array if necessary. For example:

a[1] = 3
a[5]=4
for i=1 to 5
print a[i]
print
next

is perfectly legal and prints:

3
undef
undef
undef
4

Arrays are handled quite liberal. You are not required to declare the index bounds, you need not declare the number of indices. As a matter of fact you can have different number of indices at different points in the array. For example the following code is also legal:

a[1] = 3
a[5,3]=4
print a[1],"\n",a[5,3]

You can even write:

a[1] = 3
a[5,3,1,6,5,4,3,6,4,3,2222]=4
print a[1],"\n",a[5,3,1,6,5,4,3,6,4,3,2222]

if you wish. But what happens if you write:

a[1] = 3
a[5,3]=4
print a[1],"\n",a[5]

ScriptBasic will print

3
ARRAY@#008C0BC8

or some similar message. What has happened? To understand we have to explain how ScriptBasic stores the arrays.

An array in ScriptBasic is stored as a list of C pointers. When a ScriptBasic variable first time used as an array a new array of a single element is created. It has one element assigned to the index that was referenced.

VARIABLE[6] = 555

Later, when other elements are referenced the array is automatically extended. For example if the array was first time referenced using the index 6 and it was accessed second time using the index 11 ScriptBasic automatically extends the array to contain six elements.

VARIABLE[11] = "a string"

This means that an array can consume significant memory even if only a few indices are used.

VARIABLE[10,3] =6.24E3

When an array element, let’s say index 10 is used with a second index, let’s say with 3 a new one-element array is created. Later this new array is treated the same as the one assigned to the variable itself, and when the element

VARIABLE[10,6] = 55

is set it is enlarged to be able to hold all values between indices 3 and 6.

When the variable in our example named VARIABLE gets a new value, like

VARIABLE = "NEW VALUE"

the arrays are released automatically.

When accessing an array element, which is an array itself ScriptBasic tries to do its best. However the result may not be what you expect. The following program:

a[1]=1
a[5,3]=4
c = a[5]
a[5,3]=2
print a[5,3],c

prints 24 in current version of ScriptBasic, but this may change later.

[Contents]

8.6.2. Functions lbound and ubound

Because array indices are automatically adjusted the lower and upper limit of indices of an array are not constant. They may change. To get the lowest and the highest index of an array that has assigned value (possibly undef) you can use the function lbound and ubound. The names stand for lower bound index and upper bound index.

For example the simple program

a[1] = undef
print lbound(a)," ",ubound(a)
print
a[2,-3] = "whoops"
print lbound(a)," ",ubound(a)
print
print lbound(a[2])," ",ubound(a[2])
print

will print

1 1
1 2
-3 -3

As you can see the argument to these functions can be a variable that has array value or an expression that has an array value. (Such an expression is likely to be an array element, which is an array by itself.)

When the argument is not an array the functions returns undef.

[Contents]

8.6.3. Deleting an array

Usually there is no need to delete an array. Arrays are allocated when they are needed and are released when they are not needed anymore. However there may be some circumstances, when there is a need to explicitly delete an array and release the memory assigned to the array. To do this you have to use command undef. The name is the same as the function undef, but instead of returning the undefined value this command sets the variables listed after the command name to hold the undefined value. The format is

undef variable list

The variable list may contain variables that are arrays, variables that hold string, integer or real values and may also contain array elements, like a[13], which is a variable itself. When you apply the command undef to a variable, which is not an array the result is the same as assigning the undefined value to the variable. For example the following lines

undef A
A = undef 

have the same effect unless A is an array. However when the variable A is an array assigning to it the undefined value will make the first element of the array undefined and not the whole array. To explore the effects try to run the following program:

sub fun(a)
print a,"\n"
print lbound(a),"\n"
print a[1]," ",a[2]," ",a[3]
print
end sub

k[1] = 1 k[3] = 2 undef k k = 7 fun k

After running it comment out the line undef k and compare the result running the program again.

[Contents]

8.7. Associative Arrays

Associative arrays were introduced in ScriptBasic v1.0build19. This section does not apply to any previous version.

Languages, like Perl or AWK have associative arrays and so does ScriptBasic. Associative arrays differ from arbitrary arrays in two points:

When you use a variable as associative array you can use strings, integers, real numbers and even the value undef as index value. Whenever you access certain element of an associative array the interpreter searches for the index value and uses the value associated with it. This leads to compact and easy to read programs.

For example:

const nl="\n"
a{"foo"} ="foo value"
a{"bar"} = "bar value"
a{"dummy"} = "dummy value"

print a{"foo"},nl

will print

foo value

If you use an index that is not present in the associative array the result is undef.

const nl="\n"
a{"foo"} ="foo value"
a{"bar"} = "bar value"
a{"dummy"} = "dummy value"

print a{"FOO"},nl

will print undef.

Note that in this situation a new element with the new key is created automatically containing the value undef.

Index values that are undef, integers or real numbers can be referenced using the exactly same value. When using strings as keys the matching is performed according to the option `compare'. The previous example altered a bit:

option compare sbCaseInSensitive
const nl="\n"
a{"foo"} ="foo value"
a{"bar"} = "bar value"
a{"dummy"} = "dummy value"

print a{"FOO"},nl

will find the key and print foo value, although the key used for definition and the key used for reference differ is case.

Associative arrays inside ScriptBasic are stored as normal arrays. The difference between normal array and associative arrays are the way the programmer handles them. Advanced programmers may and most probably should use associative arrays as normal arrays as well.

An associative array is nothing else than a normal array storing the keys on the even indices and the values on the odd indices. When you reference

a{"foo"}

the interpreter starts to search the string "foo" on the even indices of the array and when it finds it returns the next element. To explain it see the following sample:

const nl="\n"
a{"foo"} ="foo value"
a{"bar"} = "bar value"
a{"dummy"} = "dummy value"

for i=lbound(a) to ubound(a) print i," ",a[i],nl next i

will print

0 foo
1 foo value
2 bar
3 bar value
4 dummy
5 dummy value

You can see that the keys and values are paired up in the array. Knowing this you can iterate over the keys of an associative array:

const nl="\n"
a{"foo"} ="foo value"
a{"bar"} = "bar value"
a{"dummy"} = "dummy value"

for i=lbound(a) to ubound(a) step 2 print "a{",a[i],"}=",a{a[i]},nl next i

to get

a{foo} =foo value
a{bar} =bar value
a{dummy} =dummy value

Note, however that in case the same key present many times in the array using the a{} notation finds always the first value. For example:

const nl="\n"
a{"foo"} ="foo value"
a{"bar"} = "bar value"
a{"dummy"} = "dummy value"
a[6] = "foo"
a[7] = "never found"

for i=lbound(a) to ubound(a) step 2 print "a{",a[i],"}=",a{a[i]},nl next i

will print

a{foo}=foo value
a{bar}=bar value
a{dummy}=dummy value
a{foo}=foo value

and it never print never found.

Knowing that the associative arrays are just normal arrays inside one can even make dirty tricks:

const nl="\n"
a{"foo"} ="foo value"
a{"bar"} = "bar value"
a{"dummy"} = "dummy value"
a[-1] ="hoho"

for i=lbound(a) to ubound(a) print i," ",a[i],nl next i

print "--------------------------------\n" top = ubound(a) for i=lbound(a) to top step 2 print i," a{",a[i],"}=",a{a[i]},nl next i

will result

-1 hoho
0 foo
1 foo value
2 bar
3 bar value
4 dummy
5 dummy value
--------------------------------
-1 a{hoho}=foo
1 a{foo value}=bar
3 a{bar value}=dummy
5 a{dummy value}=undef

Adding an extra -1-th element to the array all indices became values and all values became indices.

Associative arrays can have multiple indices just like normal arrays. For example:

sub printit(zz)
  print zz{"foo"},"\n"
end sub
a{"dummy","bar"} = 13
a{"dummy","foo"} = 14
printit a{"dummy"}
print a{"dummy","bar"}, _
  " ",a{"dummy","foo"}

will print

14
13 14

[Contents]

8.7.1. Notes for using associative arrays

Strictly speaking there are no associative arrays in ScriptBasic. Rather arrays can be used in associative mode. This implementation of associative arrays is simple and is powerful enough to use small arrays with a small amount of keys. The typical use of associative arrays is to get a feature like record in PASCAL or struct in language C. Note that this type of storage is less than optimal in case it is used for a huge number of keys and values.

Searching the keys in the array is linear. It means that accessing a single element needs time proportional to the size of the array. The external module "hash" provides an implementation, which is much more powerful and much faster for large associative arrays.

[Contents]

8.8. Using Array Mixed Mode

Once you will reach a point when you want to use an array in a mixed indexed mode. For example you want to handle an array of associative arrays. In other words you want to have an array, of which each element is an array itself.

For example you want to store the phone number, the room number and the name of the dog for each of your employee. You have 78 employees in your firm. In this case you need an array having 78 elements, each element being an associative array itself, with the keys "phone", "room", "dogname".

To do this you can

FOR I=1 to 78
  PRINT I,". ",EMPLOYEE[I]{"phone"}," ",EMPLOYEE[I]{"room"},_
        " ",EMPLOYEE[I]{"dogname"},\n"
NEXT I

You can mix the indices any order in any deepness. For example

ARR{"1"}[2,3][3]{undef,"3",4}{"5"}[66]

is correct. What you can use it for is another question though.

Note that

a[3][5]

is the same as

a[3,5]

but the latter is a bit more efficient (due to ScriptBasic internal algorithms).

[Contents]

8.9. Expressions

To calculate values you will need expressions. The simplest expression is the one that contains only a constant number or string of the format described in the previous sections. A bit more complex expression contains variables, array elements, operators, function calls, and even parentheses.

ScriptBasic expression format is much the same as that of many other basic languages. The operators have precedence of the usual order, and you can alter the order of operator evaluation using parentheses. The operators are in order of precedence from the highest to the lowest:

These binary operators are evaluated from left to right. This means that 6-3+3 is 6 and not zero. This should be usual and obvious. 3 is subtracted from 6 and the last 3 is added to the result of the subtraction.

Operand types are not determined during compilation. There are no integer, real or string variables in ScriptBasic, any variable can hold any value (but only one at a time). Whenever a numeric operator gets a string value it converts the value automatically to numeric. The concatenation operator automatically converts the numeric operands to string.

Operators that work with both numeric and string operands do not convert the operands.

[Contents]

8.10. Operators

[Contents]

8.10.1. Power operator (^)

The power operator a^b calculates the b-th power of a.

The actual implementation of the power operator is quite complex. It tries to be as precise as possible. In case the calculation can be carried out using integer numbers only integer calculation is used. This way no unnecessary rounding errors are introduced.

If the mantissa is positive then the calculation is definite. However unlike other implementations ScriptBasic tries to calculate the result of the power operator even when the mantissa is negative. Actually power operator always results an integer or real number when possible. The implementation internally uses complex numbers and in case the imaginary part of the result is zero the real value is used as the result.

If either of the operands are undef then the result is undef.

[Contents]

8.10.2. Multiplication operator (*)

The multiplication operator multiplies the operands. If any of the operands are undef, the result is also undef. If any of the operands is real, then the result is real, otherwise it is integer. The string operands are converted to numbers.

[Contents]

8.10.3. Division operator (/)

This operator divides the first operand with the second. If any of the operands are undef then the result is undef. If the second operand is zero then the result is undef. If one of the operands is a real number then the calculation is carried out on real numbers and the result is real. If the operands are integer, but the calculation can not be performed to result an integer number without remainder then the operand values are converted to real. If both of the operands are integers and there is no remainder after the division the result is integer. String operands are automatically converted to numeric value. In case the option

OPTION RaiseMathError sbMathErrDiv

was used the operator will raise an error when the second argument is zero.

[Contents]

8.10.4. Integer division operator (\)

This operator divides the first operand with the second. If any of the operands are undef then the result is undef. If the second operand is zero then the result is undef. If one of the operands is a real number then the calculation is carried out on real numbers and the result is real. If the operands are integer then the calculation is performed using integer numbers and the result is truncated towards zero. String operands are automatically converted to numeric value. In case the option

OPTION RaiseMathError sbMathErrDiv

was used the operator will raise an error when the second argument is zero.

[Contents]

8.10.5. Modulus operator (%)

This operator calculates the remainder of the two operands. The operands are converted to integer value and the result is integer. If the second operand is zero the result is undef. In case the option

OPTION RaiseMathError sbMathErrDiv

was used the operator will raise an error when the second argument is zero.

[Contents]

8.10.6. Addition and subtraction operators (+, -)

These operators are both binary and unary operators. In their unary form they are used out of the precedence orders. Unary operators are always applied first, unless parentheses drive different calculation order.

If any of the operands is undef then the result is undef. If any of the operands is real the calculation is performed using real numbers and the result is real. If both operands are integer the calculation is performed using integer numbers and the result is integer.

[Contents]

8.10.7. Bit-wise and logical NOT (NOT)

This unary operator calculates the logical negate of the operand. The calculation is done on integer numbers, thus the operand is converted to integer value. The operator inverts each bit of the operand. If the operand is undef the result is –1, which is the internal value for TRUE.

Great care should be taken when using the NOT operator in logical expressions. The precedence of the operator is higher than that of any binary operator. Therefore the expression

not true or false

is true, while

not (true or true)

is false.

[Contents]

8.10.8. Equality operator (=)

This operator compares the operands. If both operators are undef the result is –1, which is the internal value for TRUE. If only one of the operands is undef then the result is zero, which is the internal value for FALSE. In other words undef is equal to undef, but nothing else.

If any of the operands is string then the comparison is done on strings. The non-string operand if any is converted to string. String comparison is case sensitive by default, but you can alter this behavior using the statement option. Two strings are equal iff they are the same length and contain the same valued bytes in the same order.

If the operands are numeric and at least one of them is a real number then the comparison is done on real numbers. Otherwise the comparison is done on integer numbers.

The result of the operator is always –1, which is the internal value for TRUE or 0, which is the internal value for FALSE.

To alter the behavior of case sensitive comparison you can use the statement

option compare sbCaseInsensitive

The comparing operator looks at the option "compare" and decides how to compare two strings. The global constant sbCaseInsensitive is pre-declared by ScriptBasic and can be used anywhere in your program. To switch back to case sensitive comparison you have to issue the command

option compare sbCaseSensitive

The global constant sbCaseSensitive is also a pre-declared constant. Note that the option "compare" is also used by the pattern-matching operator like and also alters the behavior of directory listing under UNIX.

[Contents]

8.10.9. Not equal operator (<>)

This operator compares the operands. If both operators are undef the result is 0, which is the internal value for FALSE. If only one of the operands is undef then the result is -1, which is the internal value for TRUE. In other words undef is equal to undef, but nothing else.

If any of the operands is string then the comparison is done on strings. The non-string operand if any is converted to string. String comparison is case sensitive. Two strings are equal iff they are the same length and contain the same valued bytes in the same order.

If the operands are numeric and at least one of them is a real number then the comparison is done on real numbers. Otherwise the comparison is done on integer numbers.

The result of the operator is always –1, which is the internal value for TRUE or 0, which is the internal value for FALSE.

Later versions may change the behavior of this operator in case of string comparison allowing case-insensitive comparison.

[Contents]

8.10.10. Compare operators (<, <=, >, >=)

These operators compare numeric and string values. The result of the operation is undef if any of the operands is undef. If any of the operands is string the operands are converted to string value and string comparison is performed. If none of the operands is string but at least one of the operands is float value then the operands are converted to floating number and the comparison is done comparing floating numbers. Otherwise the comparison is done with integer numbers.

The result of the operator is always –1, which is the internal value for TRUE or 0, which is the internal value for FALSE.

[Contents]

8.10.11. Logical operators (and, or, xor)

These operators can be used for bot logical and bit-wise operators. ScriptBasic does not have a separate type for logical values. Boolean values are stored in integer storage. The logical TRUE is represented as integer value –1 and the logical FALSE is 0. In other words the logical TRUE is an integer value, which is represented by a memory location of n bytes having all bits set to 1. On the other hand logical FALSE is an integer value represented by a memory location of n bytes having all bits reset.

The operators and, or and xor perform the calculation on integer values, If any of the operands is not integer it is converted to integer value before the operation takes place. The operations are performed on each bit of the operands.

This means that the operators can also be used for logical operations.

[Contents]

8.10.12. Concatenation operator (&)

This operator converts the operands to string value unless the operand is already a string value and results a string, which is the concatenation of the operands. The result string is placed in a newly allocated place, in other words the operation does not alter any of the operands.

[Contents]

8.10.13. ByVal operator

This operator is a very special one. This actually does nothing but results the operand unaltered. Why to have such an operator? The reason is to help calling functions and passing arguments by value. When an expression is passed as an actual value for a function argument it is passed by value. In other words the function gets the value of the expression. When a variable is used as the actual argument for the function the function gets the variable and not the value. Of course the function can use the value of the variable, but when it alters the variable, the original variable is also changed. To avoid this you can use the operator ByVal in front of the variable, which is passed to a function as argument.

[Contents]

8.10.14. LIKE operator

The like operator is a pattern matching operator. Pattern matching is complex issue and is documented in a separate chapter in details. See chapter 19 on page 76.

[Contents]

8.10.15. Extension operators

Extension operators are defined and recognized by the ScriptBasic syntax analizer but no execution is implemented behind. Thus using any of the extension operator will result an error "command is planned, but not implemented". Why to have these operators?

These operators can be implemented by external modules. An external module developer may decide to define the behavior of any of the operators4. For example an extension may decide to implement a hashing algorim and decide that a->b should result the actual hash value from hash a having the key b. Any program wanting to use this functionality can load the module using the statement declare sub and use the operator.

Operators that are undefined by default but can be used by external modules are:

?     !     #     `     @
+^    +<    +>    +?    +=    +*    +/    +%    +!    +#    +&    +\    +`    +'    
+@    -^    -<    ->    -?    -=    -*    -/    -%    -!    -#    -&    -\    -`    
-'    -@    ^^    ^<    ^>    ^?    ^=    ^*    ^/    ^%    ^!    ^#    ^&    ^\    
^`    ^'    ^@    <^    <<    <?    <*    </    <%    <!    <#    <&    <\    <`    
<'    <@    >^    ><    >>    >?    >*    >/    >%    >!    >#    >&    >\    >`    
>'    >@    ?^    ?<    ?>    ??    ?=    ?*    ?/    ?%    ?!    ?#    ?&    ?\    
?`    ?'    ?@    =^    =<    =>    =?    ==    =*    =/    =%    =!    =#    =&    
=\    =`    ='    =@    *^    *<    *>    *?    *=    **    */    *%    *!    *#    
*&    *\    *`    *'    *@    /^    /<    />    /?    /=    /*    //    /%    /!    
/#    /&    /\    /`    /'    /@    %^    %<    %>    %?    %=    %*    %/    %%    
%!    %#    %&    %\    %`    %'    %@    !^    !<    !>    !?    !=    !*    !/    
!%    !!    !#    !&    !\    !`    !'    !@    #^    #<    #>    #?    #=    #*    
#/    #%    #!    ##    #&    #\    #`    #'    #@    &^    &<    &>    &?    &=    
&*    &/    &%    &!    &#    &&    &\    &`    &'    &@    \^    \<    \>    \?    
\=    \*    \/    \%    \!    \#    \&    \\    \`    \'    \@    `^    `<    `>    
`?    `=    `*    `/    `%    `!    `#    `&    `\    ``    `'    `@    '^    '<    
'>    '?    '=    '*    '/    '%    '!    '#    '&    '\    '`    ''    '@    @^    
@<    @>    @?    @=    @*    @/    @%    @!    @#    @&    @\    @`    @'    @@

The analyzer recognizes these operators as valid. They can be used as unary operator as well as binary operator. Used as binary operator they have the highest precedence, one level above the power operator and they are all evaluated from left to right.

Since build25 the unary operator # is implemented as identical operator resulting the argument of it unchanged. This is to aid the old BASIC programmers, who got used to write

open "file" for input as #1

[Contents]

8.11. Planned, Future Operators

Later versions of ScriptBasic will contain other operators implemented. The current development focuses on fixing bugs and producing a working version. The planned operators are:

Shl, shr bit shift operators

[Contents]

8.12. Assignments (LET)

Assignment is the most important command of most languages. This is used to assign calculated values of expressions to variables. You have already seen examples of it but we could not discuss it so long as long we have not defined expressions and variables. An assignment is nothing else than a variable on the left side of an = character and an expression on the right side.

The command calculates the expression first. The expression may contain the variable that stands on the left side. In this case the variable holds its original value while calculating the expression. When the expression is fully evaluated the command releases the old value of the variable and then assigns the new value just calculated.

The calculated value can be string, integer, real number or even undefined. You can not assign whole arrays to variables. The variable can be a global variable, local variable, argument of a subroutine or function or element of an array. For example:

' This assigns the value 18 to the variable A
A = 13 + 5
' This assignes the value "apple" to the array element
B[55] = "apple"

To be precise the command first calculates the variable on the left side of the command and then it calculates the expression. Why is this important? There can be some special cases. Look at the following example:

function q
 z = z + 1
 q = z
end function

z =55 a[q()] = z print a[56]

Will it print 55 or 56? Because the left side of the command is evaluated first it does print 56.

Some BASIC implementations allow a keyword LET to be used before the variable on the left side of the command before the variable. This is rarely used by programmer and is not allowed by ScriptBasic.

[Contents]

8.13. Operator Assignments

Most of the times we assign the value of an expression to a variable, which uses the variable itself. For example

A = A + 1

increments the variable A. To ease programming ScriptBasic allows the construct

A += expression

instead of

A = A + expression

This is a well known and widely used form by many languages, well readable, though not BASIC like. Likewise programmers can write

A -= expression instead of A = A - expression
A *= expression instead of A = A * expression
A /= expression instead of A = A / expression
A \= expression instead of A = A \ expression
A &= expression instead of A = A & expression

This is more readable for most of the programmers and results slightly faster execution for addition, subtraction, multiplication, division, integer division and string concatenation respectively.

[Contents]

8.14. Comments

You can insert comments into the source file. A line starting with the keyword REM or starting with the character (apostrophe) is treated as comment line.

REM This is a simple program that prints the words "HELLO" and "WORD"
REM to the screen
PRINT "HELLO WORD"

Or you can write it

’ This is a simple program that prints the words "HELLO" and "WORD"
’ to the screen
PRINT "HELLO WORD"

However you can not use comments after commands, and therefore you can not write:

PRINT "HELLO WORD" ’This comment is invalid in ScriptBasic

or

PRINT "HELLO WORD" REM This comment is invalid in ScriptBasic

[Contents]

8.15. Including Files

You prepare your BASIC programs for ScriptBasic using some text editor. This is usually NOTEPAD under Windows operating systems and it is emacs or vi under UNIX. You enter the program line by line, but sometimes you may want to include other files into your program. One way is to use the copy and paste functionality of the editor. However this will lead unnecessary big text files and unmanageable projects. Instead you can use the INCLUDE command of ScriptBasic.

Whenever you want to include the file named myinclude.bas you should write

INCLUDE "myinclude.bas"

into your program. Although statements are not case sensitive, file names are on UNIX systems. On Windows systems the operating system preserves the case of the file names, but you can reference the file using any case letters.

If you want to maintain portability of your programs (and you usually should) use always lower case file names and reference them using lower case letters.

When you specify file names that reside in a different directory you can specify relative file names, or absolute file names including the path of the file. If myinclude.bas is in a subdirectory you should write:

INCLUDE "sudir/myinclude.bas"

Here we can mention another point of portability. UNIX uses the forward slash as file separator character. Windows operating systems use the back slash. In ScriptBasic you can use both separators on Windows and on UNIX as well. You can even mix them in a single path. We recommend that you use the forward slash character for the purpose.

If you use the back-slash characters in file names you usually have to double the character because this character play the role of the escape character in strings. This is not the case for the include statement. You should write

REM a correct include statement
INCLUDE "subdir\myinclude.bas"

but

REM bad include statement
INCLUDE "subdir\\myinclude.bas"

will eventually fail to include the file. Whenever you specify a relative directory, which is above the directory of the including file use the double dot notation of the parent directory, like

INCLUDE "../parentel.bas"

Whenever a relative file name is specified in an include statement the file name should be relative to the file that contains the include statement. For example you may have the three files:

C:\BASIC\INCLUDE\myinclude.bas
C:\BASIC\myprog.bas
C:\BASIC\INCLUDE\SYS\system.bas

The program `myprog.bas' includes the file `myinclude.bas' using the statement

INCLUDE "include/myinclude.bas"

The program `myinclude.bas' includes the file `system.bas' using the statement

INCLUDE "sys/system.bas"

The INCLUDE statement has another form:

INCLUDE mymodule.inc

This is similar to the format discussed above with the exception that there is no quote character around the file name. In this case ScriptBasic tries to locate the named file in one of the configured include directories. The file name in this case should not contain space. Such include files usually belong to binary modules that ScriptBasic can dynamically load, and the included files declare the function implemented in the binary module.

The statement INCLUDE is processed before the variables, numbers, string and other lexical elements are recognized. This may result some strange behavior. For example the code:

T$ = """this is a multi-line string that 
Include "otherfile.txt"
includes another file."""

is correct and results a string that contains the content of the file otherfile.txt. This may seem strange but it is the way ScriptBasic preprocessing handles the include directive.

As your program goes larger and larger you split it up into included files. The code that includes them may include one file, including another file and so on. It may finally result some file included more than once. To avoid redefinition of thing and unnecessary code repetition ScriptBasic has another preprocessor statement named IMPORT.

IMPORT behaves the same way as INCLUDE does with the exception that it does not include the file if the file was already included by a previous INCLUDE or IMPORT preprocessor statement.

[Contents]

8.16. Use (internal preprocessors)

ScriptBasic does not have a built-in macro preprocessor, like language C. However it has an open interface that third party developers can use to develop preprocessors. A ScriptBasic program may have one or more USE commands in it. This command should stand alone on a line being a preprocessor command. It can not be part of an IF or other statement. The format of the command is:

USE preprocessor_name

After ScriptBasic has loaded the source code and has included all the files specified by the include statements it scans the lines containing a use statement. When it finds one it loads the external preprocessor module compiled by the third party developer into a DLL or SO file and calls the preprocessor function. The preprocessors get total control over the source file loaded into memory and are free to alter the source code.

The preprocessor DLL or SO file should be specified in the configuration file. For example the line

preproc (
  internal (
    dbg "E:\\MyProjects\\sb\\Debug\\dbg.dll"
    )
…

from the default Win32 scriba.conf file defines what DLL file to load when the source file uses the preprocessor called dbg that actually happens to be the debugger preprocessor.

Internal preprocessors can also be loaded using the command line option `-i'. For example in case you want to start a ScriptBasic program from the command line using the command line debugger you can type:

# scriba -i dbg mybugous_program.bas

[Contents]

8.17. Using external preprocessor

An external preprocessor is a stand-alone program that lets the programmer easily extend the language. The simplest available external preprocessor is the HEB preprocessor that lets a programmer embed ScriptBasic into HTML. This preprocessor is written in ScriptBasic and converts HTML embedded ScriptBasic code into pure ScriptBasic.

When ScriptBasic recognizes that an external preprocessor is to be started it does not read the source file but starts the external preprocessor and uses the file created by that. The external preprocessor should be an executable program that gets at least two arguments: the name of the file to preprocess and the file name to create containing the preprocessed text.

The external preprocessor exit with the code zero in case it was executing successfully and should exit with the code 1 if an error occurred and the output file can not be used.

There can be more than one external preprocessors applied to a program. In case there are more than one preprocessors applied to a file then ScriptBasic starts each of them one after the other each getting the output file of the previous to work on.

The preprocessors should be configured in the configuration file before ScriptBasic can execute any of them. Each preprocessor should have a symbolic name. This is the choice of the person who configured the ScriptBasic installation. You can name a preprocessor you like, it will not alter the behavior.

For example the HTML embedded basic preprocessor is named heb in the sample configuration file. To use this preprocessor you can use the option `-p' on the command line following with the name of the preprocessor:

# scriba -p heb myheb.bas

On the other hand you can also assign file extensions to preprocessors. The sample configuration file contains the line:

epreproc$heb heb

This means that the extension heb is assigned to the preprocessor heb. (Well, this is a stupid example because the extension is the same as the symbolic name of the preprocessor, but usually this is the convention. The extension stands after the $ sign and the symbolic name of the preprocessor is the value of the configuration line.) You can assign the same preprocessor to many extensions, for example:

epreproc$heb heb
epreproc$htb heb
epreproc$hb heb

can exist in the same configuration file. Whenever ScriptBasic processes a source file with the extension hb it will start the HEB external preprocessor.

A file may have multiple extensions. These are worked up from left to right. For example the command:

# scriba sample.heb.sql.bas

will start ScriptBasic starting first the preprocessor assigned to the extension heb, then the preprocessor assigned to the extension sql and finally the preprocessor assigned to the extension bas. If there is no preprocessor assigned to some of the extensions then they eventually will not be executed. This is not an error. (Note that there is no sql preprocessor currently for ScriptBasic, this is an artificial example.)

This behavior lets you to chain preprocessors. On Windows NT using the ISAPI variation of ScriptBasic you can configure the extension "bas" in the web server to execute using the ISAPI dll of ScriptBasic. You need not configure each possible preprocessor extension. You can invoke preprocessors using "internal" extensions and finishing the file names with ".bas" to tell the IIS web server to start it as ScriptBasic code.

External preprocessors have to be configured. You have to tell ScriptBasic what program to use for the specific preprocessing. To do this you have to use the configuration line:

eprep$exe$heb /usr/bin/scriba heber.bas

This configuration line tells ScriptBasic that the executable of the preprocessor having the symbolic name heb is scriba heber.bas. This is not actually the name of an executable. This is the start of the command line. The first element of it should be the name of the executable. The next elements are the command line options that precede the input and the output file name. Using this configuration line ScriptBasic will start another issue of ScriptBasic with the command line:

#/usr/bin/scriba heber.bas sample.heb.bas outputfile

where sample.heb.bas is the HTML embedded BASIC code we actually want to execute; `outputfile' is the file that the preprocessor has to create. When this process finishes ScriptBasic reads the generated output and executes the program.

Of course you can use any other executable as preprocessors. You can write preprocessors in C, C++, Perl or in any other language that can be started on the command line, is capable reading and writing file and is able to signal error via exit code. Note that ScriptBasic exists with the last non caught error code.

Before ScriptBasic is able to start the external preprocessor it has to calculate the name of the output file that the preprocessor has to write. Each preprocessor has to be configured to use a temporary directory tp store the preprocessed file. To specify the directory you have to use the configuration line:

eprep$dir$heb /etc/temp/heb/

This means that the temporary directory for the files created by the preprocessor with the symbolic name "heb" is the directory `/etc/temp/heb/'. When a file is to be preprocessed with this preprocessor ScriptBasic calculates a unique file name based on the name of the source file and asks the external preprocessor to put the result into that file in this directory. Finally the command line that ScriptBasic starts for the example above is:

#/usr/bin/scriba heber.bas sample.heb.bas /etc/temp/heb/EACAPAOALAEAHAHAAANAJAOALAGAKAPA

The algorithm used to calculate the unique file name is the same as for the cache file name.

[Contents]

8.18. #! /usr/bin/scriba

Your basic program may contain a first line that looks like the title of this section. This may seem strange for Windows users, but this is usual in UNIX. On UNIX systems such a first line tells the operating system, which program to use to execute the program. This is usually /usr/bin/scriba on UNIX systems, but the ScriptBasic interpreter may be in a different location or may have different name.

When the interpreter starts the program it sees this first line and ignores it. To ease life of ScriptBasic users this kind of first line is ignored on Windows systems.

[Contents]

8.19. @goto start

Your basic program may contain a first line that looks like the title of this section. This may strange for UNIX users, but this is usual under Windows. On Windows systems such a first line is used to transfer the execution of the Windows command interpreter to the end of the command file, where the interpreter is invoked for the same file.

This looks like:

@goto start

scriptbasic program lines

rem """ :start @echo off scriba %0 %1 %2 %3 %4 %5 %6 %7 %8 %9 rem """

assuming that scriba is in the path this file named something xxxx.cmd can be started from the command line and will execute the BASIC program. The first line is ignored by the ScriptBasic interpreter, but the Windows command processor will jump to the line containing start:. On this line the ScriptBasic interpreter is started and reads and interprets the lines. The first line is ignored, by ScriptBasic implementation, the last few lines are comment at least for ScriptBasic.

To ease portability such a first line is ignored when executing a BASIC program under UNIX as well.

[Contents]

8.20. #!/@goto

After the previous two sections here is a small trick that allows you to have a ScriptBasic program that can be executed under UNIX as well as under Windows just typing the name of the BASIC program file and without any modification.

#! /usr/bin/scriba
goto start
start:
     
scriptbasic program lines

rem """ :start @echo off scriba %0 %1 %2 %3 %4 %5 %6 %7 %8 %9 rem """

Unfortunately under Windows NT the command interpreter will complain about the very first line. However in addition to that there is no other issue.

[Contents]

9. Interacting with the user

Although this topic is detailed elsewhere we present a short introduction here, because of its importance. Interacting with the user is the most important action a program takes. There are many ways to do it, but the most frequently used is printing to the screen and reading from the keyboard and interpreting command line arguments. To do this ScriptBasic has the command print and the command line input.

[Contents]

9.1. Print

The print statement can either print to a file or to the standard output, which is usually the terminal window. If no file number is specified after the keyword the statement print prints to the screen. It takes a list of expressions, formats them and prints the values to the screen.

print "haho!"
print "hahaho!"
print
print "kukac\n"
print"oooh"

[Contents]

9.2. Input

ScriptBasic does not have the usual input statement. Input statement in other basic languages reads the keyboard and puts a string, real or integer number into the variable depending on the type of the variable. In ScriptBasic there is no type of the variable, any variable can hold any type of value.

ScriptBasic has the statement line input. This command reads a whole line from the file specified after the # sign or from the standard input if no file number was specified. The string read is put into the variables listed on the command. Each variable gets a line including the line terminating new line.

print "Give me a line:"
line input a
print "This is a=",a,"This was a\n"

To remove the new line from the end of the read string you can use the function chomp.

[Contents]

9.3. Handling command line arguments

Though the command line is parsed up to the name of the executable basic program name and all other command line options and arguments are passed to the basic program to handle. To access this part of the command line the basic program should use the function command(). This function does not need any argument and returns a string containing the command line arguments.

In the variation STANDARD the command line returned by the function command() contains the parameters separated by a single space. Even if the real command line contains multiple spaces or tabs between the parameters it is converted to a list of parameters separated by single space.

' This program demonstrates the command function
' start the program using the command line:
'     scriba commandline.bas a b c d e f
'
print "The command line was:\n",command(),"\n"

will print

The command line was:
a b c d e f

[Contents]

10. Name spaces

ScriptBasic does not have real name spaces to separate modules from each other. There are no such things as public and private variables of modules or classes. Whenever you develop a module you have to trust the user of the module that he or she will use the module in the way it is intended and the way you hopefully documented. A programmer should not use the internal variables of a module not because he can not but because he is supposed not to.

ScriptBasic name spaces only help the programmers to avoid accidental name collisions. When you develop a module you start it saying

Module MyModule

After this line until the end of the module which is noted by the line

End Module

will belong to the specific module and all global variables will belong to that module.

However all these module and name space handling is done on low level altering the names of the variables and functions. When you start to write a program and you write:

A = 3
print A

you actually use the variable main::A. This is because the default name space is main, and if there is no name space defined in a variable its name is altered so that the variable will belong to the current name space. Try the following:

A = 3
print main::A

It prints 3. Surprising? Try the following:

A=5
module boo
A=3
print main::A
end module

It will print 5. This is because main::A is 5, boo::A is 3 and the variable name main::A is not converted to boo::main::A, because it is explicitly denoted to belong to the name space main.

Name spaces can be nested. You can write:

A=1
module boo
 A= 2
 module boo::baa
 A= 3
 print A
 print boo::A
 print main::A
end module
end module

which will print 321.

To ease the nesting of modules you can write the same code as

A=1
module boo
 A= 2
 module ::baa
 A= 3
 print A
 print _::A
 print main::A
end module
end module

When the module name in the module declaration starts with double colon ScriptBasic knows that the module is to be nested into the current module. The variable name _::A means: the variable A of the surrounding name space. This is the same as the operating system directories. You can think of name spaces as directories and variables as files. Whenever you write

../../mydir/file.c

a similar construct may say

_::_::mynamespace::variable

When the parser sees the end module statement it always returns to the previous name space. You can start and close modules many times, ScriptBasic will not complain that the module was already defined. You can even totally neglect the module statement and you can write the above program as

main::A=1
boo::A= 2

boo::baa::A= 3 print boo::baa::A print boo::A print main::A

This is a bit less readable. Name spaces can serve another purpose. See the following code:

string = "AF"
hex = 0
while string <> ""
 chr = mid(string,1,1)
 string = mid(string,2)
 hex = hex*16 + asc(chr) - asc("A")
wend
print hex

when you try to compile you will probably get many errors. Why? Because string, chr and hex are predefined functions and can not be used as variable names. What to do then? You can use them as variable names when you specify that they are variables:

::string = "AF"
::hex = 0
while ::string <> ""
 ::chr = mid(::string,1,1)
 ::string = mid(::string,2)
 ::hex = ::hex*16 + asc(::chr) - asc("A")
wend
print ::hex

When you write var{::string}, ::chr or ::hex ScriptBasic will know that they are variables belonging to the current name space. This is a bit weird, so better avoid using function names and predefined function names as variable names.

[Contents]

11. File Handling

[Contents]

11.1. Opening and creating files

ScriptBasic handles the files the same way as any other BASIC type language. You open a file, read from it, write to it and finally close the file. To make a jumpstart see a simple example:

open "myfile.txt" for output as 1
print#1,"This is the first line of myfile\n"
close 1

open "myfile.txt" for input as 1 line input #1, a close 1 print a

This simple program opens the file names myfile.txt in the current directory, prints a single line into it, and closes the file. Next time it opens the file for reading, reads a line from it, closes the file and prints the line read from the file into the screen.

When you open a file you have to have a file number that you want the file associated with. In the example above this number is 1. This is called many times the "file number". Whenever you do something with an opened file you have to use the file number.

There are more things than reading from a file and writing to a file. You can read from a certain position on a file and you can write to a certain position. You can determine the length of a file and dates and times the file has, like last time it was accessed, modified or created.

[Contents]

11.2. Text and binary files

Before going into details we have to talk about the different types of files and the different ways a file can be opened.

A file is a byte stream for ScriptBasic. You can open a file for reading, writing, or to do both of these operations without closing and reopening the file. Some operating systems, like Windows NT, distinguish between text files and binary files. Text files usually contain lines of text. Binary files contain may any code. When you open a file that you wan to write textual data into, or you know that the file contains textual data, you have to use one of the text mode opening. When you want to handle arbitrary data, use binary type of opening.

Calling a file to be text or binary under Windows NT is not precise. All files are the same. The differentiation between textual and binary should be set up on the way we use the files. You can open a text file in binary mode and you can open a binary file in text mode. A file itself is not binary or text. The difference is how we handle them.

Binary files are simple. Bytes follow each other and when we read or write such a file the operating system reads or writes the bytes as they are without any conversion. Whenever you want to go to a certain position of a file using the command seek, you can without problem.

Textual files are a bit different. Whenever you open a file in textual mode and start to read it, the operating system converts each carriage-return and line-feed character pairs to a single line feed character. Whenever you write to a file opened in textual mode all line-feed characters produce two character in the output file: a carriage-return and a line-feed character.

Setting the position in a textual file is also tricky. Should we count the bytes in the file or the bytes we have wrote to the file. They are different. If we have send x pieces of new-line characters to the file that generated two times x bytes. The safest rule is do not position within a text file. Read it from start, write it from start or append to it. The not so safe rule is position to "non-calculated" position. Your calculation may be erroneous, but if you position to a value that was reported by the function pos, you may be safe.

UNIX operating systems handle all files in one way that makes life simpler. Text file lines are separated with a single line-feed character and opening a file in textual mode is just the same as opening in binary mode. If you write a program for UNIX you need not worry about textual and binary mode. So long as long your program is running under UNIX. Whenever someone starts to use your program under Windows he or she may start to report you bugs that did not appear under UNIX. Therefore it is recommended that you use the appropriate file opening modes under UNIX the same way you have to under Windows NT.

There are five different ways you can open a file. These are

There is an extra mode called socket but that will be detailed later.

The first four modes are textual modes. Open a file for input whenever you want to read data from the file. If you open a file for output you can write to the file. However all data that was stored in the file is destroyed, and in case the file did not exist it is created. If you want to write new data to a file still keeping the existing content open the file for appending to it, using the keyword append.

Random gives a relative freedom to open a file for both reading and writing. When you open a file using the keyword random, you can both read from the file and write to the file. You can, for example, read from the file until you find the end of the file and then append after the current content new lines of text. In another scenario you can read the current content, seek to the position zero, which means the start of the file and rewrite the existing content. However you should only use the mode random only when you really want to write the file. Operating system permission may allow you to read a certain file and not to write. Trying to open the file in mode random will not be successful in that situation and prevent your program’s ability to read the file content.

The last mode is binary. This opens the file for both reading and writing in binary mode. You can send any data to the file without conversion and you can position the file pointer to any position without worry.

All file modes but input create the file for you if it did not exists and all modes but input create the directory for the file if it did not exist. In other words there is no need to ensure that the directory exists for a file before opening it using any of the outputting modes.

The format of the open statement is:

open file for mode as [ # ] file-number [LEN= record_length]

(parts between the [ and ] are optional.)

The file is the name of the file to be opened. The mode is one of the keywords above. The file-number is the number you want to refer to the opened file. This number should be between 1 and 512 in the current implementation of ScriptBasic. 512 is the number of concurrently opened files in ScriptBasic, unless the underlying operating system gives a lower limit. The file number should not be in use. However if you close a file, the file number is released and can be used in subsequent open statements.

The # character is optionally allowed before the file number for compatibility reasons with other BASIC interpreters. The last optional part specifies the record length. If this record length is not defined the file is treated as series of bytes. If the record length is larger than one the positioning statements count the position and length in records instead of bytes.

[Contents]

11.3. Switching between binary and text mode

Whenever you have opened a file in a mode you may want to switch the mode you have opened it. You can not switch between reading and writing mode, but you can switch between binary and textual mode. To do this there are two commands:

binmode [#] fn

and

textmode [#] fn

The sign # is optional. The argument fn should be the file number of an opened file. These commands are implemented on the Windows operating system, but can also be used under UNIX with no effect. The use of these commands under UNIX results portable code.

There are two files automatically opened in almost any environment. These are the standard input and the standard output. You can switch the mode how the program handles these files using these commands. You can say

Binmode input
Binmode output

to switch the standard input and output to binary mode and also

Textmode input
Textmode output

to switch the standard input and output to text mode. Use of these commands is extremely useful writing Windows NT cgi programs that output binary data, for example a png file.

[Contents]

11.4. Getting a free file number

At certain situation you do not know a number which is sure available to be associated with a file. In that case you can use the function FreeFile to get a file number usable in the next open statement.

The following code demonstrates the usage of the function FREEFILE:

fn = FREEFILE
Open "myfile.txt" for input as fn

The first line of the code stores a number in the variable fn, which can be used in the next line as a file number. You can print out the actual value, store it in one or more variables.

To be absolutely safe you can also check if there is any available file number. If there is no available file number then the function FREEFILE returns undef.

fn = FREEFILE
if fn <> undef then

open "myfile.txt" for input as fn else

‘ do whatever you have to do ‘ when you can not open the file end if

In case the file number is specified using a single variable initialized to zero the interpreter will find automatically the first available file number and will put this value into the variable. This way you can also write:

fn = 0
if fn <> undef then
  open "myfile.txt" for input as fn
else
  ‘ do whatever you have to do 
  ‘ when you can not open the file
end if

[Contents]

11.5. Positioning in a file

When you open a file for RANDOM or BINARY access you may want to move inside the file back and forth before reading or writing data. There are many functions and statements that help you to move the file pointer. Before going into details, lets explain what the file pointer is.

A file is a series of bytes stored on the disk. Whenever you read a byte from a file you do it from a certain position. The file pointer of the opened file identifies this position. When you read a line or some bytes from a file the file pointer automatically moves after the last character of the line. Therefore the next read will go on reading the next available unread character of the file. The same is TRUE for writing the file. Whenever you write bytes to a file the file pointer associated with the opened file will move after the last position written.

If the file pointer is positioned after the very last byte of the file reading will result an end-of-file signal and the function EOF(fn) will be TRUE. If we try to write to the file in the same situation the length of the file will increase,

To get the actual length of an opened file you have to call the function LOF(fn) that stands for Length Of File. To get the current position of the file pointer you have to call the function POS(fn). To position to a certain position of an opened file you have to write:

seek #fn, position

where position is the number of bytes, or records from the start of the file. When you want to position to the start of the file, you can therefore write either

seek #fn, 0

or

Rewind #fn

Rewind is nothing else than a short form for seek#fn,0 because it is often needed and is more readable.

The functions LOF and POS return values in terms of records. If there is no record length specified in the statement open you can treat the return values as counts of bytes. On the other hand if there is a record length larger than one specified on the statement open these functions return a value in terms of records. LOF tells you how many records there are in the file and POS tells you which record the file pointer is positioned in.

You have to be careful interpreting the returned values of these functions with files opened with record length more than one. LOF tells you how many records there are in the file actually, but there may be some more bytes after the last complete records. POS tells you which record the file pointer is positioned in, but it does not guarantee that the file pointer is positioned on the first byte of the record.

There is a function named FILELEN that gives the length of a file based on the name of the file. The argument for this function is the name of the file, while the argument of LOF is the file number of the opened file. Another difference is that FILELEN returns the size of the file in terms of bytes always and never in terms of records.

[Contents]

11.6. Reading and writing files

To write into a file you can use the statement print in the form:

Print#fn, expression list

This is the same format as the ordinary print statement with the exception that a file number is specified that references an opened file. The expression list is printed into the file. If you want to start a new line in the file you can do it in one of two ways. You can write:

Print#fn, "\n"

or

Printnl#fn

In the first case the string containing a new line character is printed to the file. In the second case we wrote a print-new-line command. Note that you can print a new line character to the screen in three ways:

Printnl Print Print "\n"

The second format can not be used to print a new line into a file, the command

Print#fn

is syntactically incorrect. This is a restriction caused by the parser that ScriptBasic is built on. Later versions may allow this format.

To read from a file you have a statement and a function. The statement

line input #fn, variable

will read a line from the file. The length of the line is not limited except that there should be enough memory. This statement will result a string in the variable that contains the line including the line terminating new line unless the line is terminated by the end of the file without closing new-line character.

For binary files that are not composed of lines the function input should be used. This function reads from a file the given number of characters or bytes and results a string that contains the bytes read. It has the format:

Variable = input( NumberOfBytes, fn)

The number of bytes actually read by the function can be determined using the string function LEN. If there are less number of bytes or records in the file from the actual file pointer until the end of the file than the required number of bytes the function results a shorter string containing the available bytes.

[Contents]

11.7. Getting and setting the current working directory

When a program runs under the UNIX or the Windows NT operating system there is a directory where the program is started. This directory is not necessarily the directory where the executable code is or where the basic program is. Sloppy saying: this is the directory where the user prompt was when the user typed in the command line starting the program. Whenever a file name is specified it can either be an absolute file name or a relative file name. Relative file names are always relative to the current working directory. For example the current working directory is `/home/pvc/scriba/example' and the file name is `hello.bas' then the operating system know that the actual file we refer to is `/home/pvc/scriba/example/hello.bas'. If we refer to the file `../source/getopt.c' then the operating system considers the file `/home/pvc/scriba/source/getopt.c'. This is because the `..' directory means : one directory above. This is the same in Windows NT as well as in any UNIX variant. (OpenVMS is a bit different.)

Using relative file names is easier than using the full path to each and any file name. ScriptBasic provides a command and a function to handle the working directory. The function curdir has no arguments and returns a string containing the current working directory path. The command chdir changes the current working directory according to its argument. For example:

print curdir,"\n"
chdir ".."
print curdir,"\n"

will print

/home/pvc/scriba/examples
/home/pvc/scriba

As argument to chdir you can define any existing directory name as absolute path or relative to the current working directory. If the directory does not exist or there is some other condition that prevents the change of the current directory an error occurs.

Note that this command is disabled in the Eszter SB Application Engine variation of the interpreter and should be disabled in all multi-thread version. The reason is that the current working directory is set for all interpreters running in the same process using this command and may generate failures in other scripts.

[Contents]

11.8. Locking a file

So far we did not consider the case when more than one programs were trying to read or to write a file. In real life, especially if you write CGI programs this can happen very often. Assume that you want to count the number of visitors. The CGI program fragment making the counting can be something like this:

1 Open "counter.txt" for input as 1
2 Line input #1,Counter
3 Close #1
4 Counter = Counter +1
5 Open "counter.txt" for output as 1
6 Print #1, Counter
7 Close #1

At first look there is no problem with this. But at second thought there is. There can be many CGI processes that run parallell. There is no guarantee that one programfinishes its working before the next one starts. Processes are run parallell, and it means that the operating system runs a little piece of one process then runs another. A CGI process runs the lines 1 to 3 and reads the conuter value, let’s say 100. Then the operating system hangs the running of this process and runs another CGI process again the lines 1 to 3. This process also reads the counter value of 100. Then the first process writes the incremented value 101 and the second process, whenever it gets it time slice to run writes 101 to the file. Both hits counted from 100 to 101 instead of one counting from 100 to 101 and the other from 101 to 102.

What is the solution?

The solution is to lock the file. A file can be locked when it is opened. We open the file for random access, lock it, read from it, increment, write the incremented value and close the file. Noone else can intercept the file access because the file is locked. How does it look like in ScriptBasic?

1 Open "counter.txt" for random as 1
2 lock#1,write
2 Line input #1,Counter
3 Counter = Counter +1
4 rewind#1
5 Print #1, Counter
6 Close #1

The command lock does lock the opened file. The first argument is the number of the opened file. The second argument is the locking type. This type currently can be write, read and release. The meaning of the different types:

If the file is already locked by another process the instruction lock waits until the other process releases the file.

The ScriptBasic instruction lock is an advisory lock. It means that it works only if all programs wanting to access the file use the lock statement before reading or writing the file. If a process locks a file and another process just goes on reading or writing the locking method will not work. The actual behavior is dependent on the operating system and as such you have to treat it as non-guaranteed behavior, or undefined behavior.

Actually the flock system call is used to implement the lock statement of ScriptBasic. This type of lock does not prohibit any process to read or write a locked file on UNIX. On Window NT operating system flock does not exist. The file locking functionality is implemented using the LockFileEx system call and locking the first 64Kbyte of the file. This results write and read failure in a process that tries to read or write the first 64Kbyte of the file and UNIX like behavior if it tries to access the file above the 64Kbyte.

[Contents]

11.9. Locking file range

ScriptBasic provides another type of file locking. This lock locks a range of a file. You can do it using the statement:

Lock range #fn from start to end for mode

fn is the opened file, start and end are expressions resulting integer values meaning the first and the last position of the locking range in terms of records. The parameter mode is the same as for the file locking mechanism, it can be read, write or release.

Note that the positioning uses the same zero offset numbering as the instruction seek. In other words the first record of a file is position 0.

See the following example:

REM locktest1.bas
open "locktest.txt" for output as 1
lock region#1 from 1 to 5 for write

for i=1 to 5 print #1, "A" next I print "5 bytes are done\n" for i=1 to 5 print #1, "B" next I print "10 bytes are done first 5 bytes are locked\n" line input a close 1

If you run this code in two different terminal windows one will lock the file range and the other will go on and wait for user input. When you press the key ENTER to go on the program grabbing the lock closes the file and the program in the other terminal windows writes out the text and starts to wait. On the other hand if you start locktest1.bas in one terminal window and when it starts to wait for user input you start the following locktest2.bas in another window, they do not interfere, because they lock different regions of the file.

REM locktest2.bas
open "locktest.txt" for output as 1
lock region#1 from 6 to 10 for write
seek#1,6
for i=1 to 5
print #1, "D"
next I
print "done\n"
close 1

This locking method implements advisory locking, and the behavior is the same as in the case of file locking. The interpreter calls the system function fcntl on UNIX systems, and LockFileEx on Windows NT. This results different behavior when programs try to read or write a file region locked by another process.

In other words all programs should behave and lock the file or the region before accessing it.

[Contents]

11.10. Truncating a file

When you write to a file it is automatically extended by the operating system and grows. On the other hand files do not shrink automatically. When you write into file before the position of the end of the file the bytes after the written region are still valid and remain in the file. The exception is when you open a file for output. In that case the original file is deleted and you can start to write of length zero,

However you may want to open a binary file, read from it, write into it and sometimes you may want to decrease the size of the file. The instruction truncate does it for you. The syntax of the instruction is:

truncate#fn,size

Where fn is the opened file number, and size is the new size of the file in terms of records. The content of the file after the position size-1 is lost. Note that the argument to the instruction truncate is the desired length of the file, which is the position of the last byte plus one. Positions start with zero offset just like in file positioning statements.

[Contents]

11.11. Deleting a file or directory

Using the command truncate you can truncate a file to size zero, but the empty file is still there. To totally delete a file you have to use the command delete. The syntax of the command is

delete expression

where the expression is the name of the file or directory to be deleted. When issuing this command to delete a directory the directory should be empty otherwise the command fails and an error occurs. There is another command with the syntax

deltree expression

that deletes a directory even if the directory is not empty. Note that this command is very dangerous, because it forcefully deletes full directory trees and therefore you have to use this format of the command with great care.

[Contents]

11.12. Creating a directory

Although the OPEN statement automatically creates the directory for the file whenever you want to create a new file you may want to create a directory without creating a file in it. For the purpose the statement mkdir can be used. The syntax of the command is very simple:

mkdir expression

The expression should result a string, which is the directory to be created. The command recursively creates the directories that do not exist by the time the command is executed and need creation in order to create the final directory. For example you can issue the command

mkdir "/usr/bin/scriba"

without issuing the commands

mkdir "/usr"
mkdir "/usr/bin"

The directories `/usr' and `/usr/bin' are automatically created in the unlikely case they did not exist.

The command executes without error if the directory is created or if the directory already existed. If the directory can not be created an error occurs. The reason for directory creation failure can be numerous. The most typical is access control prohibiting for the process directory creation. Another usual problem is when some sub-path of the desired directory already exists, but is not a file. For example we want to create the directory /usr/bin/scriba/exe and /usr/bin/scriba already exists and is a plain file.

[Contents]

11.13. Setting file parameters

Files usually have parameters in addition to the information contained in the file itself. Such information is the owner of the file, the group the file belongs to, creation time, modify time, access time, change time and so on. To set these parameters of a file ScriptBasic provides the command set file. The syntax of the command is

set file filename parameter=value

where `filename' is the name of the file for which the parameter is to be set, the parameter is the name of the parameter to change and value is the desired value of the parameter.

The parameter can be

If the file does not exist or some other condition prevents the successful change of the parameter the program generates an error with one of the error codes sbErrorSetCreateTime, sbErrorSetModifyTime, sbErrorSetAccessTime.

Note that Windows NT and UNIX operating systems store file time values in GMT. Do not forget to convert the local time to GMT if needed when changing some of the file time values.

[Contents]

11.14. Listing files

Up to know we have discussed instructions and operations that were dealing with files. To access a file you needed to know the name of the file. Many times the program does not know the file and needs to get the list of all files in a certain directory. To do this you can use the commands open directory, close directory and the function NextFile.

The command open directory opens a directory for getting the list of the file. The command reads the list of the files and creates an internal list of the files.

The function NextFile can be used to retrieve the files of the directory opened one by one. When the last file name has been retrieved or when there is no need to retrieve the remaining file names the command close directory should be used to close the listing.

[Contents]

11.14.1. Open directory

The syntax of the command open directory is

open directory dir_name pattern pattern_value option option_value as dir_number

The parameter dir_name should be an expression evaluating to a string, which is the name of the directory for which the listing is needed. It can be absolute path or relative to the current working directory. It may but need not contain the trailing slash.

The parameter pattern_value should be an expression evaluating to a string. When the list of the files is gathered this pattern is used to select the files. Only the files matching the pattern will be included in the list of files. The pattern matching uses the same algorithm as the operator like. Altering the joker and wild characters for the operator like also alters the pattern matching mechanism of file selection. The command open directory leaves the pattern matching memory in a non-matched state. In other words the function joker will return the undef value after executing an open directory command until the next like operator is executed.

Pattern matching and selection is done during the execution of the command open directory and the other file list handling commands and the function NextFile do not interfere with the pattern matching.

Pattern matching while building up the list is a time and processor consuming tasks. If you do not want to perform pattern matching just getting all the file names use value undef or empty string as pattern value. In either case pattern matching is skipped during file list build up.

Note that pattern matching can either be case sensitive or insensitive based on the setting of the option `compare'. This option alters the behavior of pattern matching during file list build up on UNIX systems, but not under Windows. Under Windows NT pattern matching during file list build up is always case insensitive, no matter how the option `compare' is set.

The parameter option_value should be an expression evaluating to an integer value. This value is used to drive the behavior of the command. Each bit of the value has a special meaning. To set the correct value ScriptBasic predefines several global constants that you can use. These constants can be used in this value. If more than one constant is needed you have to use the operator and to connect them. The predefined constants and their corresponding meanings are:

The final parameter dir_number should be an expression evaluating to an integer value between 1 and 512. This number should be used as argument to the function NextFile and as parameter to the commands close directory and reset directory. If this parameter is a variable having an integer value zero then the command will alter the value of the variable assigning a directory number automatically. This is similar to the mechanism when opening files. There is no FreeDir or any similar function.

If the directory does not exists or is not readable by the program for security or for any other reason an error occurs.

Note that the number used to open a directory is separate from the numbers used in opening files. If you use the file number 14 to open a file you still can use 14 to open a directory.

File and directory names are collected into a list during the command open directory. Any alteration in the file system like deletion of files, new files, modification of files will be reflected in the listing only if the directory is closed using the statement close directory and reopened again using the statement open directory.

[Contents]

11.14.2. Function NextFile

When a directory is successfully opened the function NextFile can be used to retrieve the file names from the list. The argument to this function is an integer number, the directory number used to open the directory. The function returns a string value. When the last file name was retrieved the function returns undef.

[Contents]

11.14.3. Function EOD

The function EOD serves End Of Directory checking. The argument to this function is the directory number used in the command open directory. The result of the function is TRUE if there are no more files to be returned by the function NextFile and FALSE otherwise.

[Contents]

11.14.4. Reset directory

You can use this command to reset the directory listing. The syntax of the command is

Reset directory dn

or

Reset directory #dn

where dn is the directory number. After executing this command the next call to NextFile will return the first file in the list and listing starts again.

[Contents]

11.14.5. Close directory

You can use this command to close the directory listing. The syntax of the command is

Close directory dn

or

Close directory #dn

Using this command you can free the space and resource allocated to the directory listing. Directory listing may need huge amount of memory in case of recursive listing of large directory structures. After using this command the directory number dn can be used again to open another or the same directory.

[Contents]

12. Networking

[Contents]

12.1. Opening a Socket

Networking in ScriptBasic is very simple. As you can open a file on the local disk you can open a service on a remote machine and print into it and get characters from it. The hard part is that you have to know how to talk to the service5. When you want to open a file to a service on a remote machine you should specify the name or the IP number of the remote machine and the port number in the open statement as a file name and you should open the file with mode socket. For example:

open "www.digital.com:80" for socket as 1

will open a connection to the web server of the machine named wwww.digital.com. The port number is 80 in this example, which is the usual port of a web server. You can also write:

open "16.193.50.33:80" for socket as 1

specifying the IP number of the machine. (The IP number listed in this documentation is an example and may not be the IP number of any server.) After opening a socket you can print into the channel and read from it using the commands print, printnl, line input and the function input. To retrieve a simple web page the following sample program can be used:

on error goto ErrorLabel
 open "16.193.50.33:80" for socket as 1
 print#1,"GET http://localhost/ HTTP/1.0\n\n"
 while not eof(1)
  line input#1,a
  print a
 wend
close 1
 stop
ErrorLabel:
 print "The web server 16.193.50.33 on port 80 is not reachable\n"

The open statement generates an error in case the remote machine is not reachable or if the domain name can not be resolved. If the IP number is specified instead of the name of the remote machine ScriptBasic does not query the DNS system, but tries to connect to the remote machine immediately. This way you can connect to remote machines a bit faster but you risk that the machine IP number is changed. A machine is usually identified by its name. The IP number is a low-level identification that reflects the topological location of the machine on the Internet. If the host is moved from one service provider to another or some other technology changes or developments make it necessary the IP number of the machine is going to change. Therefore it is recommended to use the name of the machine if possible.

[Contents]

12.2. Getting the host name

ScriptBasic can easily get the name of the host the code is running calling the function hostname. This function returns the internet name of the host. For example the code fragment

print hostname,"\n"

Prints vphome on my machine.

[Contents]

13. String handling commands

[Contents]

13.1. Split and splita

The commands split and splita split a string into sub-strings. The syntax of the arguments are

split expression by expression to variable_list
splita expression by expression to variable

The first expression on the command is the string to split up. The second expression is the string used to split up the first one. For example

const nl="\n"
split "first,second,third,fourth" by "," to fi,se,th,fo
print fi,nl,se,nl,th,nl,fo

will print

first
second
third
fourth

splits the string by the commas. The resulting sub-strings get into the variables listed after the keyword to. In case of command splita there is only a single variable after the keyword to. This variable will become an array and the elements of the array will hold the sub-strings. The resulting array will be indexed starting with zero. Thus

splita "first,second,third,fourth" by "," to q
print lbound(q)," ",ubound(q)

will print

0 3

and the sub-strings get into the array q[0], q[1], q[2] and q[3]. In this case the array will have as many elements as need by the split string. If the string to be split is undef the result will also be undef. In this case the variable will not become an array, but remain simple variable holding the value undef.

If there are more variables in a split statement than sub-string the rest of the variables will become undef.

split "q,w,e" by "," to q,w,e,r,t
print r," ",t 

will print

undef undef

If there are less variables than sub-strings the last variable will hold the remaining part of the original string holding the separator strings as well. For example:

split "q,w,e" by "," to q,w
print q,"\n",w,"\n"

will print

q
w,e

This way you can write programs that chop off few elements from a string containing some kind of list and leave the rest of the string in a variable. For example:

ListVar = "1,2,3,4,5,6,7,8,9,10,55"
while IsDefined(ListVar)
split ListVar by "," to FirstMember,ListVar
print FirstMember,"\n"
wend 

will print

1
2
3
4
5
6
7
8
9
10
55

If two separator strings follow each other then the resulting sub-string is empty string. For example:

splita "1,,2" by "," to q
for i=lbound(q) to ubound(q)
print q[i],"\n"
next i 

will print

1

2

However separator strings on the start and on the end of the string are ignored:

splita ",1,2,3," by "," to q
for i=lbound(q) to ubound(q)
print q[i],"\n"
next i 

will print

1
2
3

The separator string can be any length, arbitrary string.

After a string is split into sub-string there is a need many times to put the parts together. To do that the string function join can be used.

[Contents]

13.2. Unpack

The command unpack should be used to split a binary record into variables. A binary record is a string that contains integer and real numbers and strings inside as sub-strings.

[Contents]

14. Conditional Execution

Conditional execution is a vital point in any programming language. Conditional execution can be performed using the statement IF. This statement has the following format:

If expression then
 REM Code to execute when the
 REM expression is TRUE
End if

Or it may have the format:

If expression then
 REM Code to execute when the
 REM expression is TRUE
Else
 REM Code to execute when the
 REM expression is FALSE
End if

Or even it may have the format:

If expression then
 REM Code to execute when the
 REM expression is TRUE

Elseif expression2 then REM Code to execute when the REM expression2 is TRUE End if

And even the format:

If expression then
 REM Code to execute when the
 REM expression is TRUE
Elseif expression2 then
 REM Code to execute when the
 REM expression2 is TRUE
Else
 REM Code to execute when both
 REM expression and expression2 are
 REM FALSE.
End if

When closing a conditional execution construct with the keyword ENDIF you can write it as one word or as two separate words, like END IF. Also you have a great freedom spelling the keyword ELSEIF. You can use any of the followings: ELSE IF (two words), ELSEIF, ELSIF or ELIF.

Most BASIC languages have the single line format of the IF statement where the conditionally executed command is on the same line as the command IF and there is no ENDIF required. You can have the same in ScriptBasic. You can write:

if expression then command

In this case the command is executed only if the expression standing after the keyword IF is TRUE. Unfortunately you can not have ELSE. If you need the ELSE branch then use the multi line format of the conditional execution closing the ELSE branch with ENDIF.

When using the single-line format of the command IF there are some restrictions on the command that follows. The command executed conditionally can not be any declaration type command, like Const, Module or Declare Sub and can not be starting or ending a loop. Finally this command can not be another IF, ELSEIF, ELSE or ENDIF.

[Contents]

15. The statement GOTO

The statement GOTO is the most famous statement of all BASIC languages. Many program theorists say that you should not ever use GOTO and they may be right. Even so, the statement GOTO is part of most programming languages and ScriptBasic is no exception.

Using the statement GOTO you can alter the execution order of the statements. GOTO statements use labels and the labels can identify program lines. The form of a GOTO statement is

goto label

where the labels is a labels, which is defined somewhere in a program.

Labels should stand on the start of a line preceding a command. There are two different label types in ScriptBasic. One is the conventional BASIC style label, a decimal number. Any line can contain an unsigned integer as a label at the start of the line. For example the following code is perfectly legal in ScriptBasic:

10 print "Hello word!!"
20 REM
30 print "This is line 30"

These types of line labels are available in ScriptBasic for compatibility reasons, and there is no need to label each line in ScriptBasic. The numeric labels can be present in any order and need not increase.

The type of label, which is more modern and lets the programmer to write more readable and as such easy to maintain programs are the alphanumeric labels. These labels follow the syntax of any identifier of ScriptBasic, stand on the start of the line and are followed by a colon. Both numeric labels and alphanumeric labels can be used in GOTO statements.

10 print "hah"
line input a
if a = "n\n" then
  goto finish
end if
goto 10

finish: REM this is the end of the program

This program loops printing three characters and waiting for input after each print so long as long the user types a single n character. When the user types the n character the program executes the statement goto to jump onto the label finish.

The labels are subject to name space modifications as any other variable or user defined function names as described in the section 10.

All goto instructions should reference a local label. This means that you can not jump out or jump into a function or subroutine code. You are free to reuse label names once in each function or subroutine. In other words if you use a label in a function you can use the same name as a label without worry in another function or subroutine as well as you can use it in global program code outside of all functions and subroutines. All these labels are different ones although all may have the same name.

All the labels are local to the subroutine or function that the label is defined in. This is implemented in ScriptBasic via name decoration. If you declare a label named MyLabel on a line ScriptBasic automatically converts it to module::MyLabel'function, where module is the name of the actual module (this is main if no module is defined); function is the name of the actual function or subroutine without the current module name. If the label is in the global program code outside of all functions and subroutines the function name is empty string. In this case the label has the decorated form module::MyLabel' The modules can reference each-others labels using explicit module name notation. However there is no possibility to reference any label, which is not in the same function or subroutine.

[Contents]

16. Loop constructs

ScriptBasic has a rich set of looping constructs. If got used to a special construct you can have it in ScriptBasic. The looping constructs that ScriptBasic supports are:

While expression
 REM loop body
Wend

Repeat the loop body so long as long the expression is TRUE. Do the test before entering the loop.

Repeat
 REM loop body
Until expression

Repeat the loop body so long as long the expression is FALSE. The loop body is executed at least once and the test is performed after the execution of the loop body.

Do While expression
 REM loop body
Loop

This is just another form of the loop while/wend. Repeat the loop body so long as long the expression is TRUE and do the testing before the loop body execution.

Do Until expression
 REM loop body
Loop

Repeat the loop body so long as long the expression is FALSE. Do the testing before the loop body execution.

Do
 REM loop body
Loop While expression

Repeat the loop body so long as long the expression is TRUE. Do the testing after the loop body execution.

Do REM loop body Loop Until expression

Repeat the loop body so long as long the expression is FALSE. Do the testing after the loop body execution.

FOR variable=StartValue TO EndValue STEP StepValue
 REM loop body
NEXT

This loop loads the value of the StartValue into the variable. After the execution of the loop the variable is increased by the value of StepValue. The StepValue can also be negative, in which case the value of the variable will decrease. The loop is repeated so long as long the value reaches or steps over the EndValue. If the StartValue is already over the EndValue the loop is never executed.

The keyword STEP and the value after the keyword is optional. In this case the step value is one.

[Contents]

17. Functions and Subroutines

Most programming languages provide a form for defining code fragments to be used later at several times. These are subroutines and functions. ScriptBasic allows the programmer to define subroutines before or after they are used; to have local variables inside functions and subroutines and allows the functions and subroutines call each other even recursively.

[Contents]

17.1. Declaration of subroutines and function

You can declare a function like

FUNCTION MyFunction(var1, var2, var3)
 REM function body
END FUNCTION

Later you can call the function in any expression, like

A = MyFunction(1,2,3)

Subroutine declaration is almost the same:

SUB MySub(Var1,Var2, Var3)
 REM subroutine body
END SUB

Later you can call the subroutine using the CALL statement:

CALL MySub(1,2,3)

The major difference between functions and subroutines is that subroutines do not have values returned. However ScriptBasic does not differentiate between functions and subroutines. SUB and FUNCTION are just two keywords that you can use almost interchangeable. A subroutine can return a value and functions may be called using the call statement, although this is not recommended.

[Contents]

17.2. Calling functions and subroutines

Functions and subroutines can be called two different ways. One way is to use the function in an expressions the same way as you would do with the built-in function like sin, cos or rnd. Calling a function this way you have to use the name of the function and supply parameters between parentheses.

Another way to call a function or subroutine is to use the call statement. The call statement has a very simple syntax:

CALL function_name( argument_list )

The function name is the name that stands after the keyword function or sub. The argument list is a comma-separated list of expressions. When you call a function in a call statement you can omit the parentheses, which is very convenient when you do not have arguments. Therefore the following lines are equivalent:

CALL MyFunction
CALL MyFunction()

or

CALL MyFunction 1,2,3
CALL MyFunction(1,2,3)

To ease readability and programmers life you can even omit the keyword CALL if the function or subroutine was already defined. Therefore you can write:

Sub MyFunction
Print "I am in my function\n"
End sub

MyFunction

On the other hand the following code

MyFunction

Sub MyFunction Print "I am in my function\n" End sub

is not valid, because the function is not defined when it is called. In such a situation you have to use the keyword CALL.

If the function or subroutine has formal arguments you can pass values inside the parentheses. Functions and subroutines can have as many arguments as you like and you can pass as many values as you like. ScriptBasic does not check that the number of actual values passed as argument is the same as the number formal arguments. If you pass more arguments than the number of the formal arguments, the last values are calculated and the result is thrown away. Have a look at the following example:

a = MyFunction(1,2,3)
a = MyFunction(1,2)
a = MyFunction(1,2,3,MyFunction("haha", "hehe","hihi"))
print a
printnl
function MyFunction(a,b,c)
local x

print a,b,c,x

printnl

end function

And the output printed to the screen:

123undef
12undefundef
hahahehehihiundef
123undef
undef

The lines 1 to 4 are results of a function call. When we call the function the first time we pass three values to the arguments, which are named a, b and c. The values are printed correct and the value of the local variable x is undef.

The second time we call the function passing only two arguments. ScriptBasic does not complain. As we do not pass any value for the argument named c the value of it is undef.

When we call the function third time the argument evaluation calls the function itself again. Although this is the fourth argument and MyFunction has only four this argument is evaluated and this result the line 3 in the output. The result of this function call, which is undef anyway, is not used.

[Contents]

17.3. Returning a value

The example above did not return any value. But functions are to return values. To return a value from a function the code should perform an assignment that looks very similar to a normal assignment statement. The difference is that the "variable" that stands on the left side of the = should be the name of the function. Let’s see the previous example a bit extended:

a = MyFunction(1,2,MyFunction(1,1,1))
print a
printnl
function MyFunction(a,b,c)
local x

print a,b,c,x printnl x = a * b * c MyFunction = a + b + c

end function

The output is

111undef
123undef
6

The value of the function call is 3 when the function is first executed. This value is passed to the function call itself as third argument; and is used to calculate the final values for the variable a.

[Contents]

17.4. Local and global variables

In the example the local variable x is assigned a value, but is never used. Local variables in ScriptBasic serve the same purpose as in other languages. They are to store values for the time of function or subroutine execution. They are automatically created when a function or subroutine starts and vanish as the subroutine or function finishes its task.

Local variables are the only variables that have to be defined in ScriptBasic. If a variable is not declared as local then ScriptBasic will think that the variable is global. Global variables keep their values while the program executes. See the following example:

call MySub()
call MySub()
sub MySub
local x

print x," ",y,"\n" x = 3 y = 4 end sub

The output of the program is:

undef undef
undef 4

The variable y is not defined when the subroutine is called the first time. The variable x is local and is also not defined. When the program calls the subroutine the second time the local variable x is freshly created and therefore has no defined value. On the other hand the global variable has the value assigned to it during the previous subroutine call.

Local variables not only exist to create new local space. They can also help the programmer to protect global variables. A function or subroutine may use a variable named ExampleVariable and may alter the value of it. Other parts of the program may use the same variable without knowing that the variable value changes in the function or subroutine call. For example the program:

ExampleVariable = 3
call MySub()
print ExampleVariable
sub MySub
ExampleVariable = 5
end sub

Prints out the value 5. If we alter the program

ExampleVariable = 3
call MySub()
print ExampleVariable
sub MySub
local ExampleVariable
ExampleVariable = 5
end sub

the output becomes 3. The variable ExampleVariable is local in this second case and it does not interfere with the global variable of the same name.

[Contents]

17.5. Parameters passed by value and by reference

When you call a function or subroutine and pass variables as arguments do you pass the value of the variable or the variable itself. In other words the example program

a = 1 call MySub(a) print a sub MySub(x) x = x + 1 end sub

Will it print 1 or 2? The answer is that it will print 2. ScriptBasic passes the variables and not the value of the variable whenever possible. If we alter the program a bit and write

a = 1
call MySub(ByVal a)
print a
sub MySub(x)
x = x + 1
end sub

the resulting output will be 1. Here the difference is the use of the operator ByVal. This operator is a dummy operator that does nothing. As a result the passed value is an expression and is not a variable anymore and as such can not be passed by reference only by value.

[Contents]

17.6. ByVal command

As you could see the caller can alter the behaviour of argument passing forcing the argument to be passed by value instead of reference. This can also be done inside the called function or subroutine. To do this the pair of the ByVal operator can be used, namely the ByVal command. The syntax of the command is very simple:

ByVal variable list

The variables listed in the command become all passed by value instead of passed by reference and can be altered without modifying the caller variable. For example

a = 1 call MySub(a) print a sub MySub(x) ByVal x x = x + 1 end sub

Will print 1, because the local variable x gets the value of a and not the reference to a. Note however that this command is executed by ScriptBasic and is not a declaration. Therefore

a = 1
call MySub(a)
print a
sub MySub(x)
x = x + 1
ByVal x
X = x + 1
end sub

will print 2. Why? Because the first increment is done on the reference to a, and therefore a is incremented. After this increment the command ByVal is executed. This command evaluates the variables listed after it and assignes the values to the variables. The only effect is that the variables now contain the value copied from the originally referenced variable and not a reference to the caller variable.

[Contents]

17.7. Calling functions indirectly

Whenever you call a function or subroutine you have to know the name of the subroutine or function. In some situation programmers want to call a function without knowing the name of the function. For example you want to write a sorting subroutine that sorts general elements and the caller should provide a subroutine that makes the comparison. This way the sorting algorithm can be implemented only once and need not be rewritten each time a new type of data is to be sorted. The sorting subroutine gets the comparing function as an argument and calls the function indirectly. ScriptBasic can not pass functions as arguments to other functions, but it can pass integer numbers. The function Address can be used to convert a function into integer. The result of the built-in function Address is an integer number, which is associated inside the basic code with the function. You can pass this value to the icall command or function as first argument. The icall command is the command for indirect subroutine call. The call

icall Address(MySubroutine()),arg1,arg2,arg3

is equivalent to

call MySubroutine( arg1,arg2,arg3)

If you call a function that has return value use can use the icall function instead of the icall statement:

A = icall(Address(MyFunction()),arg1,arg2,arg3)

is equivalent to

A = MyFunction(arg1,arg2,arg3)

The real usage of the function Address and icall can be seen in the following example:

sub MySort(sfun,q)
local ThereWasNoChange,SwapVar
repeat
 ThereWasNoChange = 1
 for i=lbound(q) to ubound(q)-1

if icall(sfun,q[i],q[i+1]) > 0 then ThereWasNoChange = 0 SwapVar = q[i] q[i] = q[i+1] q[i+1] = SwapVar endif

next i until ThereWasNoChange

end sub

function IntegerCompare(a,b) if a < b then cmp = -1 elseif a = b then cmp = 0 else cmp = 1 endif end function

h[0] = 2 h[1] = 7 h[2] = 1

MySort address(IntegerCompare()) , h

for i=lbound(h) to ubound(h) print h[i],"\n" next i

Note that the argument of the function Address is a function call. ScriptBasic allows variables and functions to share the same name. Address is a built-in function just as any other built in function, and therefore the expression

Address(MySub) THIS IS WRONG!

Is syntactically correct. The only problem is that it tries to calculate the address of the variable MySub, which it can not and results a run-time error. Instead you have to write

Address( MySub() )

using the parentheses. In this situation the function or subroutine MySub() will not be invoked. The parentheses tell the compiler that this is a function and not a variable.

[Contents]

17.8. GOSUB and RETURN

Most basic language implementations contain the commands GOSUB and RETURN. ScriptBasic is no different. This command pair is the old form of subroutine calling that existed in the basic language before functions and subroutines were invented. When a GOSUB command is executed there is no new function or subroutine started with local variables and return value. This is almost the same as executing the command GOTO. You have to specify where the execution of the code is to be continued using a label and the execution will go on at that label. The only difference between GOTO and GOSUB is that GOTO forgets where it jumped to the specified label from. GOSUB on the other hand remembers and whenever a RETURN command is executed it jumps back to the command following the instruction GOSUB.

A code fragment started by a GOSUB command can use another GOSUB command any level deep and the command RETURN will always return to the command line following the last executed, matching GOSUB. For example:

print 1
gosub sub1
print 5
stop
sub1:
print 2
gosub sub2
print 4
return
sub2:
print 3
return 

will print

12345

When a GOSUB command is executed inside a function or a "real" subroutine that starts with the command SUB the label following the keyword GOSUB can not be outside the actual function or subroutine. It is not a must to return from a code segment started with GOSUB. If there is a GOSUB in the program and the program finishes before reaching the pairing RETURN the program just finishes normally. This is not an error. Also when a function or sub finishes before executing a RETURN command for an already executed GOSUB in that function or sub the execution simply returns to the following command where the function or sub was called and the unpaired GOSUB return address or addresses are dropped. For example:

sub sub1
print 3
gosub kukk
print "never printed"
exit sub
kukk:
print 4
end sub

print 1 gosub obenal print 6 stop obenal: print 2 call sub1 print 5 return

will print

123456

[Contents]

18. Reference Variables

[Contents]

19. Pattern matching

ScriptBasic provides a simple, yet powerful pattern matching mechanism that eases string handling. ScriptBasic pattern matching is not based on regular expressions although it was planned originally. The pattern matching that ScriptBasic provides is simpler and therefore easier to learn and use and execute faster. Most programs do not need the power of regular expression match6.

[Contents]

19.1. The operator like

Pattern matching in ScriptBasic is similar to the pattern matching that you get used to on the UNIX or Windows NT command line. The operator like compares a string to a pattern.

string like pattern

Both string and pattern are expressions that should evaluate to a string. If the pattern matches the string the result of the operator is TRUE, otherwise the result is FALSE.

The pattern may contain normal characters, wild card characters and joker characters. The normal characters match themselves. The wild card characters match one or more characters from the set they are for. The joker characters match one character from the set they stand for. For example:

Const nl="\n"
print "file.txt" like "*.txt",nl
print "file0.txt" like "*?.txt",nl
print "file.text" like "*.txt",nl 

will print

-1 -1 0

The wild card character * matches a list of characters of any code. The joker character ? matches a single character of any code. In the first print statement the * character matches the string file and .txt matches itself at the end of the string. In the second example * matches the string file and the joker ? matches the character 0. The wild card character * is the most general wild card character because it matches one or more of any character. There are other wild card characters. The character # matches one or more digits, $ matches one or more alphanumeric characters and finally @ matches one or more alpha characters (letters).

A space in the pattern matches one or more white spaces, but the space is not a regular wild card character, because it behaves a bit different.

Note that wild card character match ONE or more characters and not zero or more as in other systems. Joker characters match exactly one character, and there is only one joker character by default, the character ?, which matches a single character of any code.

[Contents]

19.2. The function joker

We can match a string to a pattern, but that is little use, unless we can tell what sub-string the joker or wildcard characters matched. For the purpose the function joker is available. The argument of this function is an integer number, n starting from 1 and the result is the sub-string that the last pattern matching operator found to match the n-thjoker or wild card character. For example

Const nl="\n"
if "file.txt" like "*.*" then
  print "File=",joker(1)," extension=",joker(2),nl
else
  print "did not match"
endif 

will print

File=file extension=txt

If the pattern did not match the string or the argument of the function joker is zero or negative, or is larger than the serial number of the last joker or wild card character the result is undef.

Note that there is no separate function for the wild card character sub-strings and one for the joker characters. The function joker serves all of them counting each from left to right. The function joker does not count, nor return the spaces, because programs usually are not interested in the number of the spaces that separate the lexical elements matched by the pattern.

[Contents]

19.3. Escaping wild cards

Sometimes you want a wild card character or joker character to match only itself. For example you want to match the string "13*52" to the pattern two numbers separated by a star. The problem is that the star character is a wild card character and therefore "#*#" matches any string that starts and ends with a digit. But that may not be a problem. A * character matches one or more characters, and therefore "#*#" will indeed match "13*52". The problem is, when we want to use the sub-strings.

Const nl="\n"
a="13*52" like "#*#"
print joker(1)," ",joker(3),nl
a="13*52" like "#~*#"
print joker(1)," ",joker(2),nl 

will print

1 52
13 52

The first # character matches one character, the * character matches the sub-string "3*" and the final # matches the number 52.

The solution is the pattern escape character. The pattern escape character is the tilde character: ~.

Originally we wanted to use the \ character as pattern escape. But \ is already used as string escape character and you have to double it to have a single \ in a string. This finally would mean that you would have been supposed to write \\\\ to match a single backslash and \\\\\\\\ to match \\. If you like that program in Perl and try the program:

$a = "\\\\"; $a =~ s/\\\\/\/\//g; print $a;

Any character following the ~ character is treated as normal character and is matched only by itself. This is TRUE for any normal character, for wild card characters; joker characters; for the space and finally for the tilde character itself. The space character following the tilde character matches exactly one space characters.

Later you will earn that "#*#" may also serve the purpose, when the role of the wild card character * is changed.

[Contents]

19.4. Ambiguous matching

Pattern matching is not always as simple as it seems to be from the previous examples. The pattern "*.*" matches files having extension and joker(1) and joker(2) can be used to retrieve the file name and the extension. What about the file sciba_source.tar.gz? Will it result

File=scriba_source.tar extension=gz

or

File=scriba_source extension=tar.gz

? The correct result is the second. Wild card characters implemented in ScriptBasic are not greedy. They eat up only as many characters as they need.

[Contents]

19.5. Advanced matching

Up to now we were talking about wild card characters and the joker character defining what matches what as final rule carved into stone. But these rules are only the default behavior of these characters and the program can alter the set of characters that a joker or wild card character matches.

There are 13 characters that can play joker or wild card character role in pattern matching. These are:

* # $ @ ? & % ! + / | < >

When the program starts only the first five characters have special meaning the others are normal characters. To change the role of a character the program has to execute a set joker or set wild command. The syntax of the commands are:

set joker expression to expression
set wild expression to expression

Both expressions should evaluate to string. The first character of the first string should be the joker or wild card character and the second string should contain all the characters that the joker or wild card character matches. The command set joker alters the behavior of the character to be a joker character matching a single character in the compared string. The command set wild alters the behavior of the character to be a wild card character matching one or more characters in the compared string. For example if you may want the & character to match all hexadecimal characters the program has to execute:

set wild "&" to "0123456789abcdefABCDEF"

If a character is currently a joker of wild card character you can alter it to be a normal character issuing one of the commands

set no joker expression
set no wild expression

where expression should evaluate to a string and the first character of the string should give the character to alter the behavior of.

The two commands are identical, you may always use one or the other; you can use set no joker for a character being currently wild card character and vice versa. You can execute the command even if the character is currently a normal character in the pattern matching game.

Using the commands now we can see that

Const nl="\n"
Set no wild "*"
a="13*52" like "#*#"
print joker(1)," ",joker(2),nl 

will print

13 52

can give the desired result.

If the expression supposed to result the character for which the role and character set is defined is none of the 13 characters listed above an error occurs.

[Contents]

20. Handling run-time errors

Tao says: error happens. Old programming languages like C handle error conditions returning NULL pointer from a function, or returning zero, or returning non-zero and applying system specific library functions, like longjump, jetlongjump and alike. Newer programming languages, like C++, Java invented exception handling.

BASIC is an old language. Indeed it is a very old language. Older than C. But it has exception handling.

When an error happens during program execution the program execution stops (exit code is the code of the error or zero on errorless termination) and usually an error message is printed on the screen. This happens for example when a file can not be opened for reading, there is not enough memory to perform some operation, an external module can not be loaded for some reason, a specified file number is out of range and many other events can cause an error.

A programmer can have two different approaches to avoid program termination caused by program error. One approach is to check every needed condition before trying to perform some action. This is impractical in some cases. The other approach is to tell the BASIC interpreter what to do instead of terminating the program when some error occurs. Programmers usually follow a mixed style. Some conditions are easier to check, while other errors are difficult to avoid.

For example you can not check all the conditions before trying to load a module. To do that you have to check the BASIC setup, configuration, the existence of the file, the existence of the functions implemented in the file. It is not possible from basic.

[Contents]

20.1. On error goto

The error handling instructions that ScriptBasic have are the usual and well known on error goto constructs. The easiest example using this construct is:

on error goto ErrorHappens

error 1

print "This won't print\n" ErrorHappens: print "This is the error message.\n"

end

will print

This is the error message.
The error is caused by the statement error, which artificially generates an error of the code given on the line after the keyword. The statement on error goto declares where to continue execution when an error happens. The execution system remembers this and starts to execute the code after the label, when an error happens.

To switch off the effect of the on error goto statement you can execute a command

on error goto 0

[Contents]

20.2. Resume

The piece of code executed when an error happens is usually tries to repair the condition that caused the error. When the reparation is done the program has to resume its normal operation. To perform this the program can use the statement resume. This statement has three forms.

Resume

continues the execution at the line that caused the error. In other words the line is executed again.

Resume next

continues the execution of the program after the line that caused the error. This means that the program does not try to execute the line that caused the error. Finally the statement

Resume label

resumes the program execution at the label specified after the keyword.

The interpreter remembers the resume point when an error occurs. After execution the resume statement this resume point is cleared and the last error code is set to zero. In other words the execution returns from error correction to normal operation.

Although the resume statement clears the last error value, there is another way to clear this value. You can execute the statement

error 0

This causes an artificial error of code zero, which means no error. This also sets the last error code to zero meaning no error. Although the statement error 0 seems to nullify an error this does not switch execution to normal from error correction, because still there is a resume point remembered.

Executing a resume statement in normal operation, when there is no resume point remembered causes an error.

[Contents]

20.3. resume

If you know that the program error does not need error code you can use one of the on error resume statements. These have almost the same effect as the statement on error goto except that the program execution continues in non-error mode when an error happens. This means that the program jumps to the location specified by the on error resume statement, but the error code is zero meaning no error and there is no remembered error location where a resume statement could return.

There are two different forms of the on error resume statement. On is similar to the on error goto statement specifying a label where to resume execution after the error. This has the form

on error resume label

The other type has the form

on error resume next

This command tells the interpreter to neglect the erroneous line and continue the operation executing the next line. Although this is a pleasant and easy way handling error great care ha to be taken. If you use the on error resume next statement in a code and error may silently be passed. On the other hand if there are more than one errors in the code the second one will terminate the program execution, because the first error switches off the effect of the on error resume next statement.

The code executing the error correction code can access the error code. The code of the last error happened is returned by the function error(). This function has no argument and returns an integer value, which is the error code. In normal operation, outside of error correction code the value of this function is zero.

[Contents]

20.4. Levels of error handling

The error correction code should be simple and carefully designed. If an error happens while executing the error correction code the interpreter will terminate the program code or at least will go up to the next level of exception handling.

When the interpreter executes a jump to the error correction code caused by an error the on error goto functionality is switched off automatically until a new on error goto instruction is executed. This is done to prevent infinite looping caused by errors in the error correction code. The error correction code therefore usually executes an on error goto statement pointing to the start of the error correction code itself immediately before the execution the resume statement.

At the same time an error correction code can not have its own error correction code. If an error correction code uses the on error goto statement to specify its own error correction code it looses the resume point when an error occurs. In other words the remembered point of code where to resume after error correction is always the statement where the last error occurred. There is only a single resume point during execution and there is no stack of resume points.

The error handling statements use labels. Labels in ScriptBasic are local, you can not refer to a label in a function or subroutine from outside and vice versa. There is no need to do so with the error handling functions as well. The issue is that the jumping instruction is executed some time after the label was used.

What happens when an on error goto statement is used in function X and the error happens in function Y called from function X. Will the code jump from one function to the other?

sub X
on error goto ErrorLabel
print "starting sub Y\n"
call Y
print "sub Y returned\n"
exit sub
ErrorLabel:
print "an error occured while executing Y\n"
end sub

sub Y error 1 end sub

call X end

will print

starting sub Y
an error occured while executing Y

The answer seems to be yes, but the case is not so simple. When the error occurs the interpreter sees that there is no on error goto statement currently in effect in the subroutine Y. Therefore it handles the error and terminates the execution of the subroutine Y. The subroutine returns and the error is inherited to the call statement invoking Y. The interpreter gets into the error condition again and now, at this level of execution there is an effective on error goto statement, which is executed.

To see that this really happens this way see the following example:

sub X
on error goto ErrorLabel
print "starting sub Y\n"
call Y
print "sub Y returned\n"
exit sub
ErrorLabel:
print "an error occured while executing Y\n"
Flag = 0
Resume
end sub

sub Y print "Y started\n" if Flag = 1 Then error 1 end if

print "Y finishes\n" end sub

Flag = 1

call X end

will print

starting sub Y
Y started
an error occured while executing Y
Y started
Y finishes
sub Y returned

In this code the subroutine Y causes error if the global variable Flag is 1. The error correction code sets this variable to zero and corrects the error with this action. After resuming the execution the code continues at the subroutine call and not at the error statement. This shows that the error condition does not jump out of the function. It rather terminates the function and propagates the error code up to the caller until the main program level is reached and program execution is terminated or there is an effective on error goto or on error resume statement.

When an on error goto/resume statement is executed it overrides the settings of the previously executed on error goto/resume statement, which is in effect. But it does not override the settings of any higher level settings, because those are not currently in effect and the settings are restored when the program returns from the actual function or subroutine. For example:

Global const nl ="\n"
sub ErrorSub
  on error goto ErrorLabel
  error 1
  print "No error has occured in the function. Weird.\n"
  goto FinishLabel
ErrorLabel:
  print "An error has occured inside the sub\n"
  print "and now generating another error, which is not
handled by the subroutine.\n"
  error 2
FinishLabel:
end sub

on error goto ErrorLabel

call ErrorSub

print "No error" goto FinishLabel

ErrorLabel: print "Error code: ",error(),nl

FinishLabel: end

will print

An error has occured inside the sub

and now generating another error, which is not

handled by the subroutine.

Error code: 2

Note that the on error goto setting of the module outside the subroutine remained valid although another on error goto setting was issued inside the subroutine.

[Contents]

20.5. Error codes

When an error happens the error code can be retrieved using the function error() in the error handling code. This value is an integer value. To compare this value against a specific error code the program should include the file error.bas. This file defines global constants for each run time error. It is advised to use these symbolic constants because there is no guarantee that the actual value of an error code remains the same between builds. The file error.bas is generated automatically when the interpreter C language constants are generated. The file also contains the English language explanation of each error following the line that defines the global constant for the error code.

[Contents]

21. Setting options

ScriptBasic implements many features, operators, functions, and statements. The developers defined the behavior of a statement, function or operator. For example ScriptBasic has an equality test operator like any other language. This is the well know = sign that result TRUE if the two values compared are equal and FALSE otherwise. This is simple when the values are integer or real values. But what about strings? Should strings be compared case sensitive or case insensitive? Should the developers of ScriptBasic decide instead of you? Not at all.

The statement option allows the programmer to alter the behavior of certain features. For example you can say

option compare sbCaseInsensitive

and string comparison as well as pattern matching becomes case insensitive when the line is executed. To switch it to case sensitive the programmer can write

option compare sbCaseSensitive

and comparison is case sensitive again. Note that the option statement alters the behavior of the feature globally. No matter where the option statement is executed. It can be inside a function, somewhere in a module: it alters the feature for the program execution at top level.

The syntax of the instruction is:

option symbol expression

symbol is any symbol that an instruction is looking at. Expression should result an integer value. Options are always integer values.

Also note that there is no checking if you misspell the symbol name. If you write

option compara sbCaseInsensitive

the statement will execute without error. It will set the option for the symbol compara instead of compare and the statement does not ever know that there is no one interested in the value associated with compara. Option values are available for commands as well as for external modules.

You can set the value of the option RaiseMathError. This will alter the behavior of certain mathematical operators and mathematical functions. When an operator gets an operand or a functions and argument that is out of the scope of the operation or function it returns undef by default. In many cases this is inconvenient and error prone, because this way errors may propagate further. Using this option these operators may alter their behavior. There are three constants that the programmer can use to alter argument handling:

These values can be joined together using the bit operator OR, for example

option RaiseMathError sbMathErrDiv or sbMathErrundef

means that ScriptBasic will raise error when division with zero occurs or a mathematical operator gets undef as argument.

[Contents]

22. Other miscellaneous commands

[Contents]

22.1. Sleep

The sleep command causes the basic program to stop and wait for a few seconds. The number of seconds is given as argument. The syntax of the command is very simple:

sleep expression

The expression specifies the number of seconds to sleep. This is the only effective way to insert delay into the code. Old style waiting constructs, like empty for loops consume processor and are not precise. The command sleep calls the system function sleep (Sleep on Windows NT) and this allows other processes to get processor time while the basic program is sleeping.

Because nor UNIX operating systems neither Windows NT or Windows95/98 are real time systems there is no guarantee that the basic program is going to run immediately after the specified number of seconds has elapsed.

Because of system limitations the time to sleep should not be more than 2,147,483 seconds. This is approximately 24 days. If you need more you can create a loop around the sleep statement and call it many times. This wastes a bit of CPU but not too much. If the argument to the command sleep exceeds this limit the result is unknown.

To test the functionality of the command sleep run the following program:

for i=1 to 20
print i
print
sleep 1
next i

[Contents]

23. Using External modules

External modules are ScriptBasic extensions that are written in C and compiled to dynamic load libraries. ScriptBasic is capable loading these libraries and the functions implemented in the module become callable from Basic.

The line

declare sub basfun alias "cfun" lib "dlllib"

declares that the function basfun is implemented in an external library named dlllib. The name of the function that implements the library is called cfun. In your basic program you can call this function the same way as any other user-defined function or subroutine:

call basfun(arg1,arg2,…,argn)

or

a$ = basfun(arg1,arg2,…,argn)

The function name cfun is the name of the function as the dll or so object file exports it. The last string gives the name of object file. In the example above it is dlllib.

The dll object file name can be specified as an absolute file name or as a relative name. If the string specifies an absolute file name ScriptBasic tries to load the specified dll file. In this case the basic program has to specify the full path, the name of the file and the extension. If the name of the library file is a relative name it should not contain the path, or the extension. In this case ScriptBasic appends the default file extension for dynamic load libraries and tries to locate the files in the configured module libraries. The default file extension for dynamic load libraries and the module library directories are defined in the configuration file.

In case ScriptBasic can not fid the module in the module directories specified in the configuration file it tries under Windows to find the module dll in the same directory where the executable scriba.exe is and finally in the directory ..\modules relative to the directory of the executable scriba.exe. For example if the installation directory for ScriptBasic is

C:\ScriptBasic 

the default location of the executable is

C:\ScriptBasic\bin\scriba.exe

In this case

C:\ScriptBasic\bin\dlllib.dll	and 
C:\ScriptBasic\modules\dlllib.dll 

is also tried. This allows you to start ScriptBasic programs without tedious installation procedures, editing the registry or setting up complex configuration file.

ScriptBasic is NOT capable calling arbitrary external functions implemented in a dll file. The functions should be implemented according to the calling conventions and parameter passing methods that ScriptBasic supports. External modules usually come with the appropriate basic include file that you should include into your basic program. The included files usually contain all the external function declarations and your job is to use the functions.

[Contents]

24. Command reference

[Contents]

24.1. ABS

Returns the absolute value of the argument. If the argument is a string then it first converts it to double or long. The return value is double or long depending on the argument.

ABS(undef) is undef.

[Contents]

24.2. ACOS

Calculates the arcus cosine of the argument, which is the inverse of the function COS. If the argument is not between (-1.0,+1.0) the return value is undef.

ACOS(undef) is undef.

[Contents]

24.3. ADDDAY

This function takes two argument. The first argument is a time value, the second is an integer value. The function increments the day by the second argument and returns the time value for the same hour and minute but some days later or sooner in case the second argument is negative.

[Contents]

24.4. ADDHOUR

This function takes two argument. The first argument is a time value, the second is an integer value. The function increments the hours by the second argument and returns the time value for the same minute and seconds but some hours later or sooner in case the second argument is negative.

[Contents]

24.5. ADDMINUTE

This function takes two argument. The first argument is a time value, the second is an integer value. The function increments the minutes by the second argument and returns the time value for the same seconds but some minutes later or sooner in case the second argument is negative.

[Contents]

24.6. ADDMONTH

This function takes two argument. The first argument is a time value, the second is an integer value. The function increments the month by the second argument and returns the time value for the same day, hour and minute but some months later or sooner in case the second argument is negative.

[Contents]

24.7. ADDRESS( myFunc() )

Return the entry point of a function or subroutine. The returned value is to be used solely in a corresponding ICALL. Faking aroud with the value may crash the interpreter.

[Contents]

24.8. ADDSECOND

This function takes two argument. The first argument is a time value, the second is an integer value. The function increments the seconds by the second argument and returns the time value.

[Contents]

24.9. ADDWEEK

This function takes two argument. The first argument is a time value, the second is an integer value. The function increments the week by the second argument and returns the time value for the same hour and minute but some weeks later or sooner in case the second argument is negative.

[Contents]

24.10. ADDYEAR

This function takes two argument. The first argument is a time value, the second is an integer value. The function increments the year by the second argument and returns the time value for the same month, day, hour and minute but some years later or sooner in case the second argument is negative.

This is a bit more complex than just adding 365*24*60*60 to the value, becuase leap-year calculation has to be done.

[Contents]

24.11. ASC(string)

Returns the ASCII code of the first character of the argument string.

[Contents]

24.12. ASIN

Calculates the arcus sine of the argument, which is the inverse of the function SIN. If the argument is not between (-1.0,+1.0) the return value is undef.

ASIN(undef) is undef.

[Contents]

24.13. BINMODE [ # fn ] | input | output

Set an opened file handling to binary mode.

The argument is either a file number with which the file was opened or one of keywords input and output. In the latter case the standard input or output is set.

See also TEXTMODE

[Contents]

24.14. CALL subroutine

Call a subroutine.

[Contents]

24.15. CHDIR directory

Change the current working directory.

[Contents]

24.16. CHOMP()

Remove the trailing new line from the space. If the last character of the string is not new line then the original stringis returned. This function is useful to remove the trailing new line character when reading a line from a file using the command LINEINPUT

[Contents]

24.17. CHR(code)

Return a one character string containing a character of ASCII code code.

[Contents]

24.18. CLOSE

Close a previously successfully opened file. If the file number is not associated with a successfully opened file then error is raised.

[Contents]

24.19. CLOSE DIRECTORY [#] dn

Close an opened directory and release all memory that was used by the file list.

See also OPENDIR.

[Contents]

24.20. COMMAND()

Return the command line string.

[Contents]

24.21. Concatenate operator &amp;

This operator concatenates two strings.

[Contents]

24.22. COS

Calculates the cosine of the argument.

COS(undef) is undef.

[Contents]

24.23. CRYPT(string,salt)

This function returns the encoded DES digest of the string using the salt as it is used to encrypt passwords under UNIX.

Note that only the first 8 characters of the string are taken into account.

[Contents]

24.24. CURDIR()

=displax CURDIR() Return the current directory.

[Contents]

24.25. DAY

This function accepts one argument that should express the time in number of seconds since January 1, 1970. 0:00 am and returns the day (1 to 31) value of that time. If the argument is missing it uses the actual local time.

[Contents]

24.26. DECLARE COMMAND function ALIAS cfun LIB library

Declare a command implemented in an external library. Note that library and the command implemented in it should be specifically written for ScriptBasic. This command is not able to declare and call just any library function.

[Contents]

24.26.1. DECLARECOMMAND Details

DEVELOPER DETAILS!

This command is used to start an external command defined in a dynamic load library.

The major difference between an external function and an external command is that arguments are passed after they are evaluated to external functions, while arguments are not evaluated for external commands.

External commands are a bit more tedious to implement. On the other hand external commands have more freedom handling their arguments. A command is allowed to evaluate or not to evaluate some of its arguments. It can examine the structure of the expression passed as argument and evaluate it partially at its wish.

External commands are called the same way as user defined functions or external functions.

[Contents]

24.27. DECLARE SUB function ALIAS cfun LIB library

Declare a function implemented in an external library. Note that library and the function implemented in it should be specifically written for ScriptBasic. This command is not able to declare and call just any library function.

[Contents]

24.27.1. DECLARESUB Details

DEVELOPER DETAILS!

This command is used to start an external function defined in a dynamic load library.

The dynamically loaded modules are implemented in ScriptBasic via an ordinary command that has the syntax:

'declare' 'sub' * function 'alias' string 'lib' string nl

For the compiler it generates a user defined function, which is defined on the line that contains the declare statement. The execution system will call itself recursively to execute lines starting at the line where the declare statement is. The command implemented in this file is executed and unlike the FUNCTION or SUB it immediately tells the execution system to return after the line was executed.

This command first checks if the line was already executed. On the first execution it loads the module and gets the address of the function named in the alias string. This entry point is stored in a struct and next time the function is called by the basic pointer it does not need to search for the function and for the module. If a function of an already loaded module is called the program does not reload the module. The program maintains a linked list with the names of the loaded modules and loads modules only when they are first referenced.

The modules are loaded using the operating system dll loading function dlopen or LoadLibrary. These functions search several locations for libraries in case the library is specified without absolute path name. The module loader can be fouled if the same library is defined with full path and with single name in the basic code.

For example, if the commands

declare sub fun2 alias "mefun" lib "libobas"
declare sub fun3 alias "youfun" lib "libobas"

refer to the same module, the code implemented here that they are different libraries.

When the module is loaded the code tries to get the function named versmodu and calls it. This function gets three arguments. The first argument is the interface version that ScriptBasic supports for the external modules. The second argument is a pointer to a ZCHAR terminated string that contains the variation of the calling interpreter. The third argument is a pointer to a NULL initialized void * pointer, which is the module pointer. This pointer is stored by ScriptBasic, and it is guaranteed not be modified. The modules can store "global" variable information using this pointer. Usually this pointer is not used in this function, especially because there are no "safe" memory allocation functions available at this point of execution.

The module should examine the version it gets and should decide if that interface version is OK for the module. If the interface version is not known the function should return 0 and ScriptBasic will interpret this value as a failure to load the module. If the module does not know if the interface version is good or not it can return the interface version that it can handle. ScriptBasic will examine the version and in case it can not handle the version it will generate an error.

This methodology will allow either ScriptBasic to revert its functionality to earlier interface versions in case the higher version interface not only extends the lower version but is incompatible with the former version. On the other hand a module designed for a higher version may be loaded and executed by a lower version of ScriptBasic in case the module is ready to use the lower interface version.

If the function versmodu can not be found in the DLL then ScriptBasic assumes that the module is ready to accept the current interface version. However such a module should not generally be written.

Note that the version we are talking about here is neither the version of ScriptBasic nor the version of the module. This is the version of the interface between the module and ScriptBasic.

After the version negotiation has been successfully done ScriptBasic tries to get the address of the function named bootmodu. If this function exists is is started. This function can perform all the initializations needed for the module. This function gets all the parameters that a ususal module implemented function except that there are no parameters and the function should not try to set a result value, because both of these arguments are NULL.

A module function (inclding bootmodu) is called with four arguments. The four arguments are four pointers.

int my_module_function(pSupportTable pSt,
                       void **ppModuleInternal,
                       pFixSizeMemoryObject pParameters,   // NULL for bootmodu
                       pFixSizeMemoryObject *pReturnValue) // NULL for bootmodu

The parameter pSt points to a struct that holds the function pointers that the function can call to reach ScriptBasic functionality. These functions are needed to access the arguments and to return a value.

The parameter ppModuleInternal is a pointer poiniting to a NULL initialized pointer. This pointer belongs to the module. ScriptBasic guarantees that the value is not modified during the execution (unless the module itself modifies it). This pointer can be used to remember the address space allocated by the module for itself. To store permanent values to remember the state of the module you can either use static variables or this pointer. However using this pointer and allocating memory to store the values is the preferred method. The reason for preferring this method is that global variables are global in the whole process. On the other hand ScriptBasic may run in several threads in a single process executing several different basic programs. This ppModuleInternal pointer will be unique for each thread.

Here is the point to discuss a bit the role of bootmodu. Why not to use DllMain under Windows NT? The reason is in the posibility of threaded execution. DllMain is executed whent he process loads the dll. bootmodu is executed when the basic executor loads the module. If there are more threads running then DllMain is not started again. Use DllMain or a similar function under UNIX if you want to initialize some process level data. Use bootmodu to initialize basic program specific data.

The parameter pParameters is a ScriptBasic array containg the values of the arguments. There is no run-time checking about the number of arguments. This is up to the module function.

The last parameter is a pointer to a ScriptBasic variable. The initial value of the pointed pointer is NULL, meaning undef return value. The final return value should be allocated using the macros defined in basext.h and this pointer should be set to point to the value. Note however that bootmodu and finimodu should not to try to dereference this variable, because for both of them the value is NULL.

For further information on how to write module extension functions read the on-line documentation of basext.c in this source documentation.

A module is unloaded when the basic program execution is finished. There is no basic code to unload a module. (Why?)

Before the module is unloaded calling one of the operating system functions dlclose or FreeLibrary the program calls the module function finimodu if it exists. This function gets all the four pointers (the last two are NULLs) and it can perform all the tasks that the module has to do clean up.

Note however that the module need not release the memory it allocated using the besALLOCATE macro defined in the file basext.h (which is generated using headerer.pl from basext.c). The memory is going to be released afterwards by ScriptBasic.

[Contents]

24.28. DELETE file/directory_name

Delete a file or an empty> directory. See DELTREE for a more powerful and dangerous delete.

[Contents]

24.29. DELTREE file/directory_name

Delete a file or a directory. If a directory is to be deleted all files and subdirectories are also deleted.

[Contents]

24.30. DO

Implements a tail checking loop that repeats commands so long as long the condition is either TRUE (do/while) or FALSE (do/until)

do
 ...
 commands to repeat
 ...
loop while expression

or

do
 ...
 commands to repeat
 ...
loop until expression

[Contents]

24.31. DO UNTIL condition

Implements front checking looping construct repeating so long as long the condition is FALSE

do until expression
 ...
 commands to repeat
 ...
loop

[Contents]

24.32. DO WHILE condition

Same as WHILE with a different syntax to be compatible with different BASIC implementations.

do while
 ...
loop

[Contents]

24.33. END

End of the program. Stops program execution.

[Contents]

24.34. ENVIRON("envsymbol") or ENVIRON(n)

Return the string value of the environmental variable for which the name is given as string argument. If the argument is a number n then the value of the n-th environmental variable is returned.

[Contents]

24.35. EOD(dn)

Checks if there is still some file names in the directory opened for reading using the directory number dn.

See also NEXTFILE.

[Contents]

24.36. EOF

This function accepts one parameter, an opened file number. The return value is true if and only if the reading has reached the end of the file.

[Contents]

24.37. ERROR() or ERROR n

Get the code of the last error that happened when used as a function or cause the error with code n when used as a command.

[Contents]

24.38. EVEN

Return true if the argument is an even number. even(undef) is undef.

[Contents]

24.39. EXECUTE("executable_program", time_out,pid_v)

Run a program and wait time_out seconds for it to return. The variable pid_v will hold the pid of the new process. The function returns the process exit code. If the process runs longer than time_out seconds the variable pid_v is set and error happens.

[Contents]

24.40. EXIT FUNCTION

Exit from a function.

[Contents]

24.41. EXIT SUB

Exit from a subroutine.

Same as EXITFUNC

[Contents]

24.42. EXP

Calculates the x-th exponent of e.

EXP(undef) is undef.

[Contents]

24.43. FALSE

This built-in contant is implemented as an argument less function. Returns the value false.

[Contents]

24.44. FILEACCESSTIME(file_name)

Get the time the file was accessed last time.

[Contents]

24.45. FILECREATETIME(file_name)

Get the time the file was modified last time.

[Contents]

24.46. FILEEXISTS(file_name)

Returns true if the named file exists.

[Contents]

24.47. FILELEN(file_name)

Get the length of a named file. If the length of the file can not be determined (for example the file does not exists, or the process running the code does not have permission to read the file) then the return value is undef.

[Contents]

24.48. FILEMODIFYTIME(file_name)

Get the time the file was modified last time.

[Contents]

24.49. FIX

This function returns the integral part of the argument. If the argument is undef then the return value is undef. Otherwise the return value is always long.

The difference between int and fix is that int truncates down while fix truncates torward zero. The two functions are identical for positive numbers. In case of negative arguments int will give a smaller number if the argument is not integer. For example:

  int(-3.3) = -4
  fix(-3.3) = -3

See INT.

[Contents]

24.50. LOCK # fn, mode

Lock a file or release a lock on a file. The mode parameter can be read, write or release. There can be more than one simultaneous read lock on a file but there can be only one write lock.

[Contents]

24.51. FOR var=exp_start TO exp_stop [ STEP exp_step ]

Implements a FOR loop. The variable var gets the value of the start expression exp_start, and after each execution of the loop body it is incremented ir decremented by the value exp_step until it reaches the stop value exp_stop.

FOR var=expression TO expression [ STEP expression ]
 ...
 commands to repeat
 ...
next

[Contents]

24.52. FORK()

NOT IMPLEMENTED Not implemented yet. Cygwin has an implementation, but we need a better one for NT. Until then we do not support it on UNIX as an intrinsic, because we support all functions and commands portable on UNIX and NT.

[Contents]

24.53. FORMAT()

Not implemented.

[Contents]

24.54. FORMATDATE

FormatDate("format",time)

Formats a time value (or date) according to the format string. The format string may contain place holders. The first argument is the format string the seconmd argument is the time value to convert. If the second argument is missing or undef then the local time is converted.

[Contents]

24.54.1. FORMATDATE Details

This function uses the first argument as a formatting string. This string may contain the following character strings:

     YEAR         four digit year
     YY           two digit year
     MON          three letter abbreviation of the month name
     MM           month
     0M           month with leading zero if needed
     *MONTH-NAME* name of the month
     DD           day of the month
     0D           day of the month with leading zero if needed
     WD           week day on a single digit starting with sunday=0
     WEEKDAY-NAME the name of the weekday
     WDN          three letter abbreviation fo the week day name
     HH           hours (24 hours notation)
     0H           hours with leading zero if needed (24 hours notation)
     hh           hours (12 hours notation)
     0h           hours with leading zero if needed (12 hours notation)
     mm           minutes
     0m           minutes with leading zero if needed
     am
     pm   is am or pm

[Contents]

24.55. FILEOWNER(FileName)

This function returns the name of the ownerof a file.

[Contents]

24.56. FRAC

The fractional part of the argument. This function always returns a double except that frac(undef) is undef.

Negative arguments return negative value (or zero if the argument is a negative integer), positive arguments result positive values (or zero if the argument is integer).

[Contents]

24.57. FREEFILE()

This function returns a free file number which is currently not associated with any opened file. If there is no such file number it returns undef.

[Contents]

24.58. FUNCTION fun()

Declare a function with possible arguments, local variables and local error handling. Function value is returned assigned to the function name.

FUNCTION fun(a,b,c)
...
fun = returnvalue
...
END FUNCTION

[Contents]

24.59. GMTIME

This function returns the GMT time expressed as seconds since January 1, 1970,. 00:00am.

[Contents]

24.60. GMTOLOCALTIME

This function converts a GMT time value to local time value.

[Contents]

24.61. GOSUB label

=H Gosub commands

Call a local subroutine. This is the good old way implementation of the BASIC GOSUB command. See also RETURN.

[Contents]

24.62. GOTO label

Go to a label and continue program execution at that label. Labels are local within functions and subroutines. You can not jump into a sub or jump out of it.

[Contents]

24.63. HEX(n)

Take the argument as a long value and convert it to a string that represents the value in hexadecimal form. The hexadecimal form will contain upper case alpha character if there is any alpha character in the hexadecimal representation of the number.

[Contents]

24.64. HOSTNAME()

Return the local host name.

[Contents]

24.65. HOUR

This function accepts one argument that should express the time in number of seconds since January 1, 1970. 0:00 am and returns the hour value of that time. If the argument is missing it uses the actual local time.

[Contents]

24.66. ICALL n,v1,v2, ... ,vn

ICALL is implicit call. The first argument of an ICALL command or ICALL function should be the integer value returned by the function ADDRESS as the address of a user defined function.

[Contents]

24.66.1. ICALL Details

Whenever you call a function or subroutine you have to know the name of the subroutine or function. In some situation programmers want to call a function without knowing the name of the function. For ex-ample you want to write a sorting subroutine that sorts general elements and the caller should provide a subroutine that makes the comparison. This way the sorting algorithm can be implemented only once and need not be rewritten each time a new type of data is to be sorted. The sorting subroutine gets the comparing function as an argument and calls the function indirectly. ScriptBasic can not pass functions as arguments to other functions, but it can pass integer numbers. The function ADDRESS can be used to convert a function into integer. The result of the built-in function ADDRESS is an integer number, which is associated inside the basic code with the function. You can pass this value to the ICALL command or function as first argument. The icall command is the command for indirect subroutine call. The call

icall Address(MySubroutine()),arg1,arg2,arg3

is equivalent to

call MySubroutine( arg1,arg2,arg3)

If you call a function that has return value use can use the icall function instead of the icall state-ment:

A = icall(Address(MyFunction()),arg1,arg2,arg3)

is equivalent to

A = MyFunction(arg1,arg2,arg3)

The real usage of the function Address and icall can be seen in the following example:

sub MySort(sfun,q)
local ThereWasNoChange,SwapVar
repeat
 ThereWasNoChange = 1
 for i=lbound(q) to ubound(q)-1

if icall(sfun,q[i],q[i+1]) > 0 then ThereWasNoChange = 0 SwapVar = q[i] q[i] = q[i+1] q[i+1] = SwapVar endif

next i until ThereWasNoChange

end sub

function IntegerCompare(a,b) if a < b then cmp = -1 elseif a = b then cmp = 0 else cmp = 1 endif end function

h[0] = 2 h[1] = 7 h[2] = 1

MySort address(IntegerCompare()) , h

for i=lbound(h) to ubound(h) print h[i],"\n" next i

Note that the argument of the function Address is a function call. ScriptBasic allows variables and func-tions to share the same name. Address is a built-in function just as any other built in function, and therefore the expression

Address(MySub) THIS IS WRONG!>

Is syntactically correct. The only problem is that it tries to calculate the address of the variable MySub, which it can not and results a run-time error. Instead you have to write

Address( MySub() )

using the parentheses. In this situation the function or subroutine MySub() will not be invoked. The parentheses tell the compiler that this is a function and not a variable.

[Contents]

24.67. IF condition THEN

Conditional execution. There are two different ways to use this commandsingle line IF and multi line IF.

A single line IF has the form

IF condition THEN command

There is no way to specify any ELSE command. If you need ELSE command you have use multi line IF.

The multi line IF should not contain any command directly after the keyword THEN. It should have the format:

IF condition THEN commands [ ELSE IF | ELSEIF | ELSIF | ELIF commands ... ] [ ELSE commands ] END IF | ENDIF

You can use as many ELSE IF branchges as you like and at most one ELSE branch. The different verbiages are allowed for ease program porting from other BASIC dialect. There is no difference between the interpretetion.

[Contents]

24.68. INPUT(n,fn)

Input specified number of characters/bytes from a file.

The first argument of the function is n the number of character to be read, the second argument fn is the file number. The actual number of bytes read can be retrieved using the LEN function of the result string.

Note that some Basic languages allow the form

a = INPUT(20,#1)
however this extra # is not allowed in ScriptBasic.

[Contents]

24.69. INSTR(base_string,search_string [ ,position ] )

This function can be used to search a sub-string in a string. The first argument is the string we are searching in. The second argument is the string that we actually want to find in the first argument. The third optional argument is the position where the search is to be started. If this argu-ment is missing the search starts with the first character position of the string. The function returns the position where the sub-string can be found in the first string. If the searched sub-string is not found in the string then the return value is undef.

See INSTRREV

[Contents]

24.70. INSTRREV(base_string,search_string [ ,position ] )

This function can be used to search a sub-string in a string in reverse order starting from the end of the string. The first argument is the string we are searching in. The second argument is the string that we actually want to find in the first argument. The third optional argument is the position where the search is to be started. If this argument is missing the search starts with the last character position of the string. The function returns the position where the sub-string can be found in the first string. If the searched sub-string is not found in the string then the return value is undef.

See INSTR

[Contents]

24.71. INT

This function returns the integral part of the argument. If the argument is undef then the return value is undef. Otherwise the return value is allways long.

The difference between int and fix is that int truncates down while fix truncates torward zero. The two functions are identical for positive numbers. In case of negative arguments int will give a smaller number if the argument is not integer. For example:

  int(-3.3) = -4
  fix(-3.3) = -3

See FIX.

[Contents]

24.72. ISDIR(file_name)

Returns true if the named file is a directory.

[Contents]

24.73. ISREG(file_name)

Returns if the named file is a regular file.

[Contents]

24.74. JOIN(joiner,str1,str2,...)

Join the argument strings using the first argument as a joiner string.

[Contents]

24.74.1. JOIN Details

This function can be used to join several strings together. The first argument of the function is the string used to join the rest of the arguments. The rest of the argument are joined together, but also elements on an array can be joined together. See the example:

for i=1 to 8
 q[i] = I
next
print join("|",q)
print
print join("/",1,2,3,4,5,6,7,8)
print
print join(" j-s ",q,2,3,4,5,6,7,8)
print
print join("/",1)
print

will print

1|2|3|4|5|6|7|8
1/2/3/4/5/6/7/8
1 j-s 2 j-s 3 j-s 4 j-s 5 j-s 6 j-s 7 j-s 8
1

The first join joins the elements of the array. The second join joins the arguments of the function. The third example also joins the arguments although the second argument is an array. Because there are more arguments each of them is treated as single value and are joined. Whenever an array is used in place of a single value, the first element of the array is taken. In this example this is 1. The last join is a special one. In this case the join string is not used, because there is only one argument after the join string. Because this argument is not an array there are no elements of it to join.

[Contents]

24.75. JOKER(n)

Return the actual match for the n-th joker character from the last executed LIKE operator.

[Contents]

24.75.1. JOKER Details

When a LIKE operator is executed ScriptBasic stores the actual strings that matched the joker and wild card characters from the pattern in an array. Using this function the programcan access the actual value of the n-th string that was matched against the n-th joker or wild card character. For example:

Const nl="\n"
if "file.txt" like "*.*" then
  print "File=",joker(1)," extension=",joker(2),nl
else
  print "did not match"
endif

will print

File=file extension=txt

[Contents]

24.76. KILL(pid)

This function kills a process given by the pid and returns true if the process was successfully killed. Otherwise it returns false.

[Contents]

24.77. LBOUND

Return the lowest index of the argument array.

[Contents]

24.78. LCASE()

Lowercase a string.

[Contents]

24.79. LEFT(string,len)

Creates the left of a string. The first argument is the string. The second argument is the number of characters that should be put into the result. If this value is larger than the number of characters in the string then all the string is returned.

See also MID, RIGHT

[Contents]

24.79.1. LEFT Details

left(x,y) cuts out a substring of y characters from the left of the string x. If the first argument is not defined the result is also undef. Otherwise the first argument is converted to string and the second ar-gument is converted to integer value.

If the second argument is not defined or has negative value it is treated as numeric zero and as such the result string will be empty string.

For compatibility reasons you can append a dollar ($) sign to the end of the function identifier.

Example

a$ = _
"superqualifragilisticexpialidosys"
print "*",left(a$,undef),"*"
print "*",left(a$,7),"*"
print "*",left(a$,-6),"*"
print "*",left(a$,0),"*"
print left(undef,"66")

will print

**
*superqu*
**
**
undef

[Contents]

24.80. LEN()

Return the length of a string.

Undefined value length is undef.

[Contents]

24.81. v = expression

Assign a value to a variable. You should NOT write the keyword LET before the variable. LET is not a valid keyword in ScriptBasic.

The variable can be a global or local variable, array element or associative array element.

[Contents]

24.82. v &= expression

Concatenate a string to a variable. You should NOT write the keyword LET before the variable. LET is not a valid keyword in ScriptBasic.

The variable can be a global or local variable, array element or associative array element.

[Contents]

24.83. v /= expression

Divide a variable by an expression. You should NOT write the keyword LET before the variable. LET is not a valid keyword in ScriptBasic.

The variable can be a global or local variable, array element or associative array element.

[Contents]

24.84. v \= expression

Integer divide a variable by a value. You should NOT write the keyword LET before the variable. LET is not a valid keyword in ScriptBasic.

The variable can be a global or local variable, array element or associative array element.

[Contents]

24.85. v -= expression

Substract a value to a variable. You should NOT write the keyword LET before the variable. LET is not a valid keyword in ScriptBasic.

The variable can be a global or local variable, array element or associative array element.

[Contents]

24.86. v += expression

Add a value to a variable. You should NOT write the keyword LET before the variable. LET is not a valid keyword in ScriptBasic.

The variable can be a global or local variable, array element or associative array element.

[Contents]

24.87. v *= expression

Multiply a variable with a value. You should NOT write the keyword LET before the variable. LET is not a valid keyword in ScriptBasic.

The variable can be a global or local variable, array element or associative array element.

[Contents]

24.88. string LIKE pattern

Compare a string against a pattern.

      string LIKE pattern

The pattern may contain joker characters and wild card characters.

[Contents]

24.88.1. LIKE Details

Pattern matching in ScriptBasic is similar to the pattern matching that you get used to on the UNIX or Windows NT command line. The operator like compares a string to a pattern.

string like pattern

Both string and pattern are expressions that should evaluate to a string. If the pattern matches the string the result of the operator is true, otherwise the result is false.

The pattern may contain normal characters, wild card characters and joker characters. The normal characters match themselves. The wild card characters match one or more characters from the set they are for. The joker characters match one character from the set they stand for. For example:

Const nl="\n"
print "file.txt" like "*.txt",nl
print "file0.txt" like "*?.txt",nl
print "file.text" like "*.txt",nl

will print

-1
-1
0

The wild card character * matches a list of characters of any code. The joker character ? matches a single character of any code. In the first print statement the * character matches the string file and .txt matches itself at the end of the string. In the second example * matches the string file and the joker ? matches the character 0. The wild card character * is the most general wild card character because it matches one or more of any character. There are other wild card characters. The character # matches one or more digits, $ matches one or more alphanumeric characters and finally @ matches one or more alpha characters (letters).

*	all characters
#	0123456789
$	0123456789abcdefghijklmnopqrstxyvwzABCDEFGHIJKLMNOPQRSTXYVWZ
@	abcdefghijklmnopqrstxyvwzABCDEFGHIJKLMNOPQRSTXYVWZ

A space in the pattern matches one or more white spaces, but the space is not a regular wild card character, because it behaves a bit different.

Note that wild card character match ONE or more characters and not zero or more as in other systems. Joker characters match exactly one character, and there is only one joker character by default, the character ?, which matches a single character of any code.

We can match a string to a pattern, but that is little use, unless we can tell what substring the joker or wildcard characters matched. For the purpose the function joker is available. The argument of this function is an integer number, n starting from 1 and the result is the substring that the last pattern matching operator found to match the nth joker or wild card character. For example

Const nl="\n"
if "file.txt" like "*.*" then
  print "File=",joker(1)," extension=",joker(2),nl
else
  print "did not match"
endif

will print

File=file extension=txt

If the pattern did not match the string or the argument of the function joker is zero or negative, or is larger than the serial number of the last joker or wild card character the result is undef.

Note that there is no separate function for the wild card character substrings and one for the joker characters. The function joker serves all of them counting each from left to right. The function joker does not count, nor return the spaces, because programs usually are not interested in the number of the spaces that separate the lexical elements matched by the pattern.

Sometimes you want a wild card character or joker character to match only itself. For example you want to match the string "13*52" to the pattern two numbers separated by a star. The problem is that the star character is a wild card character and therefore "#*#" matches any string that starts and ends with a digit. But that may not be a problem. A * character matches one or more characters, and therefore "#*#" will indeed match "13*52". The problem is, when we want to use the substrings.

Const nl="\n"
a="13*52" like "#*#"
print joker(1)," ",joker(3),nl
a="13*52" like "#~*#"
print joker(1)," ",joker(2),nl

will print

1 52
13 52

The first # character matches one character, the * character matches the substring "3*" and the final # matches the number 52.

The solution is the pattern escape character. The pattern escape character is the tilde character: ~. Any character following the ~ character is treated as normal character and is matched only by itself. This is true for any normal character, for wild card characters; joker characters; for the space and finally for the tilde character itself. The space character following the tilde character matches exactly one space characters.

Pattern matching is not always as simple as it seems to be from the previous examples. The pattern "*.*" matches files having extension and joker(1) and joker(2) can be used to retrieve the file name and the extension. What about the file sciba_source.tar.gz? Will it result

File=scriba_source.tar extension=gz

or

File=scriba_source extension=tar.gz

The correct result is the second. Wild card characters implemented in ScriptBasic are not greedy. They eat up only as many characters as they need.

Up to now we were talking about wild card characters and the joker character defining what matches what as final rule carved into stone. But these rules are only the default behavior of these characters and the program can alter the set of characters that a joker or wild card character matches.

There are 13 characters that can play joker or wild card character role in pattern matching. These are:

*  #  $  @  ?  &  %  !  +  /  |  <  >

When the program starts only the first five characters have special meaning the others are normal characters. To change the role of a character the program has to execute a set joker or set wild command. The syntax of the commands are:

set joker expression to expression set wild expression to expression

Both expressions should evaluate to string. The first character of the first string should be the joker or wild card character and the second string should contain all the characters that the joker or wild card character matches. The command set joker alters the behavior of the character to be a joker character matching a single character in the compared string. The command set wild alters the behavior of the character to be a wild card character matching one or more characters in the compared string. For ex-ample if you may want the & character to match all hexadecimal characters the program has to execute:

set wild "&" to "0123456789abcdefABCDEF"

If a character is currently a joker of wild card character you can alter it to be a normal character issuing one of the commands

set no joker expression set no wild expression

where expression should evaluate to a string and the first character of the string should give the character to alter the behavior of.

The two commands are identical, you may always use one or the other; you can use set no joker for a character being currently wild card character and vice versa. You can execute the command even if the character is currently a normal character in the pattern matching game.

Using the commands now we can see that

[Contents]

24.89. LINE INPUT

Read a line from a file or from the standard input.

The syntax of the command is

LINE INPUT [# i , ] variable

The parameter i is the file number used in the open statement. If this is not specified the standard input is read.

The variable will hold a single line from the file read containing the possible new line character terminating the line.

See also CHOMP

[Contents]

24.90. LOC()

Return current file pointer position of the opened file.

[Contents]

24.91. LOCATLTOGMTIME

This function converts a local time value to GMT time value.

[Contents]

24.92. LOCK REGION # fn FROM start TO end FOR mode

Lock a region of a file. The region starts with the position start and ends with the position end including both end positions.

The mode can be read, write and release.

[Contents]

24.93. LOF()

Length Of File. Return length of the opened file referenced with the file number.

[Contents]

24.94. LOG

Calculates the natural log of the argument. If the argument is zero or less than zero the result is undef

LOG(undef) is undef.

[Contents]

24.95. LOG10

Calculates the log of the argument. If the argument is zero or less than zero the result is undef

LOG10(undef) is undef.

[Contents]

24.96. LTRIM()

Remove the space from the left of the string.

[Contents]

24.97. MID(string,start [ ,len ])

Return a subpart of the string. The first argument is the string, the second argument is the start position. The third argument is the length of the sub-part in terms of characters. If this argument is missing then the subpart lasts to the last character of the argument string.

See also LEFT, RIGHT.

[Contents]

24.97.1. MID Details

mid(x,y,[z]) cuts out a sub-string from the string x. If the first argument of the function is undefined the result is undef. Otherwise the first argument is converted to string and the second and third arguments are converted to numeric value. The third argument is optional.

The second argument specifies the start position of the resulting substring in the original string x; and the last argument specifies the number of characters to take from the original string x. If the third argument is missing the substring lasts from the start position to the end of the string. If the second argu-ment is not defined the start of the substring is at the start of the original string. In other words if the second argument is missing it is the same as value 1. If the second argument is zero or negative it will specify the start position counting the characters from the end of the string.

If the staring position y points beyond the end of the string the result is empty string. If the length of the substring is larger than the number of characters between the starting position and end of the original string then the result will be the substring between the start position and the end of the original string.

If the length of the substring is negative the characters before the starting position are taken. No more than the available characters can be taken in this case either. In other words if the length is negative and is larger in absolute value than the starting position the resulting sub-string is the character between the position specified by the second argument and the start of the string.

Note that the order of the characters is never changed even if some position or length parameters are negative.

For compatibility reasons you can append a dollar ($) sign to the end of the function identifier.

Example:

a$ = "superqualifragilisticexpialidosys"
print mid(a$,undef)
print mid(a$,1,5)
print mid(a$,undef,6)
print mid(a$,6,5)
print mid(a$,"-3")
print "*",mid(a$,0),"*"
print mid(undef,"66")
print mid(a$,6,-3)
print mid(a$,6,3)
print mid(a$,-4,-3)
print mid(a$,-4,3)

will print

superqualifragilisticexpialidosys
super
superq
quali
sys
**
undef
erq
qua
ido
osy

[Contents]

24.98. MINUTE

This function accepts one argument that should express the time in number of seconds since January 1, 1970. 0:00 am and returns the minute value of that time. If the argument is missing it uses the actual local time.

[Contents]

24.99. MKDIR firectory_name

Create a directory. Note that all directories on the path are created recursively if neccessary.

[Contents]

24.100. MONTH

This function accepts one argument that should express the time in number of seconds since January 1, 1970. 0:00 am and returns the month (1 to 12) value of that time. If the argument is missing it uses the actual local time.

[Contents]

24.101. NEXTFILE(dn)

Retrieve the next file name from an opened directory list. If there is no more file names it returns undef.

See also OPENDIR and CLOSEDIR.

[Contents]

24.102. NOW

This function returns the local time expressed as seconds since January 1, 1970,. 00:00am.

[Contents]

24.103. OCT(n)

Take the argument as a long value and convert it to a string that represents the value in octal form.

[Contents]

24.104. ODD

Return true if the argument is an odd number. odd(undef) is undef

[Contents]

24.105. ON ERROR GOTO [ label | 0 ]

Set the entry point of the error handling routine. If the argument is 0 then the error handling is switched off.

[Contents]

24.106. ON ERROR RESUME [ label | next ]

Setting ON ERROR RESUME will try to continue execution on the label or on the next statement when ean error occures without any error handling code.

See also ONERRORGOTO, RESUME and ERROR.

[Contents]

24.107. OPEN file_name FOR mode AS [ # ] i [ LEN=record_length ]

Open or create and open a file. The syntax of the line is

OPEN file_name FOR mode AS [ # ] i [ LEN=record_length ]

The parameters:

[Contents]

24.108. OPEN DIRECTORY dir_name PATTERN pattern OPTION option AS dn

Open a directory to retrieve the list of files.

See also CLOSEDIR and NEXTFILE.

[Contents]

24.109. OPTION symbol value

Set the integer value of an option. The option can be any string without the double quote. Option names are case insensitive in ScriptBasic.

This command has no other effect than storing the integer value in the option symbol table. The commands or extenal modules may access the values and may change their behavior accoring to the actual values associated with option symbols.

You can retrieve the actual value of an option symbol using the function OPTIONF

[Contents]

24.110. OPTION("symbol")

Retrieve the actual value of an option symbol as an integer or undef if the option was not set. Unlike in the command OPTION the argument of this function should be double quoted.

[Contents]

24.111. pack("format",v1,v2,...,vn)

Pack list of arguments into a binary string.

The format strings can contain the packing control literals. Each of these characters optionally take the next argument and convert to the specific binary string format. The result is the concatenated sum of these strings.

Some control characters do not take argument, but result a constant string by their own.

See also UNPACK

[Contents]

24.112. PI

This built-in contant is implemented as an argument less function. Returns the approximate value of the constant PI which is the ratio of the circumference of a circle to its diameter.

[Contents]

24.113. POW10

Calculates the x-th exponent of 10.

POW10(undef) is undef.

[Contents]

24.114. PRINT [ # fn , ] print_list

The command prints the print_list to an opened file given by the file number fn. If fn is not specified the command prints to the standard output.

If there is no print_list specified the command prints a new line.

[Contents]

24.115. ref v1 = v2

Assing a variable to reference another variable. Following this command altering one of the variables alters both variables.

[Contents]

24.116. REPEAT

Implements a tail checking loop that repeats the commands so long as long the condition is false.

repeat
  ...
  commands to repeat
  ...
until expression

[Contents]

24.117. REPLACE(base_string,search_string,replace_string [,number_of_replaces] [,position])

This function replaces one or more occurrences of a sub-string in a string. REPLACE(a,b,c) searches the string a seeking for occurrences of sub-string b and replaces each of them with the string c.

The fourth and fifth arguments are optional. The fourth argument specifies the number of replaces to be performed. If this is missing or is undef then all occurrences of string b will be replaced. The fifth argument may specify the start position of the operation. For example the function call

REPLACE("alabama mama", "a","x",3,5)

will replace only three occurrences of string "a" starting at position 5. The result is "alabxmx mxma".

[Contents]

24.118. RESET

Close all files.

[Contents]

24.119. RESET DIRECTORY [#] dn

Reset the directory file name list and start from the first file name when the next call to NEXTFILE is performed.

See also OPENDIR, CLOSEDIR, NEXTFILE, EOD.

[Contents]

24.120. RESUME [ label | next ]

Resume the program execution after handling the error. RESUME without argument tries to execute the same line again that caused the error. RESUME NEXT tries to continue execution after the line that caused the error. RESUME label tries to continue execution at the specified label.

See also ONERRORGOTO, ONERRORRESUME and ERROR.

[Contents]

24.121. RETURN

Return from a subroutine started with GOSUB.

[Contents]

24.122. REWIND [ # ]fn

Positions the file cursor to the start of the file. This is the same as SEEK fn,0 or SEEK#fn,0

[Contents]

24.123. RIGHT(string,len)

Creates the right of a string. The first argument is the string. The second argument is the number of characters that should be put into the result. If this value is larger than the number of characters in the string then all the string is returned.

See also MID, LEFT.

[Contents]

24.123.1. RIGHT Details

RIGHT(x,y) cuts out a substring of y characters from the right of the string x. If the first argument is not defined the result is also undef. Otherwise the first argument is converted to string and the second argument is converted to integer value.

If the second argument is not defined or has negative value it is treated as numeric zero and as such the result string will be empty string.

For compatibility reasons you can append a dollar ($) sign to the end of the function identifier.

[Contents]

24.124. RND

Returns a random number as generated by the C function rand().

[Contents]

24.125. ROUND

This function rounds its argument. The first argument is the number to round, and the optional second argument specifies the number of fractional digits to round to.

If the second argument is missing the function rounds to integer value.

The return value is long if the number of decimal places to keep is zero, otherwise the return value is double.

Negative value for the number of decimal places result rounding to integer value.

ROUND(undef) is undef. Also ROUND(val,undef) is equivalent to ROUND(value). See INT.

[Contents]

24.126. RTRIM()

Remove the space from the right of the string.

[Contents]

24.127. SEC

This function accepts one argument that should express the time in number of seconds since January 1, 1970. 0:00 am and returns the seconds value of that time. If the argument is missing it uses the actual local time.

[Contents]

24.128. SEEK fn,position

Go to a specified position in a file.

[Contents]

24.129. SET FILE filename parameter=value

Set some of the parameters of a file. The parameter can be:

[Contents]

24.130. SET JOKER "c" TO "abcdefgh..."

Set a joker character to match certain characters when using the LIKE operator. The joker character "c" can be one of the following characters

*  #  $  @  ?  &  %  !  +  /  |  <  >

The string after the keyword TO should contain all the characters that the joker character should match. To have the character to match only itself to be a normal character say

SET NO JOKER "c"

See also SETWILD, LIKE (details), JOKER

[Contents]

24.131. SET WILD "c" TO "abcdefgh..."

Set a wild character to match certain characters when using the LIKE operator. The wild character "c" can be one of the following characters

*  #  $  @  ?  &  %  !  +  /  |  <  >

The string after the keyword TO should contain all the characters that the wild card character should match. To have the character to match only itself to be a normal character say

SET NO WILD "c"

See also SETJOKER, LIKE (details), JOKER

[Contents]

24.132. SIN

Calculates the sine of the argument.

SIN(undef) is undef.

[Contents]

24.133. SLEEP(n)

Suspend the execution of the interpreter (process or thread) for n seconds.

[Contents]

24.134. SPACE(n)

Return a string of length n containing spaces.

[Contents]

24.135. SPLIT string BY string TO var_1,var_2,var_3,...,var_n

Takes the string and splits into the variables using the second string as delimiter.

[Contents]

24.136. SPLITA string BY string TO array

Split a string into an array using the second string as delimiter. If the string has zero length the array becomes undefined. When the delimiter is zero length string each array element will contain a single character of the string.

See also SPLIT

[Contents]

24.137. SQR

Calculates the square root of the argument.

[Contents]

24.137.1. SQR Details

If the argument is a string it is converted to numeric value. If the argument is negative the result is undef.

If the argument is integer and is the square of an integer value then the result is integer.

SQR(undef) is undef.

[Contents]

24.138. STOP

Stop program execution.

[Contents]

24.139. STR(n)

Converts a number to string. This function is rarely needed, because conversion is done automatically.

[Contents]

24.139.1. STR Details

Converts a number to string. This function is rarely needed, because conversion is done automatically. However you may need

 STRING(13,STR(a))

to be sure that the value a is interpreted as string value.

[Contents]

24.140. STRING(n,code)

Create a string of length n containing characters code. If code is a string then the first character of the string is used to fill the result. Otherwise code is converted to long and the ASCII code is used.

[Contents]

24.141. STRREVERSE(string)

Return the reversed string (aka. all the characters in the string in reverse order).

[Contents]

24.142. SUB fun()

Declare a subroutine with possible arguments, local variables and local error handling.

SUB fun(a,b,c)
...
...
END SUB

Note that functions and subroutines are not quite different is ScriptBasic. ScriptBasic allows you to return a value from a subroutine and to call a function using the command CALL. It is just a convention to have separateléy SUB and FUNCTION declarations.

[Contents]

24.143. SYSTEM(executable_program)

Run a program and return the PID of the new process. Does not wait for the termination of the new process.

[Contents]

24.144. TEXTMODE [ # fn] | input | output

Set an opened file handling to text mode.

The argument is either a file number with which the file was opened or one of keywords input and output. In the latter case the standard input or output is set.

See also BINMODE

[Contents]

24.145. TIMEVALUE

This function gets zero or more, at most six arguments and interprets them as year, month, day, hour, minute and seconds and calculates the number of seconds elapsed since January 1, 1970. till the time specified. If some argument is missing or undef the default value is January 1, 1970. 0:00 am.

[Contents]

24.146. TRIM()

Remove the space from both ends of the string.

[Contents]

24.147. TRUE

This built-in contant is implemented as an argument less function. Returns the value true.

[Contents]

24.148. TRUNCATE fn,new_length

Truncate a file to the specified size.

[Contents]

24.149. UCASE()

Uppercase a string.

[Contents]

24.150. UNDEF variable

Undefines a variable or release whole array. When this command is used as a function, it simply returns the value undef.

[Contents]

24.150.1. UNDEF Details

Note that when this command is called in a function then the local variable is undefed and the caller variable passed by reference is not changed. Therefore

sub xx(a) undef a end sub

q = 1 xx q print q

will print 1 and not undef.

On the other hand

sub xx(a) a = undef end sub

q = 1 xx q print q

does print undef.

[Contents]

24.151. UNPACK string BY format TO v1,v2,...,vn

Unpack the binary string string using the format string into the variables. The format string should have the same format as the format string the in the function PACK.

[Contents]

24.152. VAL

Converts a string to numeric value. If the string is integer it returns a long. If the string contains a number presentation which is a float number the returned value is double. In case the argument is long or double no conversion is done.

VAL(undef) is undef

[Contents]

24.153. WEEKDAY

This function accepts one argument that should express the time in number of seconds since January 1, 1970. 0:00 am and returns the week day value of that time. If the argument is missing it uses the actual local time.

[Contents]

24.154. WHILE condition

Implements the while loop as it is usually done in most basic implementations.

while expression
  ...
  commands to repeat
  ...
wend

This command implements a looping construct checking the condition at start and does loop when the condition is TRUE.

[Contents]

24.155. YEAR

This function accepts one argument that should express the time in number of seconds since January 1, 1970. 0:00 am and returns the year value of that time. If the argument is missing it uses the actual local time.

[Contents]

24.156. YEARDAY

This function accepts one argument that should express the time in number of seconds since January 1, 1970. 0:00 am and returns the year-day value of that time. If the argument is missing it uses the actual local time.