Vous êtes sur la page 1sur 148

By Vladimir Kazimirchik

2011

a.k.a. KZM a.k.a. VK

j of BASE,
TAF of C

CONTENTS

CONTENTS

Contents
1 Preface

2 R11 is out

3 Standalone TAFC

4 T24 data access from standalone TAFC

11

5 Some useful programming with no more standalone TAFC

13

6 Some useless programming with standalone TAFC

17

7 Tips and tricks: custom data conversion

27

8 Some performance tricks dynamic array population, substring search

36

9 More useless programming - Brainf**k interpreter

41

10 More jBC programming stuff - program exit status

51

11 Proceeding text files - long lines support

54

12 Proceeding text files - big file splitting; usage of external tools

58

13 Writing a program to produce a simple report

62

14 Creating report representation

66

15 More about redirection

76

16 Some more tricks with select lists; paragraphs

79

17 Easter eggs

89

18 And yet more programming tips

100

19 2Gb issue; file distribution

110

20 Some more sorting; variables typing; PRECISION

129

J of BASE, TAF of C

By KZM

CONTENTS

CONTENTS

21 Working with jBASE cache

134

22 Data retrieval without Telnet or SSH access

142

23 Conclusions

148

J of BASE, TAF of C

By KZM

1 PREFACE

Preface

ello everyone. Almost one year passed since Ive released my first book called This
::::: is how Globus works (link to it is at the section Conclusions). As time passed, I
realized that it needs the continuation. So here it is.
As far as you know, Temenos believes that T24 programming is for TAM department
only and technical consultants in Temenos shouldnt program at all. But what if you are
supporting T24, extracting data from it or doing some other kind of T24-related technical
work? Surely you need to program. Not necessarily in jBC. (You see, I no more call it Basic.)
So were going to dive into jBASE and jBC just below T24 level, see what we can do
there and what we cant, use some tricks (with all the responsibility of someone who isnt
going to ruin the things above). Well try to use different methods of solving an issue. Well
see here that we often dont need to write a T24 subroutine to achieve our goals.
As usual, all opinions are those of the author and all trademarks are what they usually
are. As usual the risk is yours so please train on a local PC or even better inside a
virtual machine.
As usual, I digress a lot but thats deliberate.
To MV gurus: many things in this book might look absolutely evident to you (and
therefore not worth mentioning at all) being at the same time kind of revelation for a T24
person.

J of BASE, TAF of C

By KZM

2 R11 IS OUT

R11 is out

m not going to cover all new cool features of R11; rather than that Id like to inform
:::: you that from technical point of view all looks very familiar. We still have OFS, we still
have jBC, jQL and jCL. This makes life of T24 supporter/reporter/whoever-we-are way too
easier.
From the previous book you already know how to install T24. The difference is that there
might be some new dependencies that are caused by usage (under Windows 32 platform) of
MS VC 2010 (which in turn might require OS upgrade) but some parts still require MS VC
2008. I hope that a tool like Dependency walker helped you to sort out the problems and
now you are ready to start at least jBASE... sorry, TAFC... at least in standalone mode. (It
looks that I tend to call it jBASE in one cases and TAFC in others dont pay any attention
to that please.)
First thing which Id like to do to T24 is to make a little fun. If youre ready to start
T24, create a text file called GLOBUS.BAN. This file should contain exactly 16 lines of text.
Which text? anything you like. Put this file to bnk.run directory and start T24. Whats
there instead of familiar world ASCII art? Correct, you see contents of your GLOBUS.BAN
file. Heres my startup screen:
T24 custom startup screen
GLOBUS Rev. 201015

SIGN.ON

Copyright (c) Temenos Systems Ltd 2011

-----------------------------------------------------------------------------|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-----------------------------------------------------------------------------13 JUN 2011 19:44:03 USER
[8817,IN]
ACTION
PLEASE ENTER YOUR SIGN ON NAME

J of BASE, TAF of C

By KZM

2 R11 IS OUT

You probably noticed that Globus revision is 201015... Ill use that one because all
dependencies of R11 left me no choice.

J of BASE, TAF of C

By KZM

3 STANDALONE TAFC

Standalone TAFC

ack to what I called a standalone mode. Now when TAFC is installed, we can create
:::: an empty directory as a sandbox to play around.
The following cmd file will serve as an entry point. Its no need to connect to localhost
via telnet we can just run that cmd file. Lets create a directory, say, sa-tafc at the root
of drive C and start creating this cmd file in it.
Whether were going to start it typing its name or double-clicking a shortcut, we place
ourselves into our chosen directory:
jrunSH.cmd - the beginning
1
2
3

@echo off
C:
cd \sa-tafc

Then (see lines 6 and 7 at the screen below) you need to specify where your installed
TAFC is located and where C compiler files are available. COMPILER HOME is not a standard
variable well use it later to save typing:
jrunSH.cmd - continued
4
5
6
7
8

set
set
set
set

HOME=C:\sa-tafc
TAFC_HOME=<path_to_TAFC>
COMPILER_HOME=<path_to_MS_VC_compiler>
JBCGLOBALDIR=%TAFC_HOME%

Note that wherever you see something like <path to TAFC> in cmd files or like, you
need to replace it to the real path that corresponds to your system settings and/or layout.
Below are certain settings that are mostly known to you already. Please note that directory for saved lists should in fact have the name &SAVEDLISTS&. In my previous book I
recommended to rename it to, say, SAVEDLISTS to avoid using special characters, but if you
dont want to do it here you are:
jrunSH.cmd - continued
9
10
11
12
13
14
15
16
17
18
19

set
set
set
set
set
set
set
set
set
set

JBCLISTFILE=%HOME%\^&SAVEDLISTS^&
JEDIFILENAME_MD=VOC
JEDIFILENAME_SYSTEM=%JBCGLOBALDIR%\src\SYSTEM
JBCEMULATE=prime
JBASE_ERRMSG_DIVIDE_BY_ZERO=19
JBASE_ERRMSG_ZERO_USED=35
JBASE_ERRMSG_NON_NUMERIC=19
JBC_CORE_DUMP=1
JEDIFILEPATH=%HOME%
JBCBASETMP=%HOME%\tmp_workfile

J of BASE, TAF of C

By KZM

3 STANDALONE TAFC

Path is deliberately set not to derive anything from Windows user setup; include and
lib (as well as the first item in the path) are for the compiler:
jrunSH.cmd - continued
20
21
22
23

set PATH=%COMPILER_HOME%\bin;%TAFC_HOME%\bin;C:\windows\system32
set INCLUDE=%COMPILER_HOME%\include;%INCLUDE%
set LIB=%COMPILER_HOME%\lib;%LIB%

24

Regional settings, codepage etc:


jrunSH.cmd - continued
25
26
27
28

set
set
set
set

JBASE_I18N=1
JBASE_CODEPAGE=utf8
JBASE_LOCALE=en_US
JBASE_TIMEZONE=Europe/London

By the way, JBASE CODEPAGE sets only how characters are displayed, so if you have any
non-ASCII characters, you might prefer to set it to be the same as your local code page.
Spooler here (dont forget to create that directory, as well as &SAVEDLISTS&):
jrunSH.cmd - continued
29
30
31

set JBCSPOOLERDIR=%HOME%\jspooler
set JBASE_IPC_LOCAL=1

32

What about JBASE IPC LOCAL? A colleague of mine supplied me with the following explanation:

JBASE IPC LOCAL=1 allows to launch TAFC without administrative rights


under Windows 2003/2008 and Windows 7. TAFC creates an OS
shared memory segment for its internal use; under Windows 7, for
example, its necessary to have administrative rights to create such
segment. Once its being created, the TAFC usage becomes possible.
For example, after system start its enough to launch jsh as administrator user and thats it (until reboot of course). Alternatively, if
jBASE Agent is started as a Windows service, it will create that segment upon first client connect.
J of BASE, TAF of C

By KZM

3 STANDALONE TAFC

Finally:
jrunSH.cmd - finished
33
34

set TERM=ntcon
jsh

Now run it. If you see jsh prompt here we are. Type jdiag and look at the output.
You can ignore the following warnings:
jdiag
WARNING:
WARNING:
...
WARNING:
...
WARNING:
...
WARNING:
WARNING:
...
WARNING:
...
WARNING:

JBCDATADIR is not set, Default ...


JBCDATADIR is subdirectory of JBCGLOBALDIR
Cannot access MD file VOC, error 2
Cannot access Executable path C:\sa-tafc\bin, error 2
Cannot access Object path C:\sa-tafc\lib, error 2
From checking the registry, It appears that VC++ is not loaded
Cannot access Program dir C:\sa-tafc\bin, error 2
Cannot access Subroutine dir C:\sa-tafc\lib, error 2

Have you noticed the annoying sound from PC speaker accompanying, for example,
Backspace key pressed when theres nothing yet entered at jsh prompt? If no, probably
your speaker is suppressed somehow and you can skip several following paragraphs.
Those who heard that really annoying sound (and if you, as well as myself, want to get
rid of it) do the following: go to TAFC home directory, then to its subdirectory src and
find there file jbase nt.ti.
Then edit it. Comment bell setting:
jbase nt.ti - before
ntcon|dumb|ansi|NT console emulation,
am, xon,
cols#80, lines#24,
bel=^G, blink=\E[5m, bold=\E[1m, cbt=\E[Z,
jbase nt.ti - after
ntcon|dumb|ansi|NT console emulation,
am, xon,
cols#80, lines#24,
#
bel=^G,
blink=\E[5m, bold=\E[1m, cbt=\E[Z,

J of BASE, TAF of C

By KZM

3 STANDALONE TAFC

Have you noticed the setting set TERM=ntcon in jrunSH.cmd earlier? We just edited
the section where ntcon is mentioned and it should match environment variable TERM for
the whole thing to work.
But thats not all. We need to produce new terminal definition file. To do that you need
to run jtic utility (tic for Unix) with one parameter name of your terminal definition file
jbase nt.ti including the full path.

J of BASE, TAF of C

10

By KZM

4 T24 DATA ACCESS FROM STANDALONE TAFC

T24 data access from standalone TAFC

ow lets see if were able to access T24 data from here. To do that some changes
:::::: in jrunSH.cmd are necessary. We can access T24 files using at least three different
methods:
Environment variable JEDIFILEPATH showing where files are stored. Unfortunately, in
directory bnk.data theres a lot of subdirectories that contain data files and well have to
mention all these directories individually. In addition, names of physical files are not the
same as their T24 names and the rules change from time to time data file for application
SPF, for example, had been F.SPF and now its F SPF. Same goes for dictionary files.
Use full path to data file (not forgetting DICT file as well). Why would we need to
specify DICT file location? Because its located not in the same directory as DATA file...
seems too complicated.
Use records in VOC file. So environment variable JEDIFILENAME MD should be pointing
to VOC file in bnk.run directory. Having changed this variable, run jrunSH.cmd and try to
list some file:
T24 data access - step 1
jsh ~ --> LIST F.SPF
No file name could be found for your query

Why? Check VOC record (use full path to VOC since notation like %JEDIFILENAME MD%
wouldnt work:
T24 data access - step 2
jsh ~ --> CT <path_to_bnk.run>\VOC F.SPF
F.SPF
001 F
002 ../bnk.data/eb/F_SPF
003 ../bnk.dict/F_SPF]D

Yes, we have relative path here both for data and dictionary files. So before starting
jsh we need to move to bnk.run directory and for convenience sake afterwards to return
back. We dont need the full path to specify environment variable JEDIFILENAME MD, therefore the only changes to our new file jrunT24.cmd which we create by copying jrunSH.cmd
are commands at lines 34 and 36:
jrunT24.cmd - the very end
33
34
35
36

set TERM=ntcon
cd <path_to_bnk.run>
jsh
cd %HOME%

J of BASE, TAF of C

11

By KZM

4 T24 DATA ACCESS FROM STANDALONE TAFC

Finally were able to see SPF data:


T24 data access - OK now
C:\sa-tafc> jrunT24.cmd
jsh ~ --> LIST F.SPF
@ID..................
@ID..................
SYSTEM.SPEC..........
RUN.DATE.............
SITE.NAME............
OP.MODE..............
OP.CONSOLE...........
MAIN.ACCOUNT.........
...

SYSTEM
SYSTEM
SYSTEM
20100712
Model Bank
O
../bnk.data

To be able to log in to T24 we also need the settings in lines 35-38 (T24 HOME variable is
not standard and was invented to make things easier):
The end of jrunT24.cmd - final version (for time being of course)
32
33

set TERM=ntcon

34
35
36
37
38

set
set
set
set

T24_HOME=<path_to_bnk.run>
JBASE_PATH=%T24_HOME%\t24bin
JBCOBJECTLIST=%T24_HOME%\lib;%T24_HOME%\t24lib
JBASE_JBCOBJECTLIST_DIR=1

39
40
41
42

cd %T24_HOME%
jsh
cd %HOME%

Now we are able to log in to T24 using the minimum set of environment variables.
Start jrunT24.cmd. Have you noticed that it loads way too slower than jrunSH.cmd? I
dont know the reason but probably its because all executables in t24bin and libraries in
t24lib are distributed to many subdirectories according to T24 updates approach (have
you noticed additional settings like JBASE PATH and JBASE JBCOBJECTLIST DIR which probably take care of that setup?)... I might be wrong here, but forget it for time being.
Why weve retained jrunSH.cmd? Well need it for standalone TAFC (where we dont
need T24 access and therefore many settings). Again, it starts faster. In addition, in it we also
can get rid of the following variables that are not necessary in this mode: JEDIFILENAME MD
and JEDIFILEPATH.
So our principle is to keep only what we need with full understanding
of what is what.
J of BASE, TAF of C

12

By KZM

5 SOME USEFUL PROGRAMMING WITH NO MORE STANDALONE TAFC

Some useful programming with no more standalone


TAFC

ets write a small program accessing T24 data. Well try a slightly different way of
:::: writing and compiling programs. As I said earlier, to be able to access T24 data we
dont necessarily need a T24 subroutine a program might be sufficient. What are advantages
of a program? Here are some:
You dont need PGM.FILE record for it.
You dont need to log in to T24 to run it (though if your program wants some global
T24 variables, you have to log in and log out before running it).
You can deploy a program in a new environment just copying the executable (provided
that OS and jBASE versions are the same).
Well keep T24 area as clean as we can without even putting any files there. The name
of our program will be... surprisingly... prog1.
Just to check if all is OK with settings for compiling and runtime, firstly we put there
just a Hello-world-like output. File is to be named prog1.b (and here is the difference
from what we did before .b is an extension rather than part of file name like AB.CD.E.F
were try to follow the pure jBASE as much as we can):
prog1 - initial version
1

PROGRAM PROG1

2
3

CRT HELLO

4
5
6

STOP
END

To compile it we dont use BASIC and CATALOG as before it can be done in one step:
prog1 - compilation
jsh ~ --> jcompile prog1.b
prog1.c
mt -nologo -manifest prog1.dll.manifest -outputresource:prog1.dll;2 failed ,
command returned a code of -1
mt -nologo -manifest prog1.exe.manifest -outputresource:prog1.exe;1 failed ,
command returned a code of -1

We dont pay attention to failed messages despite them our program was compiled:
prog1 - compilation results
Directory of C:\sa-tafc
14.06.2011

20:27

J of BASE, TAF of C

<DIR>

13

By KZM

5 SOME USEFUL PROGRAMMING WITH NO MORE STANDALONE TAFC

14.06.2011
13.06.2011
14.06.2011
14.06.2011
13.06.2011
14.06.2011
14.06.2011
14.06.2011
14.06.2011
14.06.2011

20:27
20:24
19:51
19:10
22:42
19:43
20:27
20:27
20:27
20:27

<DIR>
<DIR>
834
1.031
<DIR>
49
4.608
4.608
1.680
401.408

..
&SAVEDLISTS&
jrunSH.cmd
jrunT24.cmd
jspooler
prog1.b
prog1.dll
prog1.exe
prog1.obj
tmp_workfile

Files with extensions dll and obj are not necessary (so far they are not; well need dll
further on its described in one of the next chapters), so right now lets make the process
of compilation more smart:
prog1 - more clean compilation
jsh ~ --> jcompile prog1.b && del prog1.obj prog1.dll

Oops... doesnt work under jsh. Actually, do we need jsh at all?


Id like you to read jsh-related information at jBASE knowledgebase the necessity of it
is described there quite well. For example, it allows to avoid problems when, say, in SELECT
statement there are symbols that are considered special in the underlying OS (like & try
to run jrunSH.cmd, then type cmd and then SELECT &SAVEDLISTS& to see that it wouldnt
work).
But do we need jsh in this particular case?
jrunComp.cmd and replace the last line with:

No.

Thus lets copy jrunSH.cmd to

jrunComp.cmd - difference from jrunSH.cmd


33
34

set TERM=ntcon
jcompile %1.b && del %1.dll %1.obj

Then we can start jrunComp.cmd with the parameter - source code file name (without
extension):
jrunComp.cmd run
C:\sa-tafc> jrunComp.cmd prog1
prog1.c
mt -nologo -manifest prog1.dll.manifest -outputresource:prog1.dll;2 failed ,
command returned a code of -1
mt -nologo -manifest prog1.exe.manifest -outputresource:prog1.exe;1 failed ,
command returned a code of -1

J of BASE, TAF of C

14

By KZM

5 SOME USEFUL PROGRAMMING WITH NO MORE STANDALONE TAFC

No unnecessary files after that. But can we run the resulting prog1.exe just under
Windows shell? If you try to do that, youll get the error message about not being able
to find libTAFCfrmwrk.dll. Yes, it can not be run by itself it needs the appropriate
environment to present.
OK, lets create it. Just to output something to the screen its enough to have the
environment of jrunSH.cmd but we will also need T24 access for other programs. To test it
well add some logic into the source code so it now looks like:
prog1 - final version
1

PROGRAM PROG1

CRT HELLO

3
4

OPEN F.COMPANY TO F.CMP ELSE


CRT NOT ABLE TO OPEN FILE
STOP
END

5
6
7
8
9

SELECT F.CMP TO V.SEL

10
11

LOOP
WHILE READNEXT V.KEY FROM V.SEL DO
READ R.CMP FROM F.CMP, V.KEY ELSE
CRT NOT ABLE TO READ FILE
STOP
END

12
13
14
15
16
17
18

CRT V.KEY : >>> : R.CMP

19
20

REPEAT

21
22

STOP

23
24

END

25

Now compile the program again, add C:\sa-tafc to the end of our environment variable PATH in jrunT24.cmd, start the latter and type prog1. If you see the raw contents of
COMPANY application on your screen, it works.
Note the absence of $INSERT I COMMON ... $INSERT I EQUATE statements which are
not necessary in this case.

J of BASE, TAF of C

15

By KZM

5 SOME USEFUL PROGRAMMING WITH NO MORE STANDALONE TAFC

Thus weve created jBC program which is able to access T24 data but
fully resides outside T24 environment, which is good, for example, for
reporting purposes especially if there are several environments to
proceed.

J of BASE, TAF of C

16

By KZM

6 SOME USELESS PROGRAMMING WITH STANDALONE TAFC

Some useless programming with standalone TAFC

ou might ask whats the idea of writing a useless program? Well, useless for T24
::::: but useful for some other purpose just to prove that jBC is capable of many things
other than access T24 files. Another reason just for fun. In my previous book there was the
Fifteen puzzle written in jBC. So to have a break from serious matters I present another
game written in jBC so-called snake.
Ive written it quite long ago but recently found some time to improve it now it can be
played in full screen mode it means that if you set the number of rows and columns in
window where you run it to something more than 24x80 it will use all available space for a
play field.
Lets see the source. Im quoting myself stating that a good comment at the top of every
program wouldnt harm anyone:
1
2
3
4
5
6

Snake game - the beginning


* PROGRAM SNAKE3
* BY V.KAZIMIRCHIK (KZM), 03/2011 based on SNAKE2 (12/2007)
* Feel free to distribute provided that you fully retain this header.
* Author bears no responsibility if you run it during work hours :-))
* ENJOY!
*_______________________________________________________________________________

Note that program clause at the top is also commented all works even without it.
Lets inform the user about options one of which is not to play at all:
Snake game - continued
7
8
9
10
11
12

CRT USE ARROW KEYS TO TURN SNAKE, + TO INCREASE LEVEL, P FOR PAUSE,\
Q FOR QUIT
CRT CONTINUE (Y/N) :
INPUT V.CONT
IF UPCASE(V.CONT) NE Y THEN STOP

Here goes the thing that is also new on a big screen (say, 70x120 instead of standard
24x80) it takes the snake much more time to get to the target and it soon becomes quite
boring. So I added this option for user to adapt the speed to the screen size and, of course,
performance of the computer:
Snake game - continued
13
14
15
16

CRT ENTER SPEED CORRECTION (DEFAULT=1, THE MORE THE FASTER) :


INPUT V.SPD
IF NOT(V.SPD) THEN V.SPD = 1

17
18

GOSUB INIT

J of BASE, TAF of C

17

By KZM

6 SOME USELESS PROGRAMMING WITH STANDALONE TAFC

Not we dive into init section. First of all, define speeds for difficulty levels, clear screen
and prepare the playfield:
256
257

Snake game - INIT section


*____________________________________________________
INIT:

258
259

V.LEVEL.L = 200/V.SPD :@FM: 150/V.SPD :@FM: 100/V.SPD :@FM: 50/V.SPD

260
261

ECHO OFF

262
263
264
265
266
267
268
269

CRT @(-1)
V.GAMEOVER =
V.LEFT.BORDER = 0
V.RIGHT.BORDER = SYSTEM(2) - 1
V.TOP.BORDER = 0
V.BOTTOM.BORDER = SYSTEM(3) - 3
V.GROW =

270
271
272

V.SIZE.X = V.RIGHT.BORDER-V.LEFT.BORDER-1
V.SIZE.Y = V.BOTTOM.BORDER-V.TOP.BORDER-1

273
274
275

DIM V.FIELD(V.SIZE.X, V.SIZE.Y)


MAT V.FIELD =

;* playfield array

276

system(2) and system(3) give us the screen size. Now its time for other actions:
277
278
279

Snake game - INIT section continued


V.LEVEL = 1
V.SCORE = 0
GOSUB UPDATE.SCORE

update.score is as you might guess used to output the current score:


219
220

Snake game - UPDATE.SCORE section


*____________________________________________________
UPDATE.SCORE:

221
222

CRT @(10, V.BOTTOM.BORDER + 1):LEVEL: : V.LEVEL : SCORE: : V.SCORE:

223
224

RETURN

225

Back to init. We store coordinates of snake body in a dynamic array (to allow the snake
to grow).
J of BASE, TAF of C

18

By KZM

6 SOME USELESS PROGRAMMING WITH STANDALONE TAFC

Snake game - INIT section finished


280

V.Y = 1

281
282

V.SNAKE =

283

;* array of snake body coordinates, tail goes first

284

V.LEN = 5
;* will later increase it
FOR V.I = 1 TO V.LEN
V.X = V.I
V.SNAKE<-1> = V.X :@VM: V.Y

285
286
287
288
289

V.FIELD(V.X,V.Y) = X
GOSUB DRAW.CELL
NEXT V.I

290
291
292
293

GOSUB DRAW.BORDER
GOSUB CREATE.GOAL

294
295
296

V.DIRECT = 1

297

;* right, 2 is down, 3 is left, 4 is up

298

RETURN

299
300
301

END

302

Thats the end of init section and at the same time the very bottom of our source code.
We also need to take a look at sections draw.cell, draw.border and create.goal which
are mentioned in the code above.
draw.cell draws a symbol X as a part of snake body or clears it with a space so our
snake is able to move:
120
121

Snake game - DRAW.CELL section


*____________________________________________________
DRAW.CELL:

122
123
124

CRT @(V.LEFT.BORDER+V.X, V.TOP.BORDER+V.Y) \


:V.FIELD(V.X,V.Y):@(35, V.BOTTOM.BORDER)

125
126

RETURN

127

@(35, V.BOTTOM.BORDER) is used to get rid of cursor appearing near the snake with
some terminal emulators.
draw.border is quite obvious:
104
105

Snake game - DRAW.BORDER section


*____________________________________________________
DRAW.BORDER:

J of BASE, TAF of C

19

By KZM

6 SOME USELESS PROGRAMMING WITH STANDALONE TAFC

106
107
108
109

CRT @(V.LEFT.BORDER+V.X, V.TOP.BORDER+V.Y):V.FIELD(V.X,V.Y):


CRT @(V.LEFT.BORDER, V.TOP.BORDER): + : STR(-, V.SIZE.X) :+
CRT @(V.LEFT.BORDER, V.BOTTOM.BORDER): + : STR(-, V.SIZE.X) :+

110
111
112
113
114
115
116

V.1 = V.TOP.BORDER + 1
V.2 = V.BOTTOM.BORDER - 1
FOR V.I = V.1 TO V.2
CRT @(V.LEFT.BORDER,V.I):|:
CRT @(V.RIGHT.BORDER,V.I):|:
NEXT V.I

117
118

RETURN

119

create.goal creates a new goal to pick up; that goal is represented by $ character.
We use random number generator with two cautions not to create it too close to the border
for junior levels and not to create it inside the body of the snake. Uses draw.cell to
draw the target:
226
227

Snake game - CREATE.GOAL section


*____________________________________________________
CREATE.GOAL:

228
229
230

V.SAVE.X = V.X
V.SAVE.Y = V.Y

231
232
233
234

LOOP
V.X = RND(V.SIZE.X)+1
V.Y = RND(V.SIZE.Y)+1

235
236
237
238
239
240
241

IF V.LEVEL LT 3 THEN ;* make it easier for beginners


IF V.X LT 3 THEN V.X = 3
IF V.X GT V.SIZE.X - 2 THEN V.X = V.SIZE.X - 2
IF V.Y EQ 1 THEN V.Y = 2
IF V.Y EQ V.SIZE.Y THEN V.Y -= 1
END

242
243
244
245

FIND V.X :@VM: V.Y IN V.SNAKE SETTING V.DUMMY ELSE BREAK ;* see if the
;* created goal is inside the snake
REPEAT

246
247
248
249

V.FIELD(V.X,V.Y) = $
GOSUB DRAW.CELL

250
251

V.X = V.SAVE.X

J of BASE, TAF of C

20

By KZM

6 SOME USELESS PROGRAMMING WITH STANDALONE TAFC

252

V.Y = V.SAVE.Y

253
254

RETURN

255

The code isnt optimal; besides that there is further improvement that I might think
about: to have several goals at the same time will be much better for big screen size. You
can try it yourself as an exercise if you like.
Back to the top. Weve just finished the initiation of all that is necessary and drew the
screen. Whats next?
The beginning and the end of the main program loop are shown here:
Snake game - main section continued
19
20

LOOP

21

Snake game - main section at its end


88
89

REPEAT

90
91
92

ECHO ON
STOP

93

Now see whats in between. We have to apply the technique that you will never use in
T24 - how to make the snake move while polling the keyboard to see if user pressed some
key. Firstly we need a keyboard polling loop inside our main loop:
Snake game - main section - polling loop
22
23
24

LOOP
V.BUFF = SYSTEM(14)
IF V.BUFF NE 0 THEN BREAK

25
26
27
28
29
30
31

IF V.DIRECT EQ 1 OR V.DIRECT EQ 3 THEN


MSLEEP V.LEVEL.L<V.LEVEL> / 1.5 ;* horisontal movement is to be
;* increased due to characters shape
END ELSE
MSLEEP V.LEVEL.L<V.LEVEL>
END

32
33
34
35
36

GOSUB MOVEIT
IF V.GAMEOVER THEN
CRT @(45, V.BOTTOM.BORDER + 1): GAME OVER...
ECHO ON

37

J of BASE, TAF of C

21

By KZM

6 SOME USELESS PROGRAMMING WITH STANDALONE TAFC

STOP

38
39

END

40
41

REPEAT

system(14) lets us know if theres anything in keyboard input buffer; in case theres
nothing we proceed with snake movement after some delay. The delay is calculated to adjust
the game speed to difficulty level and speed multiplier entered at the very start. Now we
move the snake (hope that comments are explaining the process quite clearly; here we also
check if weve scored and therefore snake shall grow, we check also if snake hit a border or
itself which means that the game is over):
128
129

Snake game - MOVEIT section


*____________________________________________________
MOVEIT:

130
131

* remove tail (except the case when snake grows)

132
133
134
135
136
137
138
139
140
141
142

IF V.GROW THEN
V.GROW =
V.LEN +=1
END ELSE
V.X = V.SNAKE<1,1>
V.Y = V.SNAKE<1,2>
V.FIELD(V.X,V.Y) =
GOSUB DRAW.CELL
DEL V.SNAKE<1>
END

143
144
145
146

* current head coordinates


V.X = V.SNAKE<V.LEN-1,1>
V.Y = V.SNAKE<V.LEN-1,2>

147
148

* lets see in which direction we go

149
150

BEGIN CASE

151
152

CASE V.DIRECT EQ 1

153
154
155
156
157
158

V.X += 1
IF V.X GT V.SIZE.X THEN
V.GAMEOVER = Y
RETURN
END

159
160

CASE V.DIRECT EQ 2

161

J of BASE, TAF of C

22

By KZM

6 SOME USELESS PROGRAMMING WITH STANDALONE TAFC

162
163
164
165
166

V.Y += 1
IF V.Y GT V.SIZE.Y THEN
V.GAMEOVER = Y
RETURN
END

167
168

CASE V.DIRECT EQ 3

169
170
171
172
173
174

V.X -= 1
IF V.X LT 1 THEN
V.GAMEOVER = Y
RETURN
END

175
176

CASE V.DIRECT EQ 4

177
178
179
180
181
182

V.Y -= 1
IF V.Y LT 1 THEN
V.GAMEOVER = Y
RETURN
END

183
184

END CASE

185
186

* see if we hit ourself

187
188
189
190
191

IF V.FIELD(V.X,V.Y) EQ X THEN
V.GAMEOVER = Y
RETURN
END

192
193

* see if we scored

194
195
196

IF V.FIELD(V.X,V.Y) EQ $ THEN
V.SCORE += V.LEVEL

197
198
199
200
201
202
203
204
205

BEGIN CASE
CASE V.SCORE
V.LEVEL =
CASE V.SCORE
V.LEVEL =
CASE V.SCORE
V.LEVEL =
END CASE

GE 100 AND V.LEVEL LT 4


4
GE 50 AND V.LEVEL LT 3
3
GE 10 AND V.LEVEL LT 2
2

206
207
208
209

GOSUB UPDATE.SCORE
GOSUB CREATE.GOAL
V.GROW = Y

J of BASE, TAF of C

;* after dinner snake grows longer...

23

By KZM

6 SOME USELESS PROGRAMMING WITH STANDALONE TAFC

210

END

211
212
213

V.FIELD(V.X,V.Y) = X
GOSUB DRAW.CELL

214
215

V.SNAKE<-1> = V.X :@VM: V.Y

216
217

RETURN

218

If a key was pressed - proceed those that we care about:


Snake game - main section - key processing
42
43

V.KEY = KEYIN()

44
45

BEGIN CASE

46
47

CASE V.KEY EQ CHAR(27)

;* Esc

V.KEY.2 = KEYIN()
V.KEY.3 = KEYIN()

;* ]

48
49
50
51
52
53
54

BEGIN CASE
CASE V.KEY.3 EQ C ;* right
IF V.DIRECT NE 3 THEN V.DIRECT = 1

55

;* we dont reverse
;* the direction

56
57
58

CASE V.KEY.3 EQ B ;* down


IF V.DIRECT NE 4 THEN V.DIRECT = 2

59
60
61

CASE V.KEY.3 EQ D ;* left


IF V.DIRECT NE 1 THEN V.DIRECT = 3

62
63
64

CASE V.KEY.3 EQ A ;* up
IF V.DIRECT NE 2 THEN V.DIRECT = 4

65
66

END CASE

67
68
69
70

CASE V.KEY EQ + AND V.LEVEL LT 4


V.LEVEL ++
GOSUB UPDATE.SCORE

71
72

CASE 1

73
74

V.KEY = UPCASE(V.KEY)

75
76

BEGIN CASE

J of BASE, TAF of C

24

By KZM

6 SOME USELESS PROGRAMMING WITH STANDALONE TAFC

77

CASE V.KEY EQ P

78
79

GOSUB SET.PAUSE

80
81

CASE V.KEY EQ Q
BREAK

82
83
84

END CASE

85
86
87

END CASE

The last piece in this puzzle is how we proceed the pause requested by the user another
P resumes the game:
94
95

Snake game - SET.PAUSE section


*____________________________________________________
SET.PAUSE:

96
97
98
99
100

LOOP
V.KEY.2 = KEYIN()
IF UPCASE(V.KEY.2) EQ P THEN BREAK
REPEAT

101
102

RETURN

103

Now compile and run it - in jsh prompt of jrunSH.cmd type snake3:

J of BASE, TAF of C

25

By KZM

6 SOME USELESS PROGRAMMING WITH STANDALONE TAFC

Snake game - typical screen


+------------------------------------------------------------------------------+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
XX
|
|
X
|
|
$
X
|
|
XXXXXX
|
|
|
|
|
|
|
|
|
|
|
+------------------------------------------------------------------------------+
LEVEL: 1 SCORE: 5

Another way of running a program which allows to save typing create another cmd file
to run a program directly without jsh. Copy jrunSH.cmd to, say, jrunEXE.cmd. Replace
jsh in the last line with:
jrunEXE.cmd - the last line
34

%1 %2 %3 %4 %5 %6 %7 %8 %9

This setup allows us to run snake3.exe just by typing jrunEXE.cmd snake3.exe from
Windows shell - as well as any other jBC program with up to 8 parameters.
Feel free to amend it either add several simultaneous goals or, maybe, introduce time
limitation to hit a goal... Enjoy!

J of BASE, TAF of C

26

By KZM

7 TIPS AND TRICKS: CUSTOM DATA CONVERSION

Tips and tricks: custom data conversion

ack to serious things. You all know that there are iconv and oconv functions in jBC
:::: that have a lot of data conversion options. Some examples of that could be (we use the
eval trick described in my previous book):
OCONV sample
jsh ~ --> LIST . SAMPLE 1 EVAL "OCONV(TIME(), MTS)"
DICT .........

OCONV(TIME(), "MTS")

&SAVEDLISTS&

20:34:15

1 Records Listed

Or an example how to create a string representing the current timestamp in the format
used in standard T24 audit field date.time:
ICONV/OCONV sample in jBC
V.TD = TIMEDATE()
V.TIME = V.TD[1,2] : V.TD[4,2]
V.DATE = ICONV(V.TD[10,11], D)
V.TD = OCONV(V.DATE, DY2) : FMT(OCONV(V.DATE, DM), "R0%2")
: FMT(OCONV(V.DATE, DD), "R0%2") : V.TIME
R.OUTPUT<V.DATE.TIME> = V.TD

There is a lot of conversion codes (see jBC manual and jBASE knowledgebase). Also
there are some special codes for example, *A9999 shows record size (try the following
under jrunT24.cmd):
Another conversion code sample
C:\sa-tafc> jrunT24.cmd
jsh ~ --> LIST F.COMPANY *A9999
@ID........

*A9999........

GB0010003
SG0010001
EU0010001
GB0010002
GB0010004
GB0010001

2166
2173
2173
2174
2166
2607

6 Records Listed

J of BASE, TAF of C

27

By KZM

7 TIPS AND TRICKS: CUSTOM DATA CONVERSION

If were way too suspicious lett check if the output is correct. To do that well change the
program prog1 to output the size of all records in COMPANY application data file rather
then their contents.
prog1 - the changes
18
19
20

CRT V.KEY : >>> : R.CMP


CRT V.KEY : >>> : BYTELEN(R.CMP)

21

Why bytelen() rather that len()? Because if we have a UTF-8 environment (and we do
have it, see environment variable JBASE I18N), len() will give us the number of characters
rather than the number of bytes in a record.
So - compile, run (jrunT24.cmd again since we need T24 data access), see:
Record sizes checked
jsh ~ -->
HELLO
GB0010003
SG0010001
EU0010001
GB0010002
GB0010004
GB0010001

prog1
>>>
>>>
>>>
>>>
>>>
>>>

2166
2173
2173
2174
2166
2607

Looks like it works. What have we just done? Achieved the same goal using 2 different
methods. Which one to choose? In this case - first (a built-in) one is easier. But its not
always the same so the final choice in every particular case is yours.

Please note that in this book some examples are run under jrunT24.cmd,
some under jrunSH.cmd etc. Make sure you lanch the same cmd that
is shown in an example, otherwise it might not work.
Little more about A conversion codes also known as A-correlatives. A*n means
field n, so if you dont have the dictionary file (or too lazy to type field names) you can
try the following:
Another conversion code sample
C:\sa-tafc> jrunT24.cmd
jsh ~ --> LIST F.COMPANY *A1 *A2 *A3
@ID........

*A1...........

J of BASE, TAF of C

*A2...........

28

*A3...........

By KZM

7 TIPS AND TRICKS: CUSTOM DATA CONVERSION

GB0010003
SG0010001
EU0010001
GB0010002
GB0010004
GB0010001

R11 MF BRANCH
1
R11 LEAD SG CO
MPANY
R11 LEAD CO GE
RMANY
R11 LEAD MF CO
MPANY
R11 MF BRANCH
2
Model Bank

R11 MF BRANCH
1
R11 LEAD SG CO
MPANY
R11 LEAD CO GE
RMANY
R11 LEAD MF CO
MPANY
R11 MF BRANCH
2
18 Place De Ph
ilosophes,
CH 1205 Geneva
,
Switzerland

MF2
SG1
EU1
MF1
MF3
BNK

6 Records Listed

As we saw earlier, A*9999 is a special code, A*9998 also is:


Yet another conversion code sample
C:\sa-tafc> jrunT24.cmd
jsh ~ --> LIST F.COMPANY *A9998
@ID........

*A9998........

GB0010003
SG0010001
EU0010001
GB0010002
GB0010004
GB0010001

1
2
3
4
5
6

6 Records Listed

No, thats not record physical location number or like as I initially thought just a
sequential number in the output. Change the sequence of records with SSELECT and see:
And yet another conversion code sample
C:\sa-tafc> jrunT24.cmd
jsh ~ --> SSELECT F.COMPANY
> LIST F.COMPANY *A9998
@ID........

*A9998........

J of BASE, TAF of C

29

By KZM

7 TIPS AND TRICKS: CUSTOM DATA CONVERSION

EU0010001
GB0010001
GB0010002
GB0010003
GB0010004
SG0010001

1
2
3
4
5
6

6 Records Listed

A*n is not limited by actual number of fields (most probably executing something equivalent to var = arr<n> ), though there is a limitation (and this number is quite large
to terminate the session). To (most probably) get out-of-memory error you might try the
following:
Trying to terminate the session
C:\sa-tafc> jrunT24.cmd
jsh ~ --> LIST F.COMPANY *A99999999

Session dies and we have a nice new file in our bnk.run directory:
Core dump
C:\sa-tafc> jrunT24.cmd
jsh ~ --> LIST . LIKE ...dmp
DICT .........
TAFC_coredump-20110707213724386.dmp
1 Records Listed

Something more about solving problems in different ways. Theres an application in T24
called OFS.REQUEST.DETAIL which you most probably already know about. Lets see its
contents. Normally its something like:
OFS.REQUEST.DETAIL sample contents
C:\sa-tafc> jrunT24.cmd
jsh ~ --> LIST F.OFS.REQUEST.DETAIL
@ID............. SCRPT11062000160~I
@ID............. SCRPT11062000160~I
MESSAGE.KEY..... SCRPT11062000160~I
APPLICATION..... AA.PRD.DES.PAYMENT.SCHEDULE
VERSION.........

J of BASE, TAF of C

30

By KZM

7 TIPS AND TRICKS: CUSTOM DATA CONVERSION

FUNCTION........ I
TRANS.REFERENCE. NOTICE.ACCOUNT-EUR-20100809
USER.NAME....... SUSER1
COMPANY......... GB0010001
DATE.TIME.RECD.. 09:08:03:875 10 MAR 2011
DATE.TIME.QUEUE.
DATE.TIME.PROC.. 09:08:03:928 10 MAR 2011
STATUS.......... PROCESSED
MSG.IN.......... AA.PRD.DES.PAYMENT.SCHEDULE,/I/PROCESS/1/0/,SUSER1/******/GB001
0001////,NOTICE.ACCOUNT-EUR-20100809/SCRPT11062000160~I,DESCRIPT
ION:1:1=Savings Account,PAYMENT.TYPE:1:1=INTEREST.ONLY,PAYMENT.T
YPE:2:1=INTEREST.ONLY,PAYMENT.METHOD:1:1=CAPITALISE,PAYMENT.METH
OD:2:1=CAPITALISE,PAYMENT.FREQ:1:1=e0Y e1M e0W e0D e0F,PAYMENT.F
REQ:2:1=e0Y e1M e0W e0D e0F,PROPERTY:1:1=CRINTEREST,PROPERTY:2:1
=DRINTEREST,DUE.FREQ:1:1=e0Y e1M e0W e0D e0F,DUE.FREQ:2:1=e0Y e1
M e0W e0D e0F,DEFAULT.NEGOTIABLE:1:1=YES,
MSG.OUT......... NOTICE.ACCOUNT-EUR-20100809/SCRPT11062000160~I/1,DESCRIPTION:1:
1=Savings Account,PAYMENT.TYPE:1:1=INTEREST.ONLY,PAYMENT.TYPE:2:
1=INTEREST.ONLY,PAYMENT.METHOD:1:1=CAPITALISE,PAYMENT.METHOD:2:1
=CAPITALISE,PAYMENT.FREQ:1:1=e0Y e1M e0W e0D e0F,PAYMENT.FREQ:2:
1=e0Y e1M e0W e0D e0F,PROPERTY:1:1=CRINTEREST,PROPERTY:2:1=DRINT
EREST,DUE.FREQ:1:1=e0Y e1M e0W e0D e0F,DUE.FREQ:2:1=e0Y e1M e0W
e0D e0F,DEFAULT.NEGOTIABLE:1:1=YES,ID.COMP.1:1:1=NOTICE.ACCOUNT,
ID.COMP.2:1:1=EUR,ID.COMP.3:1:1=20100809,CURR.NO:1:1=1,INPUTTER:
1:1=4964_SEAT.USER_I_INAU_OFS_SEAT,DATE.TIME:1:1=1103100908,DATE
.TIME:2:1=1103100908,AUTHORISER:1:1=4964_SEAT.USER_OFS_SEAT,CO.C
ODE:1:1=GB0010001,DEPT.CODE:1:1=2006
ACTION..........
GTS.CONTROL..... 1
NO.OF.AUTH...... 0

As we know, in OFS messages comma is a delimiter. Imagine wed like to see each part
of the message at a different line. What can we do here? Possible choices are:
Create an ENQUIRY with some special processing using ENQUIRY field conversion
...and build a web service based on that enquiry... no, forget a web service were aiming
to low level rather than adding more and more wrappers. In general, it looks feasible but
showing a single-valued field in several lines (like a multi-valued) might be a little tricky in
ENQUIRY.
Create an I-descriptor with user routine and then display the output either in ENQUIRY or in jsh. Good idea but lett go deeper and try not to harm any animal... sorry,
I meant keep T24 intact.
Create what in jBASE (and not only here but in all multi-valued world) is called
user exit. This technique is described in details in jBASE knowledgebase; lett take an
example from there and amend it to cater our needs.

J of BASE, TAF of C

31

By KZM

7 TIPS AND TRICKS: CUSTOM DATA CONVERSION

There are two methods to create a custom conversion code (read: user exit) - create
one routine per each new code or to put everything into one routine with standard name
JBCUserConversions. In this example well use the second one.
Our chosen name for a conversion code will be XZ. So then the source of the routine
JBCUserConversions will look like:
JBCUserConversions - amended
1
2

* Amended by KZM
SUBROUTINE JBCUserConversions (result, source, code, type, error)

BEGIN CASE
CASE code EQ "XZ"
IF type THEN
result = source : " was OCONV"
result = source
CHANGE , TO @VM IN result
END ELSE
result = source : " was ICONV"
END

4
5
6
7

8
9
10
11

*
*

12
13

CASE 1
error = 1
END CASE

14
15
16
17

RETURN

18
19
20

END

21

Some code was commented by me but retained to give you the better idea of this method.
Now lets compile it (for reasons yet unknown to me I wasnt able to compile any subroutine using jcompile so heres good old BASIC and CATALOG). But firstly we need to add
some environment variables to jrunSH.cmd:
New variables in jrunSH.cmd
29
30
31

set JBCOBJECTLIST=%HOME%\lib
set JBCDEV_LIB=%HOME%\lib

32

Here goes compilation:


Compilation of JBCUserConversions
C:\sa-tafc> jrunSH.cmd
jsh ~ --> BASIC . JBCUserConversions

J of BASE, TAF of C

32

By KZM

7 TIPS AND TRICKS: CUSTOM DATA CONVERSION

JBCUserConversions
BASIC_1.c
Source file JBCUserConversions compiled successfully
jsh ~ --> CATALOG . JBCUserConversions
JBCUserConversions
Object JBCUserConversions cataloged successfully
mt -nologo -manifest C:\sa-tafc\lib\lib0.dll.manifest -outputresource:
C:\sa-tafc\lib\lib0.dll;2 failed , command returned a code of -1
Library C:\sa-tafc\lib\lib0.dll rebuild okay
jsh ~ -->

We can see that the file $JBCUserConversions appeared in sa-tafc (we can delete it),
the directory sa-tafc\lib was automatically created and what matters for us in it we
have the new file lib0.dll where the compiled routine is supposedly been placed; in any
case, jshow will let us know if it is so:
Check of JBCUserConversions
jsh ~ --> jshow -c JBCUserConversions
Subroutine:
C:\sa-tafc\lib\lib0.dll
jBC JBCUserConversions version 201014.0 Mon Jun 20 09:58:23 2011
jBC JBCUserConversions source file .
jsh ~ -->

As soon as were going to apply our new conversion codeXZto T24 data file, we need
our conversion subroutine to be visible for work in T24 environment. To achieve that we
need to add the directory with our lib0.dll here:
Correction of jrunT24.cmd
37

set JBCOBJECTLIST=%T24_HOME%\lib;%T24_HOME%\t24lib;%HOME%\lib

Now try to see the results. After toying a little with output width finally we can see
something more readable (the full command didnt fit here properly and of course should be
entered in one line):
Results of customization of JBCUserConversions
C:\sa-tafc> jrunT24.cmd
jsh ~ --> LIST F.OFS.REQUEST.DETAIL ID-SUPP EVAL "@ID[1,20]"
EVAL "OCONV(MSG.IN, XZ)" EVAL "OCONV(MSG.OUT, XZ)"
@ID[1,20
.
OCONV(MSG.IN, "XZ")...........

J of BASE, TAF of C

OCONV(MSG.OUT, "XZ")..........

33

By KZM

7 TIPS AND TRICKS: CUSTOM DATA CONVERSION

SCRPT11062
000160~I

AA.PRD.DES.PAYMENT.SCHEDULE
/I/PROCESS/1/0/
SUSER1/******/GB0010001////
NOTICE.ACCOUNT-EUR-20100809/SC
RPT11062000160~I
DESCRIPTION:1:1=Savings Accoun
t
PAYMENT.TYPE:1:1=INTEREST.ONLY
PAYMENT.TYPE:2:1=INTEREST.ONLY
PAYMENT.METHOD:1:1=CAPITALISE
PAYMENT.METHOD:2:1=CAPITALISE
PAYMENT.FREQ:1:1=e0Y e1M e0W e
0D e0F
PAYMENT.FREQ:2:1=e0Y e1M e0W e
0D e0F
PROPERTY:1:1=CRINTEREST
PROPERTY:2:1=DRINTEREST
DUE.FREQ:1:1=e0Y e1M e0W e0D e
0F
DUE.FREQ:2:1=e0Y e1M e0W e0D e
0F
DEFAULT.NEGOTIABLE:1:1=YES

NOTICE.ACCOUNT-EUR-20100809/SC
RPT11062000160~I/1
DESCRIPTION:1:1=Savings Accoun
t
PAYMENT.TYPE:1:1=INTEREST.ONLY
PAYMENT.TYPE:2:1=INTEREST.ONLY
PAYMENT.METHOD:1:1=CAPITALISE
PAYMENT.METHOD:2:1=CAPITALISE
PAYMENT.FREQ:1:1=e0Y e1M e0W e
0D e0F
PAYMENT.FREQ:2:1=e0Y e1M e0W e
0D e0F
PROPERTY:1:1=CRINTEREST
PROPERTY:2:1=DRINTEREST
DUE.FREQ:1:1=e0Y e1M e0W e0D e
0F
DUE.FREQ:2:1=e0Y e1M e0W e0D e
0F
DEFAULT.NEGOTIABLE:1:1=YES
ID.COMP.1:1:1=NOTICE.ACCOUNT
ID.COMP.2:1:1=EUR
ID.COMP.3:1:1=20100809
CURR.NO:1:1=1
INPUTTER:1:1=4964_SEAT.USER_I_
INAU_OFS_SEAT
DATE.TIME:1:1=1103100908
DATE.TIME:2:1=1103100908
AUTHORISER:1:1=4964_SEAT.USER_
OFS_SEAT
CO.CODE:1:1=GB0010001
DEPT.CODE:1:1=2006

Note ID-SUPP keyword suppressing the output of @id since default display parameter
for it is too big to fit all we need into screen width.
If we hadnt included our lib directory to JBCOBJECTLIST, we would have got the following
error:
Error message
Error in named attribute XZ
Unknown conversion code.

J of BASE, TAF of C

34

By KZM

7 TIPS AND TRICKS: CUSTOM DATA CONVERSION

This conversion code now also can be used in ENQUIRY field conversion. Of course
to make your custom conversion code generally available you need the compiled subroutine
JBCUserConversions to be visible for all T24 users, whether its Classic or Browser (in latter
case its environment.vars or whichever file used in jBoss or TCServer is to be amended).
The template for an individual routine for each custom conversion you can find going to
TAFC home directory, then to subdirectory src. Its named jBASE UserExit.

J of BASE, TAF of C

35

By KZM

8 SOME PERFORMANCE TRICKS DYNAMIC ARRAY POPULATION,


SUBSTRING SEARCH

Some performance tricks dynamic array population, substring search

f course performance is the thing that is the most interesting for all of us. Lets see
::::: how a little trick can be used to gain some.
Dynamic arrays in jBC are quite handy but sometimes slow. For example, if we try to
populate a large array it might take some time. Here goes the code:
Test of dynamic array population - source of dynarr.b
1
2
3

* KZM, 2011
* Dynamic array tests
V.COUNT = 100000

V.ARR =

5
6

V.NARR = Dynamic array population


V.START = SYSTEM(12) / 1000

7
8
9

FOR V.I = 1 TO V.COUNT


V.ARR<V.I> = V.I :@VM:Value : V.I
NEXT V.I

10
11
12
13

V.END = SYSTEM(12) / 1000


V.TIME = V.END - V.START
IF V.TIME EQ 0 THEN
CRT INCREASE V.COUNT AND RUN AGAIN
ABORT
END ELSE
CRT V.NARR : , iterations/sec: : V.COUNT / V.TIME
END

14
15
16
17
18
19
20
21
22

STOP

23
24
25

END

26

Compile and run it couple of times to see if results are consistent:


Test of dynamic array population - test run
C:\sa-tafc> jrunComp.cmd dynarr
dynarr.c
mt -nologo -manifest dynarr.dll.manifest -outputresource:dynarr.dll;2 failed ,
command returned a code of -1
mt -nologo -manifest dynarr.exe.manifest -outputresource:dynarr.exe;1 failed ,
command returned a code of -1

J of BASE, TAF of C

36

By KZM

8 SOME PERFORMANCE TRICKS DYNAMIC ARRAY POPULATION,


SUBSTRING SEARCH

C:\sa-tafc> jrunEXE.cmd dynarr.exe


Dynamic array population, iterations/sec: 1009.4372
C:\sa-tafc> jrunEXE.cmd dynarr.exe
Dynamic array population, iterations/sec: 1010.7135

But what if to start array population from the end? Probably in this case memory
allocation will work differently and this will affect the performance?
The code now is:
Test of dynamic array population - changes in the source
9
10
11
12

FOR V.I = V.COUNT TO 1 STEP -1


;* reverse order
V.ARR<V.I> = V.I :@VM: Value : V.I
NEXT V.I

13

Compile and run, again couple of times:


Test of dynamic array population - second method
C:\sa-tafc> jrunComp.cmd dynarr
dynarr.c
mt -nologo -manifest dynarr.dll.manifest -outputresource:dynarr.dll;2 failed ,
command returned a code of -1
mt -nologo -manifest dynarr.exe.manifest -outputresource:dynarr.exe;1 failed ,
command returned a code of -1
C:\sa-tafc> jrunEXE.cmd dynarr.exe
Dynamic array population, iterations/sec: 2729.1384
C:\sa-tafc> jrunEXE.cmd dynarr.exe
Dynamic array population, iterations/sec: 2727.977

We dont discuss here the implications of using a big dynamic array (which isnt much
recommended by Temenos). This particular trick helped us to increase the speed of array
population. It was tested and worked the same way on 4 platforms. Will it work on your
platform and your environment? Not necessarily, its up to you to test if it helps.
The next example shows us that environment matters, and sometimes it really matters.
See below a program written by one of my good friends who also happens to be my colleague.
This program shows faster search of a substring in a string than jBC function index() does.

J of BASE, TAF of C

37

By KZM

8 SOME PERFORMANCE TRICKS DYNAMIC ARRAY POPULATION,


SUBSTRING SEARCH

asearch.b
1

DIM prefix(10000)

2
3
4

a = SPACE(1000*1000 - 1):.
CRT "LEN(a): ", LEN(a)

5
6
7

b = SPACE(10000 - 1):.
CRT "LEN(b): ", LEN(b)

8
9
10
11
12
13

msg = "INDEX"
GOSUB StartTest
position = INDEX(a, b, 1)
GOSUB EndTest
CRT "I = ":position

14
15

position = -1

16
17
18
19
20
21

msg = "KMP"
GOSUB StartTest
GOSUB kmp
GOSUB EndTest
CRT "I = ":position

22
23

STOP

24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

kmp:
position = -1
prefix(1) = 0
k = 0
b_len = LEN(b)
FOR i = 2 TO b_len
b_char = b[i, 1]
LOOP WHILE k > 0 AND b[k + 1, 1] NE b_char DO
k = prefix(k)
REPEAT
IF b[k + 1, 1] EQ b_char THEN k = k + 1
prefix(i) = k
NEXT i
k = 1
a_len = LEN(a)
FOR i = 1 TO a_len
a_char = a[i, 1]
LOOP WHILE k > 1 AND (b[k + 1, 1] NE a_char) DO
k = prefix(k)
REPEAT
IF b[k + 1, 1] EQ a_char THEN k = k + 1
IF k EQ b_len THEN
position = i - k + 1

J of BASE, TAF of C

38

By KZM

8 SOME PERFORMANCE TRICKS DYNAMIC ARRAY POPULATION,


SUBSTRING SEARCH

48
49
50
51
52

GOTO kmp_done
END
NEXT i
kmp_done:
RETURN

53
54
55
56
57
58

StartTest:
CRT FMT(msg, "T#16"):" running... ":
T1 = TIME()
S1 = SYSTEM(9)
RETURN

59
60
61
62
63
64

EndTest:
T2 = TIME()
S2 = SYSTEM(9)
CRT "Elapsed = ":OCONV(T2-T1,"MTS"):", CPU = ":S2-S1
RETURN

65

Nice! Compile, run...


Test of substring search
C:\sa-tafc> jrunComp.cmd asearch
asearch.c
mt -nologo -manifest asearch.dll.manifest -outputresource:asearch.dll;2 failed ,
command returned a code of -1
mt -nologo -manifest asearch.exe.manifest -outputresource:asearch.exe;1 failed ,
command returned a code of -1
C:\sa-tafc> jrunEXE.cmd asearch.exe
LEN(a):
1000000
LEN(b):
10000
INDEX
running... Elapsed = 00:00:06, CPU = 6922
I = 990001
KMP
running...

After quite a long time of waiting, lets see if we hit an infinite loop or something. Ctrl-C
takes us to debugger; we go there twice to see if loop counter changes as time passes:
Test of substring search - continued
KMP
running... Interrupt signal
Source changed to .\asearch.b
0042
LOOP WHILE k > 1 AND (b[k + 1, 1] NE a_char) DO
jBASE debugger->v i
i
: 147570

J of BASE, TAF of C

39

By KZM

8 SOME PERFORMANCE TRICKS DYNAMIC ARRAY POPULATION,


SUBSTRING SEARCH

jBASE debugger->c
Interrupt signal
0046
IF k EQ b_len THEN
jBASE debugger->v i
i
: 149381
jBASE debugger->c

No, its not an infinite loop... waiting...


And waiting...
Finally:
Test of substring search - finished
Elapsed = 01:25:46, CPU = 5146069
I = 990001

Wheres the pitfall? It was found out that the reason of so huge difference is the presence
of environment variable JBASE I18N in my environment. Try that:
Another test of substring search
C:\sa-tafc> jrunSH.cmd
jsh ~ --> set JBASE_I18N=
jsh ~ --> asearch.exe
LEN(a):
1000000
LEN(b):
10000
INDEX
running... Elapsed = 00:00:07, CPU = 6937
I = 990001
KMP
running... Elapsed = 00:00:02, CPU = 1438
I = 990001
jsh ~ -->

It looks that the work with utf-8 strings is much slower than with regular single-byte
character set; avoid utf-8 if of course its possible in your environment.

Bottomline: custom algorithm could be faster but it doesnt always


work.

J of BASE, TAF of C

40

By KZM

9 MORE USELESS PROGRAMMING - BRAINF**K INTERPRETER

More useless programming - Brainf**k interpreter

here is a so-called esoteric programming langugage called Brainf**k (actually, theres


::::: no asterisks, they just mask here exactly what youre thinking about). Though it consists only of 8 commands, its Turing-complete. To see what its like heres the Hello World
source code:
Brainf**k - hello world
++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++
.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.
------.--------.>+.>.

Now you got an idea why it was given its name. Theres a lot of Brainf**k interpreters
but no one written in jBC. So I thought its a good idea to write one.
We start with allocating the space for the cells which should contain 30,000 positions
(each of them containing a zero value at the beginning); also we assign a pointer position
to 1:
1
2
3
4

bf-run.b
*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* By V.Kazimirchik, 2011.
~~~~~~~~~~~~~
* Brainf**k interpreter written in jBC.
*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

5
6
7
8

DIM V.VALUES(30000)
MAT V.VALUES = 0
V.POINTER = 1

Now get the name of a file with BF source that is to be processed and read it (we can
safely read it all - we dont expect the source code of 1Gb or so):
bf-run.b - continued
9
10

V.FILE.IN = SENTENCE(1)

11
12
13
14
15

IF V.FILE.IN EQ THEN
CRT Usage: bf-run <file>
STOP
END

16
17
18
19
20

OSREAD V.SRC FROM V.FILE.IN ELSE


CRT INPUT FILE - READ ERROR
STOP
END

21

J of BASE, TAF of C

41

By KZM

9 MORE USELESS PROGRAMMING - BRAINF**K INTERPRETER

22

V.VOLUME = LEN(V.SRC)

23

Left and right brackets are loop boundaries in BF (and loops can be nested). Here goes
the comment that I used to fetch them all for future use them while debugging this program.
Id rather leave it in the source:
bf-run.b - continued
24
25
26
27
28
29
30
31
32
33
34
35
36
37

*~~~~~~~ cache loop points


*
1 2
3
3
4
4
2
5 6
* >++++++++++>>+<+[[+++++[>++++++++<-]>.<++++++[>--------<-]+<<]>.>[->[
*
17 18
24
36
46
58 62 66 69
*
*
*
7
8
9
10
11 11
12
12/10/6 13 13 5 1
* <++>-[<++>-[<++>-[<++>-[<-------->>[-]++<-[<++>-]]]]]]<[>+<-]+>>]<<]
*
77
83
89
95
107 109 114 120
127 132 136 139
*
* For left brackets populate the arrays (position to 1st, 0 to 2nd),
*
for right - find the last 0 in 2nd array
*
and put current position there
*~~~~~~~

And heres the code which does that:


bf-run.b - continued
38
39
40
41
42

V.LEFT.BRACKETS =
V.RIGHT.BRACKETS =
V.BR.CNT = 0
V.BR.CNT.MATCH = 0

43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

FOR V.I = 1 TO V.VOLUME


V.INSTR = V.SRC[V.I,1]
IF V.INSTR EQ [ THEN
V.BR.CNT ++
V.LEFT.BRACKETS<V.BR.CNT> = V.I
V.RIGHT.BRACKETS<V.BR.CNT> = 0
END ELSE
IF V.INSTR EQ ] THEN
FOR V.J = V.BR.CNT TO 1 STEP -1
IF V.RIGHT.BRACKETS<V.J> EQ 0 THEN
V.RIGHT.BRACKETS<V.J> = V.I
BREAK
END
NEXT
END

J of BASE, TAF of C

42

By KZM

9 MORE USELESS PROGRAMMING - BRAINF**K INTERPRETER

59

END

60
61

NEXT V.I

62

Now we are ready to start the main loop. Move along BF source code until its end,
skipping everything that isnt a BF command:
bf-run.b - continued
63

*~~~~~~~~~~~~~

64
65

V.CNT = 0

66
67

LOOP

68
69
70

V.CNT ++
IF V.CNT GT V.VOLUME THEN STOP

71
72
73

V.INSTR = V.SRC[V.CNT,1] ;* bf instruction


IF NOT(INDEX(.,+-<>[], V.INSTR, 1)) THEN CONTINUE

74

Now processing of a command begins; > moves the pointer one position to the right
(within the span of cell space):
75
76
77
78

bf-run.b - continued
BEGIN CASE
CASE V.INSTR EQ >
V.POINTER ++
IF V.POINTER GT 30000 THEN V.POINTER = 30000

79

< moves the pointer one position to the left:


80
81
82

bf-run.b - continued
CASE V.INSTR EQ <
V.POINTER -IF V.POINTER LT 1 THEN V.POINTER = 1

83

+ shall increase the value in a cell under the pointer (unsigned 8-bit integer is the
limitation defined by language specification):
bf-run.b - continued
84
85
86

CASE V.INSTR EQ +
V.VALUES(V.POINTER) ++
IF V.VALUES(V.POINTER) EQ 256 THEN V.VALUES(V.POINTER) = 0

87

J of BASE, TAF of C

43

By KZM

9 MORE USELESS PROGRAMMING - BRAINF**K INTERPRETER

- quite expectantly decreases the same:


bf-run.b - continued
88
89
90

CASE V.INSTR EQ -
V.VALUES(V.POINTER) -IF V.VALUES(V.POINTER) EQ -1 THEN V.VALUES(V.POINTER) = 255

91

Dot instruction outputs the symbol with ASCII value taken from the cell under the
pointer:
bf-run.b - continued
92

CASE V.INSTR EQ .

93
94
95
96
97

V.CURR.VAL = V.VALUES(V.POINTER)
IF V.CURR.VAL GT 0 AND V.CURR.VAL LT 256 THEN
CRT CHAR(V.CURR.VAL):
END

98

Comma waits for a keyboard input and the ASCII value of a key which was pressed is
being put to the cell under the pointer:
bf-run.b - continued
99

CASE V.INSTR EQ ,

100
101
102
103
104

ECHO OFF
V.KEY = KEYIN()
ECHO ON
V.VALUES(V.POINTER) = SEQ(V.KEY)

105

Now comes the tricky part loops. If theres a non-zero value in the current cell we
continue processing:
bf-run.b - continued
106

CASE V.INSTR EQ [

107
108

IF V.VALUES(V.POINTER) NE 0 THEN CONTINUE

109

If theres zero it means that the loop is finished and we have to jump right after the
matching right bracket:

J of BASE, TAF of C

44

By KZM

9 MORE USELESS PROGRAMMING - BRAINF**K INTERPRETER

bf-run.b - continued
110

* find matching bracket

111

FIND V.CNT IN V.LEFT.BRACKETS SETTING V.POSN ELSE


CRT STRUCTURE ERROR AT POSITION : V.CNT
STOP
END

112
113
114
115
116

V.CNT = V.RIGHT.BRACKETS<V.POSN>
IF V.CNT EQ 0 THEN
CRT STRUCTURE ERROR AT POSITION : V.CNT
STOP
END

117
118
119
120
121
122

And here a loop end that also requires a decision to return to loop start or to move
forward:
bf-run.b - continued
CASE V.INSTR EQ ]

123
124

IF V.VALUES(V.POINTER) EQ 0 THEN CONTINUE

125
126

FIND V.CNT IN V.RIGHT.BRACKETS SETTING V.POSN ELSE


CRT STRUCTURE ERROR AT POSITION : V.CNT
STOP
END

127
128
129
130
131

V.CNT = V.LEFT.BRACKETS<V.POSN>

132
133

And thats the end of our program:


bf-run.b - finished
END CASE

134
135

REPEAT

136
137

STOP

138
139
140

END

141

Now compile it and try some BF examples (Im far from programming in BF neither I
want you to, so I found some examples on the Internet, keeping the copyright notices if they
were present; first try the Hello world described at the beginning of this chapter):
J of BASE, TAF of C

45

By KZM

9 MORE USELESS PROGRAMMING - BRAINF**K INTERPRETER

Hello world in BF
C:\sa-tafc> jrunComp.cmd bf-run
bf-run.c
mt -nologo -manifest bf-run.dll.manifest -outputresource:bf-run.dll;2 failed ,
command returned a code of -1
mt -nologo -manifest bf-run.exe.manifest -outputresource:bf-run.exe;1 failed ,
command returned a code of -1
C:\sa-tafc> jrunEXE.cmd bf-run.exe helloworld.bf
Hello World!

Try more examples; you can copy them and save to files, then use file names one by one
as an argument to bf-run.exe (the latter prefixed with jrunEXE.cmd of course). No line
numbers in these examples because line breaks dont matter in BF; text notes can also be
copied they simply will be ignored:
More BF examples
[ This program prints Sierpinski triangle on 80-column display. ]
>
+ +
+
+
[ < + +
+
+
+ +
+ +
>
]
>
+ + + + + + + +
[
>
+ +
+ +
<
]
>
> + + >
> > + >
>
>
+
<
< <
< <
< <
< <
<
[
[
>
+
<
] > [ - < + > > > . < < ] > > >
[
[
- >
+ +
+
+
+
+
+ + [ >
+ + + +
<
]
>
. <
< [
- >
+ <
]
+
>
[
>
+
+
+ + + + + + + +
< < + > ] > . [
]
>
]
] +
< <
< [
- [
>
+
<
]
+
>
[

J of BASE, TAF of C

46

By KZM

9 MORE USELESS PROGRAMMING - BRAINF**K INTERPRETER

- < + >
> > - [
- > + <
] + + >
[
<
>
]
<
<
< ]
< <
< <
] +
+ +
+ +
+ +
+ +
+
.
+
+
+
.
[
]
<
]
+
+
+
+
+
* * * * * M a d e * B y : * N Y Y R I K K I * 2 0 0 2 * * * * *
More BF examples
;; 99 bottles of beer on the wall
;; warning: alcohol destroys brain cells! (not as much as brainfuck)
;; written by Keymaker
++++[>+++++<-]>+++
[
[>+>+<<-]
>++++++[<+>-]+++++++++[<++++++++++>-]
>[<+>-]<]
+++>+++++++++[<+++++++++>-]
>++++++[<++++++++>-]<--[>+>+<<-]>>[<<+>>-]<->>++++[<++++++++>-]++++++++++
>+++++++++[>+++++++++++<-]>
[
[>+>+>+<<<-]>[<+>-]>>
[
[>+>+<<-]>>[<<+>>-]<
[<->-[<->-[<->-[<->-[<->-[<->-[<->-[<->-[<->-[<<<+>---------->->
[-]]]]]]]]]]]<
]
<<[>>++++++++++++[<++++<++++>>-]<<[.[-]>]<<]
>[<++++++[>++++++++<-]>.[-]]<<
<<<.<<<<<.[<]>>>>>>>>>.<<<<<..>>>>>>>>.>>>>>>>.
[>]>[>+>+<<-]>[<+>-]>[-[[-]+++++++++[<+++++++++++++>-]<--.[-]>]]<<
<<<.[<]>>>>>>>>>.>>>>>>>>>.[>]<<.<<<<<.<<<..[<]>>>>>>.[>]<<.
[<]>>>>>>>>>.>.[>]<<.[<]>>>>.>>>>>>>>>>>>.>>>.[>]<<.
[<]>.[>]<<<<<<.<<<<<<<<<<<..[>]<<<.>.
[>]>[>+>+>+<<<-]>[<+>-]>>
[
[>+>+<<-]>>[<<+>>-]<
[<->-[<->-[<->-[<->-[<->-[<->-[<->-[<->-[<->-[<<<+>---------->->
[-]]]]]]]]]]]<
]
<<[>>++++++++++++[<++++<++++>>-]<<[.[-]>]<<]

J of BASE, TAF of C

47

By KZM

9 MORE USELESS PROGRAMMING - BRAINF**K INTERPRETER

>[<++++++[>++++++++<-]>.[-]]<<
<<<.<<<<<.[<]>>>>>>>>>.<<<<<..>>>>>>>>.>>>>>>>.
[>]>[>+>+<<-]>[<+>-]>[-[[-]+++++++++[<+++++++++++++>-]<--.[-]>]]<<
<<<.[<]>>>>>>>>>.>>>>>>>>>.[>]<<.<<<<<.<<<..[<]>>>>>>.[>]<<<<.>>>.
<<<<.<.<<<<<<<<<<.>>>>>>.[>]<<.[<]>>>>>>>>>.>.>>>>>>>>>.[>]<<.
<<<<<<<.[<]>>>>>>>>>.[<]>.>>>>>>>>>.[>]<<.<<<<.<<<<<<<<<<<<<.[>]<<<<<<<<<.
[>]<<.[<]>>>>>>>>.[>]<<<<<<.[<]>>>>>..[>]<<.<<<<<<<<<<<<.[<]>>>>.
[>]<<.<<<<.[<]>>>>>>.>>>.<<<<<<.>>>>>>>.>>>>>>>>>>.[>]<<<.>.
>>>[>+>+>+<<<-]>[<+>-]>>
[
[>+>+<<-]>>[<<+>>-]<
[<->-[<->-[<->-[<->-[<->-[<->-[<->-[<->-[<->-[<<<+>---------->->
[-]]]]]]]]]]]<
]
<<[>>++++++++++++[<++++<++++>>-]<<[.[-]>]<<]
>[<++++++[>++++++++<-]>.[-]]<<
[>+>+<<-]>[<+>-]+>[<->[-]]<
[-<<<[<]>>>>>>>>>>.<.[>]<<.[<]>>>>>>>>>>>.<<.<<<.[>]<<<<<<<<<<.[>]>>]<
<<<.<<<<<.[<]>>>>>>>>>.<<<<<..>>>>>>>>.>>>>>>>.
[>]>[>+>+<<-]>[<+>-]+>
[<->-[<+>[-]]]<
[++++++++[>+++++++++++++<-]>--.[-]<]<
<<<.[<]>>>>>>>>>.>>>>>>>>>.[>]<<.<<<<<.<<<..[<]>>>>>>.[>]<<.
[<]>>>>>>>>>.>.[>]<<.[<]>>>>.>>>>>>>>>>>>.>>>.[>]<<.
[<]>.[>]<<<<<<.<<<<<<<<<<<..[>]<<<<.>>>..
>>
]
More BF examples
;; collatz sequences
;; this program prints out collatz sequences from 1 to infinity
;; written by Keymaker

>>+>+<[[->>[>>]>>>[>>]+[<<]<<<[<<]>[>[>>]>>+>[>>]<+<[<<]<<<[<
<]>-]>[>>]>>[<<<<[<<]>+>[>>]>>-]<<<<[<<]+>>]<<[+++++[>+++++++

J of BASE, TAF of C

48

By KZM

9 MORE USELESS PROGRAMMING - BRAINF**K INTERPRETER

+<-]>.<++++++[>--------<-]+<<]>>[>>]+[>>>>[<<+>+>-]<-[>+<-]+<
[<<->>-[<<+>>[-]]]>>>[<<<+<<+>>>>>-]<<<[>>>+<<<-]<<[[-]>+>>->
[<+<[<<+>>-]<[>+<-]<[>+<-]>>>>-]<[>+<-]+<[->[>>]<<[->[<+++>-[
<+++>-[<+++>-[<[-]++>>[-]+>+<<-[<+++>-[<+++>-[<[-]+>>>+<<-[<+
++>-[<+++>-]]]]]]]]]<[>+<-]+<<]>>>+<[->[<+>-[<+>-[<+>-[<+>-[<
+>-[<+>-[<+>-[<+>-[<+>-[<[-]>>[-]+>+<<-[<+>-]]]]]]]]]]]<[>+<]+>>]<<[<<]>]<[->>[->+>]<[-[<+>-[<->>+<-[<+>-[<->>+<-[<+>-[<>>+<-[<+>-[<->>+<-[<+>-[<->>+<-[<+>-[<->>+<-[<+>-[<->>+<-[<+>
-[<->>+<-[<+>-[<->>+<-[<+>-]]]]]]]]]]]]]]]]]]]>[<+>-]<+<[<+++
+++++++>-]<]>>[<+>->>]<<[>+>+<<-]>[<+>-]+>[<->[-]]<[-<<-]<<[<
<]]++++++[>+++++++<-]>++.------------.[-]>[>>]<<[+++++[>+++++
+++<-]>.<++++++[>--------<-]+<<]+<]>[<+>-]<]>>>[>>]<<[>[-]<-<
<]++++++++++.[-]<<<[<<]>>>+<[->[<+>-[<+>-[<+>-[<+>-[<+>-[<+>[<+>-[<+>-[<+>-[<[-]>>[-]+>+<<-]]]]]]]]]]<[>+<-]+>>]<<[<<]>>]
More BF examples - factorials from 1 to infinity
>++++++++++>>>+>+[>>>+[-[<<<<<[+<<<<<]>>[[-]>[<<+>+>-]<[>+<-]<[>+<-[>+<-[>
+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>[-]>>>>+>+<<<<<<-[>+<-]]]]]]]]]]]>[<+>]+>>>>>]<<<<<[<<<<<]>>>>>>>[>>>>>]++[-<<<<<]>>>>>>-]+>>>>>]<[>++<-]<<<<[<[
>+<-]<<<<]>>[->[-]++++++[<++++++++>-]>>>>]<<<<<[<[>+>+<<-]>.<<<<<]>.>>>>]
This program doesnt terminate; you will have to kill it.
Daniel B Cristofani (cristofdathevanetdotcom)
http://www.hevanet.com/cristofd/brainfuck/
More BF examples - Fibonacci numbers from 1 to 100
+++++++++++
>+>>>>++++++++++++++++++++++++++++++++++++++++++++
>++++++++++++++++++++++++++++++++<<<<<<[>[>>>>>>+>
+<<<<<<<-]>>>>>>>[<<<<<<<+>>>>>>>-]<[>++++++++++[<-[>>+>+<<<-]>>>[<<<+>>>-]+<[>[-]<[-]]>[<<[>>>+<<<
-]>>[-]]<<]>>>[>>+>+<<<-]>>>[<<<+>>>-]+<[>[-]<[-]]
>[<<+>>[-]]<<<<<<<]>>>>>[+++++++++++++++++++++++++
+++++++++++++++++++++++.[-]]++++++++++<[->-<]>++++
++++++++++++++++++++++++++++++++++++++++++++.[-]<<
<<<<<<<<<<[>>>+>+<<<<-]>>>>[<<<<+>>>>-]<-[>>.>.<<<
[-]]<<[>>+>+<<<-]>>>[<<<+>>>-]<<[<+>-]>[<+>-]<<<-]
More BF examples
>++++++++++>>+<+[[+++++[>++++++++<-]>.<++++++[>--------<-]+<<]>.>[->[
<++>-[<++>-[<++>-[<++>-[<-------->>[-]++<-[<++>-]]]]]]<[>+<-]+>>]<<]
Output powers of two
And even more BF examples
++++[>+++++<-]>[<+++++>-]+<+[
>[>+>+<<-]++>>[<<+>>-]>>>[-]++>[-]+

J of BASE, TAF of C

49

By KZM

9 MORE USELESS PROGRAMMING - BRAINF**K INTERPRETER

>>>+[[-]++++++>>>]<<<[[<++++++++<++>>-]+<.<[>----<-]<]
<<[>>>>>[>>>[-]+++++++++<[>-<-]+++++++++>[-[<->-]+[<<<]]<[>+<-]>]<<-]<<]
[Outputs square numbers from 0 to 10000.
Daniel B Cristofani (cristofdathevanetdotcom)
http://www.hevanet.com/cristofd/brainfuck/]

Finally, my favourite:
More, more BF examples
>>>>+>+++>+++>>>>>+++[
>,+>++++[>++++<-]>[<<[-[->]]>[<]>-]<<[
>+>+>>+>+[<<<<]<+>>[+<]<[>]>+[[>>>]>>+[<<<<]>-]+<+>>>-[
<<+[>]>>+<<<+<+<--------[
<<-<<+[>]>+<<-<<-[
<<<+<-[>>]<-<-<<<-<----[
<<<->>>>+<-[
<<<+[>]>+<<+<-<-[
<<+<-<+[>>]<+<<<<+<-[
<<-[>]>>-<<<-<-<-[
<<<+<-[>>]<+<<<+<+<-[
<<<<+[>]<-<<-[
<<+[>]>>-<<<<-<-[
>>>>>+<-<<<+<-[
>>+<<-[
<<-<-[>]>+<<-<-<-[
<<+<+[>]<+<+<-[
>>-<-<-[
<<-[>]<+<++++[<-------->-]++<[
<<+[>]>>-<-<<<<-[
<<-<<->>>>-[
<<<<+[>]>+<<<<-[
<<+<<-[>>]<+<<<<<-[
>>>>-<<<-<]]]]]]]]]]]]]]]]]]]]]]>[>[[[<<<<]>+>>[>>>>>]<-]<]>>>+>>>>>>>+>]<
]<[-]<<<<<<<++<+++<+++[
[>]>>>>>>++++++++[<<++++>++++++>-]<-<<[-[<+>>.<-]]<<<<[
-[-[>+<-]>]>>>>>[.[>]]<<[<+>-]>>>[<<++[<+>--]>>-]
<<[->+<[<++>-]]<<<[<+>-]<<<<
]>>+>>>--[<+>---]<.>>[[-]<<]<
]
[Enter a number using ()-./0123456789abcdef and space, and hit return.
Daniel B Cristofani (cristofdathevanetdotcom)
http://www.hevanet.com/cristofd/brainfuck/]

J of BASE, TAF of C

50

By KZM

10 MORE JBC PROGRAMMING STUFF - PROGRAM EXIT STATUS

10

More jBC programming stuff - program exit status

y the way, arent you tired of that messages? I mean the messages that appear during
:::: compilation and are a bit misleading:
Annoying messages while complation
mt -nologo -manifest bf-run.dll.manifest -outputresource:bf-run.dll;2 failed ,
command returned a code of -1
mt -nologo -manifest bf-run.exe.manifest -outputresource:bf-run.exe;1 failed ,
command returned a code of -1

By reaching this point, I really am.


Can we get rid of them? Obviously while our source is compiled, some program called
mt is to be started. Try to start it manually:
Start mt to see whats that
C:\sa-tafc> jrunSH.cmd
jsh ~ --> mt
[3] Unable to execute program mt
jsh ~ -->

Hmmm... Judging on its command line, it has something to do with manifests and the
stuff alike which we in fact dont need.
Maybe some jcompile settings can help us but heres the simpler way. Write a program
(in jBC of course) called mt which does nothing but terminates correctly, then check if all is
correct with our programs which were compiled without the usage of this plug.
So:
mt.b
1
2

STOP
END

mt.b compilation
C:\sa-tafc> jrunComp.cmd mt
mt.c
mt -nologo -manifest mt.dll.manifest -outputresource:mt.dll;2 failed ,
command returned a code of -1

As you saw before, for compilation of jBC program mt is launched twice (and for a
subroutine - once, we saw it when compiled JBCUserConversions). In this case the error
message appeared only once - on the second attempt our mt.exe already existed. Try then:
J of BASE, TAF of C

51

By KZM

10 MORE JBC PROGRAMMING STUFF - PROGRAM EXIT STATUS

dynarr.b compilation and run


C:\sa-tafc> jrunComp.cmd dynarr
dynarr.c
C:\sa-tafc> jrunEXE.cmd dynarr.exe
Dynamic array population, iterations/sec: 2765.7085

So no more messages, execution result is the same. Now comes the question - mt.exe
terminates normally (which is the default behaviour) but imagine that we want to terminate
a program abnormally?
Here comes the thing that you never use with T24 - abort. Amend the mt.b to return
an error:
mt.b
1
2

ABORT 201
END

Why 201? Just because I saw it in some source, dont remember which. Delete mt.exe,
try to compile mt.b and get the error message. Actually, three messages:
mt.b compilation expecting an error
C:\sa-tafc> jrunComp.cmd mt
mt.c
mt -nologo -manifest mt.dll.manifest -outputresource:mt.dll;2 failed ,
command returned a code of -1
** Error [ 201 ] **
Unable to open file
mt -nologo -manifest mt.exe.manifest -outputresource:mt.exe;1 failed ,
command returned a code of 51

So they are:
-1 because mt.exe itself didnt exist at this point of time.
201 is our jBASE error message.
51 is derived from our 201 error message - so far I have no idea why the number is
different.
Now revert to initial code of mt.b and compile it so we dont have more error messages
during compilation.
Where the text of error message 201 is stored? In TAFC home directory theres a file
called jbcmessages. Its standard hashed jBASE file so:
J of BASE, TAF of C

52

By KZM

10 MORE JBC PROGRAMMING STUFF - PROGRAM EXIT STATUS

jBASE error file


C:>sa-tafc> jrunSH.cmd
jsh ~ --> CT <path to TAFC>\jbcmessages 201
201
001 ** Error [ 201 ] ** ^NEWLINE^Unable to open file %s^EXIT51^

Yes! We see here the error code 51 that is passed back to operating system.

So if you want to output some standard error messages and to analyze the results of your program execution after its exit now you
know where to look.

J of BASE, TAF of C

53

By KZM

11 PROCEEDING TEXT FILES - LONG LINES SUPPORT

11

Proceeding text files - long lines support

he task of processing a text file might be a little tricky in jBC. Firstly dont forget that
::::: line length matters. If its more that 1024 characters then you might fall into very
common misunderstanding. To illustrate that lets create such text file. The code is:
textcreate.b
1

* Create a file with long lines

OPENSEQ ., test.txt TO F.OUT.FILE THEN


WEOFSEQ F.OUT.FILE
END ELSE
CREATE F.OUT.FILE ELSE
CRT OUTPUT FILE CREATION ERROR
STOP
END
END

3
4
5
6
7
8
9
10
11
12

* Create a ruler-like output

13

V.LINE =

14
15

FOR V.I = 1 TO 1100 STEP 10


V.J = V.I + 9
V.LINE := STR(-, 10 - LEN(V.J)) : V.J
NEXT V.I

16
17
18
19
20
21

* Now write it

22

FOR V.I = 1 TO 10
WRITESEQ V.LINE TO F.OUT.FILE ELSE
CRT FILE WRITE ERROR
STOP
END
NEXT V.I

23
24
25
26
27
28
29

STOP

30
31
32

END

33

We either create a file or truncate it if it already exists. A line with the length of 1100
bytes is written to it 10 times. Compile, run, see the test.txt in your favourite editor or in
JED:
test.txt - end of line
File . , Record test.txt
Command->

J of BASE, TAF of C

+1040 Insert

54

23:07:19

By KZM

11 PROCEEDING TEXT FILES - LONG LINES SUPPORT

0001 ------1050------1060------1070------1080------1090------1100
0002 ------1050------1060------1070------1080------1090------1100
0003 ------1050------1060------1070------1080------1090------1100
0004 ------1050------1060------1070------1080------1090------1100
0005 ------1050------1060------1070------1080------1090------1100
0006 ------1050------1060------1070------1080------1090------1100
0007 ------1050------1060------1070------1080------1090------1100
0008 ------1050------1060------1070------1080------1090------1100
0009 ------1050------1060------1070------1080------1090------1100
0010 ------1050------1060------1070------1080------1090------1100
-------------------------------- End Of Record --------------------------------

Now were ready to read it. Another program to do that:


textread.b
1

* Program to read a text file

OPENSEQ ., test.txt TO F.IN.FILE THEN


NULL
END ELSE
CRT ERROR OPENING FILE
STOP
END

3
4
5
6
7
8
9

READSEQ V.LINE FROM F.IN.FILE ELSE


CRT ERROR READING FILE
STOP
END

10
11
12
13
14

CRT V.LINE

15
16

STOP

17
18
19

END

20

Compile, run, what we see?


textread.exe - the output
--------10--------20--------30--------40--------50--------60--------70--------80
--------90-------100-------110-------120-------130-------140-------150-------160
-------170-------180-------190-------200-------210-------220-------230-------240
-------250-------260-------270-------280-------290-------300-------310-------320
-------330-------340-------350-------360-------370-------380-------390-------400
-------410-------420-------430-------440-------450-------460-------470-------480
-------490-------500-------510-------520-------530-------540-------550-------560

J of BASE, TAF of C

55

By KZM

11 PROCEEDING TEXT FILES - LONG LINES SUPPORT

-------570-------580-------590-------600-------610-------620-------630-------640
-------650-------660-------670-------680-------690-------700-------710-------720
-------730-------740-------750-------760-------770-------780-------790-------800
-------810-------820-------830-------840-------850-------860-------870-------880
-------890-------900-------910-------920-------930-------940-------950-------960
-------970-------980-------990------1000------1010------1020----

By default readseq is able to read 1024 bytes at a time. (Or 1024 characters? Not sure
if we had some two-byte characters here what would be the output. But the point is not
that, though I hope we can look at some utf stuff later...)
How to overcome this default? There are two ways:
Repeat readseq as many times as its necessary to retrieve the whole line. The end of
this process can be understood by checking if the length of retrieved data is less than 1024.
Override this default using IOCTL function:
Corrected textread.b
1

* Program to read a text file

2
3

INCLUDE JBC.h

OPENSEQ ., test.txt TO F.IN.FILE THEN


NULL
END ELSE
CRT ERROR OPENING FILE
STOP
END

5
6
7
8
9
10
11

IF IOCTL(F.IN.FILE, JIOCTL_COMMAND_SEQ_CHANGE_RECORDSIZE, 2048) THEN


NULL
END ELSE
CRT IOCTL FAILED !!!
STOP
END

12
13
14
15
16
17
18

READSEQ V.LINE FROM F.IN.FILE ELSE


CRT ERROR READING FILE
STOP
END

19
20
21
22
23

CRT V.LINE

24
25

STOP

26
27
28

END

29

J of BASE, TAF of C

56

By KZM

11 PROCEEDING TEXT FILES - LONG LINES SUPPORT

Now run it:


textread.exe - the new output
--------10--------20--------30--------40--------50--------60--------70--------80
--------90-------100-------110-------120-------130-------140-------150-------160
-------170-------180-------190-------200-------210-------220-------230-------240
-------250-------260-------270-------280-------290-------300-------310-------320
-------330-------340-------350-------360-------370-------380-------390-------400
-------410-------420-------430-------440-------450-------460-------470-------480
-------490-------500-------510-------520-------530-------540-------550-------560
-------570-------580-------590-------600-------610-------620-------630-------640
-------650-------660-------670-------680-------690-------700-------710-------720
-------730-------740-------750-------760-------770-------780-------790-------800
-------810-------820-------830-------840-------850-------860-------870-------880
-------890-------900-------910-------920-------930-------940-------950-------960
-------970-------980-------990------1000------1010------1020------1030------1040
------1050------1060------1070------1080------1090------1100

The obvious caveat of using this method is that you need to know how long is your longest
line.
Other useful things in ioctl() you can get the time and date of last update for each
individual record in a hashed jBASE file; check if records exists without reading it and others
(there are quite comprehensive examples of that in JBASE BASIC.pdf ).

J of BASE, TAF of C

57

By KZM

12 PROCEEDING TEXT FILES - BIG FILE SPLITTING; USAGE OF EXTERNAL


TOOLS

12

Proceeding text files - big file splitting; usage of


external tools

nother task which can be described here is: we have big text file (containing, say,
::::: 1,500,000 lines) that we need to split into several smaller ones following certain rule.
The rule is: theres an account number at the certain field in each line (line is delimited with
semicolons). There can be many lines in the file contaning the same account number, and
these lines are not necessarily located one right after another. We need to split this big file
into three smaller files but the particular account number shouldnt appear in more than one
resulting file. And last but not least these files should contain about the same number of
lines.
What that might be needed for? Well, for example, to start several parallel sessions of
creating accounting entries in order to avoid locks.
Trying to resolve this task I went through the following approaches:
Wrote a program in jBC that proceeds the file line by line and stores account numbers
in a dynamic array, keeping in another array the number of times that particular account
appears in the file. This number was then used to distribute accounts among resulting files.
And on the second run through the big file create smaller files. Ended up with very low
speed of find when dynamic array is quite big.
Having python installed on my PC, wrote a python program to do the same. Python
is much better in handling big amount of data in memory (for this task I used a key-value
array type which is called a dictionary ), so it took less than a minute to proceed. Anyway,
the task that seemed a one-time one became a regular chore. Understanding that people who
might need to perform it probably dont have python, I reverted to jBC with a thought to
create a temporary hashed file that can be used to keep the necessary work information.
But finally a simple thought came to my mind: I had Unix as jBASE server OS and
since the order of lines didnt matter I sorted the file by account numbers using appropriate
Unix command, so I simplified jBC program and put the following comment to the beginning:
Comment
1
2
3
4
5
6

*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Split big file account-wise (for parallel load).
|
* By V.Kazimirchik.
|
* Firstly you have to sort this file:
|
* bash-3.00$ sort -t ";" +6 My.File > My.File.sorted
|
*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Then I was able to create smaller files one by one read all lines belonging to particular
account from a big file and then decide whether all of them go to the currently written smaller
file or we have to create the next one and write them there.
This issue shows us that though jBC is more or less universal language, not necessarily
we use only it to achieve our targets. Another example of using an external tool: once I was
J of BASE, TAF of C

58

By KZM

12 PROCEEDING TEXT FILES - BIG FILE SPLITTING; USAGE OF EXTERNAL


TOOLS

asked if its possible to list not all values of a field in a list but only the particular. See the
usual output of listing fields day.no and turnover.debit in FBNK.ACCT.ACTIVITY (with
our day of choice as 16):
Listing output
C:\sa-tafc> jrunT24.cmd
jsh ~ --> LIST FBNK.ACCT.ACTIVITY DAY.NO TURNOVER.DEBIT WITH DAY.NO EQ "16"
@ID............................................

DAY.NO

43214-201007

05
16
26
28
05
06
09
10
13
16
18
29
05
06
09
13
16
24
26
28
29

45748-201007

12165-201007

TURNOVER.DEBIT.....

-50
-3302.08
-10115
-50250
-40
-145
-125333.33
-1500
-61441.62
-57390.85
-18716.54
-38532.18
-21278.11
-52575
-46553.79
-210061.02

...

What if we want only data for day.no = 16 to be shown (with the total turnover being
calculated)? Hopefully without programming. Whatever I tried using LIST still includes
all values to the output. To add some difficulty, the position of the necessary value in a
field could be any (so standard methods of extraction which shall know the exact position
wouldnt work).
The answer could be if we dont need @ids we can (under Unix) filter the output via
grep command using something like (note that the command should be entered at one line):
Filtering output
jsh ~ --> bash
bash-3.00$ jsh -c "LIST FBNK.ACCT.ACTIVITY ID.SUPP DAY.NO TURNOVER.DEBIT

J of BASE, TAF of C

59

By KZM

12 PROCEEDING TEXT FILES - BIG FILE SPLITTING; USAGE OF EXTERNAL


TOOLS

WITH DAY.NO EQ 16" | grep " 16 "


16
16
16
16
16
16

-21278.11
-460.27
-148146.42

...

This method doesnt give us totals though and involves an external tool (under standard
Windows theres no grep though the port of this utility exists for this platform). Somewhat
better results we can get with SELECT...SAVING EVAL with saving the resulting list (again
its a long command shown at two lines just to fit the screen):
SELECT comes to the scene
C:\sa-tafc> jrunT24.cmd
jsh ~ --> SELECT FBNK.ACCT.ACTIVITY WITH DAY.NO EQ "16" SAVING
EVAL "DAY.NO:;:TURNOVER.DEBIT"
1216 Records selected
> SAVE.LIST report.csv
1216 record(s) saved to list report.csv
jsh ~ --> EDIT.LIST report.csv
.jBASE.el.4
TOP
.P
TOP
001 05;
002 16;
003 26;-50
004 28;-50
005 05;-3302.08
006 06;-10115
007 09;-50250
008 10;-40
009 13;-145
010 16;-145
011 18;-125333.33
012 29;-1500
013 05;-61441.62
014 06;-57390.85
015 09;-18716.54

J of BASE, TAF of C

60

By KZM

12 PROCEEDING TEXT FILES - BIG FILE SPLITTING; USAGE OF EXTERNAL


TOOLS

016 13;-38532.18
017 16;-21278.11
018 24;-52575
019 26;-46553.79
020 28;-46553.79
021 29;-210061.02
022 05;-1500000
.EXIT
Record .jBASE.el.4 exited from file .
List report.csv exited
jsh ~ -->

Furthermore, we can sort this list using SORT.LIST command. Then we can take it into
a tool like MS Excel or Openoffice to proceed further. Of course it adds additional layer of
manual work. Maybe its a better idea to write a program after all...

Use external tools with caution think about portability, usability,


development time and performance.

J of BASE, TAF of C

61

By KZM

13 WRITING A PROGRAM TO PRODUCE A SIMPLE REPORT

13

Writing a program to produce a simple report

ell... not so simple just one that cant be produced using other ways (or if theres no
:::::: time to investigate these other ways deeper). Here well use the last sample from
the previous chapter to produce a report. Firstly - just plain delimited text. (For simplicity
sake just make it for the BNK company only but lets add some complexity and try to
report not only some particular day but an arbitrary period of time.)
To proceed we need to understand that the file FBNK.ACCT.ACTIVITY might be big. Very
big depending on the number of accounts and movements of funds. So Id prefer to use
this opportunity to introduce so-called Basic SELECT. Whats the difference between it
and a regular jQL SELECT?
Basic SELECT isnt able to limit the selection it just selects all @ids. But its ready
to proceed records immediately and doesnt depend on number of records you can consider
it as a pointer moving over a file. Herewith goes another feature of it: if new records are
added to the file during the process, they most probably will be picked up as well. Quite the
opposite, jQL SELECT makes a snapshot of record @ids available in a file and of course
might take very long time doing that.
In the previous chapter we missed the fact that data is grouped month-wise so we tried
to sum up all movements occured on the 16th day of every month which makes not much
sense (unless its a tax payday and you want to calculate the total turnover and compare it to
average values). Thats why here well have a possibility to include any period of time to the
output. Assuming that @ids are in a form account id.year month we really need Basic
SELECT here since date is at the end of @id rather than at the beginning and therefore
jQL SELECT will be slow - especially if user decides to set quite a big range of dates to include
to the report. Creating a secondary index is possible but often is not an option so we dont
consider that (though in other case you might do).
For compiler to find T24 insert files (one of them I F.ACCT.ACTIVITY well certainly
need) its necessary to add the path to T24.BP to jrunComp.cmd:
Change in jrunComp.cmd
34

jcompile -I <path_to_bnk.run_parent_directory>\T24.BP %1.b && del %1.dll %1.obj

So heres the program. Necessary dates go as parameters which well quickly check (I
deliberately omit a check if a date is a fully valid one to save some space):
mvmtrep.b
1
2
3

* Program to report debit


* turnover for any period of time
$INCLUDE I_F.ACCT.ACTIVITY

4
5

* take dates (YYYYMMDD) as parameters, make a quick check

J of BASE, TAF of C

62

By KZM

13 WRITING A PROGRAM TO PRODUCE A SIMPLE REPORT

7
8

V.BEG = SENTENCE(1)
V.END = SENTENCE(2)

9
10
11
12
13

IF NOT(V.BEG MATCHES 8N) OR NOT (V.END MATCHES 8N) THEN


CRT DATES ARE NOT VALID
STOP
END

14
15
16
17
18

IF V.END LT V.BEG THEN


CRT END DATE IS LESS THAN START DATE
STOP
END

Well create a report consisting of delimited strings of data:


mvmtrep.b - continued
19
20

* delimiter for report

21
22

V.DLM = ;

;* make it comma if you like it more

23

For our report we need to proceed all records in FBNK.ACCT.ACTIVITY. Before we can
issue Basic SELECT we need the file to be opened:
mvmtrep.b - continued
24

* open the file

25
26
27
28
29

OPEN FBNK.ACCT.ACTIVITY TO F.AC.ACT ELSE


CRT ERROR OPENING ACCT.ACTIVITY
STOP
END

30
31

* select the file

32
33

SELECT F.AC.ACT TO 9

Now in a loop extract an @id; first of all see if it matches our range of dates:
mvmtrep.b - continued
34
35

* main loop

36
37
38

LOOP
READNEXT V.ID FROM 9 ELSE BREAK

39

J of BASE, TAF of C

63

By KZM

13 WRITING A PROGRAM TO PRODUCE A SIMPLE REPORT

V.MTH = FIELD(V.ID, -, 2)
IF V.BEG[1,6] GT V.MTH OR V.END[1,6] LT V.MTH THEN CONTINUE

40
41
42

If it does then:
mvmtrep.b - finished
43

* Read and proceed the record

44

READ R.AC.ACT FROM F.AC.ACT, V.ID ELSE


CRT UNABLE TO READ FBNK.ACCT.ACTIVITY RECORD : V.ID
STOP
END

45
46
47
48
49

V.DAYNO.L = R.AC.ACT<IC.ACT.DAY.NO>
V.DAYNO.QTY = DCOUNT(V.DAYNO.L, @VM)
V.ACCT = FIELD(V.ID, -, 1)

50
51
52

;* list of available days


;* a/c number for report

53

FOR V.I = 1 TO V.DAYNO.QTY


V.DAYNO = V.DAYNO.L<1,V.I>
IF V.DAYNO EQ THEN CONTINUE
V.DATE = V.MTH : V.DAYNO

54
55
56
57
58
59

* proceed only days that belong to our period

60

IF V.DATE GE V.BEG AND V.DATE LE V.END THEN


V.DR = R.AC.ACT<IC.ACT.TURNOVER.DEBIT,V.I>
IF NOT(V.DR) THEN CONTINUE
V.LINE = V.DATE : V.DLM : V.ACCT : V.DLM : V.DR
CRT V.LINE ;* just print it - redirection rules :)
;*
though not always :((
END
NEXT V.I

61
62
63
64
65
66
67
68
69

REPEAT

70
71

STOP

72
73

END

74

Compile and run it:


mvmtrep invocation
C:\sa-tafc> jrunComp.cmd mvmtrep
mvmtrep.c

J of BASE, TAF of C

64

By KZM

13 WRITING A PROGRAM TO PRODUCE A SIMPLE REPORT

C:\sa-tafc>j runT24.cmd
jsh ~ --> mvmtrep 20100725 20100805
...
20100728;JPYUSD140160017;-18000
20100802;EUR144410003;-68152.09
20100804;EUR144410003;-1416239.08
20100805;EUR145550001;-20000
20100805;51284;-200000
20100728;USDSGD140160014;-3.74
20100805;EUR143040074;-20000
20100805;USD141960017;-37188.85
jsh ~ -->

This is the fastest way to get raw output. The programming itself shouldnt take more
than one hour.
Bottomline: consider using external tools exclusively or combined
with jQL or jBC, but sometimes just to write jBC program is the
easiest and/or the fastest way.
As you can see, the results are not sorted, theres no totals etc. To create a nice-looking
report we can use any of the following methods:
Amend the program to produce the final layout. If its a simple ASCII report like T24
account statement its quite easy to do.
As it was already suggested earlier, take the output to Excel-like tool.
Amend jBC program to create output in some fancy format like xml and proceed it
somewhere else. Very fashionable option but we dont consider it here assuming the fact that
there are some limitations in jBC. xml shouldnt be a problem (provided that you have an
external tool to proceed it) but other formats - like pdf - certainly will. And forget about
creating something like doc file - even Microsoft is unable to proceed all flavours of them.
Use some external tool to proceed the final output from the raw output we already
have. Will do that in the next chapter (though you might try the previous option if you like
it more).

J of BASE, TAF of C

65

By KZM

14 CREATING REPORT REPRESENTATION

14

Creating report representation

ere well try to create a nice report using an external tool. The external tool
rd
party
::::: were talking about might be either an operating system command or utility, 3
software or even another jBC program written specially for this purpose.
Firstly lets think how do we deliver the output of our jBC program to that tool? Well
try to use standard output redirection. Its possible to redirect the output to file but thats
not so interesting, neither its fast. To gain some performance wed better redirect the output
of one program to the input of another.
Report output redirection
C:\sa-tafc> jrunT24.cmd
jsh ~ --> mvmtrep 20100725 20100805 | more
...
20100728;USDSGD140160014;-3.74
20100805;EUR143040074;-20000
20100805;USD141960017;-37188.85
jsh ~ -->

More is to output one page and wait for the user command to continue... No, all was
output at once. Now try the same in OS shell:
Report output redirection - now OK
C:\sa-tafc> jrunT24.cmd
jsh ~ --> cmd
Microsoft Windows XP [Version 5.1.2600]
(C) Copyright 1985-2001 Microsoft Corp.
C:\....\bnk\bnk.run> mvmtrep 20100101 20110101 | more
20100806;13218;-2
20100707;14044;-1000000
20100713;14044;-500657.53
-- More --

Here we see another difference between jsh and OS shell. We can continue with our
solution using the latter.
Of course we wouldnt use more command for our purpose we just made sure that this
approach works. As it was written above, the tool that we can use can be any executable
that is able to proceed the standard input provided by the report preparation program. In
Unix or Linux almost everything that we might need already presents in the system. Under
J of BASE, TAF of C

66

By KZM

14 CREATING REPORT REPRESENTATION


Windows we need either some 3rd party software but before doing that lets create a jBC
program. This program takes the standard input and converts it to very simple html file that
we then are able to see in browser (not T24 browser just any browser like IE, Chrome
etc). But first of all we need to amend the program mvmtrep.b since the new program will
read the standard input in a loop it needs to know when the data flow finishes. To achieve
that lets write some predefined string when the output is over:
mvmtrep.b - the change
71

CRT <<<EOF>>>

72
73

STOP

74
75

END

76

And here the code that is to proceed the data further on:
procrep.b
1

* Program to represent a report

CRT <html><head><meta http-equiv="Content-Type" content="text/html; \


: charset=utf-8" /></head>

3
4
5

CRT <body><table border="1">

6
7

LOOP
INPUT V.LINE
IF V.LINE[1,9] EQ <<<EOF>>> THEN BREAK
IF V.LINE EQ THEN CONTINUE ;* we have an empty string
;* after each line - probably caused by
CRT <tr>
;* char(13) in line end under windows

8
9
10
11
12
13
14

V.CNT = DCOUNT(V.LINE, ;)
FOR V.I = 1 TO V.CNT
V.FLD = FIELD(V.LINE, ;, V.I)
CRT <td> : V.FLD : </td>
NEXT V.I

15
16
17
18
19
20

CRT </tr>

21
22

REPEAT

23
24

CRT </table></body></html>

25
26

STOP

27
28
29

END

30

J of BASE, TAF of C

67

By KZM

14 CREATING REPORT REPRESENTATION

Now compiling all and toying a little with redirection (browsers arent able to my
knowledge to get contents from stdin , so were using a temporary file here) we were able
to see the output in the browser that is set in the system as a default one:
Creation of a nice report; long command is split again
C:\sa-tafc> jrunT24.cmd
jsh ~ --> cmd
Microsoft Windows XP [Version 5.1.2600]
(C) Copyright 1985-2001 Microsoft Corp.
C:\....\bnk\bnk.run> mvmtrep 20100101 20110101 | procrep > %TEMP%\out.html
&& %TEMP%\out.html

Heres what we get:

J of BASE, TAF of C

68

By KZM

14 CREATING REPORT REPRESENTATION

We can improve this report by adding a header, converting the date to more appropriate
format, add column headers, place some logo to the top etc. To add totals well need mvmtrep
to supply currency codes as well - we really are not going to add dollars to euros.
New source of mvmtrep.b:
1
2

Amended mvmtrep.b
* Program to report debit turnover
* for any period of time

3
4
5

$INCLUDE I_F.ACCOUNT
$INCLUDE I_F.ACCT.ACTIVITY

6
7

* take dates (YYYYMMDD) as parameters, make a quick check

8
9
10

V.BEG = SENTENCE(1)
V.END = SENTENCE(2)

J of BASE, TAF of C

69

By KZM

14 CREATING REPORT REPRESENTATION

11
12
13
14
15

IF NOT(V.BEG MATCHES 8N) OR NOT (V.END MATCHES 8N) THEN


CRT DATES ARE NOT VALID
STOP
END

16
17
18
19
20

IF V.END LT V.BEG THEN


CRT END DATE IS LESS THAN START DATE
STOP
END

21
22

* delimiter for report

23
24

V.DLM = ;

;* make it comma if you like it more

25
26

* open files

27
28
29
30
31

OPEN FBNK.ACCT.ACTIVITY TO F.AC.ACT ELSE


CRT ERROR OPENING ACCT.ACTIVITY
STOP
END

32
33
34
35
36

OPEN FBNK.ACCOUNT TO F.ACCT ELSE


CRT ERROR OPENING ACCOUNT TABLE
STOP
END

37
38
39
40
41

OPEN FBNK.ACCOUNT$HIS TO F.ACCT.HIS ELSE


CRT ERROR OPENING ACCOUNTS HISTORY
STOP
END

42
43

* select the file

44
45

SELECT F.AC.ACT TO 9

46
47

* main loop

48
49
50

LOOP
READNEXT V.ID FROM 9 ELSE BREAK

51
52
53

V.MTH = FIELD(V.ID, -, 2)
IF V.BEG[1,6] GT V.MTH OR V.END[1,6] LT V.MTH THEN CONTINUE

54
55

* Read and proceed the record

56
57
58

READ R.AC.ACT FROM F.AC.ACT, V.ID ELSE


CRT UNABLE TO READ FBNK.ACCT.ACTIVITY RECORD : V.ID

J of BASE, TAF of C

70

By KZM

14 CREATING REPORT REPRESENTATION

STOP
END

59
60
61

V.DAYNO.L = R.AC.ACT<IC.ACT.DAY.NO>
V.DAYNO.QTY = DCOUNT(V.DAYNO.L, @VM)
V.ACCT = FIELD(V.ID, -, 1)

62
63
64

;* list of available days


;* a/c number for report

65

IF V.ACCT[1,3] MATCHES 3A THEN V.CCY = V.ACCT[1,3] ;* internal a/c


ELSE
READ R.ACCT FROM F.ACCT, V.ACCT ELSE
;* a/c might be closed already
READ R.ACCT FROM F.ACCT.HIS, V.ACCT : ;1 ELSE
CRT ACCOUNT : V.ACCT : NOT FOUND
STOP
END
END

66
67
68
69
70
71
72
73
74

V.CCY = R.ACCT<AC.CURRENCY>
END

75
76
77

FOR V.I = 1 TO V.DAYNO.QTY


V.DAYNO = V.DAYNO.L<1,V.I>
IF V.DAYNO EQ THEN CONTINUE
V.DATE = V.MTH : V.DAYNO

78
79
80
81
82
83

* proceed only days that belong to our period

84

IF V.DATE GE V.BEG AND V.DATE LE V.END THEN


V.DR = R.AC.ACT<IC.ACT.TURNOVER.DEBIT,V.I>
IF NOT(V.DR) THEN CONTINUE
V.LINE = V.DATE : V.DLM : V.ACCT : V.DLM : V.DR : V.DLM : V.CCY
CRT V.LINE ;* just print it - redirection rules :)
;*
though not always :((
END
NEXT V.I

85
86
87
88
89
90
91
92
93

REPEAT

94
95

CRT <<<EOF>>>

96
97

STOP

98
99

END

100

And here goes the new source of procrep.b:


Amended procrep.b
1
2

* Program to represent
* the report

J of BASE, TAF of C

71

By KZM

14 CREATING REPORT REPRESENTATION

3
4
5

CRT <html><head><meta http-equiv="Content-Type" content="text/html; \


: charset=utf-8" /></head><body> \
: <img src="logo.png"><h1>Debit turnover summary</h1>

6
7
8
9

CRT <table border="1" width="80%"> \


: <tr><th>Date</th><th>Account</th><th>Turnover</th> \
: <th>CCY</th></tr>

10
11
12

V.TOTAL.L =
V.TOT.QTY = 0

;* list of totals

(CCY @VM AMT)

13
14
15
16
17
18
19

LOOP
INPUT V.LINE
IF V.LINE[1,9] EQ <<<EOF>>> THEN BREAK
IF V.LINE EQ THEN CONTINUE ;* we have an empty string
;* after each line - probably caused by
CRT <tr>
;* char(13) in line end under windows

20
21
22
23

V.CNT = DCOUNT(V.LINE, ;)
FOR V.I = 1 TO V.CNT
V.FLD = FIELD(V.LINE, ;, V.I)

24
25

BEGIN CASE

26
27
28

CASE V.I EQ 1
V.FLD = V.FLD[2] : / : V.FLD[5,2] : / : V.FLD[1,4]

29
30
31

CASE V.I EQ 3
V.AMT = V.FLD

32
33

CASE V.I EQ 4

34
35

FIND V.FLD IN V.TOTAL.L SETTING V.POSN ELSE V.POSN =

36
37
38
39
40
41
42

IF V.POSN EQ THEN
V.TOT.QTY ++
V.TOTAL.L<V.TOT.QTY> = V.FLD : @VM : V.AMT
END ELSE
V.TOTAL.L<V.POSN, 2> += V.AMT
END

43
44

END CASE

45
46
47

CRT <td> : V.FLD : </td>


NEXT V.I

48
49

CRT </tr>

50

J of BASE, TAF of C

72

By KZM

14 CREATING REPORT REPRESENTATION

REPEAT

51
52

CRT <tr><td><b>Totals:</b></td><td></td><td>

53
54

FOR V.I = 1 TO V.TOT.QTY


CRT <tr><td> : V.TOTAL.L<V.I,1> : </td><td> : </td><td> \
: V.TOTAL.L<V.I,2> : </td></tr>
NEXT V.I

55
56
57
58
59

CRT </table></body></html>

60
61

STOP

62
63
64

END

65

Now see the new output:

J of BASE, TAF of C

73

By KZM

14 CREATING REPORT REPRESENTATION

Of course you need to have the file logo.png in the directory where you put the html
file. For this example Ive used an online service to create a custom logo. To sort the data
you can use an OS command for sorting or (if the data amount is huge) create a temporary
jBASE file with @id structure that reflects the desired sort order. And dont forget that on
a client-server environment youll form the output on a server; so you need to make it visible
for a Web server as well and the client is to be supplied the correct URL to see the output.
Why use two different programs to prepare the raw data for the report and to proceed
it? Firstly the second program can be a standard one to proceed many similar reports.
Secondly to have flexibility: imagine that we need pdf to be the output format. Theres
nothing for pdf manupilation in jBC so well replace the procrep.exe with something else.
For simple pdf you can use Perl or Python, for more complex things Id suggest something
like TEX. Anyway, to create a fancy output youll either need some existing (and possibly
quite expensive) report engine or put a lot of labour to development of something more or
J of BASE, TAF of C

74

By KZM

14 CREATING REPORT REPRESENTATION

less appropriate. See this was just an example (with hard-coding that shouldnt happen in
the final product rather youll need to merge some templates with data). You see, thats a
lot of work and the final decision is as usual yours.

J of BASE, TAF of C

75

By KZM

15 MORE ABOUT REDIRECTION

15

More about redirection

ve communicated the impossibility of output redirection in jsh to one of my colleagues


:::: and see what I got as a reply:
Try to switch to sh mode pressing F2.
OK, here we are (note the change of prompt):
Another test of redirection; long command is split again
C:\sa-tafc> jrunT24.cmd
jsh ~ --> <F2>
sh ~ --> mvmtrep 20100101 20110101 | procrep > %TEMP%\out.html
&& %TEMP%\out.html

It works now. More about shell modes you can read in jBASE knowledgebase but be
aware of another difference sh mode doesnt store a select list. To illustrate that Ill
use an example from my previous book (which has a typo semicolon instead of a comma
right after FIELD(@ID sorry for that). This example shows how many T24 accounts have
history. But first of all return to jsh mode pressing F1:
Shells compatibility
sh ~ --> <F1>
jsh ~ --> SELECT FBNK.ACCOUNT$HIS SAVING UNIQUE EVAL "FIELD(@ID,;,1)"
406 Records selected
> SAVE.LIST AC.HIST
406 record(s) saved to list AC.HIST
jsh ~ --> EDIT.LIST AC.HIST
.jBASE.el.5
TOP
.P
TOP
001 17736
002 52237
003 36293
004 21307
005 57495

J of BASE, TAF of C

76

By KZM

15 MORE ABOUT REDIRECTION

006 57568
007 59803
008 17768
009 10437
010 42404
011 17051
012 41351
013 USD141300001
014 43397
015 20508
016 29823
017 41629
018 57948
019 11177
020 14516
021 43087
022 41971
.EXIT
Record .jBASE.el.5 exited from file .
List AC.HIST exited
jsh ~ -->

All looks correct so far. Then switch to sh mode (F2 again) and try the same:
Shells compatibility - continued
jsh ~ --> <F2>
sh ~ --> SELECT FBNK.ACCOUNT$HIS SAVING UNIQUE EVAL "FIELD(@ID,;,1)"
!!! Error message EVAL expression missing.
not found !!!

OK, thankfully theres another character to substitute a quote so SELECT works but
theres no active list after it:
Shells compatibility - continued
sh ~ --> SELECT FBNK.ACCOUNT$HIS SAVING UNIQUE EVAL \FIELD(@ID,;,1)\

406 Records selected


sh ~ --> SAVE.LIST AC.HIST
No default select list is active

J of BASE, TAF of C

77

By KZM

15 MORE ABOUT REDIRECTION

There is another mode msh (F3) but I havent investigated it yet. (I was told by
the colleague mentioned above that you can use OS commands and inheritance of SELECT
lists also works.) Next time you log in check in which mode you are this setting is saved
according to port number which, to be honest, doesnt make much sense nowadays since
every time you log in theres probably a different port number youre assigned. Check the
port of jsh using WHERE and then you can see where its stored:
Shells compatibility - the last note
jsh ~ --> <F3>
msh ~ --> WHERE
Port
*1
5

Device
ntcon
ntcon

Account
PID
Vladimir.Kaz 4004
Vladimir.Kaz 2868

Command
WHERE
jsh

msh ~ --> JED <path_to_TAFC>\tmp\jutil_ctrl jsh_o_5


File <path_to_TAFC>\tmp\jutil_ctrl , Record jsh_o_5
Insert
20:28:28
Command->
0001 $%s $%a $%c $%m-->
0002 msh
0003 $%m>
---------------------------------- End Of Record ------------------------------

Command jshelltype can also be used to set the shell type.


Having touched the theme of select lists here, its a good idea to continue with the
following chapter.

J of BASE, TAF of C

78

By KZM

16 SOME MORE TRICKS WITH SELECT LISTS; PARAGRAPHS

16

Some more tricks with select lists; paragraphs

here are some things that can be achieved using manipulations with select lists. And
::::: this might make life easier, sometimes a lot. For example, how to put all records in
particular file to DL.DEFINE record? We can use a list to populate said record and it needs
to contain data in the format APPLICATION>@ID. How to get such list? Easy:
Creation of a list for DL.DEFINE
C:\sa-tafc> jrunT24.cmd
jsh ~ --> SELECT F.ABBREVIATION SAVING EVAL "ABBREVIATION>:@ID"
290 Records selected
> SAVE.LIST ABBR
290 record(s) saved to list ABBR
jsh ~ --> EDIT.LIST ABBR
.jBASE.el.1
TOP
.P
TOP
001 ABBREVIATION>AGC
002 ABBREVIATION>COND
003 ABBREVIATION>FT
004 ABBREVIATION>COMM
005 ABBREVIATION>ESDC
006 ABBREVIATION>FTCOM
007 ABBREVIATION>SON
008 ABBREVIATION>IP
009 ABBREVIATION>POS
010 ABBREVIATION>ST
011 ABBREVIATION>GAP
012 ABBREVIATION>SCV
013 ABBREVIATION>MVL
014 ABBREVIATION>TICKLER
015 ABBREVIATION>CHQP
016 ABBREVIATION>LRT
017 ABBREVIATION>AVL
018 ABBREVIATION>EEE
019 ABBREVIATION>ESM
020 ABBREVIATION>SC5
021 ABBREVIATION>VE
022 ABBREVIATION>LDP
.EXIT
Record .jBASE.el.1 exited from file .
List ABBR exited

J of BASE, TAF of C

79

By KZM

16 SOME MORE TRICKS WITH SELECT LISTS; PARAGRAPHS

And now its ready for DL.DEFINE as soon as we copy this list (since DL.DEFINE
doesnt respect JBCLISTFILE environment variable and is yet unable to locate our select list):
Copying a list into T24
C:\sa-tafc> jrunT24.cmd
jsh ~ --> COPY FROM C:\sa-tafc\&SAVEDLISTS& TO &SAVEDLISTS& ABBR
1 records copied

Now enter the list name into field select.list of DL.DEFINE record:
Taking a list into DL.DEFINE
Model Bank

DL.DEFINE INPUT

UNIT.NAME......... TMNS000-ALL.ABBR
-----------------------------------------------------------------------------1. 1. 1 GB DESCRIPTN ALL ABBREVIATION
2. 1 GB SHORT.DESC.. ALL ABBR
3 LANGUAGE/COUNTRY..
4. 1 INDICES........
5 OPERATION......... S
6 SELECT.LIST....... ABBR_
7 TOP.LEVEL.TYPE....
8 TOP.LEVEL.ITEM....
9. 1 FILE.NAME......
10. 1 RECO
11. 1. 1 RECORD.DESC.
12. 1 SAVED.FROM.....
13. 1 SAVED.RELEASE..
14. 1 SAVED.DATE.....
15. 1 RESTORED.USER..
16. 1 RESTD.COMPANY..
-----------------------------------------------------------------------------29 JUN 2011 19:51:04 USER (09 AUG) VLADIMIR.K
[8427,INPAGE 1
>>>3>>>
ACTION

Now press Enter and voila we have 57 pages of the record being populated:
The list appears in DL.DEFINE
Model Bank

DL.DEFINE INPUT

UNIT.NAME......... TMNS000-ALL.ABBR
------------------------------------------------------------------------------

J of BASE, TAF of C

80

By KZM

16 SOME MORE TRICKS WITH SELECT LISTS; PARAGRAPHS

1. 1. 1 GB DESCRIPTN ALL ABBREVIATION


2. 1 GB SHORT.DESC.. ALL ABBR
3 LANGUAGE/COUNTRY..
4. 1 INDICES........
5 OPERATION......... S
6 SELECT.LIST.......
7 TOP.LEVEL.TYPE....
8 TOP.LEVEL.ITEM....
9. 1 FILE.NAME...... ABBREVIATION
10. 1 RECO AGC
11. 1. 1 RECORD.DESC.
9. 2 FILE.NAME...... ABBREVIATION
10. 2 RECO COND
11. 2. 1 RECORD.DESC.
9. 3 FILE.NAME...... ABBREVIATION
10. 3 RECO FT
-----------------------------------------------------------------------------29 JUN 2011 19:51:30 USER (09 AUG) VLADIMIR.K
[8427,INPAGE 1
>>57>>>
ACTION

Using SELECT...SAVING EVAL we can construct quite complex output for example,
set of OFS messages to proceed every record in unauthorised file. See the example of that:
Creation of OFS messages for mass processing - step 1
C:\sa-tafc> jrunT24.cmd
jsh ~ --> SELECT FBNK.FUNDS.TRANSFER$NAU WITH RECORD.STATUS EQ INAU
SAVING EVAL "FUNDS.TRANSFER,/A/PROCESS//0,INPUTT/123456,:@ID"
29 Records selected
>

(Following this example you need to be careful with the field record.status it might
also contain such values as CNAU or RNAU.)
Creation of OFS messages for mass processing - step 2
> SAVE.LIST QWE
29 record(s) saved to list QWE

Creation of OFS messages for mass processing - double-checking


jsh ~ --> EDIT.LIST QWE

J of BASE, TAF of C

81

By KZM

16 SOME MORE TRICKS WITH SELECT LISTS; PARAGRAPHS

.jBASE.el.2
TOP
.P
TOP
001 FUNDS.TRANSFER,/A/PROCESS//0,INPUTT/123456,FT102217DKNB
002 FUNDS.TRANSFER,/A/PROCESS//0,INPUTT/123456,FT10221SB54Y
003 FUNDS.TRANSFER,/A/PROCESS//0,INPUTT/123456,FT102211J2ZC
004 FUNDS.TRANSFER,/A/PROCESS//0,INPUTT/123456,FT102212BZ1C
005 FUNDS.TRANSFER,/A/PROCESS//0,INPUTT/123456,FT102211JH7V
006 FUNDS.TRANSFER,/A/PROCESS//0,INPUTT/123456,FT10221D843N
007 FUNDS.TRANSFER,/A/PROCESS//0,INPUTT/123456,FT102218J7CZ
008 FUNDS.TRANSFER,/A/PROCESS//0,INPUTT/123456,FT102174JFHF
009 FUNDS.TRANSFER,/A/PROCESS//0,INPUTT/123456,FT10221N587H
010 FUNDS.TRANSFER,/A/PROCESS//0,INPUTT/123456,FT10221KKNH8
011 FUNDS.TRANSFER,/A/PROCESS//0,INPUTT/123456,FT10221QVGZD
012 FUNDS.TRANSFER,/A/PROCESS//0,INPUTT/123456,FT102215SR4W
013 FUNDS.TRANSFER,/A/PROCESS//0,INPUTT/123456,FT10221WPC4Z
014 FUNDS.TRANSFER,/A/PROCESS//0,INPUTT/123456,FT10221SC80C
015 FUNDS.TRANSFER,/A/PROCESS//0,INPUTT/123456,FT10221LL3FG
016 FUNDS.TRANSFER,/A/PROCESS//0,INPUTT/123456,FT102214H8GF
017 FUNDS.TRANSFER,/A/PROCESS//0,INPUTT/123456,FT102214HGVB
018 FUNDS.TRANSFER,/A/PROCESS//0,INPUTT/123456,FT10221DNJHC
019 FUNDS.TRANSFER,/A/PROCESS//0,INPUTT/123456,FT10221BPZQB
020 FUNDS.TRANSFER,/A/PROCESS//0,INPUTT/123456,FT102219VV0M
021 FUNDS.TRANSFER,/A/PROCESS//0,INPUTT/123456,FT10221MD8GW
022 FUNDS.TRANSFER,/A/PROCESS//0,INPUTT/123456,FT10221KNLRM
.EXIT
Record .jBASE.el.2 exited from file .
List QWE exited

Launch tSS from jsh prompt:


Creation of OFS messages for mass processing - step 3
jsh ~ --> tSS TAG
<tSS version="1.1"><t24version>201015</t24version><t24pid>3508</t24pid>
<t24ofssource>TAG</t24ofssource><clientIP/></tSS>

Now copy and paste the first line from the saved list:
Creation of OFS messages for mass processing - step 4
FUNDS.TRANSFER,/A/PROCESS//0,INPUTT/123456,FT102217DKNB

J of BASE, TAF of C

82

By KZM

16 SOME MORE TRICKS WITH SELECT LISTS; PARAGRAPHS

FT102217DKNB//1,TRANSACTION.TYPE:1:1=AC,DEBIT.ACCT.NO:1:1=10693,CURRENCY.MKT.D
R:1:1=1,DEBIT.CURRENCY:1:1=USD,DEBIT.VALUE.DATE:1:1=20100809,CREDIT.ACCT.NO:1:
1=15997,CURRENCY.MKT.CR:1:1=1,CREDIT.CURRENCY:1:1=USD,CREDIT.AMOUNT:1:1=35820.
00,CREDIT.VALUE.DATE:1:1=20100809,PROCESSING.DATE:1:1=20100809,COMMISSION.CODE
:1:1=WAIVE,CHARGE.CODE:1:1=WAIVE,PROFIT.CENTRE.CUST:1:1=100318,RETURN.TO.DEPT:
1:1=NO,FED.FUNDS:1:1=NO,POSITION.TYPE:1:1=TR,AMOUNT.DEBITED:1:1=USD35820.00,AM
OUNT.CREDITED:1:1=USD35820.00,CREDIT.COMP.CODE:1:1=GB0010001,DEBIT.COMP.CODE:1
:1=GB0010001,LOC.AMT.DEBITED:1:1=35820.00,LOC.AMT.CREDITED:1:1=35820.00,CUST.G
ROUP.LEVEL:1:1=99,DEBIT.CUSTOMER:1:1=100318,CREDIT.CUSTOMER:1:1=100318,DR.ADVI
CE.REQD.Y.N:1:1=N,CR.ADVICE.REQD.Y.N:1:1=N,CHARGED.CUSTOMER:1:1=100318,TOT.REC
.COMM:1:1=0,TOT.REC.COMM.LCL:1:1=0,TOT.REC.CHG:1:1=0,TOT.REC.CHG.LCL:1:1=0,RAT
E.FIXING:1:1=NO,TOT.REC.CHG.CRCCY:1:1=0,TOT.SND.CHG.CRCCY:1:1=0,AUTH.DATE:1:1=
20100809,STMT.NOS:1:1=159340765753138.00,STMT.NOS:2:1=1-2,OVERRIDE:1:1=WITHDRA
WL.LT.MIN.BAL}WITHDRAWL MAKES A/C BAL LESS THAN MIN BAL,OVERRIDE:2:1=ACCT.DEBI
T.COLLATERAL}ACCOUNT &, DEBIT TO COLLATERAL{10693}100283.2.1,OVERRIDE:3:1=ACCT
.UNAUTH.OD}Unauthorised overdraft of & & on account &.{USD}24947.57}10693{USD{
24947.57{10693{100318{213{{,CURR.NO:1:1=1,INPUTTER:1:1=75_SUJA1_I_INAU_OFS_BRO
WSERTC,INPUTTER:2:1=3883_CONVERSION.DETAILS,DATE.TIME:1:1=1108161445,DATE.TIME
:2:1=1012220511,AUTHORISER:1:1=7657_INPUTTER_OFS_TAG,CO.CODE:1:1=GB0010001,DEP
T.CODE:1:1=1
EXIT
jsh ~ -->

After that you can continue with copy/paste even selecting all the rest at once will do.
(Though I noticed that very seldom some records were not processed so its better to issue
again the main SELECT after youve finished.) Alternatively, you can put the file containing
the SELECT list to the appropriate directory to be processed by batch file listener. Isnt
that a good replacement for EBS.AUTO.FUNCTION?
More about lists processing: two lists can be compared with logical AND, OR or
XOR operations applied to them. Here were getting T24 ACCOUNT records that were
created, then edited but the changes are not yet authorised and therefore these records
present in both live and $NAU files:
AND-LISTS example
C:\sa-tafc> jrunT24.cmd
jsh ~ --> SELECT FBNK.ACCOUNT
3620 Records selected
> SAVE.LIST ACCT1
3620 record(s) saved to list ACCT1
jsh ~ --> SELECT FBNK.ACCOUNT$NAU

J of BASE, TAF of C

83

By KZM

16 SOME MORE TRICKS WITH SELECT LISTS; PARAGRAPHS

6 Records selected
> SAVE.LIST ACCT2
6 record(s) saved to list ACCT2
jsh ~ --> AND-LISTS ACCT1 ACCT2
1 Records selected
> LIST ONLY FBNK.ACCOUNT
USD109100001
1 Records Listed

To get account records that are new that is, were created but not yet authorised so they
appear in $NAU file only we use a negative select:
NSELECT example
jsh ~ --> SELECT FBNK.ACCOUNT$NAU
6 Records selected
> NSELECT FBNK.ACCOUNT
5 Records selected
> LIST ONLY FBNK.ACCOUNT$NAU
@ID................
10014
22071
22063
46922
10022
5 Records Listed

If we use two subsequent SELECTs, results will differ under jsh and sh modes (because the latter will not store the select list after first SELECT). See (well use the option
(R to suppress error messages Error 202 Record not on file):
Two subsequent SELECTs under jsh
jsh ~ --> SELECT FBNK.ACCOUNT$NAU

J of BASE, TAF of C

84

By KZM

16 SOME MORE TRICKS WITH SELECT LISTS; PARAGRAPHS

6 Records selected
> SELECT FBNK.ACCOUNT (R
1 Records selected
> CLEARSELECT
jsh ~ -->

Two subsequent SELECTs under sh


C:\sa-tafc> jrunT24.cmd
jsh ~ --> <F2>
sh ~ --> SELECT FBNK.ACCOUNT$NAU
6 Records selected
sh ~ --> SELECT FBNK.ACCOUNT (R
3620 Records selected

How to avoid this confusion? Use require-select keyword:


Two subsequest SELECTs under sh- REQUIRE.SELECT used
C:\sa-tafc> jrunT24.cmd
jsh ~ --> <F2>
sh ~ --> SELECT FBNK.ACCOUNT$NAU
6 Records selected
sh ~ --> SELECT FBNK.ACCOUNT REQUIRE.SELECT (R
!!! Error message A Select list was required and not supplied.
not found !!!

require-select is the correct solution in any case, since there might be no records at
all in FBNK.ACCOUNT$NAU and that will be handled correctly.
By the way, we can automate this task creating so-called paragraph in VOC:
J of BASE, TAF of C

85

By KZM

16 SOME MORE TRICKS WITH SELECT LISTS; PARAGRAPHS

Creation of paragraph
C:\sa-tafc> jrunT24.cmd
jsh ~ --> JED VOC PA.SAMPLE
File VOC , Record PA.SAMPLE
Insert
21:17:12
Command->
0001 PA
0002 SELECT FBNK.ACCOUNT$NAU
0003 SELECT FBNK.ACCOUNT REQUIRE.SELECT
0004 LIST ONLY FBNK.ACCOUNT$NAU
----------------------------------- End Of Record -----------------------------

Save and run it:


Execution of paragraph
C:\sa-tafc> jrunT24.cmd
jsh ~ --> PA.SAMPLE
USD109100001
1 Records Listed

Paragraphs can have parameters; they also have a possibility of interaction with the user.
See example:
Creation of login paragraph
C:\sa-tafc> jrunT24.cmd
jsh ~ --> JED VOC K.LOGIN
File VOC , Record K.LOGIN
Insert
21:26:33
Command->
0001 PA
0002 ETS
0003 EX
0004 DATA INPUTT
0005 DATA 123456
0006 DATA <<C2,>>
----------------------------------- End Of Record -----------------------------

Save and run it and (provided that login and password are correct) you are in the chosen
application:

J of BASE, TAF of C

86

By KZM

16 SOME MORE TRICKS WITH SELECT LISTS; PARAGRAPHS

Execution of login paragraph


C:\sa-tafc> jrunT24.cmd
jsh ~ --> K.LOGIN AC
Model Bank

ACCOUNT

------------------------------------------------------------------------------

-- LAST SIGN.ON, DATE: 30 JUN 2011


TIME: 14:45
30 JUN 2011 19:48:23 USER (09 AUG) INPUTTER
ACTION
AWAITING FUNCTION

ATTEMPTS: 0 -------[6485,IN]

If you dont want to disclose your password (which is indeed a good idea) change the
paragraph:
change in login paragraph
0001
0002
0003
0004
0005
0006

PA
ETS
EX
DATA INPUTT
DATA <<PASSWORD>>
DATA <<C2,>>

And then youll be prompted for the password (though your input will be still visible on
the screen).
J of BASE, TAF of C

87

By KZM

16 SOME MORE TRICKS WITH SELECT LISTS; PARAGRAPHS

Note: paragraphs can be started only in jsh mode.


For more information about paragraphs see jBASE knowledgebase. Being there also take
a closer look at jCL thats another way to automate things. An example of its usage is
in VOC entry related to default jBASE login (which can be triggered at session start using
command jpqn VOC\loginproc instead of jsh in cmd file):
Default login to jBASE
C:\sa-tafc> jrunT24.cmd
jsh ~ --> JED VOC loginproc
File VOC , Record loginproc
Insert
22:04:19
Command->
0001 PQN
0002 OSTART GLOBUS Y/N
0003 IP%1
0004 IF %1 # "Y" IF %1 # "y" GO 99
0005 HEBS.TERMINAL.SELECT
0006 P
0007 HEX
0008 P
0009 99 Hjsh -s jsh
0010 PX
----------------------------------- End Of Record -----------------------------

J of BASE, TAF of C

88

By KZM

17 EASTER EGGS

17

Easter eggs

ired? Lets relax a bit. You know about easter eggs in a context that is related to
::::: software it is some functionality that was by accident or deliberately put there and
isnt easy to see (normally you should know exactly how to activate one). As an example,
for very long time in every jBASE object file even one resulting from compilation of a local
routine there was a string Austin Powers. Now its not there possibly because I asked
Martin Bailey about that fact couple of years ago but if you have older release like R08
you might still have it in every .obj or .o file.
Another example GLOBUS.BAN case that Ive described earlier. One more now serious
example is the usage of no.fatal.error option in opf subroutine belonging to T24
API. Many things like that can be found out only if you have the opportunity to examine
the core source code. For example, that you can type debug in tSS and enter the debugger.
(And then you can store all available variables if you type V * > out.txt it really worth
looking at, especially after some OFS request like one described in the previous chapter.)
And often you can see an advice in discussion groups: use T24 routine THIS.AND.THAT
to achieve your goal. Should you? Maybe yes but firstly check if its a core or a part of the
official API. In case of core, better dont probably a person advising that has the access to
core sources and he thinks that its an appropriate one. But youre not able to see that source
and therefore youre at your own risk. Moreover, any routine might become deprecated in
the next release and disappear just like Austin Powers did.
There are also some parts that possibly are long since redundant but are still there
nevertheless. Ive written earlier about Universe object code that is still in Model Bank.
Another rather funny thing an application called BLOCK.LETTERS:
Application BLOCK.LETTERS
C:\sa-tafc> jrunT24.cmd
jsh ~ --> LIST F.BLOCK.LETTERS
@ID.............
@ID.............
ASCII.CODE......
LINE.1..........
LINE.2..........
LINE.3..........
LINE.4..........
LINE.5..........
RECORD.STATUS...
CURR.NO.........
INPUTTER........
DATE.TIME.......
AUTHORISER......
CO.CODE.........
DEPT.CODE.......
AUDITOR.CODE....
AUDIT.DATE.TIME.

66
66
66
BBBB
B
B
BBBB
B
B
BBBB
1
1_INPUTTER
9410031621
1_AUTHORISER
GB0010001
200

J of BASE, TAF of C

89

By KZM

17 EASTER EGGS

@ID.............
@ID.............
ASCII.CODE......
LINE.1..........
LINE.2..........
LINE.3..........
LINE.4..........
LINE.5..........
RECORD.STATUS...
CURR.NO.........
INPUTTER........
DATE.TIME.......
AUTHORISER......
CO.CODE.........
DEPT.CODE.......
AUDITOR.CODE....
AUDIT.DATE.TIME.

51
51
51
33333
3
33333
3
33333
1
1_INPUTTER
9410031621
1_AUTHORISER
GB0010001
200

...

See? Here are all printable characters rendered for ASCII-art-like output. In Universe
there was a command with the same name if I remember correctly of course which
produced similar-looking output. Can we put it back to life? Why not? Lets try to produce
a custom GLOBUS.BAN using these renderings.
First of all we take the phrase from command line and proceed it according to screen
limitations:
1
2
3

custgban.b - the beginning


* Program to create
* a custom GLOBUS.BAN. Let it be a word or phrase up to 12 characters
* placed at the center of the screen.

4
5
6

DIM V.LINE.L(5)
MAT V.LINE.L =

;* array to keep rendered characters


;* every element will be a dynamic array

7
8

V.PHRASE = SYSTEM(1000)

9
10
11
12

DEL V.PHRASE<1>
CHANGE @FM TO IN V.PHRASE
V.PHRASE = UPCASE(V.PHRASE[1,12])

;* its program name itself


;* only uppercase images in file

Then open our files. Since we are overwriting GLOBUS.BAN here never try this program
on a server since other users might be affected.
custgban.b - continued
13
14

V.FILE = F.BLOCK.LETTERS

J of BASE, TAF of C

90

By KZM

17 EASTER EGGS

15

OPEN V.FILE TO F.BL ELSE ABORT 201, V.FILE

16
17
18
19
20
21
22
23
24

OPENSEQ ., GLOBUS.BAN TO F.OUT.FILE THEN


WEOFSEQ F.OUT.FILE
END ELSE
CREATE F.OUT.FILE ELSE
CRT OUTPUT FILE CREATION ERROR
STOP
END
END

25

Now were filling the array with rendered characters:


custgban.b - continued
26
27
28
29
30

V.DIFF = 0
V.LEN = LEN(V.PHRASE)
V.OUT.LEN = V.LEN
V.OUT = 0
FOR V.I = 1 TO V.LEN

31
32

V.OUT ++

33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

V.ID = SEQ(V.PHRASE[V.I,1])
IF V.ID EQ 32 THEN
;* space
V.L1 = STR( , 3)
;* make it narrower
V.L2 = STR( , 3)
V.L3 = STR( , 3)
V.L4 = STR( , 3)
V.L5 = STR( , 3)
V.DIFF += 2
;* and count the difference
END ELSE
READ R.BL FROM F.BL, V.ID ELSE
V.OUT.LEN -V.OUT -CONTINUE ;* ignore absent characters
END
V.L1 = FMT(R.BL<1>, 5L)
V.L2 = FMT(R.BL<2>, 5L)
V.L3 = FMT(R.BL<3>, 5L)
V.L4 = FMT(R.BL<4>, 5L)
V.L5 = FMT(R.BL<5>, 5L)
END

54
55
56
57
58

V.LINE.L(1)<V.OUT>
V.LINE.L(2)<V.OUT>
V.LINE.L(3)<V.OUT>
V.LINE.L(4)<V.OUT>

J of BASE, TAF of C

=
=
=
=

V.L1
V.L2
V.L3
V.L4

91

By KZM

17 EASTER EGGS

59

V.LINE.L(5)<V.OUT> = V.L5

60
61

NEXT V.I

Some calculations to make output look nice:


custgban.b - continued
62
63

* See how wide should be the gap between characters

64
65
66
67
68

V.GAP = INT((79 - V.OUT.LEN * 5 + V.DIFF) / (V.OUT.LEN + 2))


V.MID.LEN = V.OUT.LEN * (V.GAP + 5) - V.GAP - V.DIFF
V.L.OFF = INT((77 - V.MID.LEN) / 2)
;* offset from left
V.R.OFF = 77 - V.L.OFF - V.MID.LEN
;* and from right

Finally with header, body and footer of the report:


custgban.b - finished
69
70

* Write the output - dummy lines first

71
72
73
74
75
76

V.EMPTYL = | : STR( , 77)


FOR V.I = 1 TO 5
V.LINE = V.EMPTYL
GOSUB WRITE.LINE
NEXT V.I

: |

77
78

* Write characters

79
80

FOR V.I = 6 TO 10

81
82
83

V.MIDDLE =
V.PROC.L = V.LINE.L(V.I-5)

84
85
86
87
88

FOR V.J = 1 TO V.OUT.LEN


IF V.J NE 1 THEN V.MIDDLE := STR( , V.GAP)
V.MIDDLE := V.PROC.L<V.J>
NEXT V.J

89
90
91

V.LINE = | : STR( , V.L.OFF) : V.MIDDLE : STR( , V.R.OFF) : |


GOSUB WRITE.LINE

92
93

NEXT V.I

94
95

* Write the output - dummy lines at the footer

96
97

FOR V.I = 11 TO 16

J of BASE, TAF of C

92

By KZM

17 EASTER EGGS

V.LINE = V.EMPTYL
GOSUB WRITE.LINE
NEXT V.I

98
99
100
101
102

STOP

103
104
105

*~~~~~
WRITE.LINE:

106
107
108
109
110

WRITESEQ V.LINE TO F.OUT.FILE ELSE


CRT WRITE ERROR
STOP
END

111
112

RETURN

113
114
115

*~~~~~
END

116

Now compile it and try something:


custgban.exe usage example
C:\sa-tafc> jrunT24.cmd
jsh ~ --> custgban.exe hi you
jsh ~ --> ETS
Terminal type set to EBS-JBASE
jsh t24 ~ -->EX

Which results in:

J of BASE, TAF of C

93

By KZM

17 EASTER EGGS

custgban results
GLOBUS Rev. 201015

|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|

SIGN.ON

Copyright (c) Temenos Systems Ltd 2011

-----------------------------------------------------------------------------|
|
|
|
|
H
H
IIIII
Y
Y
OOO
U
U
|
H
H
I
Y Y
O
O
U
U
|
HHHHH
I
Y
O
O
U
U
|
H
H
I
Y
O
O
U
U
|
H
H
IIIII
Y
OOO
UUUUU
|
|
|
|
|
|
|
-----------------------------------------------------------------------------06 JUL 2011 20:13:08 USER
[5257,IN]
ACTION
PLEASE ENTER YOUR SIGN ON NAME

J of BASE, TAF of C

94

By KZM

17 EASTER EGGS

Or:
custgban results
GLOBUS Rev. 201015

|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|

SIGN.ON

Copyright (c) Temenos Systems Ltd 2011

-----------------------------------------------------------------------------|
|
|
|
|
H
H IIIII
Y
Y OOO U
U
TTTTT H
H EEEEE RRRR EEEEE
|
H
H
I
Y Y O
O U
U
T
H
H E
R
R E
|
HHHHH
I
Y
O
O U
U
T
HHHHH EEEEE RRRR EEEEE
|
H
H
I
Y
O
O U
U
T
H
H E
R R
E
|
H
H IIIII
Y
OOO UUUUU
T
H
H EEEEE R R EEEEE
|
|
|
|
|
|
|
-----------------------------------------------------------------------------06 JUL 2011 20:13:44 USER
[7289,IN]
ACTION
PLEASE ENTER YOUR SIGN ON NAME

Before we return to serious matters, lets try to solve the task of placing the current
system date to T24 login screen. Firstly we need a small program that forms a string with
current date and then triggers custgban with appropriate data. (Of course we could put this
functionality to custgban.b but Id like to illustrate the usage of chain command in jBC
which passes the execution to another program without ever returning back.)
1

date2gban.b
* Run custgban.exe with current date

V.DATE = OCONV(DATE(),"D")
CHAIN custgban : V.DATE

3
4
5
6

END

Then compile it and amend our login paragraph (well stop at the point where we are
able to see the login screen):

J of BASE, TAF of C

95

By KZM

17 EASTER EGGS

New version of K.LOGIN


C:\sa-tafc> jrunT24.cmd
jsh ~ --> JED VOC K.LOGIN
File VOC , Record K.LOGIN
Insert
21:12:07
Command->
0001 PA
0002 date2gban
0003 ETS
0004 EX
0005 DATA INPUTT
----------------------------------- End Of Record -----------------------------

Result:
date2gban + custgban results
GLOBUS Rev. 201015

|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|

SIGN.ON

Copyright (c) Temenos Systems Ltd 2011

-----------------------------------------------------------------------------|
|
|
|
|
00000
6
JJJJJ U
U L
22222 00000
1
1
|
0
0
6
J
U
U L
2 0
0
111
111
|
0
0
666
J
U
U L
22222 0
0
1
1
|
0
0
6 6
J J
U
U L
2
0
0
1
1
|
00000
66
JJJJ
UUUUU LLLLL
22222 00000 11111 11111
|
|
|
|
|
|
|
-----------------------------------------------------------------------------06 JUL 2011 20:13:10 USER
[3876,IN]
ACTION
PLEASE ENTER YOUR SIGN ON NAME

We also could pass the data between programs using a common area. Also we cound use
yet another table F.CODED.BLOCK.LETTERS... And last but not least we could just use
a paragraph named GLOBUS.BAN for this purpose it has the preference over a text file with
the same name:
J of BASE, TAF of C

96

By KZM

17 EASTER EGGS

jCL example
C:\sa-tafc> jrunT24.cmd
jsh ~ --> JED VOC GLOBUS.BAN
File VOC , Record GLOBUS.BAN
Insert
21:17:17
Command->
0001 PQN
0002 T (25,10), *=10, Welcome!, *=10
----------------------------------- End Of Record -----------------------------

Now see the effect:


jCL execution results
GLOBUS Rev. 201015

SIGN.ON

Copyright (c) Temenos Systems Ltd 2011

------------------------------------------------------------------------------

==========Welcome!==========

-----------------------------------------------------------------------------17 AUG 2011 21:31:55 USER


[6441,IN]
ACTION
PLEASE ENTER YOUR SIGN ON NAME

Very simple example though jCL is capable of many things like hashed files opening,
reading and writing (the latter I surely dont recommend at the stage of logging in to T24),
if...else logic etc. Ive tried to use it to solve the task described in one of earlier chapters
output all records of ACCT.ACTIVITY containing movements for the 16th day of every
month (suppressing unnecessary values). It took me about 4 hours to compose a paragraph
that outputs the day number, record @id and debit turnover:
More complex jCL example
C:\sa-tafc> jrunT24.cmd
jsh ~ --> JED VOC TEST.ACCT.ACT

J of BASE, TAF of C

97

By KZM

17 EASTER EGGS

File VOC , Record TEST.ACCT.ACT


Insert
23:32:21
Command->
0001 PQN
0002 F-OPEN 5 FBNK.ACCT.ACTIVITY
0003 X ERROR: Cant open activity file
0004 HSSELECT FBNK.ACCT.ACTIVITY
0005 STON
0006 HPQ-SELECT 1
0007 P
0008 STOFF
0009 10 MV %1 !1
0010 IF # %1 X<<<EOF>>>
0011 F-READ 5 %1
0012 X ERROR: Cant read activity file
0013 MV !2 &5.1
0014 MV !3 &5.3
0015 30 MV %1 !2
0016 MV %2 !3
0017 IF # %1 GO 10
0018 IF %1 = 16 T %1, ;, &5.0, ;, %2, I13, I10
0019 GOTO 30
0020 GO 10
----------------------------------- End Of Record -----------------------------

and here are results:


jCL preparing raw report data
jsh ~ --> TEST.ACCT.ACT

2749 Records selected

2749 Records selected


16;CHFUSD140160018-201007;-70.84
16;CHFUSD140160019-201007;-63.29
16;10596-201007;-500
16;10693-201007;
...
16;USDJPY140160017-201007;-1348.84
16;USDJPY140160018-201007;-2312.07
16;USDJPY140180004-201007;-0.52
16;USDSGD140160012-201007;-22.39
16;USDSGD140160013-201007;
<<<EOF>>>

J of BASE, TAF of C

98

By KZM

17 EASTER EGGS

jsh ~ -->

To my opinion, jCL is slightly easier to learn than Brainf**k. But for very simple things
it still might be used. I really hate the idea of making this paragraph compatible with the
latest version of mvmtrep.b and test all the report processing chain described in an earlier
chapter. But if you have some spare time, you can try it yourself.

J of BASE, TAF of C

99

By KZM

18 AND YET MORE PROGRAMMING TIPS

18

And yet more programming tips

hy all these exercises that most possibly will never be used under T24? I think its
:::::: good to know your main work tool especially what it can do and what it cant. I
can give you the following example: once I was given a code that split a long line into several
ones with smaller length (nothing complex like keeping words bounds just cut the line).
That code implemented a loop that cut pieces and put them to dynamic array one by one.
Having seen that, I asked: why not use fmt()? See:
fmtsample.b
1

* FMT() in string cutting

V.IN = This is quite a long line that we have to break into parts \
: with length of 35 characters while not thinking about cutting \
: a word in two in the very middle. So we are going to use FMT() \
: for this tremendous task
V.OUT = FMT(V.IN, 35L)
CHANGE @TM TO @FM IN V.OUT

3
4
5
6
7
8
9

V.CNT = DCOUNT(V.OUT, @FM)


FOR V.I = 1 TO V.CNT
CRT Line : V.I : is: : V.OUT<V.I>
NEXT V.I

10
11
12
13
14

STOP

15
16
17

END

18

The output:
fmtsample output
C:\sa-tafc> jrunSH.cmd
jsh ~ --> fmtsample
Line 1 is: This is quite a long line that we h
Line 2 is: ave to break into parts with length
Line 3 is: of 35 characters while not thinkin
Line 4 is: g about cutting a word in two in th
Line 5 is: e very middle. So we are going to u
Line 6 is: se FMT() for this tremendous task

Unfortunately its not in manuals that if you use fmt() to format a string padding it
to less width than it actually has youll end up with that string delimited with text marks
(@tm, or ASCII 251). I came to that looking at some strange output in a report where a
programmer issued an fmt() for a longer string and strange characters appeared as a result.

J of BASE, TAF of C

100

By KZM

18 AND YET MORE PROGRAMMING TIPS

I happened to have two different consultants to give me a chunk of code doing the same
things as one-two lines can do. One of them after seeing my proposition said: didnt
know that, will use it from now on. Another told me why should I care since my code
works? So here you see the difference between T24 consultant and good T24 consultant.
Another issue. Once upon a time I had a discussion with my colleagues about some
recommendations in the Temenos document describing programming standards. Among
other things, it says (quoting from my memory): use remove...from to retrieve items
from a list in a loop, its faster than extract each element using for...next. I then made a
test to see if its true. But firstly we need to see where we can obtain a list which isnt very
small. So:
In search of a big SELECT
C:\sa-tafc> jrunT24.cmd
jsh ~ --> COUNT FBNK.CUSTOMER
423 Records counted

Oops... what about number of accounts?


Still in search of a big SELECT...
jsh ~ --> COUNT FBNK.ACCOUNT
3620 Records counted

Still not enough... Well then use a trick - put all the fields of every ACCOUNT record
into the select list:
BSELECT helps us
jsh ~ --> BSELECT FBNK.ACCOUNT
774007 Records selected
>

Well, much better now. Here goes the code of our test:
retritem.b
1

* Test of array retrieval speed

2
3
4
5

COMMON /MY.COMMON/ V.RUN.NR


V.RUN.NR ++
IF V.RUN.NR GT 1 THEN

J of BASE, TAF of C

101

By KZM

18 AND YET MORE PROGRAMMING TIPS

CRT Try again a a new session


STOP

6
7
8

END

Here we use named common to understand if this program was invoked in a fresh
session to avoid inaccuracy of results caused by, say, cache etc. But before continuing, lets
test this approach first. Just add stop...end after that code (it probably will work and
without that but its better to keep formalities). Compile and run it:
COMMON area test
C:\sa-tafc> jrunComp.cmd retritem
retritem.c
C:\sa-tafc>jrunT24.cmd
jsh ~ --> retritem 1
jsh ~ --> retritem 1
jsh ~ -->

No it doesnt tell us that we need to run it in a new session. Obviuosly common area
wasnt preserved. Why? My colleague already mentioned here a few times told me that:

...jcompile always creates 2 executables (.so, .el for Unix/Linux and


.exe, .dll for Windows). In our case, if .dll presents then its being
loaded into memory, thus giving us the ability to store the common
area. If theres no .dll, .exe is executed but then nothing neither
common nor chain sequence can be stored. In addition, jBASE uses
environment variable %path% to find a .dll so we need the path to
our work directory to present there.
Yes, I noticed that without .dll chain didnt work as expected the execution returned
to parent program after child has finished...
Firstly lets amend our compilation command file so it no more deletes .dll file after
compilation:
More changes in jrunComp.cmd
34

jcompile -I <path_to_bnk.run_parent_directory>\T24.BP %1.b && del %1.obj

J of BASE, TAF of C

102

By KZM

18 AND YET MORE PROGRAMMING TIPS

We need then to correct the %path% in jrunT24.cmd and jrunSH.cmd:


More changes in %PATH%
20
21

set PATH=%COMPILER_HOME%\bin;%TAFC_HOME%\bin;C:\windows\system32\;%HOME%

Now:
COMMON area test
C:\sa-tafc> jrunComp.cmd retritem
retritem.c
C:\sa-tafc> jrunT24.cmd
jsh ~ --> retritem 1
jsh ~ --> retritem 1
Try again a a new session
jsh ~ -->

OK now. By the way, why weve just increased the value of v.run.nr and didnt get an
error? So it was 0 from the beginning? And where is it specified?
Here we touch an area that is called emulation. For T24 prime emulation is used.
Have you noticed an environment variable JBCEMULATE being set to prime? Take a look
into configuration file; the setting that interests us is named common:
jBASE emulation settings - prime
C:\sa-tafc> jrunSH.cmd
jsh ~ --> CT <path_to_TAFC>\config Config_EMULATE
...
192 #
193 # Emulation for Prime.
194 #
195 prime:
196 .dup = jbase
...
204 .named_common = zero

What if we hadnt set up that environment variable? See the default:


jBASE default emulation settings
009 jbase:
010 default:
...
028 .named_common = null

You can find decriptions of emulation settings in the file Config EMULATE.txt which is
located in the same directory as Config EMULATE. For example:
J of BASE, TAF of C

103

By KZM

18 AND YET MORE PROGRAMMING TIPS

Settings for named common


named_common = unassigned|null|zero
Shows how to set named common when first referenced. Default is unassigned.
setting can be :
"zero" to initialise to numeric 0
"unassigned" to keep it as an unassigned variable
"null" to create it as a zero length null string.

So under different emulations our program will have different behaviour since were not
able to add 1 to an unassigned variable or a null without getting an error or, in fact, we
are since error messages are successfully suppressed by our current setup which is typical for
T24.
There are certain following environment variables namely, jbase errmsg zero used
and jbase errmsg non numeric which control jBASE behaviour in such cases. For
example, we have them being set to suppress error messages and not enter debugger. There
is a self-explanatory one jbase errmsg divide by zero as well.
To see how they work well tackle them and try to violate some rules. Example 1:
testnull.b
1

V.RUN.NR ++
CRT V.RUN.NR

2
3
4

STOP

5
6

END
testnull run
C:\sa-tafc> jrunComp.cmd testnull
testnull.c
C:\sa-tafc> jrunEXE.cmd testnull.exe
1

So far, so good. Add some control (using putenv() to change the environment):
testnull.b - changed
1
2
3
4
5

IF NOT(PUTENV(JBASE_ERRMSG_ZERO_USED=0)) THEN
CRT PUTENV failed
STOP
END

6
7

V.RUN.NR ++

J of BASE, TAF of C

104

By KZM

18 AND YET MORE PROGRAMMING TIPS

CRT V.RUN.NR

8
9

STOP

10
11

END

12

testnull run
C:\sa-tafc> jrunComp.cmd testnull
testnull.c
C:\sa-tafc>jrunEXE.cmd testnull.exe
Invalid or uninitialised variable -- NULL USED ,
Var V.RUN.NR , Line
7 , Source testnull.b
1

Example 2. Add a number to a string:


testnull.b
1

V.RUN.NR = ABC
V.RUN.NR ++
CRT V.RUN.NR

2
3
4
5

STOP

6
7

END

testnull run
C:\sa-tafc> jrunComp.cmd testnull
testnull.c
C:\sa-tafc> jrunEXE.cmd testnull.exe
1

Looks OK but only looks so. Add the control now:


testnull.b - changed
1
2
3
4
5

IF NOT(PUTENV(JBASE_ERRMSG_NON_NUMERIC=0)) THEN
CRT PUTENV failed
STOP
END

6
7
8
9

V.RUN.NR = ABC
V.RUN.NR ++
CRT V.RUN.NR

J of BASE, TAF of C

105

By KZM

18 AND YET MORE PROGRAMMING TIPS

10

STOP

11
12

END

13

testnull run
C:\sa-tafc> jrunComp.cmd testnull
testnull.c
C:\sa-tafc> jrunEXE.cmd testnull.exe
Non-numeric value -- ZERO USED ,
Variable (UNKNOWN) , Line
8 , Source testnull.b
1

See the difference? Id strongly recommend to test your programs with these variables
set to 0.
Back to out task in case you havent forgotten yet what it was. And it was to see
which method used for retrieving an array element is faster remove or for...next. Heres
the code (I repeat the first part that checks if this program was run already to refresh
your memory):
retritem.b
1

* Test of array retrieval speed

2
3
4
5
6
7
8

COMMON /MY.COMMON/ V.RUN.NR


V.RUN.NR ++
IF V.RUN.NR GT 1 THEN
CRT Try again a a new session
STOP
END

Then we get the method being used this time (passed as first parameter, defaults to 1):
retritem.b
9
10
11

V.METHOD = SENTENCE(1)
IF V.METHOD NE 2 THEN V.METHOD = 1

Now its time for bselect to get a decent SELECT list:


retritem.b
12
13

EXECUTE BSELECT FBNK.ACCOUNT RTNLIST V.BIG.L

14

J of BASE, TAF of C

106

By KZM

18 AND YET MORE PROGRAMMING TIPS

Unfortunately as a quick test showed that list wasnt still long enough, to expand it
just add it to itself 5 times:
retritem.b
V.CNT = 5
FOR V.I = 1 TO V.CNT
V.BIG.L := @FM : V.BIG.L
NEXT V.I

15
16
17
18

Time to start; method #1...


retritem.b
19

V.STRT = TIME()

20
21

IF V.METHOD EQ 1 THEN

22
23

LOOP
REMOVE V.ID FROM V.BIG.L SETTING V.STATUS
IF V.STATUS EQ 0 THEN BREAK
REPEAT

24
25
26
27
28

...and #2, then print the result:


retritem.b
END ELSE

29
30

V.CNT = DCOUNT(V.BIG.L, @FM)

31
32

FOR V.I = 1 TO V.CNT


V.ID = V.BIG.L<V.CNT>
NEXT V.I

33
34
35
36

END

37
38

CRT TIME() - V.STRT

39
40

STOP

41
42

END

43

Compile, run - and see that for...next wins:


retritem run
C:\sa-tafc> jrunComp.cmd retritem

J of BASE, TAF of C

107

By KZM

18 AND YET MORE PROGRAMMING TIPS

retritem.c
C:\sa-tafc> jrunT24.cmd
jsh ~ --> retritem 1
774007 Records selected
10
jsh ~ --> exit
C:\sa-tafc> jrunT24.cmd
jsh ~ --> retritem 2
774007 Records selected
5
jsh ~ -->

Not sure if other platforms give the same result you can try it yourself to see. You
might see 0 as a result for both methods in this case your computer is faster than that
virtual machine of mine (almost 100% sure that it is). Then just increase the array being
tested.
Last for this chapter cant resist to put here <-1> usage note. You might have said
that in the line 17 of retritem.b I could have used the following syntax:
retritem.b
17

V.BIG.L<-1> = V.BIG.L

Why Im not the big fan of <-1>? Not only it might be slower (as some sources say
especially when adding subvalues) but it also works a bit differently from my chosen method.
See:
additem.b
1
2
3
4
5
6
7
8
9

V.ARR =
V.ARR<-1> =
V.ARR<-1> =
V.ARR<-1> =
V.ARR<-1> =
V.ARR<-1> =
V.ARR<-1> =
V.ARR<-1> =

10
11

V.ARR.2 =

J of BASE, TAF of C

108

By KZM

18 AND YET MORE PROGRAMMING TIPS

V.ARR.2<1>
V.ARR.2<2>
V.ARR.2<3>
V.ARR.2<4>
V.ARR.2<5>
V.ARR.2<6>
V.ARR.2<7>

12
13
14
15
16
17
18

=
=
=
=
=
=
=

19

CHANGE @FM TO ^ IN V.ARR


CHANGE @FM TO ^ IN V.ARR.2

20
21
22

CRT V.ARR
CRT V.ARR.2

23
24
25

STOP

26
27

END

28

Before you compile and run it, ask yourself: will the output be the same?
additem run
C:\sa-tafc> jrunComp.cmd additem
additem.c
C:\sa-tafc> jrunEXE.cmd additem
A^^B^^C
^^A^^B^^C

I know I hadnt any empty values at the start for my last test but in other cases it could
be different so I just always use the safer method.

J of BASE, TAF of C

109

By KZM

19 2GB ISSUE; FILE DISTRIBUTION

19

2Gb issue; file distribution

hen a file reaches 2Gb in size it might become corrupted in case it has j4 type that
:::::: many T24 files do. The common question is what to do to have your data safe
should it happen?
There are several opportunities:
Convert to some newer type that supports more than 2Gb size. Namely: jp or jr.
Before doing that think: do you really need so big files in your system? What about backups
taking longer and longer time? Most probably you no more need the historical data in that
file to be always available? 2 Gb issue mainly concerns such things like STMT.ENTRY
or FUNDS.TRANSFER ($his for the latter) and these files accumulate data that rarely or
never change. (You might say that data never changes there yes, individual records do not
but from logical point of view by rarely I meant that in FT history an additional record
for existing deal might be created later in case of reversal, for example.)
Another option is to proceed with Archive functionality of T24. It creates a new
data file $arc and puts there records that belong to dates earlier than a date that
you set for archiving. The problem here that only one additional file is created (and what
if it reaches 2Gb as well?). In adition, not all records for old dates might be moved. (I
remember a rather funny reply of a helpdesk for such issue: For the CATEG.ENTRY record
135920009041014.000001, the value date is 18 MAR 3005. As this is a forward categ.entry
generated for the PL category 52-344, this is not archived.) Again, youll need to create
new enquiries for $arc file. (And if you need a report that takes data from both files youll
need to develop a nofile enquiry.) Last but not least if you upgrade your T24 and the file
structure changes, $arc file will still keep the old structure at least it did last time Ive
checked.
My favourite option to distribute the data file. Distribution means that several
physical data files can be logically one file for jBASE (and therefore for T24). The procedure
is available at the helpdesk on request (in my opinion its not complete but it works). Of
course you can also try to apply archival described above and then distribute the $arc
file.
Lets proceed with example of file distribution right now.
Firstly we need to choose a file. It should be not very little file; it should be T24 file (since
were going to play with VOC entries a little to achieve one nice thing that goes beyond the
official procedure but makes life much easier).
Browsing the subdirectories of bnk.data with files sorted by size, what we see? aa...
better not to meddle with AA stuff... ac...biggest are stmt.entry and categ.entry...
Could be used despite about 10,000 records only but their @ids are not fit to what Im
thinking about and I think about organizing parts of a distributed files (promptly named
part files) on date principle so we have separate part file(s) for particular periods of time
and then we can store them separately to reduce backup time or even put away from our
server and get back only when required. Unfortunately in @ids of both stmt.entry and
categ.entry only the system time of their creation presents rather than the bank date and
J of BASE, TAF of C

110

By KZM

19 2GB ISSUE; FILE DISTRIBUTION

for distribution we can only use @ids.


Going further... de... for delivery there are their own procedures called end of period,
or at least there was last time Ive checked.... eb... why F LOCKING is the biggest? Take a
look:
Contents of F.LOCKING
C:\sa-tafc> jrunT24.cmd
jsh ~ --> LIST ONLY F.LOCKING
@ID.....................................
UNIQUE.KEY.857
DUMMY.LOCKS-BNK/SYSTEM.END.OF.DAY3-IC.COB-2-154-3-567-20100709
DUMMY.LOCKS-BNK/SYSTEM.END.OF.DAY3-IC.COB-2-119-1-308-20100709
DUMMY.LOCKS-EU1/SYSTEM.END.OF.DAY5-EOD.CRF.PL.UPDATE-24-3-1-514-20100730
DUMMY.LOCKS-BNK/SYSTEM.END.OF.DAY5-EOD.CRF.PL.UPDATE-24-131-1-292-20100804
DUMMY.LOCKS-BNK/SYSTEM.END.OF.DAY5-EOD.CRF.PL.UPDATE-24-113-7-1-856-20100705
DUMMY.LOCKS-BNK/SYSTEM.END.OF.DAY5-EOD.CRF.PL.UPDATE-24-126-5-536-20100804
DUMMY.LOCKS-BNK/SYSTEM.END.OF.DAY3-IC.COB-2-105-3-1-671-20100706
DUMMY.LOCKS-BNK/SYSTEM.END.OF.DAY3-IC.COB-2-119-5-536-20100716
DUMMY.LOCKS-BNK/SYSTEM.END.OF.DAY5-EOD.CRF.PL.UPDATE-24-157-9-853-20100705
DUMMY.LOCKS-BNK/SYSTEM.END.OF.DAY5-EOD.CRF.PL.UPDATE-24-114-1-536-20100716
DUMMY.LOCKS-BNK/SYSTEM.END.OF.DAY5-EOD.CRF.PL.UPDATE-24-112-9-574-20100729
DUMMY.LOCKS-BNK/SYSTEM.END.OF.DAY5-EOD.CRF.PL.UPDATE-24-16-1-522-20100726
UNIQUE.KEY.2625
DUMMY.LOCKS-BNK/SYSTEM.END.OF.DAY5-EOD.CRF.PL.UPDATE-24-113-8-1-559-20100713
DUMMY.LOCKS-BNK/SYSTEM.END.OF.DAY3-IC.COB-2-146-5-574-20100729
DUMMY.LOCKS-BNK/SYSTEM.END.OF.DAY5-EOD.CRF.PL.UPDATE-24-121-4-1-609-20100722
NULL.READ.KEY_20100804_628
NULL.READ.KEY_20100805_715
DUMMY.LOCKS-BNK/SYSTEM.END.OF.DAY5-EOD.CRF.PL.UPDATE-24-118-2-1-775-20100729

Definitely most of this temporary stuff can be removed (but ask helpdesk first). Further
on goes F ENQUIRY SELECT which holds selections for all users also temporary stuff:
Contents of F.ENQUIRY.SELECT
C:\sa-tafc> jrunT24.cmd
jsh ~ --> LIST ONLY F.ENQUIRY.SELECT
@ID.......
%DOCUMENT.REQUIRED_GB0010001_BUILDUSER0112_1299579055130_
SC.HOLD.SUM.BY.SEC_GB0010001_BUILDUSER13_workarea039915088101_tab2
INV.PROG.LIST_GB0010001_BUILDUSER21_INVESTMENT027973490300_
AC.DETAILS.ARRANGEMENT_GB0010001_BUILDUSER1_Arrangement010244547401_
FT.TXN.DELIVERY.LIVE_GB0010001_BUILDUSER2_FTLIVE010974091003_
AA.REQUEST.PAYOFF_GB0010001_CREDITMANAGER1_RequestPayoff097624965310_

J of BASE, TAF of C

111

By KZM

19 2GB ISSUE; FILE DISTRIBUTION

CREDIT.INT.CONDS.SCV_GB0010001_BUILDUSER1_CreditInterest014492600803_
SA.SCORE.CARD.MIFID.DETS_GB0010001_PWMRM_PFO096742495804_
EXPLC.OVERVIEW.SCV_GB0010001_BUILDUSER76_LCOverview039672578001_tab4
PEND.OSTATION.CHQS_GB0010001_BUIILDUSER11_ENQUIRY067292378900_
AA.MANAGE.PENDING.ACTIVITY_GB0010001_BU007_Pending077083817506_
EXP.CASHFLOW_GB0010001_BUILDUSER33_1299678443320_
CUSTOMER.DETAILS.SCV_GB0010001_BUILDUSER31
%TELLER.ID,_GB0010001_BUILDUSER86_1299735445922_
AA.DETAILS.ARRANGEMENT.ACCOUNT_GB0010001_CSAGENT1_AccountDates077083916510_
SC.MIFID.CLIENT.INFORMATION_GB0010001_BUILDUSER31_1299751531128_
TF.LCAC.IMP.CSM_GB0010001_BUILDUSER67_ChargesEnquiry074954634803_tab5
ACCT.DETAILS.ACCOUNT_GB0010001_BUILDUSER1_AccountStatic041964563301_
CUSTOMER.SIGN.SCV_GB0010001_PRA_CustomerSignature051375229304_
LC.EXP.ADD.DETAILS.SCV_GB0010001_BUILDUSER76_AdditionalDetails090643220803_tab4
FT.STO.EXEC_GB0010001_SUJA1_FRAMEA023864269600_tab2
CUSTOMER.PHOTO.SCV_GB0010001_BUILDUSER32
REPO.POSITION_GB0010001_BUILDUSER13_1299576318731_
DEPOSITS.DETAILS.SCV_GB0010001_CSAGENT1_DepositsDetailsEnquiry083873421919_
INTERNET.BANKING.CSM_GB0010001_BUILDUSER099_INTERNETBANKING001095068801_tab2
DE.CUSTOMER.PREFERENCES.SCV_GB0010001_BUIILDUSER6565
LD.DEP.PRODWISE.CORP.CSM_GB0010001_BUILDUSER119_AccountEnquiry041964671803_
DEPOSITS.DETAILS.SCV_GB0010001_CSAGENT1_DepositsDetailsEnquiry045773678908_

Finally a good candidate was found - its a history file of FUNDS.TRANSFER application
(which Ive mentioned above already; it normally becomes very big after some time). Record
@id contains the bank day when it was created and the record goes to the history immediately
after COB in most cases (we wouldnt consider transactions with future value date that might
still stay in live file for some time Im not 100% sure about that case). But generally
we can think that if the record went to history then after some time say, couple of months
the deal isnt going to create any new records in the history so the part file(s) might reside
outside bnk.data and be even readonly. Lets try this approach.
@id of FUNDS.TRANSFER$HIS looks like that (though in your case it might be different,
see AUTO.ID.START>FUNDS.TRANSFER and the helptext or the sample in my previous book
in this case):
@IDs of FBNK.FUNDS.TRANSFER$HIS
C:\sa-tafc> jrunT24.cmd
jsh ~ --> LIST ONLY FBNK.FUNDS.TRANSFER$HIS
@ID......................
FT10186BSXJQ;1
FT10187VGWVN;1
FT102171VDH0;1
FT10217WHJ8Y;1
FT10217TTH0L;1

J of BASE, TAF of C

112

By KZM

19 2GB ISSUE; FILE DISTRIBUTION

FT101861ZC2M;1
FT102070BQB9;1
FT10210G0XJT;1
FT102143R6QS;1
FT10186M9Q0S;1
...

Here two digits after FT represent year number and they are followed by day number in
the year. Sorted output tells us that the earliest date for such record is 10186 which stands
for.. for...
EVAL helps us again
C:\sa-tafc> jrunT24.cmd
jsh ~ --> LIST . SAMPLE 1 EVAL "OCONV(ICONV(10186,DJ),D)"
DICT .........

OCONV(ICONV(10186,"DJ"),"D")

%HOME%

05 JUL 2010

1 Records Listed

...for 5th of July, 2010. Our current bank date is:


DATES listing
jsh ~ --> LIST F.DATES TODAY
@ID............

TODAY......

GB0010003
SG0010001
GB0010001-COB
EU0010001
GB0010003-COB
GB0010001
EU0010001-COB
GB0010002
GB0010002-COB
GB0010004
GB0010004-COB
SG0010001-COB

20100809
20100809
20100809
20100809
20100809
20100809
20100809
20100809
20100809
20100809
20100809
20100809

12 Records Listed

J of BASE, TAF of C

113

By KZM

19 2GB ISSUE; FILE DISTRIBUTION

Well, that doesnt give us great time span but well try anyway. Were putting all records
for July 2010 into one file and records that are after that to another. Firstly we need to
create the stub file:
Stub file creation
C:\sa-tafc> jrunT24.cmd
jsh ~
[ 417
[ 417
jsh ~

--> CREATE-FILE FT-HIS-ALL TYPE=DISTRIB


] File FT-HIS-ALL]D created , type = J4
] File FT-HIS-ALL created , type = DISTRIB
-->

Now check its current state which was set by default:


Stub file verification
jsh ~ --> VERIFY-DISTRIB FT-HIS-ALL
Partitioning Algorithm is SYSTEM, with delimiter -
There are no Part files defined

Of course we need to change partitioning algorithm which is used by jBASE to determine in which part file particular record is to be found (or placed into). As it was already
mentioned, only @id is available for such algorithm. To achieve that we write a subroutine:
Creation of algorithm
C:\sa-tafc> jrunT24.cmd
jsh ~ --> JED . FT-PART-ALGO
File . , Record FT-PART-ALGO
Insert
Command->
0001 * algorithm for FT$HIS
0002 SUBROUTINE FT-PART-ALGO(sReserved, sKey, sPartNumber)
0003
0004
nDate = sKey[3,5]
0005
0006
IF ISDIGIT(nDate) THEN
0007
0008
IF nDate LT 10213 THEN
0009
sPartNumber = 1
0010
END ELSE
0011
sPartNumber = 2
0012
END
0013

J of BASE, TAF of C

114

19:52:04

By KZM

19 2GB ISSUE; FILE DISTRIBUTION

0014
END ELSE
;* we are cautious
0015
sPartNumber = 99
0016
END
0017
0018
RETURN
0019
0020 END
0021
-------------------------------- End Of Record --------------------------------

Then we compile it:


Compilation of algorithm
C:\sa-tafc> jrunT24.cmd
jsh ~ --> BASIC . FT-PART-ALGO
FT-PART-ALGO
BASIC_12.c
Source file FT-PART-ALGO compiled successfully
jsh ~ --> CATALOG . FT-PART-ALGO
FT-PART-ALGO
Object FT-PART-ALGO cataloged successfully
Library C:\sa-tafc\lib\lib0.dll rebuild okay
jsh ~ -->

Stop, stop... we are no more in the situation when we can separate a subroutine from
T24. To ensure that T24 will work normally with that file we need to put our algorithm to
T24 libraries:
Recompilation of algorithm
jsh ~ --> DECATALOG . FT-PART-ALGO
Object FT-PART-ALGO decataloged successfully
Library C:\sa-tafc\lib\lib0.dll rebuild okay
jsh ~ --> set JBCDEV_LIB=<path_to_bnk.run>\lib
jsh ~ --> CATALOG . FT-PART-ALGO
FT-PART-ALGO
Object FT-PART-ALGO cataloged successfully
Library <path_to_bnk.run>\lib\lib2.dll rebuild okay
jsh ~ --> jshow -c FT-PART-ALGO
Subroutine:

J of BASE, TAF of C

<path_to_bnk.run>\lib\lib2.dll
jBC FT-PART-ALGO version 201014.0 Mon Jul 18 19:57:12 2011
jBC FT-PART-ALGO source file .

115

By KZM

19 2GB ISSUE; FILE DISTRIBUTION

Now we can add our algorithm to our stub file. Its a good idea to verify the file after
each step:
Attachment of algorithm to stub file
C:\sa-tafc> jrunT24.cmd
jsh ~ --> CREATE-DISTRIB -pUSER,FT-PART-ALGO FT-HIS-ALL
jsh ~ --> VERIFY-DISTRIB FT-HIS-ALL
Partitioning Algorithm is USER Subroutine FT-PART-ALGO
User subroutine OK.
There are no Part files defined

Good, exactly where we expected it to be. Create and add part files (note that we need
part file 99 to handle exceptions according to the algorithm; such handling is absolutely
mandatory):
Creation and assigment of the part files
jsh ~ --> CREATE-FILE FT-HIS-PART1 TYPE=J4 1 101
[ 417 ] File FT-HIS-PART1]D created , type = J4
[ 417 ] File FT-HIS-PART1 created , type = J4
jsh ~ --> CREATE-FILE FT-HIS-PART2 TYPE=J4 1 101
[ 417 ] File FT-HIS-PART2]D created , type = J4
[ 417 ] File FT-HIS-PART2 created , type = J4
jsh ~ --> CREATE-FILE FT-HIS-PART99 TYPE=J4 1 101
[ 417 ] File FT-HIS-PART99]D created , type = J4
[ 417 ] File FT-HIS-PART99 created , type = J4
jsh ~ --> CREATE-DISTRIB -a FT-HIS-ALL 1 FT-HIS-PART1
Part file FT-HIS-PART1, Part number 1 added
jsh ~ --> CREATE-DISTRIB -a FT-HIS-ALL 2 FT-HIS-PART2
Part file FT-HIS-PART2, Part number 2 added
jsh ~ --> CREATE-DISTRIB -a FT-HIS-ALL 99 FT-HIS-PART99
Part file FT-HIS-PART99, Part number 99 added
jsh ~ --> VERIFY-DISTRIB FT-HIS-ALL
Partitioning Algorithm is USER Subroutine FT-PART-ALGO
User subroutine OK.
Part file FT-HIS-PART1, part number 1 - OK
Part file FT-HIS-PART2, part number 2 - OK

J of BASE, TAF of C

116

By KZM

19 2GB ISSUE; FILE DISTRIBUTION

Part file FT-HIS-PART99, part number 99 - OK

Congratulations, we are ready to copy the data:


Copying of data
jsh ~ --> COPY FROM FBNK.FUNDS.TRANSFER$HIS TO FT-HIS-ALL ALL
682 records copied

Checking how all was copied:


Check of data
jsh ~ --> LIST FT-HIS-PART1 ONLY
FT-HIS-PART1..
FT10210K6805;1
FT101973MNRH;1
FT101876Q21B;1
FT10194426BX;1
FT10194HGYRH;1
FT1019435RQ2;1
FT10186HWG3J;1
FT1018658RY6;1
FT10194BHDN2;1
FT10186XMK5T;1
FT101869CJKW;1
...

jsh ~ --> LIST FT-HIS-PART2 ONLY


FT-HIS-PART2..
FT10217WBR9V;2
FT10217CVD22;1
FT102163R16Y;1
FT102172R2FT;1
FT102170KCRS;1
FT102162WT69;1
FT10217M6B8V;1
FT10217ZJPFL;1
FT1021883BK2;1
FT102174B729;1
FT10217K6MNZ;1

J of BASE, TAF of C

117

By KZM

19 2GB ISSUE; FILE DISTRIBUTION

...
jsh ~ --> COUNT FT-HIS-PART1
465 Records counted
jsh ~ --> COUNT FT-HIS-PART2
216 Records counted

Well... doesnt look like we have all 682 records here. Thats why our file 99 was necessary:
Check of data - continued
jsh ~ --> LIST FT-HIS-PART99 ONLY
FT-HIS-PART99.
FTAA10186QY2ZR;1
1 Records Listed

We see that the @id doesnt follow the standard so it went as an exception. The total
number of records is correct:
Check of data - finished
jsh ~ --> COUNT FT-HIS-ALL
682 Records counted

We also see here that we can address the individual part files as well as a whole distributed
file.
But the file isnt yet been recognized by T24. To achieve that firstly lets think: do we
need all new data files in the bnk.run? Of course no. We need to move them to bnk.data and
then adjust VOC entries for T24 to find all of them. Well move FT-HIS-ALL, FT-HIS-PART1,
FT-HIS-PART2 and FT-HIS-PART99 to ../bnk.data/ft and then:
Necessary VOC entries - stub file
jsh ~ --> COPY FROM VOC FBNK.FUNDS.TRANSFER$HIS,FBNK.FUNDS.TRANSFER-SAVE
1 records copied

J of BASE, TAF of C

118

By KZM

19 2GB ISSUE; FILE DISTRIBUTION

jsh ~ --> JED VOC FBNK.FUNDS.TRANSFER$HIS


File VOC , Record FBNK.FUNDS.TRANSFER$HIS
Insert
20:41:33
Command->
0001 F
0002 ../bnk.data/ft/FT-HIS-ALL
0003 ../bnk.dict/F_FUNDS_TRANSFER]D
-------------------------------- End Of Record --------------------------------

Necessary VOC entries - part file 1


jsh ~ --> COPY FROM VOC FBNK.FUNDS.TRANSFER$HIS,FT-HIS-PART1
1 records copied
jsh ~ --> JED VOC FT-HIS-PART1
File VOC , Record FT-HIS-PART1
Insert
20:45:13
Command->
0001 F
0002 ../bnk.data/ft/FT-HIS-PART1
0003 ../bnk.dict/F_FUNDS_TRANSFER]D
-------------------------------- End Of Record --------------------------------

Do the same with part files #2 and #99 and check it:
Necessary VOC entries - final check
jsh ~ --> VERIFY-DISTRIB FBNK.FUNDS.TRANSFER$HIS
Partitioning Algorithm is USER Subroutine FT-PART-ALGO
User subroutine OK.
Part file FT-HIS-PART1, part number 1 - OK
Part file FT-HIS-PART2, part number 2 - OK
Part file FT-HIS-PART99, part number 99 - OK

As you might have noticed, these VOC entries use the same dictionary the one for initial
history file, so we dont need the files FT-HIS-ALL]D, FT-HIS-PART1]D, FT-HIS-PART2]D and
FT-HIS-PART99]D in bnk.run and you can delete them. Alternatively, we could have used
DATA keyword in part files creation command, like:
Creation of data part only for a part file
jsh ~ --> CREATE-FILE DATA FT-HIS-PART1 TYPE=J4 1 101

J of BASE, TAF of C

119

By KZM

19 2GB ISSUE; FILE DISTRIBUTION

Also we can back up and remove the old data file from ../bnk.data/ft.
Now the final check log in to T24 and type FT L ; L at AWAITING APPLICATION
prompt, then press F5:
T24 now uses a distributed file
Model Bank

FUNDS.TRANSFER$HIS - DEFAULT HISTORY LIST

ID
RECORD.STATUS INPUTTER
FUNCT.
-----------------------------------------------------------------------------1 FT10186B51RP;1
MAT
4145_SEAT.USER__OFS_SEAT
2 FT10186BB96G;1
MAT
4899_SEAT.USER__OFS_SEAT
3 FT10186BC4NJ;1
MAT
4145_SEAT.USER__OFS_SEAT
4 FT10186BJ634;1
MAT
291_SEAT.USER__OFS_SEAT
5 FT10186BJNYP;1
MAT
5436_SEAT.USER__OFS_SEAT
6 FT10186BSXJQ;1
MAT
4116_SEAT.USER__OFS_SEAT
7 FT10186BT3NR;1
MAT
7038_SEAT.USER__OFS_SEAT
8 FT10186C5QG3;1
MAT
4957_SEAT.USER__OFS_SEAT
9 FT10186CBZZ5;1
MAT
4360_SEAT.USER__OFS_SEAT
10 FT10186CFWVV;1
MAT
4145_SEAT.USER__OFS_SEAT
11 FT10186CH4PQ;1
MAT
7038_SEAT.USER__OFS_SEAT
12 FT10186CH48K;1
MAT
522_SUJA1__OFS_BROWSERTC
13 FT10186CJWQ0;1
MAT
3789_SEAT.USER__OFS_SEAT
14 FT10186CTSLV;1
MAT
3436_SEAT.USER__OFS_SEAT
15 FT10186D9F66;1
MAT
7285_SEAT.USER__OFS_SEAT
16 FT10186DB93N;1
MAT
7392_SEAT.USER__OFS_SEAT
-----------------------------------------------------------------------------18 JUL 2011 21:01:46 USER (09 AUG) VLADIMIR.K
[5377,INPAGE
1 >>>3>>>
ACTION
AWAITING PAGE INSTRUCTIONS

If you are still not sure, dont leave this screen and open another session, then find out
the port number for T24 session above and see opened files for it:
Yet another final check
jsh ~ --> WHERE
Port
13

*14

Device
ntcon

Account
t24

PID
5584

ntcon

Vladimir.Kaz 4608

Command
<path_to_TAFC>\bin\jsh para K.LOG
EX
jsh
WHERE

jsh ~ --> LIST-OPEN-FILES 13 (V

J of BASE, TAF of C

120

By KZM

19 2GB ISSUE; FILE DISTRIBUTION

Port
...
13
13
13
13
13
13
...

File name
..\bnk.data\ft\FT-HIS-PART99
..\bnk.data\ft\FT-HIS-PART2
..\bnk.data\ft\FT-HIS-PART1
..\bnk.data\ft\FT-HIS-ALL
..\bnk.data\ft\FBNK_FUNDS_TRANSFER#NAU
..\bnk.data\ft\FBNK_FUNDS_TRANSFER

You see that while live and $nau files are the same as they were the $his file is represented by our distributed one.
By default, all part files are opened whenever a stub is opened; this can be changed by
setting the environment variable jedi distrib defopen to 1.
Now to decrease backup time we can think of moving older part of the distributed
file away from bnk.data. You can try it yourself; the only thing that is necessary apart from
moving the file is to change its VOC entry. I even made this file readonly and all still worked.
Its a good idea also to resize each part file.
Having done that, why not to try another option - temporarily remove that part file
pretending theres no space on the disk. Can we do it? The day numbers in that file were up
to (but not including) 10213 so move this file away (or simply rename) and firstly doublecheck with verify-distrib it shouldnt see part 1 (cannot open error 2). Then try to
see the implications:
One part file is missing
C:\sa-tafc> jrunT24.cmd
jsh ~ --> VERIFY-DISTRIB FBNK.FUNDS.TRANSFER$HIS
Partitioning Algorithm is USER Subroutine FT-PART-ALGO
User subroutine OK.
Part file FT-HIS-PART1, part number 1 - Cannot open (error 2)
Part file FT-HIS-PART2, part number 2 - OK
Part file FT-HIS-PART99, part number 99 - OK
jsh ~ --> SELECT FBNK.FUNDS.TRANSFER$HIS
217 Records selected

jBASE now sees less records than it did before. What T24 says? Use FT L ; L command
again:

J of BASE, TAF of C

121

By KZM

19 2GB ISSUE; FILE DISTRIBUTION

T24 now uses a distributed file with one part missing


Model Bank

FUNDS.TRANSFER$HIS - DEFAULT HISTORY LIST

ID
RECORD.STATUS INPUTTER
FUNCT.
-----------------------------------------------------------------------------1 FT10214HKVSZ;1
MAT
514_INPUTTER
2 FT10214J13N8;1
MAT
514_INPUTTER
3 FT10214J60FB;1
MAT
777_INPUTTER
4 FT10214JRRSS;1
MAT
514_INPUTTER
5 FT10214K5FD9;1
MAT
772_INPUTTER
6 FT10214L1TJP;1
MAT
777_INPUTTER
7 FT10214NT8QV;1
MAT
778_INPUTTER__OFS_SWIFTIN
8 FT10214P581G;1
MAT
512_INPUTTER
9 FT10214PSD8J;1
MAT
513_INPUTTER
10 FT10214PYSQN;1
MAT
512_INPUTTER
11 FT10214RC54G;1
MAT
778_INPUTTER__OFS_SWIFTIN
12 FT10214W9RR0;1
MAT
512_INPUTTER
13 FT10214W860N;1
MAT
512_INPUTTER
14 FT10214YS176;1
MAT
514_INPUTTER
15 FT10214YTPFX;1
MAT
299_INPUTTER
16 FT10216CPVP3;1
MAT
762_INPUTTER
-----------------------------------------------------------------------------22 JUL 2011 10:57:58 USER (09 AUG) VLADIMIR.K
[6059,INPAGE
1 >>>3>>>
ACTION
AWAITING PAGE INSTRUCTIONS

See that the list starts from other @ids than it did when all part files were available?
The behaviour is a bit unexpected to me since I thought that an error message would be
raised and well have to set jedi distrib defopen to 1 and correct user enquiries. Anyway,
it will serve us well if correction of enquiry is described anyway otherwise users might end
up with wrong reports that miss some data. As a base well use a sample enquiry-related
build.routine named ENQ.NARROW from my previous book and rename it to ENQ.WARNING.
ENQ.WARNING subroutine - the beginning
1
2
3
4
5
6
7
8
9
10

*---------*
SUBROUTINE ENQ.WARNING(P.ENQ)
* V.Kazimirchik (KZM), 2011.
* Build routine for enquiry to see if all data is
* available for an enquiry since particular part file
* (1 in our case) might be absent.
*-----------------------------------------------------*
$INSERT T24.BP I_COMMON
$INSERT T24.BP I_EQUATE
$INSERT T24.BP I_ENQUIRY.COMMON

J of BASE, TAF of C

122

By KZM

19 2GB ISSUE; FILE DISTRIBUTION

Nothing unusual at the beginning. (Note that here $INSERT T24.BP I COMMON etc is
used instead of $INSERT I COMMON to avoid additional parameters to basic command
this wouldnt cause any harm unless Temenos changes the product name and tries to wipe
out all references to the old one as it was attempted with Globus before.) Now we open
FT history file and get the part files information using statement status. Note enq.error
variable that is used to pass an error back to user:
ENQ.WARNING subroutine - continued
11
12
13

F.FT.HIST =
CALL OPF(F.FUNDS.TRANSFER$HIS, F.FT.HIST)

14
15
16
17
18

STATUS V.INFO.L FROM F.FT.HIST ELSE


ENQ.ERROR = FT HISTORY STATUS COULD NOT BE READ
RETURN
END

19
20

IF V.INFO.L<21> NE DISTRIB THEN RETURN

21
22
23

V.PARTS.L = V.INFO.L<24>
V.FILES.L = V.INFO.L<25>

;* part numbers list, @VM-delimited


;* part file names list

We need the file name for the part #1:


ENQ.WARNING subroutine - continued
24
25
26

* Not sure if "1" is always first, especially if that part was


* removed and then added back, so find it.

27
28
29
30
31

FIND 1 IN V.PARTS.L SETTING V.DUMMY, V.POSN ELSE


ENQ.ERROR = PROBLEMS WITH DISTRIBUTED FILE STRUCTURE
RETURN
END

32
33

V.PART.ONE = V.FILES.L<1,V.POSN>

Here we set the threshold for the dates and get the selection from user input:
ENQ.WARNING subroutine - continued
34
35
36
37
38

V.MIN.DATE = 10213 ;* hardcoded but only not to bloat this sample


V.SELS.L = TRIM(P.ENQ<4,1>, , D)
CHANGE TO @FM IN V.SELS.L
V.CNT = DCOUNT(V.SELS.L, @FM)

39
40

V.ONE.OPENED =

J of BASE, TAF of C

123

By KZM

19 2GB ISSUE; FILE DISTRIBUTION

Selections loop started. We check if a selection has proper format (we allow only @id
LK FTYYDDD..., note that several FTYYDDD... parts may be input one after another).
@id and LK parts are controlled at enquiry level, well see it below.
ENQ.WARNING subroutine - continued
41

FOR V.I = 1 TO V.CNT


V.SEL = V.SELS.L<V.I>
IF V.SEL MATCHES "FT5N..." THEN

42
43
44

Take the date, check it against our threshold set above and... carefully read the comment
below:
ENQ.WARNING subroutine - continued
45

V.DATE = V.SEL[3,5]
IF V.DATE LT V.MIN.DATE AND V.ONE.OPENED EQ THEN

46
47
48
49
50
51
52
53
54
55

*
*
*
*
*
*
*

Now we can try to open a part file to see if its available.


In general, opening the same file into two different file variables
is very sensitive issue (since that file could have already been opened
by core T24).
Though the following code works in my local environment Id suggest
better not to use OPEN here but rather check for file presence; its
location can be taken from corresponding VOC entry.

Open the part file (but do it only once see variable v.one.opened here and above);
set error to be returned to user if file is not present:
ENQ.WARNING subroutine - continued
56

OPEN V.PART.ONE TO F.PART.ONE ELSE


V.OUT.DATE = OCONV(ICONV(V.DATE,DJ),D)
ENQ.ERROR = DATA FOR " : V.DATE : \
" ( : V.OUT.DATE : ) IS NOT AVAILABLE
RETURN
END
V.ONE.OPENED = Y

57
58
59
60
61
62
63
64

END

65

And heres the end of this subroutine (including the error message reporting wrong selection criteria):
66
67

ENQ.WARNING subroutine - finished


END ELSE
ENQ.ERROR = ONLY "FTYYDDD..." FORMAT IS ALLOWED

J of BASE, TAF of C

124

By KZM

19 2GB ISSUE; FILE DISTRIBUTION

RETURN
END

68
69
70

NEXT V.I

71
72

RETURN

73
74

END

75

Compile the routine (again use set JBCDEV LIB=<path to bnk.run>\lib before catalog for T24 to be able to see the subroutine.
Time for enquiry. It will be quite simple. See blue lines for selection control and attachment of the build routine.
ENQUIRY FT.HIST
Model Bank

ENQUIRY SEE

ENQUIRY........... FT.HIST
-----------------------------------------------------------------------------1 PAGE.SIZE ........ 4,19
2 FILE.NAME......... FUNDS.TRANSFER$HIS
6. 1 SELECTION.FLDS. @ID
8. 1 SEL.FLD.OPER... LK
9. 1 REQUIRED.SEL... Y
12. 1 BUILD.ROUTINE.. ENQ.WARNING
14. 1 FIELD.NAME..... @ID
15. 1. 1 OPERATION... @ID
16. 1 COLUMN......... 4
17. 1 LENGTH.MASK.... 25L
35. 1 SINGLE.MULTI... S
14. 2 FIELD.NAME..... 2
15. 2. 1 OPERATION... DEBIT.ACCT.NO
16. 2 COLUMN......... 19
17. 2 LENGTH.MASK.... 12L
35. 2 SINGLE.MULTI... S
14. 3 FIELD.NAME..... 11
15. 3. 1 OPERATION... CREDIT.ACCT.NO
16. 3 COLUMN......... 32
17. 3 LENGTH.MASK.... 12L
35. 3 SINGLE.MULTI... S
14. 4 FIELD.NAME..... 2
15. 4. 1 OPERATION... DEBIT.AMOUNT
16. 4 COLUMN......... 45
17. 4 LENGTH.MASK.... 16R
35. 4 SINGLE.MULTI... S
14. 5 FIELD.NAME..... 5

J of BASE, TAF of C

125

By KZM

19 2GB ISSUE; FILE DISTRIBUTION

15. 5. 1 OPERATION... DEBIT.CURRENCY


16. 5 COLUMN......... 62
17. 5 LENGTH.MASK.... 3L
35. 5 SINGLE.MULTI... S
14. 6 FIELD.NAME..... 5
15. 6. 1 OPERATION... DEBIT.VALUE.DATE
16. 6 COLUMN......... 67
17. 6 LENGTH.MASK.... 11R
20. 6 TYPE........... DATE
35. 6 SINGLE.MULTI... S
36. 1 ENQU FUNDS.TRANSFER S @ID
37. 1. 1 SEL.CRIT ... @ID 1
40 PAGE.FIELDS....... 6 123456
42 MULTI.FIELDS...... 0
43 BREAK.FIELDS...... 0
44 PROCESS.BREAKS.... 0
45 TOTAL.FIELDS...... 0
46 NEXT.LVL.FLDS..... 1 1
77 CURR.NO........... 8
78. 1 INPUTTER....... 2590_VLADIMIR.K_I_INAU
79. 1 DATE.TIME...... 22 JUL 11 20:11
79. 2 DATE.TIME...... 22 JUL 11 20:11
80 AUTHORISER........ 2590_VLADIMIR.K
81 CO.CODE........... GB-001-0001
Model Bank
82 DEPT.CODE......... 1
Implementation
------------------------------------------------------------------------------

Now lets try it (part file #1 should be absent). Log in to T24, type ENQ FT.HIST , then
input your selection, e.g.:
ENQUIRY FT.HIST - runtime
Model Bank

ENQUIRY INPUT

SELECT NAME....... FT.HIST


-----------------------------------------------------------------------------1. 1 SELECTION.TYPE. <k>
2. 1 SELECTION.FIELD @ID
3. 1 OPERAND........ LK
4. 1. 1 LIST........ FT10186... FT10217...
1. 2 SELECTION.TYPE. <k>
2. 2 SELECTION.FIELD
3. 2 OPERAND........
4. 2. 1 LIST........
1. 3 SELECTION.TYPE.
2. 3 SELECTION.FIELD
3. 3 OPERAND........

J of BASE, TAF of C

126

By KZM

19 2GB ISSUE; FILE DISTRIBUTION

4. 3. 1 LIST........
1. 4 SELECTION.TYPE.
2. 4 SELECTION.FIELD
3. 4 OPERAND........
4. 4. 1 LIST........
-----------------------------------------------------------------------------25 JUL 2011 22:00:36 USER (09 AUG) VLADIMIR.K
[8212,INPAGE 1
>>64>>>
ACTION
AWAITING APPLICATION

Commit and see the error message:


ENQUIRY FT.HIST - error message
-----------------------------------------------------------------------------25 JUL 2011 22:00:36 USER (09 AUG) VLADIMIR.K
[8212,IN
ACTION
DATA FOR "10186" (05 JUL 2010) IS NOT AVAILABLE
AWAITING APPLICATION

Now close the session, open a new one, rename (or put back) part file #1 and run the
same enquiry with the same selection:
ENQUIRY FT.HIST - runtime
Model Bank

-----------------------------------------------------------------------------FT1018602K9X;1 44679
42544
61000.00 GBP 05 JUL 2010
FT1018603VB7;1 PL60060
41904
10000.00 USD 05 JUL 2010
FT101860DKVB;1 44822
41629
62000.00 CHF 05 JUL 2010
FT101860Z660;1 43885
41618
305000.00 USD 05 JUL 2010
FT1018613LSJ;1 22578
40878
1300.00 USD 05 JUL 2010
FT1018613MFV;1 20656
11298
200000.00 USD 05 JUL 2010
FT101861F3C2;1 44784
40967
275000.00 USD 05 JUL 2010
FT101861L2C8;1 20656
13013
200000.00 USD 05 JUL 2010
FT101861NMHL;1 20656
16397
200000.00 USD 05 JUL 2010
FT101861PBVG;1 44326
42207
405000.00 USD 05 JUL 2010
FT101861PVKC;1 14761
10707
10000.00 USD 05 JUL 2010
FT101861T5MN;1 23825
10723
1250.00 USD 05 JUL 2010
FT101861Z0B6;1 10715
40827
4500.00 EUR 05 JUL 2010
FT101861ZC2M;1 20656
28169
200000.00 USD 05 JUL 2010
FT1018622CJX;1 23701
15393
GBP 05 JUL 2010
FT1018625DB8;1 41149
43915
USD 05 JUL 2010
-----------------------------------------------------------------------------25 JUL 2011 22:06:52 USER (09 AUG) VLADIMIR.K
[439,IN]PAGE
1 >>>3>>>
ACTION
AWAITING PAGE INSTRUCTIONS

J of BASE, TAF of C

127

By KZM

19 2GB ISSUE; FILE DISTRIBUTION

By the way, the table F.ENQUIRY.SELECT was mentioned above the place where user
selections are stored. See:
Stored selection criteria
jsh ~ --> CT F.ENQUIRY.SELECT FT.HIST-VLADIMIR.K
FT.HIST-VLADIMIR.K
001 <k>]<k>]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]<d>]]]]]]]]]]]]]]]]]]]]
]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
]]]]]]]]<d>]<d>]<d>]<d>]<d>]<d>]<d>]<d>]<d>]<d>]<d>]<d>]<d>]<d>]<d>]<d>]<d
>]<d>]<d>]<d>]<d>]<d>]<d>]<d>]<d>]<d>]<d>
002 @ID
003 LK
004 FT10186... FT10217...
005 FT.HIST
006
007
008
009
010
011
012
013
014
015 1
016 439_VLADIMIR.K
017 2207251206
018 439 VLADIMIR.K
019 GB0010001
020 1

J of BASE, TAF of C

128

By KZM

20 SOME MORE SORTING; VARIABLES TYPING; PRECISION

20

Some more sorting; variables typing; PRECISION

orting a dynamic array is a very common task. Apart from it works solution of
:::: implementing a so-called bubble sort I often see the suggestion to use locate for
this purpose, like see a test program below:
Source of testsort.b
1

* dynamic array sorting

2
3

* prepare an array of 1000 random numbers from 0 to 1000

V.ARR =
FOR V.I = 1 TO 1000
V.ARR := @FM : RND(1000)
NEXT V.I
DEL V.ARR<1>

5
6
7
8
9
10

V.SORTED =

11
12
13

* Now sort

14

FOR V.I = 1 TO 1000


V.IN = V.ARR<V.I>
LOCATE V.IN IN V.SORTED<1> BY AN SETTING V.INS.POSN ELSE NULL
INS V.IN BEFORE V.SORTED<V.INS.POSN>
NEXT V.I

15
16
17
18
19
20
21

* Show some results

22

FOR V.I =
IF V.I
CRT
CRT
V.I
END

23
24
25
26
27
28

1 TO 1000 STEP 10
EQ 101 THEN

...
= 901

29

CRT
FOR V.J = 1 TO 10
CRT V.SORTED<V.I+V.J-1> : , :
NEXT V.J

30
31
32
33
34

NEXT V.I

35
36

STOP

37
38

END

39

Compile, run, see that it works:


J of BASE, TAF of C

129

By KZM

20 SOME MORE SORTING; VARIABLES TYPING; PRECISION

testsort run
C:\sa-tafc> jrunComp.cmd testsort
testsort.c
C:\sa-tafc> jrunEXE.cmd testsort
1,3,3,4,6,8,8,10,11,11,
13,13,19,19,20,21,22,23,23,24,
26,27,28,29,29,30,30,32,34,34,
37,38,39,39,43,44,48,49,50,51,
51,52,56,56,59,59,59,59,61,62,
63,63,64,64,66,67,68,69,70,70,
70,70,71,72,77,78,82,84,85,86,
86,87,88,88,90,91,92,93,93,94,
94,95,97,97,98,99,102,103,104,105,
106,107,107,109,111,112,114,114,115,115,
...
888,889,896,897,898,898,900,900,901,903,
903,905,905,907,908,908,910,913,913,914,
915,915,916,918,919,921,924,924,925,926,
927,928,928,931,932,932,932,933,936,936,
936,937,940,940,941,942,943,943,944,944,
946,946,947,947,948,949,952,952,954,955,
957,959,961,961,962,963,965,965,965,966,
967,967,967,968,968,971,972,972,973,973,
976,977,978,979,979,981,981,983,985,986,
991,991,992,992,992,995,995,996,997,999,
C:\sa-tafc>

But recently Ive stumbled into sselectv statement that as far as I could tell is also
suitable for this purpose:
New source of testsort.b
1

* dynamic array sorting

2
3

* prepare an array of 1000 random numbers from 0 to 1000

4
5
6
7
8
9

V.ARR =
FOR V.I = 1 TO 1000
V.ARR := @FM : RND(1000)
NEXT V.I
DEL V.ARR<1>

10
11

V.SORTED =

12
13

* Now sort

14

J of BASE, TAF of C

130

By KZM

20 SOME MORE SORTING; VARIABLES TYPING; PRECISION

SSELECTV V.ARR TO V.SORTED ON ERROR


CRT SSELECTV ERROR
STOP
END

15
16
17
18
19
20

* Show some results

21

FOR V.I =
IF V.I
CRT
CRT
V.I
END

22
23
24
25
26
27

1 TO 1000 STEP 10
EQ 101 THEN

...
= 901

28

CRT
FOR V.J = 1 TO 10
CRT V.SORTED<V.I+V.J-1> : , :
NEXT V.J

29
30
31
32
33

NEXT V.I

34
35

STOP

36
37

END

38

Well, though first method has more flexibility in providing different sort options (including
descending sort order), the second one is more simple. About the sorting options of locate weve used ascending numeric order which seemed the most appropriate for this case since
our items were numeric. For items containing amounts the right alignment is preferred, for
text the left one. However, in jBC weak typing is used for variables and, for example, even
if a variable looks like a numeric, it might be a text. See the following example:
Source of testtype.b
1

* test typing

V.DATE = 17 JAN 2011

3
4

V.DAY = V.DATE[1,2]

5
6
7

DEBUG

V.DAY *= 1

9
10

STOP

11
12
13

END

14

J of BASE, TAF of C

131

By KZM

20 SOME MORE SORTING; VARIABLES TYPING; PRECISION

Well need debugger to demonstrate the change of type:


Run of testtype
C:\sa-tafc> jrunComp.cmd testtype
testtype.c
C:\sa-tafc> jrunEXE.cmd testtype
DEBUG statement seen
Source changed to .\testtype.b
0007 DEBUG
jBASE debugger-> V -V V.DAY
008DFD14 : V.DAY
: (V) String
: 2 bytes at address 003D7A38 : 17
jBASE debugger-> S
0009
V.DAY *= 1
jBASE debugger-> S
0011
STOP
jBASE debugger-> V -V V.DAY
008DFD14 : V.DAY
: (V) Scaled (4) float
: 17
jBASE debugger->

This explains some jBC code that might seem stupid but in fact isnt (like adding zero to
a variable). You might think that int(v.day) or dround(v.day, 0) will make it numeric?
No, they wont at least under my platform.
One more consideration: of course 17 is equal to 17. But we saw that after multiplying
by 1 the variable is stored as a float one which means that it isnt actually 17 but might
be something like 16.9999999963. So its time to see what precision is.
Add some small number to numeric variable and compare it to a string one. In brief, is
17 equal to 17.00000001? The answer is: sometimes yes, sometimes no.
Amended source of testtype.b
1

* test typing

2
3

V.DATE = 17 JAN 2011

4
5

V.DAY = V.DATE[1,2]

6
7
8

V.DAY.N = V.DAY * 1
V.DAY.N += 0.00000001

9
10
11
12
13
14

IF V.DAY EQ V.DAY.N THEN


CRT THE SAME
END ELSE
CRT DIFFERENT
END

15

J of BASE, TAF of C

132

By KZM

20 SOME MORE SORTING; VARIABLES TYPING; PRECISION

STOP

16
17
18

END

19

Compile, run, see that 17 is equal to 17.00000001. Now add precision statement:
Even more amended source of testtype.b
1

* test typing

PRECISION 13

3
4

V.DATE = 17 JAN 2011

5
6

V.DAY = V.DATE[1,2]

7
8

V.DAY.N = V.DAY * 1
V.DAY.N += 0.00000001

9
10
11

IF V.DAY EQ V.DAY.N THEN


CRT THE SAME
END ELSE
CRT DIFFERENT
END

12
13
14
15
16
17

STOP

18
19
20

END

21

And now they are different. Value of 13 is set in I COMMON and thats a default for T24
(though both manual and knowledgebase insist that precision might be up to 9).
Be careful about that you might have noticed that in the samples in this book we quite
rarely include I COMMON. Use precision 13 in case of doubt (as an example, use it if you
do any math with part file numbers in file distribution algorihtm).

J of BASE, TAF of C

133

By KZM

21 WORKING WITH JBASE CACHE

21

Working with jBASE cache

base cache is quite a new thing that havent yet found its way into manuals (at least
In the file JBC.h that is located in your TAFC
::::: to ones that are available to me).
include subdirectory you can find the following:
JBC.h excerpt
*
* In-memory cache functions
*
DEFC VAR CachePut(VAR, VAR, VAR)
DEFC VAR CacheGet(VAR, VAR)
DEFC VAR CacheDelete(VAR, VAR)
DEFC VAR CacheExists(VAR, VAR)
DEFC VAR CacheClear(VAR)
DEFC VAR CacheClearAll()
DEFC VAR CacheKeyList(VAR, VAR)
DEFC VAR CacheBucketList(VAR)
*

The solution of a task of finding a value in memory corresponding to some key isnt
that obvious in jBC. Firstly, this language lacks pass-by-reference mechanism where you
can store a value in a variable, variable name in another variable and get the value using
the second variable as a key (though you can call a subroutine this way, e.g. call @var).
Dynamic arrays are not fully fit to that purpose to find something in an array using find
or locate isnt very fast, especially with big arrays (since they proceed with full scan of
contents). Lets see if this cache thing helps us with that.
To test search speed well need a big list. To simplify things a little, lets not search a big
file in Model Bank like we did before just write a program to create such list with random
@ids. Well also need a smaller list that contains some subset of these @ids that will be used
in the search. The source is quite self-explanatory we create 1,000,000 entries in this list,
@ids are 16-characters long and are generated randomly; 10,000 of which will be searched
and their position in the big list is also chosen on a random basis (note also that we sort the
smaller list to avoid same processing order during search phase:
Source of createbiglist.b
1
2

* Create a big list


* IDs are random characters, ID length 16

3
4
5
6
7
8
9
10

OPENSEQ &SAVEDLISTS&, BIG.LIST TO F.OUT.FILE THEN


WEOFSEQ F.OUT.FILE
END ELSE
CREATE F.OUT.FILE ELSE
CRT OUTPUT FILE CREATION ERROR
STOP
END

J of BASE, TAF of C

134

By KZM

21 WORKING WITH JBASE CACHE

11

END

12
13
14
15
16
17
18
19
20

OPENSEQ &SAVEDLISTS&, SMALL.LIST TO F.SMALL.FILE THEN


WEOFSEQ F.SMALL.FILE
END ELSE
CREATE F.SMALL.FILE ELSE
CRT 2ND OUTPUT FILE CREATION ERROR
STOP
END
END

21
22
23
24
25
26

V.POSN.L =
FOR V.I = 1 TO 10000
V.POSN.L := @FM : RND(1000000) + 1
NEXT V.I
DEL V.POSN.L<1>

27
28

FOR V.I = 1 TO 1000000

29
30

IF V.I[3] EQ 000 THEN CRT .:

31
32

V.ID =

33
34
35
36
37

FOR V.J = 1 TO 16
V.RND = RND(26) + 65
V.ID := CHAR(V.RND)
NEXT V.J

;* A...Z

38
39

GOSUB WRITE.LINE

40
41

NEXT V.I

42
43
44

CLOSESEQ F.OUT.FILE
CLOSESEQ F.SMALL.FILE

45
46

EXECUTE SORT-LIST SMALL.LIST

47
48

STOP

49
50
51

*~~~~~
WRITE.LINE:

52
53
54
55
56

WRITESEQ V.ID : @VM : Value # : V.I TO F.OUT.FILE ELSE


CRT WRITE ERROR
STOP
END

57
58

FIND V.I IN V.POSN.L SETTING V.POSN ELSE V.POSN = 0

J of BASE, TAF of C

135

By KZM

21 WORKING WITH JBASE CACHE

59
60
61
62
63
64

IF V.POSN THEN
WRITESEQ V.ID TO F.SMALL.FILE ELSE
CRT WRITE ERROR
STOP
END
END

65
66

RETURN

67
68
69

*~~~~~
END

70

Compile, launch, wait.


Execution of createbiglist
C:\sa-tafc> jrunSH.cmd
jsh ~ --> createbiglist
...............................................................................
...............................................................................
...............................................................................
...............................................................................
...............................................................................
...............................................................................
...............................................................................
...............................................................................
..........List SMALL.LIST sorted with 8654 records

Why not 10,000? Well, it means that rnd() gave us about 14% of non-unique numbers
when being asked to produce a random value ranging from 1 to 1,000,000. Anyway, we can
use such quantity for out purpose.
See the lists:
Big list
File &SAVEDLISTS& , Record BIG.LIST
Command->
00000001 REFEVSOFXPOVNLSK]Value #1
00000002 XNYGLFPYLZTLMCQV]Value #2
00000003 WJHAEGPLHNVJUCUA]Value #3
00000004 OIWKMACMOKAZARJZ]Value #4
00000005 KCAWDNDIOKNSYGTT]Value #5
00000006 RQLIKMJABTOWZVPQ]Value #6
00000007 VNMKQDZMTIVLUPSO]Value #7
00000008 JBJZSGDKYUKBVLQM]Value #8

J of BASE, TAF of C

21:12:20

136

By KZM

21 WORKING WITH JBASE CACHE

00000009
00000010
...
00999991
00999992
00999993
00999994
00999995
00999996
00999997
00999998
00999999
01000000

PMLGDXZFFFRCNQFO]Value #9
FEGKGSJMYQCIPLMP]Value #10
DYCPHJHYOOWJMAKJ]Value
HCQGWAIJTIGEUAHN]Value
BUTOORTKNHLTQXXA]Value
ISAYKZRFHITZAKNT]Value
RSAHJTXDXUQGXGPN]Value
BBFCFYDTAZALUABK]Value
ZJIMHTLFCNSKANSQ]Value
SQVGUUWRTBWLXKVR]Value
THRUAWIOEGWRTGAA]Value
MADDMXQBXXCVGTFQ]Value

#999991
#999992
#999993
#999994
#999995
#999996
#999997
#999998
#999999
#1000000

Small list
File &SAVEDLISTS& , Record SMALL.LIST
Command->
00001 AAAJYCRVCVVTCPGR
00002 AAAZPNAPWREDNBUI
00003 AADMYVIPBUZVYXLG
00004 AAFJVKHAZQVGGFBH
00005 AALLHJGMMVGMOVAU
00006 AAMGWASPCHCUEKMH
00007 AAMMDQTYPCRJNEZI
00008 AAZSJPCISROAZRBV
00009 AAZVIDZTDOJCWUVO
00010 ABBOEXEBUGVMVSIY
...
08650 ZZTHEXEAORLTCFNF
08651 ZZTHGTTCCUSVFBPT
08652 ZZVVNUHJBRESYRGX
08653 ZZVZLTSTKJGZHFYW
08654 ZZZKZPCQBUSQRYVA

21:14:23

Now its time for the proceeding program. Well use getlist statement to get our lists,
create both a dynamic array and a cache and then will try to compare the search speed:
Source of procbiglist.b
1

* Proceed a big list

2
3

INCLUDE JBC.h

4
5
6
7

CRT GETTING LISTS...


GETLIST BIG.LIST TO V.BIG.L ELSE
CRT ERROR GETTING LIST 1

J of BASE, TAF of C

137

By KZM

21 WORKING WITH JBASE CACHE

STOP

END

9
10

GETLIST SMALL.LIST TO V.SMALL.L ELSE


CRT ERROR GETTING LIST 2
STOP
END

11
12
13
14
15

CRT CREATING CACHE...


FOR V.I = 1 TO 1000000
V.RET = CachePut(BIGLIST, V.BIG.L<V.I,1>, V.BIG.L<V.I,2>)
NEXT V.I

16
17
18
19
20

V.QTY = DCOUNT(V.SMALL.L, @FM)

21
22

CRT STARTING SEARCH IN DYN ARRAY...


V.STRT = TIME()
FOR V.I = 1 TO V.QTY
V.ID = V.SMALL.L<V.I>
FIND V.ID IN V.BIG.L SETTING V.POSN ELSE CRT ?:
NEXT V.I
CRT TIME() - V.STRT

23
24
25
26
27
28
29
30

CRT STARTING SEARCH IN CACHE...


V.STRT = TIME()

31
32
33

FOR V.I = 1 TO V.QTY


V.ID = V.SMALL.L<V.I>
V.RET = CacheGet(BIGLIST, V.ID)
IF V.RET EQ THEN CRT ?:
NEXT V.I
CRT TIME() - V.STRT

34
35
36
37
38
39
40

CRT STOPPING...
STOP

41
42
43
44

END

45

Compile, launch, wait again. Output ends with a lot of question marks indicating that
a value wasnt obtained from the cache. Why? Is there size limit? Maybe. Lets test it.
Create a cache and then read the first entry repeatedly to see when its forgotten:
Source of testcache.b
1

* Program for "cache" test

2
3

INCLUDE JBC.h

J of BASE, TAF of C

138

By KZM

21 WORKING WITH JBASE CACHE

FOR V.I = 1 TO 1000000

5
6

IF V.I[3] EQ 000 THEN CRT .:

7
8

V.ID =

9
10

FOR V.J = 1 TO 16
V.RND = RND(26) + 65
V.ID := CHAR(V.RND)
NEXT V.J

11
12
13
14

;* A...Z

15

V.RET = CachePut(BUCKET1, V.ID, Value # : V.I)


IF NOT(V.RET) THEN
CRT CachePut() error at iteration # : V.I
STOP
END

16
17
18
19
20
21

IF V.I EQ 1 THEN
V.FIRST.ID = V.ID
END ELSE
V.RET = CacheGet(BUCKET1, V.FIRST.ID)
IF NOT(V.RET) THEN
CRT CacheGet() error at iteration # : V.I
STOP
END
END

22
23
24
25
26
27
28
29
30
31

NEXT V.I

32
33

CRT OK

34
35

STOP

36
37
38

END

39

No... OK at the end. Well, if this is a cache, it really shouldnt forget a record that
was retrieved quite recently. So well use a reverse search of the cache now:
New source of testcache.b
1

* Program for "cache" test

2
3

INCLUDE JBC.h

4
5

V.ID.L =

6
7

FOR V.I = 1 TO 1000000

J of BASE, TAF of C

139

By KZM

21 WORKING WITH JBASE CACHE

IF V.I[3] EQ 000 THEN CRT .:

9
10

V.ID =

11
12

FOR V.J = 1 TO 16
V.RND = RND(26) + 65
V.ID := CHAR(V.RND)
NEXT V.J

13
14
15
16

;* A...Z

17

V.RET = CachePut(BUCKET1, V.ID, Value # : V.I)


IF NOT(V.RET) THEN
CRT CachePut() error at iteration # : V.I
STOP
END

18
19
20
21
22
23

V.ID.L<-1> = V.ID

24
25

NEXT V.I

26
27

FOR V.I = 1000000 TO 1 STEP -1

28
29

IF V.I[3] EQ 000 THEN CRT ::

30
31

V.RET = CacheGet(BUCKET1, V.ID.L<V.I>)


IF V.RET EQ THEN
CRT CacheGet() error at iteration # : V.I
STOP
END
NEXT V.I

32
33
34
35
36
37
38

CRT OK

39
40

STOP

41
42
43

END

44

Now we see where the limit is:


Execution of testcache
.............................................................
.............................................................
.............................................................
.............................................................
.............................................................
.............................................................

J of BASE, TAF of C

140

By KZM

21 WORKING WITH JBASE CACHE

.............................................................
.............................................................
........................................:::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::CacheGet() error at iteration #56282

So, oldest 56,282 values were purged. Well, heres another excerpt from JBC.h explaining
that:
JBC.h excerpt
*
EQUATE CACHE_MAX_SIZE
EQUATE CACHE_PURGE_THRESHOLD
DEFC VAR CacheSetOption(VAR,
DEFC VAR CacheGetOption(VAR,

TO 1
;* Maximum bucket size (default: 1M)
TO 2
;* Bucket purging threshold (default: 90)
VAR, VAR)
VAR)

Anyway, our intention was to find out if caching mechanism can be used for a fast search
of a value based on its key. So simply reduce cache size from 1,000,000 to 900,000, create
the lists anew and try again. The results of the search tests now are:
Execution of procbiglist (with 900,000 items)
GETTING LISTS...
CREATING CACHE...
STARTING SEARCH IN DYN ARRAY...
11
STARTING SEARCH IN CACHE...
21
STOPPING...

We see that in this particular case dynamic array wins, though it might not be the case
under other platform or using other algorithm of caching (e.g. creating several buckets etc).

What to use then? If the volume isnt very big, use dynamic array
(or a dimensioned array if the number of items is fixed). If volume
is quite big - create a temporary hashed file for that purpose.

J of BASE, TAF of C

141

By KZM

22 DATA RETRIEVAL WITHOUT TELNET OR SSH ACCESS

22

Data retrieval without Telnet or SSH access

ere we try to get the data from a remote system that doesnt give us any access except
::::: interaction with jBASE agent. Unfortunately, we can use only Java or C# for that.
But theres another thing in jBC called callj so we can try to marry it with Java to be
able to access a remote system from local jBC.
Lets start jBASE agent first. For simplicity sake it wouldnt be a sophisticated scheme
with public/private keys or account/password authentication (like it has to be in any production environment). To start our agent, we take jrunT24.cmd, copy it to jrunAgent.cmd
and amend it:
jrunAgent.cmd - the very end
39
40
41
42

set TERM=ntcon
cd <path_to_bnk.run>
jbase_agent
cd %HOME%

Now start it and see something like:


Execution of jrunAgent.cmd
(Tue Aug 30 2011 21:05:52.427000|3976|2268) NOTICE starting up jAgent, Process P
er Connection mode, listening on port 20002, c:\src\tafc\pb201014\jagent\SocketA
cceptor.h +107

If you start it without administrative rights, you might see a popup window saying that
some features of that process were blocked; I ignored it and still all worked well.
Now its time to write some Java code. Here it is (last time Ive written something in
Java was exactly one year ago when I composed my first book so dont pay any attention to
its style; Im not Java man so all I can say is it works):
Source of callagent.java
1

// jRemote usage example

2
3
4
5
6
7
8
9
10
11
12
13

import
import
import
import
import
import
import
import
import
import
import

com.jbase.jremote.DefaultJConnectionFactory;
com.jbase.jremote.JConnection;
com.jbase.jremote.JConnectionFactory;
com.jbase.jremote.JDynArray;
com.jbase.jremote.JStatement;
com.jbase.jremote.JResultSet;
com.jbase.jremote.JRemoteException;
com.jbase.jremote.JRecordNotFoundException;
com.jbase.jremote.JExecuteResults;
com.jbase.jremote.JFile;
java.util.Properties;

J of BASE, TAF of C

142

By KZM

22 DATA RETRIEVAL WITHOUT TELNET OR SSH ACCESS

14
15
16
17

import
import
import
import

java.io.ByteArrayOutputStream;
java.io.OutputStreamWriter;
java.io.Writer;
java.io.UnsupportedEncodingException;

18
19

public class callagent {

20
21

public String jagentcall(String data) {

22
23
24

String returnText = "";


String[] tokens = data.split(" ");

25
26
27
28

int mode = Integer.valueOf(tokens[0]);


String host = tokens[1];
int port = Integer.valueOf(tokens[2]);

29
30
31
32
33

DefaultJConnectionFactory factory = new DefaultJConnectionFactory();


factory.setHost(host);
factory.setPort(port);
JDynArray resarray = new JDynArray();

34
35

try {

36
37

JConnection c = factory.getConnection();

38
39

switch (mode)

40
41

42
43

case 1:

44
45
46
47

String extcmd = "";


for (int i = 3; i < tokens.length; i++)
extcmd = extcmd + " " + tokens[i];

48
49

extcmd = extcmd.substring(1);

50
51

JExecuteResults results = c.execute(extcmd);

52
53

if (results != null) {

54

resarray = results.getCapturingVar();
int nattr = resarray.getNumberOfAttributes();
for(int a = 1; a <= nattr; a++)
{
returnText = returnText + resarray.get(a) + "\n";
}

55
56
57
58
59
60
61

J of BASE, TAF of C

143

By KZM

22 DATA RETRIEVAL WITHOUT TELNET OR SSH ACCESS

62

return returnText;

63
64

case 2:

65
66

String getfile = tokens[3];


String getrec = tokens[4];

67
68
69

JFile vFile = c.open(getfile);

70
71

if (vFile != null) {

72
73

Boolean isRec = vFile.exists(getrec);


if (isRec) {

74
75
76

resarray = vFile.read(getrec);
if (resarray != null) {
returnText = resarray.toString();
}

77
78
79
80
81

} else {
returnText = "Record not found";
}

82
83
84
85

} else {
returnText = "File not found";
}

86
87
88
89

return returnText;

90
91

92
93

c.close();

94
95

} catch (JRemoteException e) {
returnText = "Connection or other error";
}

96
97
98
99

return returnText;

100

101
102

103

This code was designed to work in two modes one to execute jsh commands and another
to retrieve a record from a file. To be able to compile it we need to place Java Developer
Kit (preferrably 1.6) to some directory on local PC and to add the following 3 lines to
jrunSH.cmd:
J of BASE, TAF of C

144

By KZM

22 DATA RETRIEVAL WITHOUT TELNET OR SSH ACCESS

jrunSH.cmd - the very end


35
36
37
38

set JAVA_HOME=<path_to_java_DK>
set PATH=%PATH%;%JAVA_HOME%\jre\bin\server;%JAVA_HOME%\bin
set CLASSPATH=%HOME%;%TAFC_HOME%\java\lib\jremote.jar;%CLASSPATH%

39
40
41

set TERM=ntcon
jsh

Then compile Java code:


Compilation of Java code
C:\sa-tafc> jrunSH.cmd
jsh ~ --> javac callagent.java
jsh ~ -->

If there were no messages all was OK and the file callagent.class appeared in the
current directory. Now to jBC part.
Source of callagent.b
1

* BASIC-JAGENT BRIDGE

V.CMD.L = SYSTEM(1000)
DEL V.CMD.L<1>
;* its program name itself
CHANGE @FM TO IN V.CMD.L

3
4
5
6

CALLJ "callagent", "jagentcall", V.CMD.L SETTING result ON ERROR CRT result

7
8

CRT result

9
10

STOP

11
12

END

13

Now compile and run it; parameters are: mode (1 or 2), IP address of the host where
jBASE agent is running, port which is used by jBASE agent, then for mode 1 the
command, for mode 2 file and record @id to retrieve.
Compilation and execution of jBC code accesing jBASE agent
C:\sa-tafc> jrunSH.cmd
jsh ~ --> jcompile callagent.b

J of BASE, TAF of C

145

By KZM

22 DATA RETRIEVAL WITHOUT TELNET OR SSH ACCESS

callagent.c
jsh ~ --> callagent 1 127.0.0.1 20002 COUNT FBNK.ACCOUNT
3620 Records counted
jsh ~ --> callagent 2 127.0.0.1 20002 F.SPF SYSTEM
<1>20100712<2>Model Bank<3>O<4><5>../bnk.data<6><7><8>201015<9>1<10><11><12>20
0<13><14>TAPE<15>Y<16>N<17>5<18><19>../bnk.data<20>../bnk.run<21>../bnk.dict<2
2><23>NT<24>./backup.hd &M&<25>./restore.hd &M&<26><27>5<28>500<29><30>201012<
31><32>10<33><34><35><35,1>BEF<35,2>ESP<35,3>ITL<35,4>JPY<35,5>PTE<35,6>XAU<35
,7>KWD<35,8>XAG<36>EURGBTMNS000<37>20111215<38><39><39,1>AA<39,2>AB<39,3>AC<39
,4>AD<39,5>AI<39,6>AL<39,7>AM<39,8>AP<39,9>AR<39,10>AS<39,11>AZ<39,12>BE<39,13
>BL<39,14>BR<39,15>CM<39,16>CO<39,17>CR<39,18>DC<39,19>DD<39,20>DE<39,21>DM<39
,22>DW<39,23>DX<39,24>EB<39,25>ET<39,26>EU<39,27>FD<39,28>FR<39,29>FT<39,30>FX
<39,31>GP<39,32>IA<39,33>IB<39,34>IC<39,35>IM<39,36>LC<39,37>LD<39,38>LI<39,39
>LM<39,40>MC<39,41>MD<39,42>MF<39,43>MM<39,44>MS<39,45>NR<39,46>NS<39,47>OF<39
,48>OO<39,49>OP<39,50>OV<39,51>PC<39,52>PD<39,53>PM<39,54>PV<39,55>PW<39,56>RE
<39,57>RP<39,58>SA<39,59>SB<39,60>SC<39,61>SE<39,62>SL<39,63>SP<39,64>ST<39,65
>SW<39,66>SY<39,67>TK<39,68>TT<39,69>TV<39,70>TX<39,71>WR<39,72>WS<39,73>XT<40
><41>Y<42>N<43>500<44><45><46><47><48><49>1<50><51><52>YES<53><54><55><56><57>
<58><59><60><61><62><63><64>YES<65><66><67><68><69><70><71><72><73><74><75><76
><77><78><79><80><81>5<82>2837_INPUTTER_I_INAU<83><83,1>1103110411<83,2>110311
0411<84>2837_INPUTTER<85>GB0010001<86>1

As you see, were running our jBC code under environment that doesnt have access to
T24 data at all (jrunSH.cmd).
What we then see in jBASE agent window:
jBASE agent window
(Tue Aug 30 2011 10:57:37.245000|3544|3984) NOTICE RequestHandlerService::open:
connected with localhost, RequestHandlerService.cpp +221
(Tue Aug 30 2011 10:57:39.776000|3544|3984) NOTICE Closing connection to localho
st, RequestHandlerService.cpp +127
(Tue Aug 30 2011 10:57:39.792000|3544|3984) NOTICE Shutting down Connection, JAg
entSocketServer.cpp +115
(Tue Aug 30 2011 10:57:40.073000|424|2432) NOTICE PID 3544 exited., WorkerProces
s.cpp +102
(Tue Aug 30 2011 10:57:48.355000|3624|3772) NOTICE RequestHandlerService::open:
connected with localhost, RequestHandlerService.cpp +221
(Tue Aug 30 2011 10:57:48.730000|3624|3772) NOTICE Closing connection to localho
st, RequestHandlerService.cpp +127
(Tue Aug 30 2011 10:57:48.745000|3624|3772) NOTICE Shutting down Connection, JAg
entSocketServer.cpp +115
(Tue Aug 30 2011 10:57:49.073000|424|2432) NOTICE PID 3624 exited., WorkerProces

J of BASE, TAF of C

146

By KZM

22 DATA RETRIEVAL WITHOUT TELNET OR SSH ACCESS

s.cpp +102

Always configure jBASE agent on your production system to require


authorisation. You see how knowing the IP and the port we could
execute any command and retrieve any data we wanted.

J of BASE, TAF of C

147

By KZM

23 CONCLUSIONS

23

Conclusions

feel heres the place I have to stop at for time being, otherwise it would never be finished.
:::: Enjoy, acquire more knowledge and dont forget to share it. Thanks to: jBASE/MV
community, LATEX community, Python community. (And of course to a colleague of mine
who preferred to remain incognito.)
cban 2011 Vladimir Kazimirchik except for samples of brainf**k code that have
copyrights of their own.
Useful links:
http://creativecommons.org/licenses/by-nc-sa/3.0 license of this book details
http://www.temenos.com Official Temenos site
http://groups.google.com/group/jbase jBASE Google group
Last but not least my previous (and telling the truth the very first) book:
http://dl.dropbox.com/u/20902365/t24/tihgw.pdf This Is How Globus Works...

J of BASE, TAF of C

148

By KZM