acon : Console and WebService Utility for allegro Databases
FLEX : Scripting Language for acon
Introduction to a35 Web Interface
| WikiPedia: "A console
is a computer
designed to be used via a text-only computer interface, such as a text
the command line interface of some operating systems (Unix, DOS, etc.) or the text-based
interface included with most Graphical User Interface (GUI) operating systems, such
as the Win32
the Terminal in Mac
OS X, and
xterm in Unix."
Typically, a console application does not conduct a dialog with its user. Instead, the user calls it up from the console, typing in its name and mostly adding appropriate arguments to let the program know what exactly it is expected to do. There are innumerable programs of this kind in every computing environment. Mostly, they are specific for the operating system they are used with.
Basics about acon
Now what is it that you can have acon do for you? Basically, it can access any allegro database and extract all information you may need from it, and in any format you want.
To do anything useful, acon will always need at least four arguments:
dbdir = The name of the database directory (default: demo2, works only if acon is started in c:\allegro and there is a folder .\demo2 there)
dbname = The name of the database definition file (a.k.a. "index parameter file", default=cat for the file cat.api)
cfg = The name of the configuration file (defaulting to a for a.cfg)
job = The name of the jobfile - and this is where FLEX comes in: that file contains the FLEX script to be executed
That makes it three things telling acon which database to access, and one thing to tell it what to do.
The syntax of an acon call is therefore this:
acon -d dbdir -b dbname -k cfg -j job [spaces after -d, -b etc. are optional]
This may be simplified by wrapping it up in a batchfile or shellscript abc (or whatever) that would then be activated by something like
If you have only one allegro database, the first three arguments will always be the same, so this makes lots of sense.
Basics about FLEX (see also FLEX Compact Command Table)
acon is no use unless it is told what to do in its own language, written down in a text file, called a "jobfile". Any job must be formulated in a language that was developed specifically for allegro databases, and that language is called FLEX. (Not to be mixed up with "Flex", the Adobe language for their "Flash" program. FLEX originated earlier than that one.) The language FLEX has almost 100 command words but many of them are only for very special tasks. And most command words come with some or many different options. By human standards, still not exactly a large vocabulary. And like with any programming language, there are very strict rules to obey. FLEX combines database functions with string manipulation, calculations, file access, Internet access, program calls, and data output. An extended version for the Windows graphical client program a99 has a range of user interaction functions not available to acon and therefore not to the browser client a35 based on it.
The "Hello World" example
Any programming language is usually introduced by the simplest possible example, consisting of the output of the message "Hello World". Easy enough to do in FLEX: Write a small file, named hw.job, containing just this line:
write "Hello World!"
Then, call acon with this command:
acon -jhw (Under UNIX/Linux : ./acon -jhw )
Actually, this will work only if you have the small demo database installed, in a subdirectory demo2 under your program directory where acon resides and where you make the call.
This job involves no database access at all, but acon needs a database context before it can do anything - so you have to provide one. This makes sense because, practically, you will hardly ever want to use acon with no database context.
Now, on to a more meaningful example, needing more than 1 line:
write "This is record nr. 1:" n kr
Put this into a file nr1.job, then enter
Just try it. To use it on your own database, you would have to enter
acon -d dbdir -b dbname -k cfg -j nr1
The first line uses the find command to retrieve the record with the internal number 1.
The second line writes text to the console:
"This is record nr. 1:" is literal text (single quotes are also usable)
n means "new line"
kr is one of many special variables you have access to; this one contains just the text of the current record.
Now, without knowing any more, you may already start trying variations of this little script, including adding more lines of output.
Do try it, to get some feeling of the matter.
for a glimpse of FLEX's potential:
| 1. The one
most important advice
3. The three most important rules
5. The five most important commands
Many commands are very important - and it always depends on context, of course.
These four are those that will be used most often, and you cannot do much without any one of them:
|To do anything of substance almost always calls for
more than one or two lines of code.
Before we go on to look at many more commands, here's an example that can serve as a pattern in many real-life situations:
How do we build a result set and then produce a listing of the records containing just a few select data fields?
In the course of this, some other very useful commands creep in to enrich our vocabulary.
To be more concrete, and using the demo database, let's make a list of titles (#20) with publishers' names (#75) and dates (#76) of all records that contain Shakespeare as author and the words "Hamlet" or "Othello" in the title. Well, actually, something like this can be achieved in two lines:
find per shakespeare, william and (tit othello or tit hamlet)
Try it out now, to see what happens: Call the script soh.job and start it with acon -jsoh.
That this works, however, is more of an accident due to the characteristics of the demo database and its short title structure, which is what the list command extracts. In practice, there will be many cases where the task is different. And even in this case, we didn't get exactly what was asked for - the short title line contains the author (#40) and call number (#90) as well, but not the publisher.
To be more flexible, we can follow a common pattern, and it may rightfully be called
The most important pattern
find per shakespeare, william and (tit othello or tit hamlet)
wri #20 ". - " #75 " (" #76 ")" n
if yes jump loop
For our little task, we already have to go beyond our five most important commands.
Walking through it line by line, with the four "new" commands marked in blue:
// A find command builds the result set:
find per shakespeare, william and (tit othello or tit hamlet)
// Now load the first member record of the set
// Here comes a jump mark that helps us structure a loop [the : is blue.]
// Compose the required line of output
wri #20 ". - " #75 " (" #76 ")" n
// There can be any number of command lines here to create the output
// Then load the next member
// If this was possible (if we weren't on the last one already) jump back to :loop
if yes jump loop
The word "loop" is not a command word. That's to say, the jump mark at the beginning of a loop can be any other word. And quite obviously, when you need more than one loop in a script, you'll need a different mark for each of them. Only the colon is part of the FLEX language. It just says: This line can be jumped to from anywhere in the script using the jump command.
Fine, you'll say, but what if I wanted a listing of my entire database, not just a result set?
That's no more difficult, and there's not even the need then for a find:
if not deleted wri #20 ". - " #75 " (" #76 ")" n
if yes jump loop
The # after first and next changes the meaning of the commands:
first # loads the record with the internal number 1, and
next # loads the one with the next internal number, following after the one currently loaded
And the phrase if not deleted makes sure the write takes effect only if the record is one that has not been deleted. This was unnecessary in the first example because a result set never contains deleted records.
But you may want to do more with every record of the database than just output a few fields.
So, what if it is not possible to express the necessary action in one command like the write ... in our example?
The pattern easily extends to a more versatile variant:
if not deleted perform proc
if yes jump loop
// here follows what is to be done after the loop terminates
// here we do all we need to do for every record, e.g., write output
That :proc line is a jump mark, but what follows is in fact a subroutine. It ends in the return line. Between :proc and :return, there can be any number of command lines. You may also go to the :proc line with a jump command; in that case, the return will be ignored.
Programmers will ask: Can we have nested subroutines, or in other words: other performs inside a subroutine? It might be useful indeed, but the answer is no. (Another incident of advice 1.1.)
"How can I get an ordered result set?" will be the next question. There's more than one answer to that. The simplest is the use of the order command. It depends on the format of the short title list. If it starts with the title, you get a list ordered alphabetically by title with the command order a0, meaning "sort the list beginning at the 1st character, ascending" (order d0 would mean "descending").
|We are turning now to the question of how exactly FLEX
operates as part of the a35 Web interface. To explain this, we may
begin with the "Hello World" paradigm again - the simplest possible
piece of operable programming, now in the context of a35.
What we need to write is : just a little FLEX job, what else? And no more than one line in this case as well:
wri "_!_POP Hello world!" n
Put this script into the scripts/jobs subdirectory of your a35 installation and name it hw.job.
Then, in any of your (potentially many) a35 database interfaces, enter this into the red frame:
and it will bring up a small "popup" window containing those two words. Replace POP by INF and the text appears in the INF quadrant (upper right).
The capital X is a command a35 understands. It means: "The following word (hw) is the name of a job. Go execute it!"
To be precise, a35 itself doesn't know what to do with it but sends it to the server where the database resides, using the avanti program as a messenger. avanti, in turn, hands it over to acon for execution. The output of acon goes all the way back via avanti and the webserver to the browser, and there the a35 page sits waiting for it, and knows what to do with the message, i.e. where and how to display "Hello world".
Now what's that "secret" of a35? From this example, you'll gather it must lie in that cryptic "_!_POP" because that's the only thing that goes beyond the first example. And indeed, the character sequence _!_ is used to mark 3-letter labels, of which POP is just one. Those labels are hidden beneath the surface of what you see in your browser when you use a35. There are numerous elements, mostly <div> elements, that have an id attribute with them. It always goes like this: <div id="XXX"> ... </div>, and one of these is id="POP". The most important ones are those for the four "Quadrants":
EXT : Display of one record (upper left)
INF : Help or info text (upper right)
ERG : Result set list (lower left)
REG : Index display (lower right)
Then, whenever one of the a35 FLEX jobs is started, this job will create a stream of HTML text, using the write command or sometimes the export command also. These commands insert labels _!_XXX, like in the example, to mark those portions of the output that are meant for those various <div> parts. All the HTML that follows _!_XXX in the long stream of output, up until the next _!_XXX occurs, will be put into that particular <div> or <span> with the id="XXX". And that's all there is to that "secret".
Besides the four quadrants, there are other areas that can be written to by _!_ statements. You can discover the labels of these areas in the source file a35-pc.php, looking for the id="..." attributes. Or simply type h labels.txt into the red frame...
All the necessary machinery that puts the HTML stuff into the various div's is in a35.js - you don't have to bother yourself with that.
You may even create new <div>'s inside the php scripts, like the startup file a35-pc.php, and then write jobs that address these to get something inserted there.
There are three PHP scripts that contain all those id labels in the startup screen:
a35-pc-cont.php for the PC variant of the a35 interface
a35-tab-cont.php for the Tablet variant
a35-app.php for the smartphone PC variant
There's a picture of the PC variant here: http://www.allegro-c.de/doku/a35/a35-1.htm
It shows the important labels in red colour: EXT, INF, ERG, REG, FRE, FRR. But there are a few more, some of which not always visible, like POP, used to create popup messages.
By the way: If there is no database access involved, just HTML textual matter, you may put labeled HTML text into a simple text file. For instance, create a file hw.txt, containing this line:
_!_INF <h1>Hello world!</h1>
and then enter this into the red frame: h hw.txt
The h is another command that a35 understands: it will load the file and interpret its contents as labeled HTML (no matter the filename extension).
A larger example for this method is a35start.htm. Load it by inputting h a35start.htm.
Do look into it, for with your new knowledge, you'll understand how it works and be able to modify it right away. This file is brought up automatically every time you call a35-pc.php.
FreeForm : An Easy Way to Generate Forms for a35
For data input and editing, any browser-based program uses HTML forms. The freeform technique invented for a35 provides a simple and flexible means to create such forms, and without the need to know anything about the HTML syntax.
A "freeform list" is a file of type .frf that specifies the fields to be presented in a form.
General rule: Remember a freeform list is not a Jobscript! It gets converted into a form, the moment it is needed, by the script freeform.job. Besides command lines described below, the list may contain empty lines and comment lines that begin with //.
In the simplest case, a freeform list consists of just the list of tags for the fields to be edited,
like for example:
which is three fields from the standard ($a.cfg) format. All the other fields will remain untouched.
As was already mentioned, a freeform list will be turned into a form and filled with the respective fields' contents from the current record. The form will normally have one line per field (but see below for other options).
Every field is automatically prefixed with the label that derives from the CFG file's field list:
That's a bare minimum. However, for every field, a number of options may be set, following the tag on the same line of the list.
The shortest freeform list consists of just one line, like
which will present a form with just one input field, holding the content of #123 for editing.
The input field will be preceded by the label for that tag as given in the CFG file.
Direct test without a list file is possible: Enter
X freeform&Dfields=123@@124 in the red command frame.
In this syntax, the very short freeform list (fields #123 and #124) directly follows after Dfields=.
Or put those two tags into a file test.frf (placed in the data folder with the actual database) and then enter
If only subfield $a of #123 is to be edited, this is the list file:
However, if field #123 may have some content preceding all its subfields, and you want to edit just that, then write
because $$ stands for "the subfield before all the subfields" - something that
MARC, for example, doesn't have but which may occur in allegro data, as in
where #74$$ denotes just the PlaceName, and #74$g is the CountryCode
Some options may be added to every line, following the tag, separated by spaces. None of the options is mandatory.
First, the width of the input field may be specified by a number, meaning pixels:
(the default is 400, set as $wid in freeform.job)
The width option must directly follow the tag, or omitted.
If the text inside the input field is longer, it scrolls horizontally.
The other options have no prescribed order:
If the CFG label is not appropriate, define a "label" like this:
#123 500 "Title:"
or without a width number as in
#123 "Title:" then the default width will be used
The same works for subfields. But for them, the "label" option is necessary because it will not be provided automatically (the CFG doesn't specify labels for subfields, only for the entire field)
#123$s 300 "Language"
If the field is empty in the current record, a default value can be set, enclosed in :...:
#123$a 300 "Language" :eng:
It may happen that there's no value in #123$a and that in this case the value from another tag is to be used and put into #123$a, say #124$x. Then add #124$x to the line:
#123$a 300 "Language" :eng: #124$x
With this, :eng: will only be used if in the current record there's no value in #124$x either.
In some cases, extra text is needed following the input field, like a helpful hint.
This text can be specified between !...!:
#123$a :xyz: "Language:" !(Enter language code)!
A >Placeholder< appears in the field, in a grey colour, while there is no input text.
Any other attributes can be added to an <input> field by adding them within %...%, like
... %readonly type=hidden%
Here's the complete order and syntax of elements to be observed:
(elements in  are optional)
[+]#tag[$x][+] [width] ["label"] [!ExtraText!] [>Placeholder<] [:default:] [#TAG] %attributes%
There can be either $x or + directly following a #tag, not both.
The + sign before the #tag serves to place that input element on the same line as the preceding element. (Adjust the widths to make sure they will fit.)
The + sign after the #tag indicates that multiple fields with the same #tag may exist,
and that these are to be presented for editing together in one edit box (an HTML <textarea>). In this case, the field tags will be visible and editable, whereas in regular input fields, the tags are invisible and unchangeable. This way, the order of the multifields can be rearranged manually.
The #TAG is used if #tag has no content in the record. It takes precedence over the :default:. The input then goes into #tag, however.
Here's an example of two fields to appear on the same line, both of width 180px:
#330 180 "Composer"
+#331 180 "Arranger"
For a potentially long field, one may specify a <textarea> rather than an <input> field:
would mean a <textarea> with 10 lines ("rows") of 54 columns to hold the contents of #98.
Dropdown lists (HTML <select>)
To create dropdown lists with a number of fixed options to choose from, there's a freeform option that can be used in addition to the others:
#nnn =listname= ...
To make this work, there needs to be an HTML <select> list. It has to be in an extra file which must be loaded after startup of a35. It must contain a section that looks like this:
(In the <option> lines, single quotes only, or you get errors.)
<select id="VVV" style="width:180px;">
_!_VAR ... [the next list]
Here, VVV is mandatory, c1, c2, ... cN are the values that end up in #nnn and Text1,...,TextN the textual designations displayed in the selectdropdown list. Use exactly this as a template and just fill in the blue elements according to your own specifications. The style attribute is optional; without it, the longest option entry will determine the width.
Then, the input to #nnn can only be one of those values, the one belonging to the chosen Text line. This section together with more predefined sections has to be in a text file filename.htm which must be read in by a35 with the command h filename.htm in the red frame, or an appropriate link like
Then, upon executing a freeform list containing a line #nnn =listname= ..., all necessary steps will be automatically done to prepare the actual dropdown list and position it on the line containing the cx value that is already present in the #nnn of the current record, if there is one. All of this is done partly by freeform.job and partly by a35.js in the receivE() function that processes the _!_ labels created by the job.
A freeform list can also contain a number of settings:
To create a new record by the use of a form, the call has to include the parameter
&Dfunc=new or &Dfunc=copy
In the case of copy, the form will not be all empty but fields of the current record will be used to populate it, plus any defaults defined in the freeform list.
Sometimes, a form should be allowed to access only if the current record contains one field or other, or does not contain those fields. Then, add appropriate if or if not conditions, but place them at the top of the list:
if #20 : do this form only if #20 is present in the record
if not #90 : ... only if #90 is not present
if #20 #40 : either #20 or #40 must be present, or both
if not #20 #40 : both #20 and #40 must not be present
if #40 : both #20 and #40 must be present (two lines!)
How to test it outside of a35:
If xyz.frf is a freeform list, then for testing, try
with nnn being the internal number of any record on which to test the form.
The form will appear in the browser, filled with data from record nnn.
Then look at the HTML source code to see how it has been constructed by the freeform.job, after processing xyz.frf.
The "generic" files can be used without changes for every database.
For the various folders see "Basic Requirements"
PHP and HTML: (Location: HTML start directory of the database)
ajax3.php using ajax3.ini and a35.ini
handles all the traffic between the browser and the webserver.
Predefined HTML files containing the variable contents described in Part 5.
Jobs: (Location: scripts/jobs on web server)
freeform.job : Generic for creating the HTML-Form out of any freeform list
prsave.job : Generic job to save the record after being edited in the form
prtest.job : The same, only without the actual saving,
but it will also display the index entries of the edited record
Freeform lists: (Location: database folder)
*.frf : form specification files as described above.
For those who need to know all the details or are curious:
What happens when you enter X freeform&Dform=test ?
1. The function reqJob() in a35.js receives the name of the job to call,
freeform.job, and the variable Dform=test - so freeform.job is to be called to
read the file test.frf. It also receives, automatically, the internal record number of
the curent record and the timestamp of the current record, and the user's ID .
2. A command goes out to ajax3.php to do what is required:
freeform.job will be started; it reads the record and the file test.frf,
then produces the form with data from the record filled in. This is
HTML code that goes back to the browser who displays it in the INF quadrant
(which is what freeform.job is specifiying).
3. The user can now edit the data in the form. Then hit the "Save" or "Test" button.
which collects the variables from the form and the other necessary things
that must be transmitted, through ajax3.php, to the job.
5. On the webserver, ajax3.php will receive the variables that have been
transmitted by the browser, including, automatically, the record number (VurN),
its timestamp and some other details, but also the name of the
Jobfile to execute, in this case prsave.job or prtest.job.
Inside every job called, a <form> variable like Dxyz or V123 arrives as $xyz or #123,
and thus VurN as #urN (i.e., V gets replaced by # and D by $).
The job is executed by the console program acon. It produces the entire
output, using FLEX statements write ... and export ....
The output goes, without further modifications, through ajax3.php straight
back to the browser. There, the function receivE() in a35.js analyzes the output
into its constituent labeled parts and then places the content following each label in
the HTML <div> or <span> which carries that label as its id attribute.
a35-pc.php, sitting inside the browser all the time, receives the output
of acon, and the function receivE() in a35.js interprets the labels _!_XYZ inside that output, in this case _!_INF.
The text following a label _!_XYZ up until the next such label is then
copied into the area <div id="XYZ"> </div> inside a35-pc.php. Also possible is span instead of div.
This same protocol governs all communication of a35 with the webserver.
How to call up a form via linking?
Any link like
will bring up the form book.frf with the content taken from the current record and with buttons for "Save" and "Test". Add &Dfunc=new if creation of a blank new record is desired, or &Dfunc=copy to say you want a copy of the current record to turn it into a new one. In the latter case, you may add NOCOPY to any field definition line if that particular field should not be copied. This way, you can create new records that adopt only selected fields from another record.
This sort of links can be placed in HTML files like a35forms.htm or in the Menu file a35-menu-pc.php. Or, via the display parameters, in the record display itself.
It is possible to add parameters like DInfo=... to the link. The Dxyz arguments translate into $xyz inside the job script freeform.job, just like the Vnnn arguments translate into #nnn.
Extensions and improvements
The freeform.job is an open source script written in the FLEX language. It is long and complicated, but extensively commented - another example for the study and learning of FLEX.
|To start learning allegro FLEX with
And what about the database server? aconneeds none, it can access the databases all by itself. The avanti Server does not access a database itself, it just feeds jobs coming from a webserver into acon, receives the resulting outputs and hands it over to the webserver.
But can several people safely access the same database at the same time? Yes.
The setup for Windows is analogous. Except there, you have a convenient graphic interface: a99.
The Special FLEX Variables
You get this list if you start the al.job on the Demo database and then use the function Admin / Settings.
It produces a text file settings.txt containing exactly this list, showing the current values of the "special variables" at that moment:
A : Access mode of current session : 3
B : Name of database : cat (Index parameters: cat.api)
D : Database pathname (includes terminating slash) : c:\allegro\demo2\
E : Name of current export file : settings.txt
G : Name of .log file (if not equal to default = c:\allegro\demo2\cat.log) : c:\allegro\demo2\cat.log
H : List of index headlines (from | lines in the index parameters) :
|1=Namen von Personen (Sonderabteilungen D Diss., ...
I : Index list : Symbolic index names (I lines in the index parameters) :
ALL e1=ALL-Wortregister^TPER 1=Personennamen^TTIT 3=... [^T = Code 20]
K : Name of Configuration file : a.cfg
K1: first letter thereof : a
Kk: Values k and t from CFG : 2/4
M : Environment Variable TEMP (User has write access there!) : \*.ald
N : Number of data file (cat_N.ald) for new records : 1
P : Name of program directory (with terminating slash) :
R : Restrictions : Symbolic names (R lines in index param) :
(Separataor code for I and R is 20 ( ^T ))
S : Short title headline (from line |<=... of index parameters) :
Titel ·Verf. ·Jahr·Signatur
T : Title of database (from line |a=... of index parameters) :
U : Name of current result set : U
Z : Value of the internal number variable iZ : 1 (capital Z)
Z2: the same, rounded to 2 decimal places (k=0...9) : 1.00
b : Width of field tags , Position of field text : 2,4
cn: Date of Input of current record : #99n
ce: Date of latest edit in current record : #99e
cg: Field for automatic IdNumber : #00
ci: structure of the id nr: 9a?5
cf: Number of filler characters for every new record : 0
cN: Line N of UIF file, e.g., c175 : "TBL gesperrt - Speichern gelang nicht"
cl: Command line that started this run of acon : acon -jal
ca: Workstore dimensions in bytes/Max.size//Number of fields/Max nr. : 255/42000//12,250
cr: Background dimensions in bytes/Max.size//Number of fields/Max nr. : 58/128000//1,1000
cp: Phrase store size/Max size//Number of phrases,max number : 539/18000//63,1200
cs: Internal Coding (D = DOS, W = Win, U = UTF-8) : D
e : Name of current export parameters (DOS: Option -e) : e-w
i : internal nr. of curr. record : 960 (in : intern. numbers of curr. result set)
j : Number of .ald file of curr. record : 1
k0: Number of fields of current record : 12
k_1: First field of curr. record : 00 823056
k_2: Next field : 20 Optoelektronik : Bauelemente der Halbleiter-Optoelektronik
k_2: Next field : 30aelk
k_3: Last field : 99n20140814/07:27:23oddd
l : Size ('length') of curr. result set : 4
m : Name of program and version (z.B. acon ac-w v34.5) : ac-w v35.3
n : new line, equivalent: 13 10 :
p : Primary key of curr. record : |9823056
pid : PID of curr. program (acon): 808
r : relative Number of current rec in curr. result set : 1
s : Short line of curr. record (from .STL file) :
t : Total number of records in database : 963
u : Date and time : 20140821/13:14:30
z : Value of the internal counter : 1001