Unix functions for obc
The obc compiler comes with a lot of handy libraries, but once in a while you just need some function that is not in the standard supplied libraries. And it is near impossible to code the function in Oberon. For example when you need the time of day. So I needed a way to access the Linux system through a C functioncall. That's what this topic is about: extend the obc compiler the lazy way.
Luckily, prof Spivey already inserted the foundations for this kind of extensions in his obc compiler. It involves the DynLink library for making things quite easy.
The new functions added
For this moment I needed just three functions added to obc:
Create unix.mod
First create an oberon file containing the function prototypes (descriptions). I named this file 'unix.mod' all in lowercase letters. These are the contents of the file:
MODULE unix;
(* Unix functions for obc Oberon; Copyleft Jan Verhoeven *)
IMPORT DynLink;
PROCEDURE Usleep* (usec: INTEGER) : INTEGER IS "unix_Usleep";
PROCEDURE Time* () : INTEGER IS "unix_Time";
PROCEDURE Clock* () : INTEGER IS "unix_Clock";
BEGIN
DynLink.Load ("./unix.so")
END unix.
The three functions are listed and they are coupled to another function. For example 'Time' is coupled to
'unix_Time'. But this 'unix' refers to another one, not to 'unix.mod'. It refers to the 'unix.c' file.
Create unix.c
We declared the oberon function prototypes in 'unix.mod'. Now we need to create a C language file that acts as the 'interpreter', the 'interface' between oberon and C. The file 'unix.c' contains:
#include <unistd.h>
#include <time.h>
#include "obx.h"
/* Unix interface file for unix functions in obc Oberon
* CopyLeft Jan Verhoeven
*/
PRIMDEF int unix_Clock ()
{
ob_res.i = clock ();
}
PRIMDEF int unix_Time ()
{
ob_res.i = time (NULL);
}
PRIMDEF void unix_Usleep(value *bp)
{
ob_res.i = usleep (bp [HEAD + 0].i);
}
Each line is built like so:
Compiling the parts
Of course it is not enough to just make these two text files. We need to bind all the C functions together and make them accessible to obc. First we compile the C part with the magic spell
gcc -fPIC -shared -I /usr/local/lib/obc unix.c -o unix.soThis creates a 'unix.so' file. Make sure this file is ALWAYS around in the directory that is used by the program that uses the unix module.
Now you can just create your obc source file like you always do. But when compiling the source you need to use some more magic. I created a make-like file for it:
#! /usr/bin/bash obc -C -o $1 unix.mod $2.mod unix.cSo when I want to compile tim.mod and make an executable TIME.EXE the command in the console would be:
./mako TIME.EXE timThis is a fairly complex obc command:
An example: get the Unix date
Below is a very short program to show how the time is colelcted from the Linux system:
MODULE tim; IMPORT unix, In, Out; VAR t, t1, t2 : INTEGER; BEGIN t1 := unix.Time (); In.Int (t); t2 := unix.Time (); Out.Int (t2 - t1, 3); Out.Int (t2, 20); Out.Ln END tim.I don't think it needs much explanation. It is short and standard oberon syntax. The time is fetched, I wait for a number to be entered from the keyboard (go fetch a slice of cheese) and then I fetch the time again. The difference is calculated and displayed on screen.
See it compile
I assume you already compiled the C interface files. Now compile tim and make a file TIM.EXE
./mako TIME.EXE tim
See it run
And lets see it run:jan@fluor:~/Oberon/$ ./TIME.EXE 2 15 1582058447I entered the '2' which happened 15 seconds after I started the program. The unix time was then '1582058447' This shows it works.