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.
 
- 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:
- This program is hereinafter known as "the Software".
- The Software comes with no warranty and is used at your own risk.
- The Software is not (and will never be) complete and as such may behave strangely.
- The Software is the copyrighted property of the author.
- The Software may be distributed freely.
- The Software may NOT be incorporated into a commercial work without prior written permission from the author.
- 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.
 
- 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 | |
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 | |
purpose | pre-define environment, arrays, load DOS vars, INI file |
features | sets 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 | |
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 |
| notes | if the user presses an extended key (eg. Alt-B), the keycode returned will have 500 added to it |
function | |
purpose | show alert/question, get keypress/answer; log to file |
features | can 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 | |
purpose | show progress bar onscreen |
features | can 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.. |
| notes | update will draw the box, but only if it has not already been drawn |
function | |
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 | |
purpose | erase current dialog box |
features | |
expects.. | to contain.. |
returns.. | containing.. |
| notes | |
function | |
purpose | display titlebar |
features | also 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 | |
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.. |
| notes | the statusbar is displayed on the bottom line of the screen |
function | |
purpose | displays a formatted line onscreen |
features | can 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.. |
| notes | output is in the form prompt: [data] |
function | |
purpose | display scrollable menu, wait for cursor-based selection, parse user response |
features | autosizing, 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 | |
purpose | get multiple data items from user in dialog box |
features | cursor 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 | |
purpose | obtain directory contents from FAT-16, FAT-32, NTFS or UNC volumes |
features | if 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 | |
purpose | detect the presence of a file on disk |
features | will 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 | |
purpose | read a range of lines from a text file into infile array |
features | will 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 | |
purpose | write a range of lines to a text file from infile array, with optional backup and append |
features | will 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 | |
purpose | append a line to a text file |
features | if 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 | |
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:
- initialise the buffer:
writf_name$ = outputfilename$
writf_append% = t%
writf_backup% = f%
writb_init% = t%
writb_head% = 0
header_length% = 0
Call writebuf
- write to the buffer (repeat this until the file is ready to be closed):
writb_s$ = outputstring$
Call writebuf
- flush the buffer
writb_flush% = t%
Call writebuf
|
function | |
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 | |
purpose | copy or move a file from one location to another |
features | can 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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
purpose | gets free disk space on specified drive letter |
features | also 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 | |
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) |
| notes | used by: this routine is usually used in readuserINI; it's not used anywhere else in the BAPI. |
function | |
purpose | changes the value of an INI setting in the INI file |
features | will 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 | |
purpose | to see whether a given string is contained within a given module |
features | This 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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
purpose | runs specified commandline |
features | commandline 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 | |
purpose | gets a password from the user |
features | can 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 |
| notes | All text entered in the auth field is starred out. |
function | |
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 | |
purpose | returns ASCII character from alt-keycode |
features | |
expects.. | to contain.. |
alt.code% | * raw extended keycode |
returns.. | containing.. |
alt.key$ | letter of keypress |
| notes | used by: drawbox, getform |
function | |
purpose | generate a unique 8-digit string |
features | can 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 | |
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.. |
epochday | number 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 | |
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 | |
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 | |
purpose | graceful abandoning of ship. |
features | will 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:
- long filenames, or filenames with embedded spaces, may not be used
- the INI file can have any name and extension, and can be in any directory
- 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 :)
- write the INI file
- choose a modulename, start a new line of INI file, at the first character position, type the modulename enclosed in square backets, eg:
[modulename]
- 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
- add the code to the program
- set p.loadmod%=1 in the program header - this tells the INI loader to load all modules in the INI file at boot
- 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]
- 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.
- complete steps 1.1 and 1.2 as above
- add the code to the program
- tell the INI loader NOT to load all modules in the INI file at boot - set p.loadmod%=0 in the program header
- 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]
- 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$.
- 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
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:
section | location | purpose |
---|
1 - program info | first 6-10 lines | defines the name, version, copyright, execution mode and INI settings for the program |
2 - BAPI | everything from 'begin BAPI' to 'end BAPI' | contains the BAPI itself |
3 - readuserini | immediately after the BAPI | initialises any program-specific variables (eg. variables for your own code to use) |
4 - startup | immediately after readuserini | runs any program-specific startup tasks * |
5 - iface | immediately after startup | creates desktop, and contains your "mainline" (the core loops of your program) |
6 - (user-defined) | to end of file | any 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.
handle | used by |
---|
255 | readdir |
254 | scanfile |
253 | readfile |
252 | writefile |
251 | appendfile |
250 | writebuf |
249 | copyblock |
248 | getmodele |
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.
code | meaning |
---|
1 | TEMP 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. |
2 | temporary directory [directoryname] inaccessible The directory specified by the TEMP environment variable cannot be accessed - does it exist, and are permissions granted? |
3 | function [functionname] missing parameter [parametername] A call was made to a BAPI function without supplying a required parameter. |
4 | unable to read DOS environment variables This usually relates to a memory or disk error - try rebooting. |
5 | INI file not found The program's INI file is required, and is missing from the current directory. |
6 | this program requires Win32 The program must be executed under a 32-bit Windows environment (eg. Windows 95, 98, 98SE, ME, NT4, 2000 or XP). |
7 | tempfiles left in [directoryname] The program has failed to clean up its tempfiles. |
8 | this program requires Win9x The program must be executed under a Windows 9x environment (eg. Windows 95, 98 or 98SE). |
9 | unable to write to logfile [logfilename] Is the path to the logfile valid? Are permissions granted? |
10 | environment variable not set [variablename] Is the variable set in AUTOEXEC, or in the shell environment? |
11 | unique 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
 
November 1992 | versions 1-5 | The 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 1996 | version 6 | added basic file read/write routines, and a date-handler
| March 1999 | version 7 | added user input and basic string searching routines
| July 1999 | version 8 | added INI handler, error handlers, refined overall structure
| March 2000 | versions 9-14 | improved speed, size, screen handling, and code legibility; created SDK and docs
| October 2000 | version 15 | added registry handling and INI [module] support, improved memory management and logging capabilities
| September 2002 | version 16 | improved INI, CSV, date, logging, memory, environment and error handling; improved SDK and docs
| January 2003 | version 16.1 | added cross-platform support; the BAPI now works with DOS, and all PC-based versions of Windows. Bundled BAPISDK 8, improved docs
| May 2003 | version 16.12 | added the ability to load the INI file at boot from a location defined by an environment variable
| January 2004 | version 16.13 | fixed some bugs, and rounded out the docs
| April 2004 | version 16.14 | efficiency improvements; bugfixes; added commandline support; bundled BAPISDK 9
| October 2004 | version 16.15 | added support for unparsed INI files and improved random number generator
| June 2005 | version 16.16 | numerous tweaks including windowing, INI-defined-from-commandline, scrollable menu and BAPISDK 12
|
 
bapi.exe  library
|