next up previous contents index
Next: Index Up: $FILE Previous: 12 Complex numbers   Contents   Index

Subsections


13 Tutorial


13.1 How to use the interpreter

The files that are useful for an application of the command interpreter are the library itself and the file interp.h which should be included by each source file where functions of the library are used. Recall that there are 4 versions of the library : dynamic or static, with or without thread support. One or several initialization files must also be written for each application. The names of the built-in commands must not be put in an initialization file, they are already known by the interpreter. The user is concerned only by the new commands of the application.

If interpcom has been build with command line editing support, the appropriate librairies must be linked with the user object files and the interpcom library to produce the final executable, i.e on Unix

	gcc _my_object_files -linter -lreadline -lhistory -ltermcap -lm -o my_program

if GNU readline library is used,

	gcc _my_object_files -linter -ltecla -lm -o my_program

if ltecla is, or

	gcc _my_object_files -linter -lm -o my_program

if no command line editing library is used (if interpcom library with no thread support is used, take -linter_nothr instead of -linter). On BeOS, -lm should be omitted.


13.1.1 The main function and related functions

We describe here the steps needed to write an application using the command interpreter library.

Step 1 - The function prog_c and the way initialization files are used

The interperter is called with the function

void   prog_c(int argc, char *argv[], char *A, char B[], int n, char *pr, int
cas);
of the library. The main function of the program should contain a call to the function prog_c, and should be written as follows :
int  main(int argc, char *argv[])
and the parameters argc and argv[] are passed to the function prog_c. If the main program has no argument, the interpreter will execute the command given in the !init section of the initialization file (if this section is present) and then the interpreter will wait for new commands. If the !init section exists, the greeting message will not be printed. If there is an argument for the main program it will be used as a filename which will be executed as a command file. The program will then run silently (the commands in the file will not be printed) and wait for new commands when the command file is finished. This way of using the interpreter can be used for instance with programs like Geomview that can use external modules. In this situation, Geomview creates pipes connected to the interpreter output, and interprets this output as graphics.


As explained in 3 there are two ways of using the initialization file. The first way is to let the interpreter read it at the beginning of the execution, the second way is to include it in the executable file. In the first way the main function will be like this

int
main(int argc, char *argv[])
{ 
    Funcs = Funcs_interp;
    _NBFONC = _NBFONC0;
    .
    .
    .
    prog_c(argc, argv, "interp.ini", NULL, 0, NULL, 0);
    return 0;
}

Here interp.ini is the name of the initialization file. In the second way the main function will be like this

extern   int  int_nb;
extern   char int_txt[];

int
main(int argc, char *argv[])
{ 
    Funcs = Funcs_interp;
    _NBFONC = _NBFONC0;
    .
    .
    .
    prog_c(argc, argv, NULL, int_txt, int_nb, NULL, 0);
    return 0;
}

Here the array int_txt and the integer int_nb are defined in an auxilliary source file. This source file is produced from a usual initialization file, using the utility interp_convert (in the directory convert of the distribution). For instance, if the initialization file is interp.ini the program interp_convert is used like this

interp_convert interp.ini init.c int_txt int_nb

This will create the source file init.c defining int_txt and int_nb. This source file must then be compiled and linked with the program. It contains the same information as interp.ini.

Step 2 - The name of the custom initialization file

The name of the custom initialization file must be specified, using the variable User_Init_File, when a built-in initialization file is used when the program is run for the first time. It can be NULL, an then the internal initialization file will always be used. If it is not NULL, a copy of the internal initialization file will be made on a file in the user's home directory the first time the program is used, with the specified name. This new initialization file will be used in subsequent executions of the program. It can be of course modified (for instance the user can specify a new command and result directories). For instance

extern   int  int_nb;
extern   char int_txt[];

int
main(int argc, char *argv[])
{ 
    Funcs = Funcs_interp;
    _NBFONC = _NBFONC0;
    .
    .
    .
    User_Init_File = ".init_file";
    prog_c(argc, argv, NULL, int_txt, int_nb, NULL, 0);
    return 0;
}

Step 3 - The initialization functions init_prog and exit_prog

Before running the interpreter, the function prog_c will call the function

void init_prog(void)

which must be provided by the user, and contains all the initializations required by the program. It may be an empty function.

After the exit command that terminates the execution of the interpreter the function

void exit_prog(void)

will be called. It must also be written by the user, and may contain for instance a call to the function exit() to end the program.

Step 4 - Initialization function for additional expression evaluators

The function init_expr_GEN must be written (cf .7.6). It can be empty if no additional expression evaluator are defined. The array Funcs_Gen[] of functions corresponding to the new expression evaluators must also be defined.

Step 5 - Thread-specific functions

Each thread (in particular the main thread) will call the functions

void  init_thread_param(flow_data *)
void  clean_thread_param(flow_data *)

that must be provided by the user (see 9.4.2). If the application needs no thread-specific parameters, these functions can be empty.

Step 6 - Commands and numeric functions

The following must also be declared in some source file (outside functions)

FUNCTION *Funcs;
FUNCTIONC *Funcs_C;

pfi *proc[]={
.
.
.
};

(see 5, 7.2). The values of Funcs, Funcs_C and of the corresponding integers _NBFONC, _NBFONC_C (cf. 7.2, 7.6.1) must be defined before the function prog_c is called. To use the default values provided by the library we can do

    Funcs = Funcs_interp;
    _NBFONC = _NBFONC0;
    Funcs_C = Funcs_interp_C;
    _NBFONC = _NBFONC0_C;

Step 7 - The function dest_prop

The user must also write the function

void  dest_prop(int, int);

(which can be empty) cf. 6.6.


13.1.2 The definition of command and function names

The user must write source files containing the arrays of functions which are the commands (cf. 5). The commands which are implemented in the library need not be redefined. Recall that there is one such array for each section !func of the initialization file, and the list of all such arrays must be given in the array proc (cf. 5). The user must also define the array Funcs of structures corresponding to numerical functions known by the expression evaluator (cf. 7), if the default one is not used.


13.1.3 How to glue several applications in a single program

It is easy to glue several applications of the command interpreter. Here we call application a set of sources that can be linked with the command interpreter library to produce an executable, together with the corresponding initialization file. The global application should contain the sources of the various preceeding applications together with a global initialization file in such a way that the final program contains all the features of the particular ones. It is then possible to add new commands which may use several features (functions, object types...) of the initial applications.

The initialization file for the glogal application must contain the sections that can appear only once (i.e the sections !param, !rep and !init, cf. 3). These sections must then be removed from the initialization files of the applications. Then the initialization files of the applications must be included in the main one (for instance by using sections !include (cf. 3.11)). It is now necessary to define the array proc (cf. 5 and 13.1.2) containing the function lists of each application, in the same order as the order of the corresponding !func in the initialization files.

There can be problems with the order of the messages defined in the sections !message of the applications. This is why the arguments of the function error_mess in theses applications should be given in an appropriate way : for instance if in some application the message number 32 is to be printed we will use something like

error_mess(flow_interp, 32  +  __APPLI3)

rather than simply error_mess(flow_interp, 32). Here __APPLI3 is a globally defined variable corresponding to the given application to be glued. It will be set in the global application : its value is the number of messages defined in the global initialization file before the !message section of the given glued application appears. Of course in the particular application its value is set to 0. The same should be done for the object or structure types defined in the initial applications.


13.2 Examples of main files

The directory test contains minimal applications of the command interpreter (i.e. only the commands contained in the library are present). The first one is a program called test_ using an external initialization file called interp.ini. In the second program test2 the initialization file is contained in the executable. An application of the command interpreter can be based on one of these two test programs (the user has only to add new commands and functions).

The directory test/extra contains other minimal applications where additional expression evaluators are used.

We reproduce here some instructive parts of the main.c files of the test programs.


13.2.1 Example 1

This is the simplest main function for an application of the command interpreter. This application has only the built-in commands.


#include "interp.h"

FUNCTION *Funcs;
FUNCTIONC *Funcs_C;
FUNCTIONGen *Funcs_Gen[] = {
};
pfi *proc[] = { };    /* Here only the basic commands of the interpreter
                         are implemented */

int
main(int argc, char *argv[])
{
    Funcs = Funcs_interp;     /* Default array of real functions for */
     _NBFONC = _NBFONC0;      /* the expression evaluator */
    Funcs_C = Funcs_interp_C; /* Default array of complex functions for */
    _NBFONC_C = _NBFONC0_C;   /* the expression evaluator */
    User_Init_File = NULL;
    prog_c(argc, argv, "interp.ini", NULL, 0, NULL, 0); 
         /* the initialization file is interp.ini */
    return 0;
}


13.2.2 Example 2

Here no initialization file is used, all the information is contained in the program.


#include "interp.h"

extern int int_nb;
extern char int_txt[];
pfi *proc[] = { };
FUNCTION *Funcs;  
FUNCTIONC *Funcs_C;
FUNCTIONGen *Funcs_Gen[] = {
};

int
main(int argc, char *argv[])
{
    Funcs = Funcs_interp;  
    Funcs_C = Funcs_interp_C;
    _NBFONC = _NBFONC0; 
    _NBFONC_C = _NBFONC0_C;
    User_Init_File = NULL;
    prog_c(argc, argv, NULL, int_txt, int_nb, NULL, 0); 
    return 0;
}


13.2.3 Example 3

Here we give the main function for the application {\tta funct} of the command interpreter library.


#include "interp.h"
#include "funct_graph.h"

int   _XRANGE_F   = 1;
int   _XRANGE_D   = 2;
int   _RFUNC_F    = 3;
int   _RFUNC_D    = 4;
int   _CFUNC_F    = 5;
int   _CFUNC_D    = 6;
int   _FOUR_TR    = 7;
int   _BESS_PAR   = 8;
int   _FUNC_MESS  = 0;
int   _GRAPH_MESS = 24;
int   _FUNC_GRAPH_MESS = 29;
int   _GRAPH_X11  = 9;
int   _GRAPH_PS   = 10;
int   _GRAPH_COL  = 11;
int   _GRAPH_FRAM = 12;

pfi *proc[] =
{
proc_func,
proc_graph,
proc_func_graph,
};

FUNCTION *Funcs;
FUNCTIONC *Funcs_C;
FUNCTIONGen *Funcs_Gen[] = {
};

extern int int_nb;
extern char int_txt[];


int
main(int argc, char *argv[])
{
    Funcs = Funcs_func;
    _NBFONC = _NBFONC_FUNC;
    Funcs_C = Funcs_interp_C;
    _NBFONC_C = _NBFONC0_C;
    User_Init_File = ".funct";
    prog_c(argc, argv, NULL, int_txt, int_nb, NULL, 0);
    return 0;
}


Here three arrays of commands are defined, each one corresponding to a section !func of the initialization file funct.ini. A new array of functions for the expression evaluator is used instead of the default one. These arrays of commands and functions are defined in other source files.


13.3 Non interactive use of the command interpreter

The function prog_c is used to initialize the command interpreter. Recall the structure of this function :

void   prog_c(int argc, char *argv[], char *A, char B[], int n, char *pr, int
cas);

The last integer parameter must be 1 to tell the interpreter that it will be run non-interactively. If the string pr is not NULL, the interpreter will execute the command contained in it. It will then return. The functions load_cmd or loadcmd can be used to load programs contained in character strings or files in the command directory respectively. The function

void   Exec_interp_command(char *h);

executes the command contained in the string h. It can be for instance the name of a program.

We reproduce below the source file test_in.c (in the directory test) that is self-explanatory.

#include "interp.h"

FUNCTION *Funcs;

pfi *proc[]={
};


int
main(void)
{
    char            h[50],
                    h2[1000],
                   *k[3];
    
    
/*------ Initialization of the command interpreter ---------------*/
    Funcs = Funcs_interp;
    _NBFONC = _NBFONC0;
    prog_c(0, NULL, "interp.ini", NULL, 0, NULL, 1);
/*----------------------------------------------------------------*/


/*------ We load the program file 'magic' ------------------------*/
    if (loadcmd("magic") == 0) 
        printf("magic loaded\n");
    else
        return 1;
/*----------------------------------------------------------------*/


/*------ We run one of the programs ------------------------------*/
    sprintf(h, "magic x\n");
    Exec_interp_command(h);
/*----------------------------------------------------------------*/


/*------ We write a program ('example') in 
         the string h2 -------------------------------------------*/
    memset(h , 0, 50);
    memset(h2 , 0, 1000);
    sprintf(h, ":example\n");
    strcat(h2, h);
    memset(h , 0, 50);
    sprintf(h, "1\n");
    strcat(h2, h);
    memset(h , 0, 50);
    sprintf(h, "0\n");
    strcat(h2, h);
    memset(h , 0, 50);
    sprintf(h, "-1\n");
    strcat(h2, h);
    memset(h , 0, 50);
    sprintf(h, "mon test_x.out\n");
    strcat(h2, h);
    memset(h , 0, 50);
    sprintf(h, "magicb\n");
    strcat(h2, h);
    memset(h , 0, 50);
    sprintf(h, "end_mon\n");
    strcat(h2, h);
    k[0] = NULL;
    k[1] = h2;
/*----------------------------------------------------------------*/


/*------  We load this program in the command interpreter --------*/
    load_cmd(0, k);
/*----------------------------------------------------------------*/


/*------ We execute the program 'example' ------------------------*/
    sprintf(h, "example\n");
    Exec_interp_command(h);
/*----------------------------------------------------------------*/


/*------ We clean the memory used by the interpreter -------------*/
    clean_exit_interp(&flow_I[0]);
/*----------------------------------------------------------------*/

   return 0;
}

void
init_prog(flow_data *flow_interp)
{
}

void
exit_prog()
{
    exit(0);
}

void
dest_prop(int typ, int i0, flow_data *flow_interp)
{
}

void
init_thread_param(flow_data *flow_interp)
{
}

void
clean_thread_param(flow_data *flow_interp)
{
}


next up previous contents index
Next: Index Up: $FILE Previous: 12 Complex numbers   Contents   Index
jmdr 2001-12-07