macro − overview of FirstBase macro language


Description of the FirstBase macro language


The macro language, one component of FirstBase, is a powerful, easy to use text and record processing language for Unix/Linux. Similar to C, macro includes many familiar programming constructs, including conditionals, loop constructs, user defined functions, and printf mechanisms.

This high level language also supports associative arrays, run time loadable code, strings, and all of the FirstBase data types. Plus, there are many built in functions covering math, string, date, screen input/output, stream input/output, and CGI/HTML processing. The macro language provides a powerful general purpose processing tool that can be used with or without a FirstBase database.

Macro files are simple text files containing macro code. Macro code can be used in three places within FirstBase. The first use is general database, text, CGI and HTML processing with the FirstBase generator tool dbmacro(1).

The second macro code use is to provide extensibility within dbvedit(1). This feature allows flexible control over the record editing process, including complex defaults and conditional field editing.

The third FirstBase tool that will process macro code is fbtstmac(8). This tool checks for syntax errors in macro code.


Dbmacro is a general purpose information processor for FirstBase databases, text files, CGI requests and HTML forms. With or without a default database, dbmacro executes macro(5) code supplied via standard input, or from the command line use of macro_file or the -m script option. Output is written to standard output. See dbmacro(1) for more details.


FirstBase macro code consists of sections of statements, with each section identified as one of begin, body, end, or function. After parsing the provided macro code, execution begins with the first body section, or the function named main, for dbvedit(1) or for dbmacro(1) without a default database.

When dbmacro is invoked with a default database, execution starts with the begin section, the body section is done once for each record, and then the end section is done once.

Each of these sections is declared using its respective keyword, and each section can contain any number of macro statements. Only function sections require more than the keyword to define. Any number of functions can be defined using the following syntax (again, the square brackets denote optional parts and are not part of the function section syntax):

function fname( [ arg1 [, arg2 ] ... ] )
/* macro statements */

The function name fname must be a valid variable name different than any built in function names. Function parameters (or arguments) are optional, but the parentheses must still be used.

Functions can accept any number of parameters and can return multiple return values, of any type, including arrays. All function parameters are local. When a function is executed, the number of arguments can vary, if needed. For example, if three parameters are defined in a particular function, but only two are passed as arguments, the third argument will be empty at run time.

Functions pass all arguments, even arrays, by value, after expression evaluation, and can return any number of expressions, objects, scalars, strings, or arrays.

Within each section, statements are sequentially executed until the end of the section, a return statement or an exit statement. The exit statement can return a status value to the outside caller. The return statement returns control to the caller, or exits if control is inside the body section or main function. An expression value can be passed back to dbvedit(1) for screen or record control.

The following is a list of all FirstBase macro code statements:

local var1 [ , var2 ] ...
if ( conditional ) statement [ else statement ]
while ( conditional ) statement
for ( [ expr-list ] ; [ conditional ] ; [ expr-list ] ) statement
switch ( expr ) {
   case expr: [ statement ] [ break ] ...
   [ default: statement ... ] }
{ [ statement ] ...}
variable = expression
return [ ( [ expr1 [ , expr2 ] ... ] ) ]
exit [ ( [ expr ] ) ]
function ( [ arg1 [ , arg2 ] ... ] )

Statements are terminated by semicolons, NEWLINE characters or right braces. Compound statements are grouped into a simple statement with curly braces. Note that the square brackets are not part of the statements — they merely denote optional components.

The switch statement is similar to C in that each set of statements after a matching case is executed until a break (thereby allowing overloading of many cases that execute a single set of statements). However, dbmacro supports case expressions, not just scalars. In addition, syntactically, the beginning and ending curly braces are required and the optional default section must be the last clause.

Expressions are strings or numbers, depending on how they are used, and are built using the operators +, -, *, /, %. Additionally, the ‘+’ is overloaded to indicate concatenation for strings. The C operators ++, --, +=, -=, *=, /=, %=, <<=, >>=, ^=, &=, |= are also available in expressions. An expression list is a comma separated list of expressions.

Furthermore, these additional binary operators can be used to build up expressions and conditionals: <<, >>, <, >, <=, >=, ==, !=, &, ^, |, &&, ||.

Variables are dynamically created without being declared and are global in all situations unless specified as local using the local directive. These variables can take on any of these data types: float, date, string, associative array, or database array. Variables are initialized to zero (0). String constants are quoted. Octal constants require a leading 0.

Arrays are subscripted using square brackets. The subscript can be any value, numeric or string, but is always converted to a string. This means that array[3] will reference the exact same value as array["3"]. Array functions, key, countkey findkey, and rmkey can be used to maintain arrays.

Variables and numbers will have a default precision of six digits past the decimal, though this is easily tuned using the functions round() and trunc(). Database variables will either adhere to this precision, or be locked into a certain precision as specified in the database dictionary. See ddict(5).

Comments inside macro code are either the C method of /* to open a comment and */ to close it, spanning as many lines as needed, or the use of the # character, which comments out remaining characters until the next NEWLINE.

To denote a database field, a ‘$’ is prepended to either the field name, a variable scalar, or to a number representing that field location in the database record.

If a scalar is used to access a field, it is evaluated before the field is referenced. Furthermore, the ‘$’ can be used on entire expressions, as in the fragment ‘$(i + 10)’.

However, if the variable or scalar used has not been assigned a value using an assignment operator, the scalar is assumed to be an actual field name. For example, the string ‘$Field1’ will work as long as Field1 is not a previously used variable.

Assignments to fields change the contents of the field. Very little type checking is done. The size of the field is checked, and the entry will be truncated if it is too long.

Arrays are used to access fields of databases opened using the macro function opendb. Once a database is open, an array is associated with that particular database using the initrec function. This association only needs to be done one time, but can also be used to clear all field values. In addition, the first call to opendb sets the default database (see the default_dbase function).

After the array/database association, elements of the array represent FirstBase fields within the current record. Note that values will not be accessible via these arrays until after a getrec or getxrec of some kind, and that a putrec or an addrec is needed to actually write these array represented fields as a record to a database.

The subscript used to access a field via an array is either a field number, like dbase[3], or a string, like dbase["Name"]. If the variable Name does not exist, dbase[Name] is equivalent to dbase["Name"]. If an existing variable is used, its value will be the subscript. An array associated with a database cannot be used to hold other subscripted values.

A large number of builtin in functions are provided. Some of these functions take a variable number of arguments, but most are fixed at either zero, one, or two arguments. Arguments are evaluated before being passed to the builtin functions.

The following lists are arranged by function type. All builtin macro functions and their expected arguments are listed. Argument types are given as e for general expressions, n for integer expressions, and se for string expressions.


In the following FirstBase database functions, db is used to represent an open database, like a channel or pointer. The value of db is a returned from opendb.


physically adds a new record to the database db, complete with record locking and autoindex entries. See addrec(3). Returns AOK (1) on success, ERROR (-1) on failure.


checks the fields of the current record in database db for adherence to the range specifications as defined in the database dictionary. Additionally, checks all auto indexes from both the database dictionary and the file (autoindex(5)) that are supposed to remain unique. Checkfields will return AOK if there are no data discrepancies, otherwise ERROR will be returned.


close the database db. See closedb(3).


sets the "default" database to db so that $var evaluates to a field from db.


delete the current record from database db and remove all autoindex references to this record. Returns AOK (1) on success, ERROR (-1) on failure.


evaluates the expression as if it were a variable. Using this mechanism, an array can be rebuilt from a series of similarly named variables (perhaps from a CGI form):

var1 = "abc"
var2 = "def"
for (i = 1; i <= 3; i++)
   array[i] = eval("var" + i)

As another example, eval can be used with database fields:

for (i = 1; i <= 2; i++){
   val = "$field" + i

Note that the following is equivalent (without using eval):

for (i = 1; i <= 2; i++){
   val = "field" + i

But, here is another example of the eval function:

array["hello"] = 16
a = "ww10"
b = a + 1
print(b)                # ww101
print(eval(b))          # hello
print(ww101)            # hello
print(array[ww101])     # 16
print(array[b])         # 0
print(array[eval(b)])   # 16

fgetrec(n, db)

used for non-btree indexes only, fgetrec returns the actual database record number (to be used with getrec) of the nth entry in the current open database index.

field_default(e, db)

return the default of the field in database db represented by the general expression e. If e is numeric, the eth field is used. If e is a string, the field with the same name is used. The default of a field is the string as defined in the database dictionary. See ddict(5).

field_name(i, db)

return the name of the ith field in database db.

field_size(e, db)

return the size of the field in database db represented by the general expression e. If e is numeric, the eth field is used. If e is a string, the field with the same name is used.

field_type(e, db)

return the type of the field in database db represented by the general expression e. If e is numeric, the eth field is used. If e is a string, the field with the same name is used.


loads the first indexed record into database db. See firstxrec(3). Returns AOK (1) on success, ERROR (-1) on failure.


mechanism to free and reset global database memory allocations and resources. For more details, see free_globals(3).


Provides access to the FirstBase global variable extern long cdb_failrec. If the value of this function is zero (0), then a failed search for a record via getxrec has gone beyond the right edge of the btree index. See also set_loadfail and getirec.

getirec(n, db)

reads logical record n from database db, and, under a certain set of conditions, synchronizes the current record with the current index so that subsequent calls to nextxrec and prevxrec locate the indexed records after and before the database record n. See getirec(3), and functions get_failrec and set_loadfail.

getrec(n, db)

reads record n from database db, returns actual number of characters read, or ERROR (-1) on failure. See getrec(3).

getxrec(se, db)

get an indexed record. Use search key se on the current index to load a record from database db. Returns AOK (1) on success, ERROR (-1) on failure.

initrec(var, db)

associates an array of name var with database db. By referencing an array element or assigning a value to it, implicit database field fetch and store commands are done. Initrec also initializes all database fields.


returns the number of indexed records in the current open index for database db.


loads the last indexed record into database db. See firstxrec(3). Returns AOK (1) on success, ERROR (-1) on failure.

lock(n, db, wait)

lock record n of database db. The wait argument controls lock request failures: if wait is zero, lock returns control to the caller with an ERROR (-1) return code; if wait is one, lock will block until the lock request can be granted. An AOK (1) is returned when a lock request is granted.

makess(se, type, n)

returns a search string that is based on se, a FirstBase field type specified by the string type, placed in a field n characters wide. The returned string is used for getxrec calls. Common types are "d" (date) and "n" (numeric). In addition, see functions field_type and field_size.


loads the next indexed record into database db. See nextxrec(3). Returns AOK (1) on success, ERROR (-1) on failure.


returns the number of fields in database db.

opendb(dbase, mode, iname)

opens database with name dbase using mode, either "r" for READ, or "w" for READWRITE. Optionally, index iname can be opened as well. However, use of autoindexes and the useidx function is the best method of index manipulation. Opendb returns an integer used as the database channel or pointer, referred to as db in many other macro code database functions. See opendb(3).


loads the previous indexed record into database db. Returns AOK (1) on success, ERROR (-1) on failure. See nextxrec(3).

putrec(n, db)

write the current record as logical record n to database db. Returns AOK (1) on success, ERROR (-1) on failure. See putrec(3).


returns the current number of records in the database db.


returns the currently loaded record number from database db.


Sets the global cdb_loadfail variable to n, either 0 or 1. When set to 1, a failed getxrec call will load the record just past where the searched for record would have been, unless the failure point is past the right edge of the btree index. See also get_failrec and getirec.

unlock(n, db)

unlock record n of database db.

useidx(n, db)

makes the automatic index n from database db the "current" index. The value of n is from 0 ... N-1, where N is the number of indexes listed in the file. Returns AOK (1) on success, ERROR (-1) on failure.


These functions are used to verify and return proper FirstBase field strings. In general, these functions accept a possibly formatted string se along with any other data restrictions, like field size. If se is verified, a FirstBase writable string is stored in the variable named ret, and an AOK (1) is returned. Otherwise a zero (0) is returned.
verify_ascii(ret, se, n)

verifies that each character of se is an ascii character in the range decimal 32 (blank) to decimal 126 (tilde), or a decimal 10 (LF). The CR character (decimal 13) is allowed, but it is stripped out. The length of se is checked against the value n.

verify_date(ret, se)

verifies that se represents a proper FirstBase date string. Most any kind of formatted or unformatted date representation is allowed.

verify_dollar(ret, se, n)

verifies that se represents a proper FirstBase dollar string. The optional argument n can be used to validate field length as well. Most any kind of formatted or unformatted dollar representation is allowed.

verify_float(ret, se, n)

verifies that se represents a proper FirstBase float string. The optional argument n can be used to validate field length as well.

verify_numeric(ret, se, n)

verifies that se represents a proper FirstBase numeric string. The optional argument n can be used to validate field length as well.

verify_pos_numeric(ret, se, n)

verifies that se represents a proper FirstBase positive numeric string. The optional argument n can be used to validate field length as well.


These functions generate and return strings that are used to create HTML documents. Many are used as open/close pairs of functions, while others take arguments, surround them with HTML structure, and return the entire string. So, these functions are typically used with print or printf from the FirstBase tool dbmacro(1).

HTML Structure

html_open(title, base_href, body_tag, excess_tag)

returns the string to begin an HTML document, using the string title as the title of the document, base_href as the BASE HREF of the document, and the optional body_tag argument as additional components inside the HTML <BODY> tag. The base_href argument is optional, but must be specified (even as "") in order to pass in a body_tag argument.

The last optional argument, excess_tag, can be used to pass in HTML tag elements that need to appear inside the HTML <HEAD> tag, for example META or SCRIPT elements. See html_meta() and html_script_open().


returns the string to close an HTML document.

html_href(ref, se, target)

returns string to reference URL ref with the visual display being shown as se. The optional argument target is the name of a target frame, or the string "new" for a new window.

html_imgsrc(img, [align, hspace, border, alt])

returns string to reference image with URL img. The other arguments are optional: align can be one of top, middle, or bottom as string values; hspace is a number that represents horizontal space; border is a number representing outside border width; and alt is a string set as the alternate text for the image (displayed when the pointer is over the image or for text only browsers).


return a string that is se constructed as an HTML comment.


return a string that is se, but all less than characters (<) are replaced with the HTML equivalent of "lt;". This is sometimes needed since the less than character is a special character in HTML.

HTML Format


string to close blockquote section.


string to open blockquote section.


string to break and start a new line.


string to center se/.


string to close a centered section.


string to open a centered section.


string to create a data definition of se. Used with html_dl_open, html_dl_close and html_dt.


string to close a data definition list.


string to open a data definition list.


string to create a data term of se. Used with html_dl_open, html_dl_close and html_dd.


string to create an H1 header of se.


string to create an H2 header of se.


string to create an H3 header of se.


string to create an H4 header of se.


string to create an H5 header of se.


string to create an H6 header of se.


string to close a header of size n.


string to open a header of size n, i.e. html_h_open(3) opens an H3 header section.


string to produce a horizontal rule. The optional n can be used to control the width. The default width is 4.


string to create a list item se. Use after an html_ul_open or html_ol_open.

html_link(se, ...)

string to create an HTML <LINK> tag used with stylesheets. Each se argument is placed inside the tag, separated by a blank.

html_meta(se, ...)

string to create an HTML <META> tag. Each se argument is placed inside the tag, separated by a blank.


string to close a numbered list.


string to open a numbered list using the optional argument n as the starting value.


string to close a paragraph. Actually, this is not needed right now, but at some point in the future, paragraphs will be true HTML containers.


string to open a paragraph.


string to close a pre-formatted section.


string to open a pre-formatted section.


string to close a SCRIPT tag.


string to create an HTML <SCRIPT> tag. The type argument is quoted and placed inside the tag.


string to close an unnumbered list.


string to open an unnumbered list.

HTML Fonts


string that renders se as bold text.


string to close bold section.


string to open bold section.


string to close emphasis section.


string to open emphasis section.


string to close the most recent font request.

html_font_color(color, typesize)

string to change the current font to the hex string color, optionally adjusting the typesize. Use html_font_close() to close.

html_font_open(typeface, typesize)

string to open font typeface using optional typesize n. The optional argument typesize can be used to control the size, and can be a relative number as well, for example -2.


string to close a font size.


string to open a font size bumped by n points, positive or negative.


string to render se in italics.


string to close italics section.


string to open italics section.


string to close strong section.


string to open strong section.

HTML Tables

html_table_open([border [, cellpadding [, directive ...]]])

string to open a table, using border value of border and a cell padding of cellpadding. Optionally, as many HTML directives as desired can be listed. These complete directives, for example, WIDTH=500 or CELLSPACING=0, will be listed inside the TABLE tag.


string to close a table.

html_table_headers(se, ...)

string representing a row of table headers with each se occupying one column or cell.

html_row(se, ...)

string representing a row of table information with each se occupying one column or cell. Special table formatting strings can be passed as individual arguments to effect the next se. These special argument strings are ALIGN= with LEFT, RIGHT or CENTER, ROWSPAN= with a numeric value, WIDTH= with a numeric value, and NOWRAP. For example, html_row("hotdogs", "align=right", 422) will return a string representing this HTML table row with the number 422 right justified.

html_row_open(se, ...)

string representing the opening of a table row. Any of the optional se arguments are assumed to be intact HTML row formatting commands and are placed accordingly in the return string.


string to close a table row.

html_cell_open(se, ...)

string representing the opening of a table cell. Any of the optional se arguments are assumed to be intact HTML cell formatting commands and are placed accordingly in the return string.


string to close a table cell.

HTML Forms

html_form_open(se, enctype)

emits string to open an HTML form using POST method and an action of se.

If the enctype argument is the string multi or multipart, then the generated HTML string will include ENCTYPE=multipart/form-data inside the HTML <FORM> tag to indicate a multipart CGI data transmission.

Multipart CGI transmissions are transparently processed by dbmacro. Input variables that are not type FILE are directly available via the name of the variable, in the standard macro manner. However, FILE input variables will contain the name of a local temporary file and the local temporary file will itself contain the contents of the one multipart component referenced.

The location of the temporary file is controlled by the FirstBase setup(5) variable TEMPDIR. In addition, each multipart transmission that dbmacro processes can control the temporary file location itself by using an input variable named TEMPDIR (usually a HIDDEN input variable). If such an input variable exisits before the FILE input variable, then its contents are used as the temporary directory used for multipart transmissions.

Another input variable that can be used to control multipart transmission behavior is MAXSIZE. If the HTML input variable MAXSIZE exists before the FILE input variable, then its value is used as the maximum number of of bytes that can be stored in the temporary file.

The temporary files generated by a multipart CGI transmission are not deleted by dbmacro. Use rename to move them (as long as they are on the same file system), or unlink to delete them. Again, the macro script itself must clean up any temporary files after a multipart CGI transmission.


string to close an HTML form.

html_input(type, name, size, maxsize, value, cval)

string to create an HTML input field of type, called name, that is size characters wide, allowing a maximum of maxsize characters, and an initial value of value. Valid type strings are one of text, number, password, checkbox, radio, submit, reset, file, or hidden. Only the first two arguments are required.

If type is file, make sure the html_form_open() uses the multipart designator.

If type is a checkbox or a radio, then cval can be set to the string CHECKED and that item will be marked or selected.

When processed, the special checkbox HTML forms input type is provided to dbmacro as an array named the same name as the field name. (The same applies to the use the keyword MULTIPLE in an HTML SELECT statement, i.e. html_select_open()). The subscripts or keys of this checkbox array are from one to countkey(array). In other words, dbmacro handles multiple occurrences of the same variable passed in from an HTML form.

However, there is one caveat with these methods where the same variable has multiple values as passed in from an HTML form: If only one of many in the HTML checkbox or selection list is selected, then dbmacro will only see one occurrence of this HTML field name, and the variable available at run time will not be an array, but will be a simple variable like any other HTML input fields. In this case, the countkey function will return a zero (0).

For example, this code prints all CGI variables coming in (via an HTML form) as category:

max_i = countkey(category)
if (max_i == 0)
   printf("%s\n", category)
   for (i = 1; i <= max_i; i++)
      printf("category[%d] = %s\n", i, category[i])

html_select(name, size, option, ...)

string to create an HTML selection list called name and a window of size selections. The selection list is made up of any number of passed in option arguments. To force a particular option to be the default, pass a string argument of SELECTED just before the selected option.

html_select_open(name, size [, directive ...])

string to open an HTML selection list called name and a window of size selections. Optionally, as many HTML directives as desired can be listed. These complete directives, for example, WIDTH=500 or MULTIPLE, will be listed inside the SELECT tag.


string to close a selection list.

html_select_option(option, ...)

string representing options in a selection list. As many calls to html_select_option can be done as needed, and each call can list as many options as desired. To force a particular option to be the default, pass a string argument of SELECTED just before the selected option.

In addition, to generate HTML that displays one string, for example Arizona, but returns a different string, for example, AZ, a value= directive needs to be triggered by prepending the string value= to the required value and passing this as an argument. For example,

html_select_option("value=" + "AZ", "Arizona")

html_textarea(name, row, col, value)

string representing an HTML form text area of input. The field name will be a text area row by col big, with an optional value embedded.



absolute value of e.


arc cosine of e.


arc sin of e.


arc tangent of e.


arc tangent of e1/e2.


convert string to number. same as ston(se).


smallest integer not less than e.


cosine of e.


exponential function of e


largest integer not greater than e




natural logarithm of e.


maximum value of the e’s.


minimum value of the e’s.

pow(e1, e2)

e1 raised to the e2 power.


return a random(3) number. Use srandom() to seed.

round(e, n)

round e to n places after the decimal.


sin of e.


seed the random number generator using value e.


square root of e.


convert string to number. same as atof(se).


tangent of e.

trunc(e, n)

truncate e at n places after the decimal.


countlines(se, sep)

counts the number of lines (or fields separated by a sep marker) within the string se. The sep argument is optional and defaults to the NEWLINE separator. (Nested choice types within FirstBase use a field separator of "|").


convert a number to a string using sprintf(3). For example, fmt("%5.3f", 16.1) returns the string 16.100.

formfield(e, type, size)

format a string using one of the firstbase data types, returns the formatted string in a field size characters wide. Types to use are "d" for date, "$" for dollar, or "a" for alpha. For example, formfield(1234.56, "$", 10) yields the string 1,234.56 in a field 10 characters wide. Also, formfield("072691", "d", 8) produces 07/26/91. Fixed width text fields can be produced using type alpha. Note that print() formats these date and dollar fields automatically.

gettoken(str, pos, buf, skip)

similar to getword() except that strings returned are exact substrings known as tokens. A token is either a run of characters and/or digits, or a single other character, like a space, a parenthesis, etc. The single character string skip is used to allow embedded underscores, for example "_". This function returns a new position value to be used in the next call to gettoken() so as to get the next token, or a 0 (zero) if there are no more tokens in str.

getword(str, pos, buf)

get a white space separated string from str beginning at position pos and store this string in buf. Returns a new position value to be used in the next call to getword() so as to get the next word, or a 0 (zero) if there are no more words in str.

index(se, sep)

returns the character position (one based) of the separator located in the string se. Same as strchr().


length of string expression.


string se in all lower case letters.

rindex(se, sep)

returns the character position (one based) of the separator located in the string se as determined from the right side of the string. Same as strrchr().


remove the leading blanks of se.


truncates se at the first LINEFEED or NEWLINE character.


truncates se at the first NEWLINE or LINEFEED character.


changes all underscore characters (_) in se to blanks.


implements the standard UNIX and C printf mechanism without printing the results. Instead, the results are passed back, and can be captured using the assignment mechanism. In other words, the result of the sprintf function is a string.

For example, newstring = sprintf("Hi %d", number) would do as expected. The result string must be less than 5120 characters. Also see printf.

strchr(se, sep)

returns the character position (one based) of the separator located in the string se. Same as index().

strrchr(se, sep)

returns the character position (one based) of the separator located in the string se as determined from the right side of the string. Same as rindex().

subline(line, se, n, sep)

places a subline into line from se, a large field or string containing newlines or fields of data separated by a sep. This function can be used to tear apart comma separated values, or a long string line by line. Note that sep is an optional argument, and defaults to "0 (NEWLINE) if not specified.

The return value from subline is a status indicator. A one (1) indicates success, a zero (0) means failure. Empty fields or lines are considered to be proper elements.

The nth subline component from se is placed into line. For example, subline(oneline, $longfield, 4) places the fourth line from field longfield into the variable oneline.

As another example, the second component of the nested choice field is placed into the variable choice by subline(choice, $nested_choice, 2, "|").

A negative value for n counts fields or lines backwards from the end of se. Also see the macro function countlines.

substr(se, pos, length)

returns the sub string within se starting at pos for length characters. A negative pos marks a position at the end of the string. As examples, substr(s, 1, 5) and substr(s, -5, 5) each return strings five characters long (assuming s is at least five characters). One returns the first 5 characters of the string, the other returns the last 5 characters of the string.


remove the trailing blanks of se.


string se in all upper case letters.


In the following functions, the argument s stands for seconds, and represents an encoding of a time/date in seconds since January 1, 1970. The functions that use an s below need to be passed an argument that is already encoded. Some of the other functions return a date encoded as a seconds argument.

Another type used in these functions is d, which represents the FirstBase internal date type. These are simple strings of the form MMDDYY.


returns a FirstBase date representation (MMDDYY) of the seconds s.


returns the seconds encoding of FirstBase date d.


covert the time in seconds to a standard, 24 character, UNIX date of the form Fri Jul 26 13:23:45 1991. Use substr() to tear this apart.


returns the day of the month: 1 to 31.


returns m/d/y encoded in seconds.


returns number of hours since midnight: 0 to 23.


returns number of minutes since last full hour: 0 to 59.


return the month: 1 (January) to 12 (December).

ndays(d1, d2)

return number of days between two FirstBase dates.

newdate(d, n)

return the FirstBase date d bumped by n days. Negative days are allowed.


return the current time encoded in seconds.


returns number of minutes since last full minute: 0 to 59.


return h:m:s encoded in seconds since midnight


return the year. Valid years start with 1970.


The following screen functions are allowed only when a macro is used from the database editor dbvedit(1). In other words, these functions have no effect on the tool dbmacro(1).


produce bell or visible bell according to the termcap entry


clear the entire screen.


clear from the cursor to the bottom of the screen.


clear from the cursor to the end of the line.


edit one or more fields from the current page, as defined by the visual editor, dbvedit(1), and its visual dictionary file. Note that the arguments do not use a $ — field names or numbers are used. Returns a status value constant of PREV, NEXT, AOK, DEFAULT, or ABORT. See the section below on editing topics.


prints the string se as a standard FirstBase simple error message, and requires a single key to be entered before execution is continued.


redraw the standard FirstBase footer line.


redraw the standard FirstBase header line.

input(row, col, max, min, fmt, addr)

provides access to the standard FirstBase input mechanism, complete with the "input dots" and intraline editing. Returns a status value constant of PREV, NEXT, AOK, or ABORT. See the section on editing topics below for more details.

move(row, col)

move the cursor to row and col. <1,1> is the upper left corner, and <24,80> the lower right corner.


pauses macro execution with a FirstBase simple error message requiring a single keystroke to continue. Sometimes helpful for debugging a macro. See also error().


redraw the current page of fields and text, as known and displayed by the database editor dbvedit(1).


causes the screen manager to display all text in the screen buffer. In other words, cursor motion and screen activity are not displayed until a refresh() is done. However, input, error, and redraw type functions do implicit refreshes.

rinput(max, min, fmt, addr)

same as input(), except row and col are defined as the current ROW and COL being used by the invoking field. An additional runtime macro variable, LEN, will contain the length of the current field.


redraw the status area (upper right hand corner) with the string se.


The stream functions allow standard UNIX handling of files. The arguments with these functions closely matches the arguments of the corresponding functions in standard UNIX manual sections (2) and (3).

Note: to intersperse combinations of fgets and fread, fseek must be used in between. The same rule applys for fputs and fwrite.

access(path, mode)

returns a -1 if the path cannot be found or if any of the access modes would not be granted, otherwise a 0 value is returned. The mode is an INCLUSIVE OR of the bits (test for read, write and execute/search permissions). A mode of 0 tests directories leading to the file, and if the file exists.

creat(filename, mode)

create filename using numeric value mode as permissions. In the special case where filename already exists, creat changes the mode of the filename to mode.

chmod(filename, mode)

change permissions of filename to numeric value mode.


close the associated stream.


flush any output in the stream.

fgets(s, n, stream)

reads a maximum of n characters, or until a NEWLINE, from stream placing the results in s. Returns 0 on EOF, 1 on successful reads.

fopen(filename, type)

opens a file named by filename and associates a stream with it. If the open succeeds, fopen() returns an integer that is used as the file stream. type is a character string from the following:


open for reading


truncate or create for writing


append; open or create for update at EOF


open for updating (reading and writing)

fprintf(stream, fmt, arg1, ...)

uses the standard printf mechanism, but writes output to stream.

fputs(s, stream)

writes s to stream

fread(s, size, stream)

attempts to read size characters from stream and store in s. The actual number of characters read is returned, or a 0 upon EOF.

fseek(stream, offset, ptrname)

set the position of the stream pointer for the next input or output operation.

fwrite(s, size, stream)

attempts to write size characters to stream from s. The actual number of characters written is returned.

link(oldpath, newpath)

creates a hard link from oldpath to newpath. Both files must be on the same file system. Returns 0 for success, -1 for failure.


calls mktemp and is Not the same as the Unix mkstemp(2).


creates a unique file name, typically in a temporary file system. The string in template should contain a file name with six trailing Xs mktemp will replace the Xs with a letter and the current process ID.

mktemp actually uses the mkstemp(2) system call, which creates the temporary file, mode 0600, and opens the temporary file, returning a file descriptor. The macro version of mktemp closes the file descriptor, leaving the file intact.

creat or chmod can be used to change the mode on these files.


close the associated stream opened with popen.

popen(pname, type)

opens a process named by pname and associates a standard file stream with it. If the popen succeeds, it returns an integer that is used as the file stream, otherwise an ERROR (-1) is returned. type is either an r to read from process pname, or a w to write to process pname.

rename(oldpath, newpath)

renames a file from oldpath to newpath. Both files must be on the same file system. Returns 0 for success, -1 for failure.

symlink(frompath, topath)

creates a symbolic link from frompath to topath. Returns 0 for success, -1 for failure.


removes the directory entry named by path. Returns 0 for success, -1 for failure.



provides a hook to the CGI initialization routines when macro code is executed using direct methods.

As dbmacro(1) points out, if invoked as dbmacro.cgi, CGI data is automatically converted to variables available in the macro code at runtime and dbmacro(1) also emits the standard CGI script lines "Content-type: text/html0.

However, if dbmacro(1) is called from a shell script, or is used as the command interpreter itself via something like


as the first line of the CGI file, then the cgi_read mechanism can be used to parse standard CGI parameters and values. Note that the script will have to print the HTML Content-type directive itself if needed.


change directory to se and return the status of the chdir(2) call, which is ERROR (-1) on failure and zero (0) on success.


return the count of the keys or subscripts of array. See also key.

findkey(key, s, array)

locate the element of array that has the value of s and place its lookup or subscript value into key. Returns either AOK or ERROR.


standard fork(2) UNIX function. Returns 0 to the child and the childs process ID to the parent.


standard getenv(2) call, returns value of UNIX environment variable se.


reads a single line from standard input and places the results in s. Returns 0 on EOF, 1 on successful reads. Used for dbmacro(1) only.


returns a 1 if e is in the set of expressions e1 ... eN, else a 0.

key(val, array)

iterates through each of the keys or subscripts of array on successive calls and returns one key each time. key places the subscript value into val. A value of one (1) is returned on success and zero (0) on errors or when key has iterated through all subscripts. Iteration can then start again. See also countkey.


Load and parse the macro_file se. This runtime load mechanism can be used to load sets of library style macro functions, or load different macro code on demand. Again, all variables in all files and functions, loaded or inline, are global unless specifically declared using the local directive.

pattern(e, pat)

returns a 1 if e matches the pattern pat, else a 0. Full regular expression pattern matching is done.


compile the pattern pat. Use with pattern_exec().


returns the end character offset of the nth remembered substring matched in pattern() or pattern_exec(). Note: return value is adjusted for one based string array.


match the string s against the pattern previously compiled via pattern_comp().


Default pattern matching is case sensitive via pattern() and pattern_exec(). Using an n of 1 causes case to be ignored for these pattern matches, and a 0 resets pattern matching to being case sensitive.

pattern_so(n, r)

returns the starting character offset of the nth remembered substring matched in pattern() or pattern_exec(). Note: return value is adjusted for one based string array.

pattern_substr(str, n)

returns the nth matched remembered substring of str after a call to pattern() or pattern_exec().


prints each of the expressions, formatting those that are of type FirstBase dollar and FirstBase date. For screen macros, use move() to position the cursor first.


implements the standard UNIX and C printf mechanism. The result string must be less than 5120 characters. The result is printed. For screen macros, use move() to position the cursor first. Also see sprintf.


standard putenv(2) call, sets value for UNIX environment variable se.


print a single line to standard output. places the results in s. Returns AOK or ERROR. Used for dbmacro(1) only.


exactly like print() except characters are printed in reverse video if possible.

rmkey(key, array)

removes element key from array.


sleep n seconds.


exactly like print() except characters are printed in standout video if possible.


use the UNIX system call to execute the command se. Returns the exit status of the sub shell.


terminates the process. Used only for exiting from a process created via fork(). Terminates (exits) with status e. Again, use exit or return for normal macro code, and terminate only when you want the process to die completely.


writes error message s to the USRLOG file in the FIRSTBASEHOME directory. Also see setup(5).



change the group for a record to the numeric group e.


change the mode for a record to the numeric mode e.


change the owner for a record to the symbolic name use name specified by se.


return the group number for a record.


return the (numeric) mode for a record.


return the symbolic owner name for a record.


Complex defaults, conditional defaults, and assignments to fields within the record based on large sets of criteria can all be readily done using a macro file.

A simple assignment to a field will store the value into the field. Later, when the record is written, this value will be written to the disk copy of the record. For example, the following stores todays date into a field named EntryDate:

$EntryDate = cdbdate(now())

Other kinds of defaults can be done using conditional statements. For example, the following only stores the date field named LastCont only if a date has already been entered into the date field NextCont.

if ($LastCont != "")
   $NextCont = newdate($LastCont, 14)
   $NextCont = ""

This example will set the field NextCont to two weeks past the current value of LastCont at macro execution time (field edit time).


One of the features of FirstBase macro fields is the ability to interactively edit fields from the current database within the flow of a macro file. This interaction occurs when using the database editor, dbvedit(1), or the visual database emitter, dbvemit(1). These are trigger fields, or conditional fields.

When a field is edited within a FirstBase macro, it is referred to as a trigger field. But, the place the macro file is attached is called the trigger point. Both trigger points and trigger fields can be edited within a macro, but there is a distinction.

Trigger fields are edited using a very simple function, editfield(). Editfield accepts a list of comma separated fields, but the arguments do not use the "$" to evaluate the fields. For example, editfield(Address) would edit a field named Address in the current FirstBase database.

Here are a few notes on trigger fields and trigger points. First, fields that are listed in the outside visual dictionary as "display only" can still be edited, and this is probably preferred since the idea is to make the trigger fields editable from the macro file only.

Second, editing the trigger point using editfield() is disallowed (as is any cycle where this occurs). In this case, input() must be used. See below.

And, third, when using editfield() or input(), the return of a proper status (signal) to the FirstBase database editor will produce better field-to-field cursor motion during the editor’s Field Level movements.

Note: in the following sections, the macro function rinput() can be used whenever the related function input() is referenced.

When editing a field, sometimes the user elects to go by the field using FirstBase keystrokes that move the cursor to the NEXT or PREV fields. The macro functions editfield() and input() both return a status value that can be used to detect these situations. The macro constants PREV, NEXT, AOK, ABORT, DEFAULT and END represent the possible signals returned. Equivalent macro constants are FB_PREV, FB_NEXT, FB_AOK, FB_ABORT, FB_DEFAULT and FB_END.

A special note on the END token: Since END can be used to define a sections of code that will only be executed one time at the end of all database processing, some uses of END will cause a syntax error. For example,

st = END

will not parse, however each of the next two will parse just fine:

st = END;
st = FB_END

In general, passing back the signal returned by editfield() or input() will produce the proper effect. For example, this macro file code fragment will produce normal editor behavior:

st = editfield(Address1, Address2, City)

Note that when given more than one argument, editfield() will properly handle the NEXT/PREV signals between these fields. If you need to use multiple calls to editfield() within one macro-file, you will either have to accept the editor behavior, or provide for the PREV/NEXT signals in the code for the macro file. (See the examples below).

Another variable that helps in determining editor behavior is the macro variable ST_UP. This variable is set to one when a macro file is invoked by moving into the field UPWARD via a PREV signal. Otherwise, ST_UP will be zero.

To allow generalized input, or input into a trigger point, the macro function input() is provided. The input() function has many arguments: row, col, max, min, format, and address. Additionally, an optional argument, extra_status, can appear last.

Input is taken from the screen at coordinates row, col. A maximum of max characters will be allowed, with a required input of min characters. (To make a forced entry, set min greater than zero). The format is one of "aAd$fnNU", the core set of standard internal FirstBase data types.

The next argument, address is the macro variable where the input() information will be stored. Note: address cannot be a FirstBase field — use a temporary storage area then assign back to a field if needed. Additionally, if address is assigned a value BEFORE entering the input() function, its contents are used with the FirstBase intraline editing functions, if EDITINPUT is set via setup(5).

As mentioned above, input() also returns a status. However, its status is more generalized than editfield(). For example, since input() detects signals and gathers keyboard input, the characters gathered are stored in address only when an AOK status is returned by input().

A specialized version of input() named rinput() will use the row and column of the current trigger point in calling input(). All other arguments remain the same.

For example, to simulate dbvedit(1) behavior, using the rinput() mechanism, on a trigger point field, (or a field that has a defined macro that edits the same field itself, recursively), the following macro file code could be used:

val = $Field3
st = rinput(LEN, 0, "$", val, e_st)
if (st == AOK){
   $Field3 = val
   st = e_st

The internal macro variable LEN will be set at run time to the length of the current field, the field size.

This code also provides an example of the optional status argument to input() and rinput(), here called e_st. If passed this argument, these macro functions will store any excess status signals in this variable.

An excess status signal is generated only when changes are made to the editable area and a NEXT or PREV keystroke is used to move the cursor. In this case, a final variable passed into input() and rinput() (e_st) will be set to this excess status, NEXT or PREV. The actual return code from the function is AOK. The above code catches these excess signals properly.

Although it is much easier to use a trigger point and editfield() with multiple arguments, it is possible to write macro code that will edit the trigger point using rinput(), edit additional fields using editfield(), and still provide normal dbvedit(1) behavior.

The following code, again, unnecessary if you use a trigger point and distinct trigger fields, does demonstrate all of the above nuances to trigger point editing and signal behavior.

# loop to provide NEXT/PREV behavior between **these** fields
for (st = NEXT; ; ){
   if (ST_UP == 0 || $Field3 == ""){       # if not here via PREV
      val = $Field3
      st = rinput(LEN, 0, "a", val, e_st)  # edit the trigger point
      if (st == AOK){
         $Field3 = val                     # make assignment if AOK
         st = e_st
      redraw()                             # redraw rinput() always
   ST_UP = 0
   if (st != NEXT)
   if ($Field3 != ""){                     # maybe edit Field4
      st = editfield(Field4)
      if (st == PREV)
      st = NEXT

Note that an equivalent piece of code using a trigger point field different than the trigger fields is simply:

return(editfield(Field3, Field4))

With this smaller piece of code, the macro cannot be tied to either Field3 nor Field4.


This section describes how to use macro(5) code to extend the standard behavior of the FirstBase database editor. Non licensed versions of dbmacro are not capable of database interaction.

To be used within dbvedit(1), the macro file is either (a) tied to the database via the database dictionary or defaults file (see dbdbas(1) and defaults(5)) or (b) named one of the special macro file names that indicate macro execution at record entry, record exit, of other dbvedit record editing points.

When the macro file is tied to an actual field, it will be executed instead of the normal Field Level editing when the cursor is moved "onto" that field. This macro-enabled field must be "editable" according to the view dictionary (the vdict file).

The macro file itself can modify other fields as long as they are listed on the same page in the view dictionary. Furthermore, macro files can edit fields that the view dictionary defines as "display only".

To provide for macro execution at record entry and record exit points, special file names are used to store the macro code. The base file name for these files is the name of the database. The additional extensions are m_begin, m_end and m_endwrite.

For example, if the database is named dbase, then the begin macro file name is dbase.m_begin, and the end macro file name is dbase.m_end.

The begin record macro file will be processed whenever a record is opened for editing, and it will always allow record entry.

The end record macro file will be processed whenever a record is exited, whether it will be written or not. This end record macro file can deny exit by controlling the value or status it returns. If this end record macro returns a status other than END, for example ERROR, then the record will not be written, and the user is returned to record level on the same record (to correct the situation causing the failure).

The endwrite record macro file will be processed after a record is written to the database, meaning all automatic increments and defaults will have been resolved. Note: changes made to the record here will not be written to the database — this macro file is only for post-write processing. Global variables inside each of these macro files are shared so that state information or flags can be used during an editing session.

During the execution of each of these record macros, an internally defined macro variable CREATE_RECORD will be set to one (1) or zero (0) according to whether the record is a new (add) record or not. Additionally, during both end record macros, an additional internally defined macro variable WRITE_RECORD will be set to one (1) or zero (0) depending on whether the record has been modified and is about to be (or was) written to the database file. Still another variable END_KEYSTROKE will be (1) or (0) during the end or endwrite macros depending on whether the END keystroke was used or whether the action was due to a w, WSIGNAL, or custom command.

There are two additional execution points within dbvedit where a macro can be used to provide extensible editor behavior. These two points are the command level macro and the field level macro, each denoted by special file name extension. Again, the base name is the name of the database, with the extension being m_com and m_fld, respectively.

Each of these macro execution points can be used to repaint the screen. Additionally, the field macro can make changes to the database record and potentially provide trigger fields or sections.


dbedit(1), dbdbas(1), dbmacro(1), fbtstmac(8)

FirstBase User’s Guide and Reference Manual