BAPI

Copyright 1992 - 2005, Stuart Udall

overview
important bits
installation
configuration and startup
controls and methods
issues and limitations
planned improvements
revision history
download

version 16.16: June 19, 2005


 
  overview

Note: a new version of this software is available BUT it's a port to PowerBASIC, and is no longer compatible with QBASIC.
Does anyone still use QBASIC? Consider migrating if so...

Welcome to BAPI - a library of BASIC subroutines intended to simplify and accelerate the development of QBASIC programs. BAPI can read and write text files of any size, display popup alerts, questions and input forms, search for strings of text in files or other strings, and read and write INI files, among other things.

Inserting this library into any QBASIC program will allow the program to make use of all the functions of the library.

Programs written with the BAPI can run on DOS 5.0 or above, or Windows 3.1, 95, 98, 98SE, ME, NT, 2000, or XP. Minimum hardware requirements: 1Mb RAM, 80386 CPU, any video card. BAPI does not directly support mice, graphics, or sound (however, programs written with the BAPI can use QBASIC's graphics and sound functions no problem, and even work with inline assembler mouse drivers on the net).

background

QBASIC is a BASIC interpreter that came with all versions of DOS 5 and 6, and is usually shipped in the MS-DOS Tools directory on Windows CDs. It is a cut-down QuickBASIC, which in turn was the DOS-based forerunner to VisualBasic. QBASIC is very simple and small, however with some patience many things can be done with it. After several years of rewriting my user input and file management routines each time I made a new program, I standardised on a set of generic routines and variablenames. Over time this has evolved into the library presented. The library is called BAPI, an acronym for BASIC Application Programming Interface.

intended usage

This is not rocket science. Rocket scientists should head toward the booth with the Java signage. The library exists because I don't have time to learn a new programming language, I want to write programs quickly that work and that do what I want. I'm not writing word-processors or flight simulators... but my programs can read and write logfiles, webpages, scripts and sourcecode, for example. And combined with a BASIC compiler such as FirstBASIC, it's possible to create a working 80k EXE in a day, pretty blue windows, INI file and all!

BAPI software development kit

Included with BAPI is BAPISDK.EXE. This utility is designed to making working with BAPI easier. In particular, the SDK can:

  • pre/postcompile a BAPI-based program: I use QBASIC to develop and edit my programs, however I use FirstBasic to compile to EXE. The FirstBasic editor is not so good; one limitation is that it can hold a file of 64k maximum. QBASIC files can be larger than that. This means you can write a program you can't compile, which is Bad. To address this, I have created a precompiler, which strips all whitespace, and all comments starting with a ' (which means REMs are not stripped). On average this strips around 20k from the source! The precompiler also adjusts the BAPI to take advantage of the larger memory space afforded by FirstBasic, and generally smoothes over the process of compiling a QBASIC program with FirstBasic. The SDK includes support for an EXE compressor, such as UPX, which automatically compresses the EXE produced by FirstBasic - OK, so there's not a huge point in compressing an 80k EXE, even at 2:1 average compression ratio, but waste not, want not, my Mum always told me - hi Mum!!! :-) Finally, the postcompilation process can publish the compressed EXE to a directory of your choice, in addition to the copy it saves in the current directory. This saves you having to copy the finished program from your development directory to your production directory.

  • compare the BAPI from program A with the BAPI from program B

  • upgrade the BAPI from program A with the BAPI from program B: You may find you end up with many different programs with many different tweaks in either your code or the BAPI (you can change almost any part of it). To manage this, BAPISDK can separate your code from the BAPI, and write each to separate files; the tool can also read these files and put the program back together again. But in between, you can extract a different BAPI from a different program, and substitute it when you reassemble your original program! In this way you can upgrade the BAPI without lots of copying and pasting.

  • separate and amalgamate a program's BAPI and program-specific routines

  • summarise a program's BAPI into an HTML report

More information on the use of BAPISDK is available here.


 
  important bits

  • requires Microsoft QBASIC 1.0 (1.1 reccommended)
  • BAPI requires MS-DOS 5.0 or above (earlier versions untested)
  • BAPISDK requires MS-Windows 9x, ME, NT, 2000 or XP
  • BAPISDK compilation requires third-party compiler such as FirstBASIC
  • the minimum hardware requirements for programs compiled with BAPI and FirstBASIC (including BAPISDK) are: 300Kb base memory, 512Kb RAM (XMS), 80386 CPU, any video card - if Windows is running these should be fine. If not, HIMEM.SYS must be loaded.
  • BAPISDK EXE compression requires third-party EXE compressor such as UPX

  • the following points are this program's terms and conditions of use:
  1. This program is hereinafter known as "the Software".
  2. The Software comes with no warranty and is used at your own risk.
  3. The Software is not (and will never be) complete and as such may behave strangely.
  4. The Software is the copyrighted property of the author.
  5. The Software may be distributed freely.
  6. The Software may NOT be incorporated into a commercial work without prior written permission from the author.
  7. The Software is "donateware" - you can choose your amount, and pay with Paypal! Donating will encourage me to create more tools. If you do decide to donate, thankyou in advance; the form is here.

There is a feedback form here.


 
  installation
  1. run the self-extracting archive
That's it. :-)


 
  configuration and startup

To use the BAPI, load the template included in the BAPI package into QBASIC, and add your code at the end. See this for more information regarding the structure of a BAPI-enabled program.

To use the SDK, simply run BAPISDK.EXE.

There are some default settings inside BAPISDK.INI; if this file doesn't exist, BAPISDK will create it.


 
  controls and methods

BAPI function reference

* denotes required field
# denotes function is found in EXTRA.BAS

mainline 16 call boot, browser, restart or exit
abort 7 graceful abandoning of ship.
alphasort 3 sorts the list array in ascending alphabetical order #
anykey 3 wait for keypress, return it
appendfile 4 append a line to a text file
auth 3 gets a password from the user
boot 16 pre-define environment, arrays, load DOS vars, INI file
changeini 3 changes the value of an INI setting in the INI file #
copyblock 4 copy a range of lines from one file to another file #
copymove 3 copy or move a file from one location to another
csv2dim 2 parse a comma-separated-string into tmp array
dialog 7 display alert/question dialog box, get keypress/answer; log to file
drawbox 7 draw a box onscreen
errx 3 generic runtime errorhandler
findfiles 3 gets *.filespec in a directory and adds to list #
findsetting 3 returns value of a specified setting in INI file
findss 5 find the next or previous occurence of a substring in a string
findssarray 3 find the next occurence of a substring in infile array
freespace 3 gets free disk space on specified drive letter #
getaltkey 2 returns ASCII character from alt-keycode
getdate 5 returns epochday, and date in "Month Daynumber, 4DigitYear" format
getdaynum 1 returns number of days since year start, or since prespecified date #
getdosvar 1 returns value of specified DOS environment variable
getform 9 get multiple data items from user in dialog box
getINIextras 1 load a set of extra elements from INI file #
getmodele 4 retrieve all elements from a module in memory or in INI file
getmodset 1 retrieve an element from a module in memory #
getosver 2 returns operating system and version
getrand 5 generate a unique 8-digit string and tempfilename
getregkey 3 retrieve a registry key from Windows' registry #
hex2dec 1 converts a hexadecimal number to decimal
ifexist 6 detect the presence of a file on disk
ifinmod 2 find an element in a module in memory #
nukebox 1 erase current dialog box
progress 4 display progress indicator, with optional dualbar
readdir 14 obtain directory contents from FAT-16, FAT-32, NTFS or UNC volumes #
readfile 7 read a range of lines from a text file into infile array
runfromdir 1 run a DOS command from within a specified directory
runlongcmd 4 run a DOS commandline up to 255 characters long, returns errorlevels
scanfile 5 find the next or previous occurence of a string in a text file
showline 2 displays a formatted line onscreen
showmenu 4 display scrollable menu, wait for cursor-based selection, parse user response
showmsg 9 display or erase statusbar
showtitle 2 display titlebar
sift 1 check for char, convert if found #
split 4 chop a string in two at first, next, previous or last delimiter, or character position
strip 2 strip leading/trailing spaces from a string, and any sub-ASCII 32 characters #
testint 1 tests that a number stored as a string is less than 32767 #
trunc 1 returns a number with x decimal points #
writebuf 4 buffered write to text file #
writefile 14 write a range of lines to a text file from infile array; can backup and/or append


function
mainline 16function index
purpose set program variables, call boot, browser, restart or exit
features 
expects..to contain..
p.name$* name of program
p.ver$* program version number
p.copy$copyright information
p.abort%-1=restart program; 0=continue execution without reinitialisation; 1=end execution, displaying error in a.msg$ if it exists
p.dev%flag for interpreted or compiled mode (1=interpreted, 0=compiled)
p.noini%flag to ignore INI file (1=ignore, 0=don't (default))
p.needini%flag to require INI file (1=require, 0=don't (default))
p.loadmod%flag to load [modules] at boot (1=load, 0=don't (default))
p.ininoclear%flag to retain INI file in memory after boot (1=retain, 0=don't (default))
p.needwin%flag to require execution under Win32 or Win9x (0=don't require Win32 (default); 1=require Win32; 2=require Win9x)
p.ourvar$environment variable to load
p.needvar%flag to require environment variable (0=don't require variable (default); 1=require variable)
p.usevardir%flag to use the environment variable as a path to the INI file (0=don't (default); 1=use variable as INI directory)
p.quiet%flag to suppress signon screen (1=suppress, 0=don't (default))
returns..containing..
notes
  • Leave p.dev% set to 1; BAPISDK changes this to 0 automatically at compiletime. If you are compiling manually, set this to 0 immediately before you compile. This is because the library increases its memory usage (eg. it can work with 4 times as much data, read twice as many module elements, etc) if it knows its running as compiled.
  • If you are compiling with something other than FirstBasic (for example, if you're using QuickBASIC 4.5 or PDS 7.1), you can set p.dev% to 0, but you will need to reduce the memory allocated to the various arrays. See this for more on controlling memory allocation.
  • If you are compiling with Powerbasic and you do your editing in Powerbasic, you can leave p.dev% set permanently to 0.
  • The library will reflect that it's running in low-memory interpreted mode by appending an "i" to the end of the version variable p.ver$
  • click here for more infomation about p.needini%, p.loadmod% and INI files
  • click here for more infomation about the runtime errors generated if a required execution condition is not met


function
boot 16function index
purpose pre-define environment, arrays, load DOS vars, INI file
featuressets up screen, tests critical variables, detects operating system, recovers DOS environment variables, sets TEMP directory, reads INI file
expects..to contain..
returns..containing..
p.abort%1=error during boot; 0=no error
a.msg$error message (empty if none)
p.temp$name of temporary directory for use by this program
p.os%operating system the program is executing under: 0=unknown; 1=DOS; 2=Win9x; 3=WinNT4; 4=Win2000; 5=WinME; 6=WinXP
getos.ver$operating system the program is executing under (descriptive string)
p.ourvarval$value of environment variable set by p.ourvar$
p.ourdir$directory set by p.ourvar$ (if p.usevardir% is true)
p.cmd$commandline used to call the program (if available)
inifilename$full name and path of INI file (if available)
numsettings%number of settings read from main section of INI file (if available)
cfg$(x,numsettings%)two-dimensional array containing settings from main section of INI file (if available)
notes
  • boot also sets a number of internal variables, and allocates memory to the various arrays (such as infile, lst, tmp, etc)
  • boot also runs the readuserini subroutine, loads the INI file, and runs the startup subroutine (in that order)
  • the values returned by the INI loader are the same to those output by getmodele
  • The commandline is only available if the program is compiled with PowerBasic (it uses PowerBasic's COMMAND$ variable). The commandline is not available to the BAPI and thus to your program if you've compiled with FirstBasic, or if you're running in interpreted mode in QBASIC.


function
anykey 3function index
purpose wait for user keypress, return keypress and keycode
features
expects..to contain..
s.msg$string to display in status bar
returns..containing..
keyb$key pressed by user
kb.code%keycode of key pressed by user
notesif the user presses an extended key (eg. Alt-B), the keycode returned will have 500 added to it


function
dialog 7function index
purpose show alert/question, get keypress/answer; log to file
featurescan also display buttons; can log messages to file; updates statusbar; returns action
expects..to contain..
d.title$the title of the dialog box
d.msg$the string to display in the dialog box
q.msgs%number of lines of multiline prompt to display
q.msg$()array containing multiline prompt lines
d.type%0=message box (OK only); 1=question (Yes/No); 2=message but don't wait for keypress
d.buttons%number of buttons to display (0=no buttons)
d.b.l$()array containing button labels
d.b.h%()array containing character position of label to highlight and capitalise
d.b.c$()array containing character to return if user presses this button
p.logging%0=don't log messages; 1=log messages to errorlog
p.errlog$name of logfile - by default, this is p.name$ plus ".log"
p.auto%automatic mode; 0=display messages; 1=don't display, only log to file
d.flag$this string is inserted at the beginning of the logfile entry. If empty, it is set to ! (exclamation mark)
returns..containing..
d.b.pressed$character value of pressed button (empty if not pressed)
action%0=NO; 1=YES (if dialog was a question)
notes
  • if d.title$ is empty, a default is supplied
  • if q.msgs% contains a value, d.msg$ is discarded, and the contents of q.msg$() are displayed instead
  • the contents of q.msg$() are not logged to disk, even if logging is enabled.


function
progress 4function index
purpose show progress bar onscreen
featurescan display a second progress bar underneath
expects..to contain..
p.done%percentile complete
p.update%0=draw whole box; 1=draw only progress bar (faster)
p.dualbar%0=single progress bar only; 1=draw dual progress bars
p.dualdone%percentile of second progress bar complete
returns..containing..
notesupdate will draw the box, but only if it has not already been drawn


function
drawbox 7function index
purpose draw a box onscreen, with optional title, note, status, content, buttons, getconfirmation, anykey and vanish
features 
expects..to contain..
b.strings%number of lines of text to display
b.str$()array containing lines of text to display
b.title$title of box
b.note$subtitle of box
s.msg$text to display in statusbar
b.h%height of box (in characters)
b.len%length of box (in characters)
b.xpos%x-position of upper-left-hand corner of box
b.ypos%y-position of upper-left-hand corner of box
d.buttons%number of buttons to display (0=no buttons)
d.b.l$()array containing button labels
d.b.h%()array containing character position of label to highlight and capitalise
d.b.c$()array containing character to return if user presses this button
b.wait%wait for keypress (0=don't wait (default), 1=wait)
b.confirm%get confirmation (0=don't (default), 1=wait for Y or N)
returns..containing..
d.b.pressed$character value of pressed button (empty if not pressed)
action%0=NO; 1=YES (if dialog was a question)
notes


function
nukebox 1function index
purpose erase current dialog box
features 
expects..to contain..
returns..containing..
notes 


function
showtitle 2function index
purpose display titlebar
featuresalso updates the statusbar, if s.msg$ is populated
expects..to contain..
s.msg$string to display in status bar
returns..containing..
notes
  • the titlebar is displayed on the top line of the screen


function
showmsg 9function index
purpose display or erase statusbar
features 
expects..to contain..
s.msg$string to display in status bar
s.nuke%toggle to erase statusbar instead (1=erase, 0=don't (default))
returns..containing..
notesthe statusbar is displayed on the bottom line of the screen


function
showline 2function index
purpose displays a formatted line onscreen
featurescan also erase the line
expects..to contain..
showl.l%the screenline upon which output is to appear (Y)
showl.t%the tabline upon which data is to appear (X)
showl.p$the prompt to display
showl.d$the data to display
showl.x%if set to 1, the line specified in showl.l% will be erased
returns..containing..
notesoutput is in the form prompt: [data]


function
showmenu 4function index
purpose display scrollable menu, wait for cursor-based selection, parse user response
featuresautosizing, returns selection, updates status
expects..to contain..
m.title$contents of title
m.items%number of list items on menu
m.maxlines%maximum number of list items to display
m.focus%default selected item
m.p$(n)prompt for item n
m.m$(n)status line for item n
m.d$(n)unique listitem ID
d.buttons%number of buttons to display (0=no buttons)
d.b.l$()array containing button labels
d.b.h%()array containing character position of label to highlight and capitalise
d.b.c$()array containing character to return if user presses this button
returns..containing..
m.abort%1 = user aborted; 0 = did not abort
m.focus%number of item selected
action$unique listitem ID of selected response from user
d.b.pressed$character value of pressed button (empty if not pressed)
notes If the number of list items exceeds m.maxlines%, or if the number of list items is too large to fit onscreen, a scrollbar will appear. Excess items can be accessed by scrolling down the menu with the cursor keys.


function
getform 9function index
purpose get multiple data items from user in dialog box
featurescursor key-based editing; insert/overtype; scrolling if input overlength; simulated cursor; titlebar; footerbar; blank field if first key is backspace; filters non-displayable characters; auto-positioning and sizing dialog box; displays buttons with hi-lit alt-keys
expects..to contain..
g.title$contents of title
g.note$contents of note
g.items%number of input items on form
g.focus%default selected item
g.automin%minimum size for any input field *
g.p$(n)prompt for item n
g.m$(n)status line for item n
g.d$(n)default setting for item n
g.c%(n)default cursor position for item n
g.l%(n)length of on-screen input field
g.lmax%(n)maximum length of input
g.hide%(n)enable/disable output masking (1=ON, 0=OFF)
g.buttons%number of buttons on form
g.b.l$(n)label for button n
g.b.c$(n)character returned by button n
g.b.h%(n)character position in label to highlight
returns..containing..
g.abort%1 = user aborted; 0 = did not abort
g.i$(n)response from user for item n
g.b.pressed$character of pressed button (empty if none pressed)
g.c.y%form item selected when form exited
notes
  • set g.c%(n) to -1 to have the cursor default to the end of the input field
  • set g.lmax%(n) to -1 to use max input size for field (250 characters)
  • set g.l%(n) to -1 to autosize the length of the input field
  • to set the above three automatically, plus the hide setting to OFF, for each menu item, place a value in g.automin%. * If g.automin% contains a value, the settings in g.c%(n), g.l%(n), g.lmax%(n) and g.hide%(n) will be ignored
  • hiding is not possible when autosizing
  • cursor position 0 is the first character of the input field
  • improve: true/false fieldtype
  • uses: getaltkey (and others..)


function
readdir 14function index
purpose obtain directory contents from FAT-16, FAT-32, NTFS or UNC volumes
featuresif not supplied, will resolve default drive and/or directory
expects..to contain..
readd.name$name of directory to read
readd.usefile$name of file containing directory listing
returns..containing..
readd.name$fully qualified path to target directory
readd.files%number of files and directories in target directory
dirinfo$(n)CSV-formatted array of file information
readd.err$error message, if any - blank if no error
notes
  • format of dirinfo array is drive:,\full\path\name,filename,extension,date,time,size
  • does not support long file names
  • Note: This routine is no longer included in the BAPI; it can be found in the file EXTRA.BAS instead.


function
ifexist 6function index
purpose detect the presence of a file on disk
featureswill use default drive and/or directory if not supplied
expects..to contain..
ifexist.name$* filename to test for
returns..containing..
ifexist.found%1=file exists; 0 if not exists
ifexist.name$fully-qualified path to file
ifexist.err$error message (if any)
notes 


function
readfile 7function index
purpose read a range of lines from a text file into infile array
featureswill set buffer overflow flag if file is too big for available memory
expects..to contain..
readf.name$* name of file to read
readf.start%* line in file to start reading from (0 for start)
readf.length%* number of lines to read (0 for all)
returns..containing..
infile$()array containing lines read
infilelines%number of lines read
readf.bytes!number of bytes read
readf.size!total size of file
readf.err$error message (blank if none)
readf.bufstat%0=complete file in RAM; 1=partial file in RAM
readf.bufline%line at which RAM filled (0 if complete file in RAM)
notes 


function
writefile 14function index
purpose write a range of lines to a text file from infile array, with optional backup and append
featureswill create file if it does not exist; can append or overwrite, can make .BAKfiles
expects..to contain..
writf.name$* name of file to write
writf.start%* line in infile array to start writing from (0 for start)
writf.stop%* line in infile array to stop writing at
infile$()array containing strings to write
lst$()array containing strings to insert as a header into target
header.length%* number of rows of strings to export from list array as header (0 to disable)
writf.append%1=append to target file; 0=overwrite (default)
writf.backup%1=create .BAKfile before write; 0=don't (default)
writf.cycle%1=silently affirm .BAKfile overwrite prompt; 0=prompt before overwriting .BAKfile (default)
returns..containing..
writf.err$error message (blank if none)
notes 


function
appendfile 4function index
purpose append a line to a text file
featuresif the file does not exist, it will be created
expects..to contain..
appef.name$* name of file to append to
appef.s$string to append
returns..containing..
appef.err$error message (if appropriate)
notes 


function
writebuf 4function index
purpose buffered write to text file
features 
expects..to contain..
writf.name$* name of file to write to
writb.s$string to write (empty will write a blank line)
writb.init%0=don't initialise (default); 1=initialise buffer and write first portion of file to disk
writb.flush%0=don't flush (default); 1=flush buffer to disk
writb.head%number of lines already in infile array to write as start of file (0 to disable)
infile$()array of strings to write as start of file
lst$()array containing strings to insert as a header into target
header.length%* number of rows of strings to export from list array as header (0 to disable)
writf.append%1=append to target file; 0=overwrite (default)
writf.backup%1=create .BAKfile before write; 0=don't (default)
returns..containing..
writf.err$error message (blank if none)
notes
  • the file will not be closed until the buffer is flushed
  • a header in the lst$() array will precede a header in the infile$() array when written to file
  • Note: This routine is no longer included in the BAPI; it can be found in the file EXTRA.BAS instead.
  • How to use:
    1. initialise the buffer:
         writf_name$ = outputfilename$
         writf_append% = t%
         writf_backup% = f%
         writb_init% = t%
         writb_head% = 0
         header_length% = 0
         Call writebuf
      
    2. write to the buffer (repeat this until the file is ready to be closed):
         writb_s$ = outputstring$
         Call writebuf
      
    3. flush the buffer
         writb_flush% = t%
         Call writebuf
      


function
copyblock 4function index
purpose copy a range of lines from one file to another file
features 
expects..to contain..
copyb.source$* file to copy from
copyb.target$* file to copy to
copyb.start%row upon which to start copying (0 for start of file)
copyb.stop%row upon which to stop copying (0 for 'until end')
copyb.noflush%0=flush enabled; 1=don't flush when end-of-block or end-of-file is reached
lst$()array containing strings to insert as a header into target
header.length%* number of rows of strings to export from list array as header (0 to disable)
writf.append%1=append to target file; 0=overwrite (default)
writf.backup%1=create .BAKfile before write; 0=don't (default)
returns..containing..
copyb.bytes!number of bytes copied
writf.err$error message (blank if none)
notes
  • this routine will overwrite the contents of the infile array
  • if the target does not exist, it will be created
  • if the target already exists, it will be overwritten, unless writf.append% is true
  • Note: This routine is no longer included in the BAPI; it can be found in the file EXTRA.BAS instead.


function
copymove 3function index
purpose copy or move a file from one location to another
featurescan copy or move across UNC paths, drives and directories
expects..to contain..
mf.from$name of file to move
mf.to$name of file to move to
mf.movefile%0=copy file (default); 1=move file
mf.checkfirst%0=overwrite if target exists; 1=make backup, prompt to overwrite or abort if target exists
mf.cycle%1=silently affirm .BAKfile overwrite prompt; 0=prompt before overwriting .BAKfile (default)
returns..containing..
movef.err$error message (blank if none)
notes


function
findss 5function index
purpose find the next or previous occurence of a substring in a string
features 
expects..to contain..
lookin$* string to search
lookfor$* string to search for
foundat%* starting position (0 for beginning or end)
f.last%0 = search from beginning of string; 1 = search from end of string
returns..containing..
foundat%starting character position of found substring in string (0 if not found)
notes 


function
findssarray 3function index
purpose find the next occurence of a substring in infile array
features 
expects..to contain..
infile$()* array of strings
infilelines%* length of array of strings
lookfor$* string to search for
findsa.y%* row in array to start searching on (0 for start)
returns..containing..
findsa.y%row in array containing string (0 if not found)
findsa.x%starting character position of string in row containg string (0 if not found)
notes
  • ensure that infilelines does not become corrupted between when the file is loaded, and when findssarray is called (particularly by an intervening call to scanfile or getmodele!).
  • improve: search backwards?


function
scanfile 5function index
purpose find the next or previous occurence of a string in a text file
features 
expects..to contain..
scanf.name$* name of file to scan
lookfor$* string to search for
scanf.start%* line upon which to start searching (0 for first line)
scanf.stop%* line upon which to stop searching (0 to search whole file)
scanf.last%0=find next occurence of string; 1=find previous occurence of string
returns..containing..
foundin$line containing found string (blank if not found)
foundline%line number of found string (0 if not found)
scanf.err$error message (blank if no error)
scanf.size!total size of scanned file (o if not found)
scanf.bytes!bytes scanned before string was found (0 if not found)
notes
  • this routine corrupts infilelines%. If you wish to preserve this number, do so before calling scanfile.


function
split 4function index
purpose chop a string in two at first, next, previous or last delimiter, or character position
features 
expects..to contain..
split.s$* string to split
split.d$delimiter to find (may be more than one character)
split.last%0 = search from start; 1 = search from end
split.start%character position to start search at
split.c%character position to split at
returns..containing..
split.l$left half of split string
split.r$right half of split string
notes
  • if both a delimiter and a character position are provided, the string will be split at the character position, and will not be searched for the delimiter.
  • this routine does NOT return the delimiting characters themselves, or the character at the character position, in EITHER split.l$ or split.r$ - it is discarded.


function
strip 2function index
purpose strip leading/trailing spaces from a string, and sub-ASCII 32 characters
features 
expects..to contain..
strip.s$* string to strip
returns..containing..
strip.s$stripped string
notes - improve: strip any character


function
sift 1function index
purpose convert all instances of a given string in a given string to another given string
features 
expects..to contain..
sift.s$* string to sift
sift.c$* string to convert from
sift.to$string to convert sifted characters to
returns..containing..
sift.s$sifted string
notes
  • if sift.to$ is left blank, then sift will delete the nominated string (sift.c$) from the string, replacing it with nothing. This will change the length of the string.
  • sift can sift either single or multiple characters.
  • if sift.to$ is a different length to sift.c$, then the returned string will be of a different length.
  • Note: This routine is no longer included in the BAPI; it can be found in the file EXTRA.BAS instead.


function
csv2dim 2function index
purpose converts a comma-separated-string to an array
features 
expects..to contain..
c2d.s$* string to convert
c2d.d$delimiter string - if not set, defaults to , (a comma)
c2d.undelimit%if set to 1, will strip the first and last character from each data item (useful for stripping 'quotes' around dataitems)
c2d.quoted%if set to 1, enables "string quoted" dataitems (embedded commas permitted)
c2d.quotemark$quote delimiting character - if not set, defaults to " (a double-quote, ASCII 34)
returns..containing..
c2d.found%number of detected items
tmp$()array containing each detected item as a separate row
notes
  • as the delimiting character can be set, this routine is not confined to processing CSV data. The routine could be used to break a sentence into an array, by changing the delimiting character to a space (ASCII 32).


function
findfiles 3function index
purpose gets *.filespec in a directory and adds to list array, or to a file containing a list of filenames
features 
expects..to contain..
dirinfo$()* directory to scan
findf.type$* type of file to find (eg. HTM, LOG)
findf.mem%store to memory or to file (0=to file (default); 1=memory)
findf.names%store fully qualified paths or filenames only (0=full names (default); 1=filenames only)
returns..containing..
findf.found%number of files found
lst$()array containing list of files found
findf.list$name of file containing list of files matching filespec
findf.err$error message (if any - blank if no error)
notes
  • expects the directory to already be loaded into memory using readdir
  • does not support Long File Names
  • the lst$() array is not populated unless findf.mem% is set TRUE.
  • if findf.mem% is set TRUE, the contents of the lst$() array may be overwritten when findfiles executes.
  • Note: This routine is no longer included in the BAPI; it can be found in the file EXTRA.BAS instead.


function
alphasort 3function index
purpose sort the list array in ascending alphabetical order
features 
expects..to contain..
lst$()array containing strings to alphabetacise
sort.start%line upon which to start sorting
sort.stop%line upon which to stop sorting
returns..containing..
lst$()array containing alphabetacised strings
notes
  • improve: sort in descending order?
  • Note: This routine is no longer included in the BAPI; it can be found in the file EXTRA.BAS instead.


function
testint 1function index
purpose tests that a number stored as a string is less than 32767
features 
expects..to contain..
testint.s$* string containing number to query
returns..containing..
testint.ok%0=number greater than 32767; 1=number less than or equal to 32767
notes
  • Note: This routine is no longer included in the BAPI; it can be found in the file EXTRA.BAS instead.


function
hex2dec 1function index
purpose converts a hexadecimal number to decimal
features 
expects..to contain..
h2d.in$* string containing number to convert
returns..containing..
h2d.out!the input number, expressed in decimal
notes
  • Note: This routine is no longer included in the BAPI; it can be found in the file EXTRA.BAS instead.


function
trunc 1function index
purpose returns a number with x decimal points
features 
expects..to contain..
trunc.num!* number to truncate
trunc.points%* number to decimal points to truncate to
returns..containing..
trunc.s$the number truncated to the required number of decimal places
notes
  • Note: This routine is no longer included in the BAPI; it can be found in the file EXTRA.BAS instead.


function
freespace 3function index
purpose gets free disk space on specified drive letter
featuresalso returns volume label, and total size of volume
expects..to contain..
driveletter$* single alphabetic character containing letter of drive to query
returns..containing..
frees.total$total size of volume (in bytes)
frees.vol$volume label
frees.size$total bytes free on volume
frees.err$error message (blank if none)
frees.sizeinmegs%set to true if returned values are megabytes, not bytes
notes
  • FAT-16/FAT-32/NTFS compatible
  • network drive compatible
  • Note: This routine is no longer included in the BAPI; it can be found in the file EXTRA.BAS instead.


function
findsetting 3function index
purpose returns value of a specified setting in INI file
features 
expects..to contain..
lookin$* name of setting to query
returns..containing..
cfgval$value of setting (blank if not found)
notesused by: this routine is usually used in readuserINI; it's not used anywhere else in the BAPI.


function
changeini 3function index
purpose changes the value of an INI setting in the INI file
featureswill create an INI file if it does not exist; will add the setting if it does not exist**
expects..to contain..
ini.target$* name of setting to change
ini.module$name of module containing this setting (blank to disable module support)
ini.change$* value of new setting
returns..containing..
cini.err$error message (blank if none)
notes
  • preserves comments no the INI line, if there are any
  • ** auto-creation of settings is not supported if the setting is in a module. You must create a blank setting= line.
  • the name of the INI file is determined by the BAPI.
  • Note: This routine is no longer included in the BAPI; it can be found in the file EXTRA.BAS instead.


function
ifinmod 2function index
purpose to see whether a given string is contained within a given module
featuresThis search can look for partial match, or an exact match, for the first x number of characters in the string. This search only looks for the first match at present.
expects..to contain..
ifinm.num%* module number to search
ifinm.s$* string to search for
ifinm.left%1=enable exact match from left; 0=match anywhere in string (default)
returns..containing..
ifinm.found%set TRUE if the string was found within the elements of the module
ifinm.foundin$the string the match was found in
ifinm.foundat%the row the match was found in
notes
  • Note: This routine is no longer included in the BAPI; it can be found in the file EXTRA.BAS instead.


function
getmodele 4function index
purpose retrieve all elements from a module in memory or in INI file
features 
expects..to contain..
getmod.file$* the name of the file containing the module to load
getmod.name$* the name of the module to load - this will be one of the modules listed in readim.s$
getmod.num%* the column number of the module array to fill with module data
p.noinistrip%default is 0 (false/off); if set to 1 (true/on), the INI loader will not strip comments or whitespace from the elements as they are loaded
returns..containing..
inimod$()two-dimensional array containing loaded values
inimodnum%number of modules loaded
inielenum%number of structured elements loaded
orphanele%number of orphaned elements loaded
inioverflow%set TRUE if number of loaded elements exceeded available module memory space (in this case, the as many elements as possible are loaded).
getmod.err$error message if unable to open INI file
notes
  • the INI loader will overwrite any existing data in the column with new data from the module being loaded
  • for more information on inimod$(), modules and INI files, see general INI pointers
  • the side-effect of using p.noinistrip% is that any comments in the INI file will be interpreted. For this reason, avoid using comments in INI files used by programs with p.noinistrip% enabled. The only exception is at the end of [modulenames], where ; comments are still permitted (and ignored) if p.noinistrip% is enabled.
  • getmodele calls scanfile and thus corrupts infilelines%. If you wish to preserve it, ensure to store this number elsewhere prior to calling getmodele.


function
getmodset 1function index
purpose retrieve an element from a module in memory
features 
expects..to contain..
getms.x%* module to query
getms.y%* element to query
returns..containing..
getms.val$value of element
notes
  • Note: This routine is no longer included in the BAPI; it can be found in the file EXTRA.BAS instead.


function
getINIextras 1function index
purpose load a set of extra elements from the main section of the INI file
features 
expects..to contain..
getini.s$* setting to load
returns..containing..
tmp$()temp array filled with settings loaded from INI file
loadmod.n%the number of extra settings loaded
getini.err$error message (if any)
inioverflow%set TRUE if number of loaded elements exceeded available module memory space (in this case, the as many elements as possible are loaded).
notes
  • settings must all start with the same string
  • Note: This routine is no longer included in the BAPI; it can be found in the file EXTRA.BAS instead.


function
getosver 2function index
purpose returns operating system and version
features 
expects..to contain..
returns..containing..
p.os%OS detected: 0=unknown; 1=DOS; 2=Win9x; 3=WinNT; 4=Win2000; 5=WinME; 6=WinXP
getos.ver$version string returned by operating system
notes


function
getdosvar 1function index
purpose returns value of specified DOS environment variable
features 
expects..to contain..
getvar.name$* name of variable to query
returns..containing..
getvar.val$value of variable (blank if not found)
notes 


function
runfromdir 1function index
purpose runs specified command from within a specified directory
features
expects..to contain..
runf.dir$* directory to execute command within
runf.cmd$* command to execute
returns..containing..
runf.err$errormessage (if any)
notes
  • The command is started minimised and execution waits until the command exits before it resumes.
  • Calls to runfromdir under NT4 with will fail unless START.EXE is in the path.
  • Note: This routine is no longer included in the BAPI; it can be found in the file EXTRA.BAS instead.


function
runlongcmd 4function index
purpose runs specified commandline
featurescommandline can be longer than the QBASIC limit of 127 characters - up to 255 characters; also returns errorlevel
expects..to contain..
longcmd$* commandline to execute
rl.quiet%attempt to suppress all output (0=no (default), 1=yes)
rl.newwin%run this command in a new window (0=no (default), 1=yes)
rl.nowait%don't wait for this command to finish before continuing (0=wait (default), 1=don't wait)
returns..containing..
longcmd.err%errorlevel returned from command
notes
  • Starting processes in their own window usually provides more memory to the child process.
  • rl.newwin% is not available under Windows NT4, as that operating system has no START.EXE command by default. It is possible to pass a START command directly to runlongcmd by placing it in the command string longcmd$. However if the system executing the program is Windows NT, the function will fail (for the same reason - START.EXE not included). The same is also true if using START with a SHELL command under NT4 - it won't work. Either place START.EXE on the system (and in the path) or don't use it, if under NT4. The BAPI provides the operating system type in the variable p.os%.
  • Calls to runlongcmd under NT4 with rl.newwin% enabled will be executed, but in the same window as the calling process.


function
auth 3function index
purpose gets a password from the user
featurescan ask for re-entry to confirm
expects..to contain..
auth.verify%if set to 1, the messagebox presented will say "verification" instead. However no verification is performed. It is up to the calling application to call auth twice, the second time with auth.verify set, and compare the passwords itself.
returns..containing..
auth.pw$the password entered
notesAll text entered in the auth field is starred out.


function
getregkey 3function index
purpose returns the contents of a specified Windows registry key
features 
expects..to contain..
getrk.key$* name of registry key to query
getrk.decode%flag to decode directory key (0=no (default), 1=yes)
getrk.read%flag to read key (0=no (default), 1=yes)
returns..containing..
getrk.file$name of file containing key (blank if decoding is enabled)
getrk.val$data associated with value within key (blank if key not found, or if reading or decoding are disabled)
getrk.err$error message (blank if no error)
notes
  • If getrk.decode% is specified, the directoryname is decoded and the keyfile deleted.
  • If getrk.read% is specified, the key is read and the keyfile deleted.
  • getrk.decode% and getrk.read% cannot be used together. getrk.decode% will execute if both settings are enabled.
  • If neither getrk.decode% or getrk.read% are specified, the key is not analysed, the keyfile is not deleted by getregkey, and the name of the keyfile is returned in getrk.file$. This file must be deleted later by your program - it is not deleted automatically, and will cause a "tempfiles left in tempdir" error on exit if not deleted.
  • Note: This routine is no longer included in the BAPI; it can be found in the file EXTRA.BAS instead.


function
getaltkey 2function index
purpose returns ASCII character from alt-keycode
features 
expects..to contain..
alt.code%* raw extended keycode
returns..containing..
alt.key$letter of keypress
notesused by: drawbox, getform


function
getrand 5function index
purpose generate a unique 8-digit string
featurescan return string as a tempfilename
expects..to contain..
gr.seq%if set to 1, getrand will allocate filenames in a sequential, rather than pseudo-random manner.
returns..containing..
uniquename$string of 8 random numbers
uniquetemp$fully qualified drive:\path\filename.tmp (uses p.temp$ variable allocated by boot)
notes Use gr.seq% if there's a chance more than one unique string will be generated per second. The pseudo-random algorithm (which is based on the time of day) is in fact designed for this, but prolonged testing on fast computers has revealed it's simply not random enough. gr.seq% answers this problem by forcing the unique strings to increment in sequence. It is thus suitable for generating a large number of unique strings very quickly.


function
getdate 5function index
purpose returns date in "Month Daynumber, 4DigitYear" format
features also returns the number of days since January 1, 1900
expects..to contain..
getdate.this$date to process (in mm-dd-yyyy format)
returns..containing..
epochdaynumber of days since January 1, 1900
ourdate$date in "Month Daynumber, 4DigitYear" format, eg., September 21, 2000
notes
  • Leave getdate.this$ blank to process the current date
  • Y2K compliant
  • epochday does not return correct values for dates prior to 1582 AD


function
getdaynum 1function index
purpose returns the number of days since year start, or since a pre-specified date
features
expects..to contain..
getday.date$date to process (in mm-dd-yyyy format)
returns..containing..
getday.num%number of days since the date specified
notes
  • Leave getday.date$ blank to return the number of days since the beginning of the current year
  • Y2K compliant
  • does not return correct values for dates prior to 1582 AD
  • Note: This routine is no longer included in the BAPI; it can be found in the file EXTRA.BAS instead.


function
errx 3function index
purpose generic runtime error handler
features
expects..to contain..
returns..containing..
errx.msg$error message
notes errx is called using the following syntax:


errx.msg$ = "" 
ON ERROR GOTO ERRX
 REM  insert statements containing potential runtime
 REM  errors such as KILL, SHELL, OPEN etc in here
ON ERROR GOTO 0
IF errx.msg$ > "" THEN
 PRINT "A runtime error was detected:"
 PRINT errx.msg$
 PRINT "operation aborted"
ELSE 
 [continue execution]
ENDIF
br>Note: errx.msg$ should be reset immediately prior to the ON ERROR GOTO ERRX statement. This is to ensure your routine does not detect a message from a previous error.

Note: errx version 2 and below report the error message in d.msg$. This however corrupted d.msg$ whenever other routines tested whether errx returned an error - they first needed to set d.msg$ to nothing (as in the above example), which meant, if it already contained a value, this was lost whenever the other routine executed. Unfortunately, the fix for this meant that any routines looking for error messages from errx version 3 and above will not find any, unless they are changed to look for the error message in errx.msg$ instead.


function
abort 7function index
purpose graceful abandoning of ship.
featureswill display errormessage if there is one; wait for keypress
expects..to contain..
a.err%error code
a.msg$error message
returns..containing..
notes A table of runtime errors is here.

INI how-to: general INI pointers

  • the INI file must be a text file (editable with Notepad)
  • the INI file must have the same filename as your program file (eg. yourprog.ini)
  • the INI file must have a .INI extension
  • the INI file must be in the current directory at runtime, unless on startup, p.ourvar$ contains an alternative location, and p.usevardir% is set; ... OR... the INI file can be pointed to by using as the FIRST parameter on the commandline /INI:, like this:
    C:\>programname /INI:full_name_and_path\to_ini_file.ext
    

    If the INI file is set via the commandline, the following notes apply:

    1. long filenames, or filenames with embedded spaces, may not be used
    2. the INI file can have any name and extension, and can be in any directory
    3. this parameter is removed from the commandline once it is processed by the boot routine, and is thus invisible to BAPI-based programs. The name of the INI file is returned as the variable inifilename$.

  • any text after a semicolon (";") is considered a comment and is NOT loaded - except if p.noinistrip$ is set to true
  • settings take the general format

    setting=value

  • don't use = or ; in settingnames
  • settingnames are not case-sensitive
  • the INI loader strips the variablenames from the left side of the equals sign; an INI entry elementname1=value1 is loaded into the array as simply value1
  • the INI file has two parts - a main section and a module section (which can contain up to 100 modules)
  • the main section is read automatically each time your program starts; the modules can also, as an option, be loaded automatically at startup
  • data from the main section is stored in cfg$(numsettings%) where numsettings% is the number of settings loaded from the main section
  • modules can be used to contain sets of extra settings
  • modulenames are delimited by [square brackets]
  • modules end on the first blank line after the [modulename]
  • loaded module elements are placed into a two-dimensional array called inimod$() - each column of the array contains the contents of each module loaded
  • each module can be referred to by its column number in the array; module 1 is the module contained in the first column. Thus, inimod$(1,3) contains element 3 of module 1; inimod$(3,5) contains element 3 of module 3
  • the first row of each column of inimod$() contains the modulename; the second row contains the total number of loaded elements (including both structured and freeform elements). The other rows contain the elements themselves. Eg.,
    [modulename]
    6
    value1
    value2
    value3
    
  • to require the INI file, set p.needini%=1 in the program header

INI how-to: loading modules automatically at startup

The simplest way to load data from modules is to have the INI loader load them automatically at startup. If you don't do this, you'll need to load the module manually during execution, which can have advantages - see below.

Note: modules loaded at boot are done so AFTER readuserini has executed - don't try and use a module element in readuserini, as it will be empty :)

  1. write the INI file

    1. choose a modulename, start a new line of INI file, at the first character position, type the modulename enclosed in square backets, eg:
      [modulename]
      

    2. define elements for the module - these can be either freeformat or structured - a combination is acceptable. Freeformat is simply a list of lines of text. Structured are variables defined as such:
      elementname1=value1
      elementname2=value2
      

  2. add the code to the program

    1. set p.loadmod%=1 in the program header - this tells the INI loader to load all modules in the INI file at boot

    2. define the modulenames to load in readuserini. set the variable readim.s$ to contain a list of modulenames to load. this is a comma-delimited string, eg.,
      [modulename1],[modulename2],[modulename3]
      

    3. define the elementnames in readuserini. set the variable readim.e$ to contain a list of elementnames to load. this is also a comma-delimited string, eg.,
      elementname1,elementname2,elementname3
      

      This step is optional. If readim.e$ is left blank, all elements will be loaded. If readim.e$ is completed, elements named will be loaded into the array *first*, in the order they are listed in readim.e$. This permits a module of the format

      [modulename]
      elementname1=value1
      elementname2=value2
      elementname3=value3
      extrasetting=rock
      extrasetting=scissors
      extrasetting=paper
      

The module(s) will then be loaded when the program starts up.

INI how-to: loading modules manually during execution

Loading modules automatically at startup is simpler, however loading modules manually during execution can conserve memory, and/or boost performance, allow you to repeatedly load different sets of options, and allow you to read settings from another INI file.

  1. complete steps 1.1 and 1.2 as above

  2. add the code to the program

    1. tell the INI loader NOT to load all modules in the INI file at boot - set p.loadmod%=0 in the program header

    2. define each modulename you intend to use in readuserini - set the variable readim.s$ to contain a list of modulenames to load. this is a comma-delimited string, eg.,
      [modulename1],[modulename2],[modulename3]
      
    3. define the elementnames to load in readuserini. set the variable readim.e$ to contain a list of elementnames to load. this is a comma-delimited string, eg.,
      elementname1,elementname2,elementname3
      

      This step is optional. If readim.e$ is left blank, all elements will be loaded. If readim.e$ is completed, elements named will be loaded into the array *first*, in the order they are listed in readim.e$.

    4. At the appropriate point in your code, call the INI loader. Do this as such:
      GOSUB getmodele
      
      See getmodele for more information.

INI how-to: including a modulelist and moduledata in one INI file

The modules must be loaded at execution, not at boot. Ensure that p.loadmod%=0.

Attempting to load them at boot will not work. This is because the INI loader tries to load the module elements from the line immediately following the modulelist. For example, a modulelist might look as such:

modulelist=[lsi],[mirror],[spacenet],[pickle-2],[test]
with each module being defined lower down in the INI file. The INI loader searches for the name of the module, and finds the modulelist line before it finds the module itself. It then attempts to load the module as usual, however because in reality it has only found the list of modules, not the module itself, the module is not loaded.

INI how-to: processing a list of freeform elements

Freeform elements are those found in a module but not defined in readim.e$

Working with these elements is complicated because the first two rows of the module's column in the inimod array are not elements, but the name and number of elements. So, to read any freeform elements from the inimod array, one must remember to allow for these...

  • freeform elements start at inielenum% + 3
  • freeform elements end at VAL(inimod$(modulenumber%,2)) + 4

BAPISDK usage notes

  • BAPISDK 11 has some speedups to make compilation faster. As well as a slightly reworked interface, there's a new 'quick compile' option which compiles the current target and exits, without prompting for any additional options, and without waiting for confirmation before exit. It will pause on an error, however. There's also a new commandline option, /qq - use this to call quick compile from the commandline. So you don't need to interact with BAPISDK at all!
  • Once BAPISDK calls Firstbasic or PowerBasic, don't edit the code while you're there, just compile and exit - it is a temporary copy that will be deleted when you exit!
  • BAPISDK requires that you leave three lines untouched, inside each program you write for use with it. These lines are used to define where the BAPI starts and stops, and where your code starts and stops. If you remove these lines, BAPISDK won't be able to process your program. The lines it requires left intact are:
    ' *** begin BAPI-
    ' * end BAPI *
    ' * begin program-specific subroutines
    
    The above three lines are vital - edit them at your peril! :)
  • If using PowerBasic to compile, you may as well disable precompilation, since PowerBasic can handle programs larger than those handled by QBASIC. To disable precompilation, simply leave the precompile: option blank (it's on BAPISDK's 'compile program' menu). To enable it, enter yes in this field. Note: this option is only available with BAPISDK 9 or higher. Aside from the commandline used to start PowerBasic (set in BAPISDK's 'compile program' menu under compiler:), and the aforementioned precompilation tweak, no other changes are required to use BAPISDK with PowerBasic.
  • the SFX generator requires MAKESFX, however MAKESFX is no longer available. Therefore, do not use this function. Instead, use the SFX generator that is now built into WinRAR.

BAPI 15 and below upgrade notes

Note: references to BAPI 16 and BAPISDK 7 apply also to BAPI 16.1 and BAPISDK 8, and above.

BAPISDK 7 new features:

  • will offer to recompile after a successful BAPI upgrade
  • can publish the compiled program to a production directory
  • support for EXE compressors such as UPX
  • the ability to create self-extracting archives
  • increased sourcecode compression during precompilation

    BAPISDK 7/BAPI 16 include some fine new features, however they unfortunately require you to make a small change to your sourcecode before BAPISDK will correctly upgrade programs written with BAPI 15 or below (although it will compile them OK).

    The change is simply moving the line that starts with "' *** begin BAPI" to a line immediately below the lines which define the name and version of the program (the variables p.name$, p.ver$, p.copy$ etc).

    These lines should all be at the top of the sourcecode file. However in BAPI 15 and below, the "'*** begin BAPI" line was above the p.name$ variables. It simply needs to be moved to below them.

    This change is to enable BAPISDK to migrate your program name, version, copyright info etc during a BAPI upgrade. BAPISDK 7 replaces everything below the "'*** begin BAPI" line.

    If you do not make this change prior to upgrading your BAPI with BAPISDK 7, you will lose your program info (eg. p.name$, p.ver$ etc) during the upgrade.

    Here's an example of the required change being made. The first code segment is BEFORE manual editing. Note the begin line comes before all other lines. As of BAPI 16/BAPISDK 7, this is incorrect.

    ' *** begin BAPI-15
    ' * mainline - set program variables, call boot, browse
    p.name$ = "weblog"
    p.ver$ = "1.0"
    p.copy$ = "(c) 1999-2000 Stuart Udall"
    p.dev% = 1
    p.needini% = 1
    p.loadmod% = 1
    GOSUB boot
    WHILE p.abort% = f%
    ...
    
    Here is the same code, manually edited to be BAPISDK 7 readable:
    p.name$ = "weblog"
    p.ver$ = "1.0"
    p.copy$ = "(c) 1999-2000 Stuart Udall"
    p.dev% = 1
    p.needini% = 1
    p.loadmod% = 1
    ' *** begin BAPI-15
    ' * mainline - set program variables, call boot, browse
    GOSUB boot
    WHILE p.abort% = f%
    ...
    
    The sourcecode is then ready for automatic upgrade to BAPI 16 by BAPISDK 7.

    notes on EXTRA.BAS

    • EXTRA.BAS, bundled with the BAPI, contains routines that for reasons primarily related to size, are no longer stored in the BAPI.
    • These routines work as documented, however you'll need to paste them into the program-specific area of your code in order to use them.

    structure of a BAPI-enabled program

    The structure of a BAPI-enabled program, working down the source listing, is as follows:

    sectionlocationpurpose
    1 - program infofirst 6-10 linesdefines the name, version, copyright, execution mode and INI settings for the program
    2 - BAPIeverything from 'begin BAPI' to 'end BAPI'contains the BAPI itself
    3 - readuseriniimmediately after the BAPIinitialises any program-specific variables (eg. variables for your own code to use)
    4 - startupimmediately after readuseriniruns any program-specific startup tasks *
    5 - ifaceimmediately after startupcreates desktop, and contains your "mainline" (the core loops of your program)
    6 - (user-defined)to end of fileany program-specific subroutines you wish to include

    * Note: BAPI initialisation is not complete until AFTER readuserini has completed. This means it's not possible to provide popup error messages (among other things) to flag errors encountered in readuserini. To handle startup errors, use the startup routine, or simply display dialogs in the iface (the area of the sourcecode containing your major loops and branches). BAPI initialisation is complete by the time startup is executed.

    reserved file handles

    These filehandles are used internally by the BAPI, and should not be used by your programs.

    If you attempt to use one of these reserved filehandles, you may hang your program, lose data, or experience other abnormal program behavior.

    handleused by
    255readdir
    254scanfile
    253readfile
    252writefile
    251appendfile
    250writebuf
    249copyblock
    248getmodele

    runtime error codes

    These messages may appear during program execution, and result in immediate termination of the program.

    They are produced by the abort routine.

    codemeaning
    1TEMP environment variable not set

    add the line SET TEMP=directoryname to your AUTOEXEC.BAT and reboot - where directoryname is a directory to be used for tempfiles.

    2temporary directory [directoryname] inaccessible

    The directory specified by the TEMP environment variable cannot be accessed - does it exist, and are permissions granted?

    3function [functionname] missing parameter [parametername]

    A call was made to a BAPI function without supplying a required parameter.

    4unable to read DOS environment variables

    This usually relates to a memory or disk error - try rebooting.

    5INI file not found

    The program's INI file is required, and is missing from the current directory.

    6this program requires Win32

    The program must be executed under a 32-bit Windows environment (eg. Windows 95, 98, 98SE, ME, NT4, 2000 or XP).

    7tempfiles left in [directoryname]

    The program has failed to clean up its tempfiles.

    8this program requires Win9x

    The program must be executed under a Windows 9x environment (eg. Windows 95, 98 or 98SE).

    9unable to write to logfile [logfilename]

    Is the path to the logfile valid? Are permissions granted?

    10environment variable not set [variablename]

    Is the variable set in AUTOEXEC, or in the shell environment?

    11unique temp counter overflow

    Your program attempted to create more than 9999999 tempfiles.

    BAPI internal variables

    p.size%the maximum length of the infile, directory, list, and tmp arrays. This value is multiplied by 4 if p.dev% is set to 0.
    p.cfgsize%the maximum number of settings permitted in the main section of the INI file.
    p.cfgsize%this setting is also multiplied by 2 to determine the maximum number of DOS environment variables to be cached.
    d.max%the maximum height of a dialog box. On a 25-line screen, it's 21 lines.
    inimodmax%the maximum number of modules permitted in the INI file. This value is multiplied by 4 if p.dev% is set to 0.
    inielemax%the maximum number of elements permitted in each module of the INI file. This value is multiplied by 4 if p.dev% is set to 0.
    p.errlog$the name of the logfile (if any).
    p.bufsize%the size in bytes of the readfile/writefile buffer.

    reducing the BAPI memory footprint

    QuickBASIC 4.5 and PDS 7.1 don't provide as much memory as FirstBasic does. Consequently, you must reduce the amount of memory BAPI uses if you wish to use it with QuickBASIC or PDS.

    BAPI memory allocation is controlled by a small number of internal variables. Although making changes to any of these can reduce memory consumption, the three primary memory-hogs are p.size%, inimodmax%, and inielemax%.

    There are two ways to control these variables, both of which are accomplished by editing the boot subroutine near the top of the BAPI. The first is to change the hardcoded numeric assignments made to either variable. For example, change p.size%=2000 to p.size%=1000 or p.size%=1500.

    The second way is to change the multipliers used to increase the size of these variables when the BAPI runs in compiled mode. To change the multipliers, find the code in the boot routine that resizes the variables in question:

      p.size% = p.size% * 4: inimodmax% = inimodmax% * 4: inielemax% = inielemax% * 4
    
    ...and change one, two, or all three numeric values. The bigger the number, the more memory is used. These values are multiplied with the variables to determine the final size of the arrays the BAPI uses internally in compiled mode. Example change:
      p.size% = p.size% * 2: inimodmax% = inimodmax% * 2: inielemax% = inielemax% * 2
    

    Although p.size% may at first be the obvious target, inimodmax% and inielemax% are actually multiplied together to form a two-dimensional array. If inimodmax% is set to 200 (allowing 200 modules in the INI) and inielemax% is also set to 200 (allowing 200 elements per module), that's 40000 data items allocated! In this case, reducing inimodmax% to 100 would only permit 100 modules in the INI, but would reduce INI memory allocation by half. If your program might only ever have five or ten modules, you can safely reduce this number.


     
      issues and limitations

    • no support for STDIN/STDOUT redirection
    • no support for graphics, sound or mice
    • limited support for long filenames

     
      planned improvements

    • add cycleoption ("true/false") to getform
    • combine dialog and getform into a "common dialog" function
    • radio buttons
    • add streaming to appendfile
    • optimising SDK: strip unused routines during precompilation
    • management of EXTRA.BAS library
    • docs: improve notes section of function reference

     
      revision history

    November 1992versions 1-5The origin of these routines is in fact a ball-bouncing program written for my colleagues. However as QBASIC has very limited graphics capabilities (particularly by modern standards) not much happened for a few years; early versions of the BAPI managed program initialisation, screen modes and a statusbar.
    August 1996version 6added basic file read/write routines, and a date-handler
    March 1999version 7added user input and basic string searching routines
    July 1999version 8added INI handler, error handlers, refined overall structure
    March 2000versions 9-14improved speed, size, screen handling, and code legibility; created SDK and docs
    October 2000version 15added registry handling and INI [module] support, improved memory management and logging capabilities
    September 2002version 16improved INI, CSV, date, logging, memory, environment and error handling; improved SDK and docs
    January 2003version 16.1added cross-platform support; the BAPI now works with DOS, and all PC-based versions of Windows. Bundled BAPISDK 8, improved docs
    May 2003version 16.12added the ability to load the INI file at boot from a location defined by an environment variable
    January 2004version 16.13fixed some bugs, and rounded out the docs
    April 2004version 16.14efficiency improvements; bugfixes; added commandline support; bundled BAPISDK 9
    October 2004version 16.15added support for unparsed INI files and improved random number generator
    June 2005version 16.16numerous tweaks including windowing, INI-defined-from-commandline, scrollable menu and BAPISDK 12

     
      download

    bapi.exe  library