                      INTERFACING C WITH CLIPPER

                            Matthew Probert
                           Servile  Software


The information in this info journal was written for the Clipper 
Summer '87 compiler, whether or not it still holds for Clipper 5 I do 
not know. 

The Clipper programming language is a popular xBase environment for 
the PC. However, it lacks many of the facilities available to 
programmers of other languages, and it is quite slow compared to C. 
Because of this there are a large number of third party add-on 
libraries available for Clipper which provide the facilities lacked. 

As a programmer you probably want to write your own library for 
Clipper, or perhaps individual functions to cater for circumstances 
which Clipper cannot handle, such as high resolution graphics. 


COMPILING AND LINKING

The  Clipper extend functions allow user defined functions to be 
written  in C, linked with and used by the Clipper application. 

The first problem a programmer must address when writing functions in 
C to link with a Clipper application is that of the C compiler's run  
time libraries. 

If one is writing functions with Microsoft C,  then most of the 
required run time  library  functions  will be found in the  
Clipper.lib  and  Extend.lib libraries which are part of Clipper. 

If,  however, one is using a different C compiler, such as Borland's 
Turbo C then the run time library routines must be supplied on the 
link line. 

All C functions must be compiled using the large memory model the 
following line is used with Microsoft C 

                  cl /c /AL /Zl /Oalt /FPa /Gs <program.c>

and this compile line may be used with Turbo C 

                            tcc -c -ml <program>

simply substitute <program> for the program name to be compiled. 
(Change tcc for bcc to compile with Borland C).

Having compiled a C function it must be linked in with the 
application. If the C function was compiled with Microsoft C then the 
link line will look a little like this; 

LINK /SE:500 /NOE program.obj cfunc.obj,,,Clipper Extend 

If the C function was linked with another C compiler you will also 
need to link in the C run time libraries,  for example to link in the 
Turbo C large memory mode library use the following link line; 

LINK /SE:500 /NOE program.obj cfunc.obj,,,Clipper Extend cl  

If one is using a number of separately compiled C functions it is a 
good idea to collect them in a library.  If you are using Microsoft C 
then  you  can simply  create  the  library by using Microsoft Lib.exe 
with  the  following command line; 

                     LIB mylib +prog1 +prog2, NUL, NUL

This tells the librarian to add prog1.obj and prog2.obj to a library  
called mylib.lib, creating it if it does not exist. The NUL parameter  
is  for supressing the listing file. 

If you have been using another C compiler you should copy the C large 
memory model run time library before adding your functions to it for 
example; 

                     COPY C:\TURBOC\LIB\cl.lib mylib.lib
                     LIB mylib +prog1 +prog2, NUL, NUL

Then when you link your Clipper application you will use a link line 
similar to; 

               LINK /SE:500 /NOE myprog,,,Clipper Extend Mylib

Often when linking C functions with  Clipper applications link errors 
will occur such as those shown below; 

Microsoft (R) Overlay Linker  Version 3.65   
Copyright (C) Microsoft Corp 1983-1988.  All rights reserved.


LINK : error L2029: Unresolved externals:


FIWRQQ in file(s):
 M:SLIB.LIB(TEST)
FIDRQQ in file(s):
 M:SLIB.LIB(TEST)

There were 2 errors detected

                             Example Link Errors


The errors shown here are 'Unresolved externals', that is they are 
references to functions which are not found in any of the object 
modules or libraries specified on the link line. These occur because 
the C compilers often scatter functions and variables through a number 
of libraries. In tracking  these  functions down  use may be made of 
the Microsoft  librarian list file option.  If you run Lib.Exe on the 
Turbo C 'emu.lib' library file and specify a listing file as follows; 

                               LIB emu,emu.lst

The librarian will create an ascii file which contains the names  of  
each object module contained in the specified library file, and the 
names of each function and public variable declared in each object 
module, as shown in this listing of Borland's EMU.LIB library. 

e086_Entry........EMU086            e086_Shortcut.....EMU086
e087_Entry........EMU087            e087_Shortcut.....EMU087
FIARQQ............EMUINIT           FICRQQ............EMUINIT
FIDRQQ............EMUINIT           FIERQQ............EMUINIT
FISRQQ............EMUINIT           FIWRQQ............EMUINIT
FJARQQ............EMUINIT           FJCRQQ............EMUINIT
FJSRQQ............EMUINIT           __EMURESET........EMUINIT


EMUINIT           Offset: 00000010H  Code and data size: 1a2H
  FIARQQ            FICRQQ            FIDRQQ            FIERQQ
  FISRQQ            FIWRQQ            FJARQQ            FJCRQQ
  FJSRQQ            __EMURESET      

EMU086            Offset: 00000470H  Code and data size: 2630H
  e086_Entry        e086_Shortcut   

EMU087            Offset: 00003200H  Code and data size: 417H
  e087_Entry        e087_Shortcut   


Example Library List File




     Function/variable
                   Object module name
                   
e086_Entry........EMU086            e086_Shortcut.....EMU086
FJSRQQ............EMUINIT           __EMURESET........EMUINIT


   Object module name      Offset within library
                                                         Size of module
                                                         
EMUINIT           Offset: 00000010H  Code and data size: 1a2H
  FIARQQ            FICRQQ            FIDRQQ            FIERQQ
  FISRQQ            FIWRQQ            FJARQQ            FJCRQQ
  FJSRQQ            __EMURESET      


                      Description Of Library List File


Receiving Parameters


Clipper provides six different functions for receiving parameters in a 
C function. These functions are; 

Receive a string      char * _parc(int,[int])
Receive a Date string char * _pards(int,[int])
Receive a logical     int _parl(int,[int])
Receive an integer    int _parni(int,[int])
Receive a long        long _parnl(int,[int])
Receive a double      double _parnd(int,[int])



To illustrate simple parameter receiving in a C function I  offer the 
following simple C function which receives two numeric parameters from  
the calling  Clipper program,  and uses these two numeric parameters 
to set the size of the cursor. 


#include <nandef.h>             /* Clipper header files */
#include <extend.h>
#include <dos.h>                /* Header file to define REGS */

CLIPPER s_curset()
{
    /* Demonstration function to set cursor shape */

    union REGS inreg,outreg;

    inreg.h.ah = 0x01;
    inreg.h.ch = _parni(1);     /* Get integer parameter 1 */
    inreg.h.cl = _parni(2);     /* Get integer parameter 2 */
    int86(0x10,&inreg,&outreg);
    _ret();                     /* Return to Clipper */
}

Clipper provides four more functions for dealing with received  
parameters; _parclen(int,[int])  which returns the length of a string 
including imbedded '\0's,  _parcsiz(int[int])  which returns the 
length of a character  string passed by reference from Clipper, 
_parinfa(int,[int]) which returns the type of a specified  array  
element  or the length of  an  array,   and  finally _parinfo(int) 
whic returns the type of a parameter. 

The following example function uses _parinfa()  to determine both the 
length of an array passed from a Clipper program,  and the type of 
each element in the array. The function then returns to Clipper an 
integer representing the number of defined elements in the array. 


#include <nandef.h>
#include <extend.h>

CLIPPER s_alen()
{
    int total;
    int n;
    int defined;
    int type;

    /* Return the number of defined elements in an array */
    /* From Clipper use defined = s_alen(arr) */

    total = _parinfa(1,0); /* Get declared number of elements in array */

    defined = 0;

    for (n = 1; n <= total; n++){
        type = _parinfa(1,n);   /* Get array parameter type */
        if (type)
            defined++;
    }
    _retni(defined);            /* Return an integer to Clipper */
}

This function goes one step further to return the mean average of all 
numeric values in an array. Notice the use of _parnd() to retrieve the 
numeric values as doubles. You may find that because of the floating 
point arithmetic in this function that it will only work if compiled 
with Microsoft C. 


#include <nandef.h>
#include <extend.h>

CLIPPER s_aave()
{
    int total;
    int defined;
    int n;
    int type;
    double sum;

    /* Return the mean average value of numbers in array */
    /* From Clipper use mean = s_aave(arr)


    total = _parinfa(1,0);              /* Get declared number of elements */

    defined = 0;

    for (n = 1; n <= total; n++){       /* Determine number of defined */
        type = _parinfa(1,n);           /* elements */
        if (type == 2)
            defined++;
    }

    sum = 0;

    for (n = 1; n <= total; n++){
        type = _parinfa(1,n);        
        if (type == 2)                  /* Only sum numeric values */
            sum += _parnd(1,n);
    }
    _retnd(sum / defined);              /* Return a double to Clipper */
}

Returning Values

The Clipper manual lists seven functions for returning from a function 
written in another language. These return functions for C are as 
follows; 

character          _retc(char *)
date               _retds(char *)
logical            _retl(int)
numeric (int)      _retni(int)
numeric (long)     _retnl(long)
numeric  (double)  _retnd(double)
nothing            _ret(void)

Omitted from the Clipper manual is the information that you may return 
different types of value back from a function! For example, you may 
wish to return a character string under normal circumstances, fine use 
_retc(). On error occurences however you can return a logical using 
_retl(). The Clipper program will assign the received value to the 
receiving variable in the correct manner. 


The following simple C function returns a random number.  Notice the 
use of integers which limits the range of the function to +-32767.   
For larger values you should use longs instead of integers. 


#include <nandef.h>
#include <extend.h>
#include <dos.h>

CLIPPER s_random()
{
    /* Returns a random number between 0 and param1 - 1 */
    /* From Clipper use x = s_random(param1) */

    int param1;
    int x;

    param1 = _parni(1);

    x = rand() % param1;
    _retni(x);
}

This function receives a string from Clipper,  and passes back an 
upper case copy of the string,  leaving the original unchanged.  The 
maximum length  of the  string  which can be processed is determined 
by the size  of  target[], here set to 5000 characters. 


#include <nandef.h>
#include <extend.h>

CLIPPER s_upper()
{
    /* Returns an upper case copy of string */
    /* From Clipper use ? s_upper("this is a string")

    char *p;
    char *q;
    char *string;
    char target[5000];
    int n;

    string = _parc(1);

    p = string;
    q = target;

    while(*string){
        *q++ = toupper(*string);
        string++;
    }
    *q = '\0';
    string = p;
    _retc(target);
}

This function may be used to change the current DOS directory. If it 
is successful it returns .T. to the calling Clipper program,  
otherwise it returns .F. 


#include <nandef.h>
#include <extend.h>
#include <dos.h>

CLIPPER s_chdir()
{
    /* Attempts to change the current DOS directory */
    /* From Clipper use result = s_chdir(path) */

    union REGS inreg,outreg;
    struct SREGS segreg;

    char *path;
    int x;

    path = _parc(1);            /* Retrieve string from Clipper */

    inreg.h.ah = 0x3b;
    segreg.ds = FP_SEG(path);
    inreg.x.dx = FP_OFF(path);
    intdosx(&inreg,&outreg,&segreg);

    x = outreg.x.ax;

    if (x == 3)
        _retl(0);       /* Return logical .F. back to Clipper */
    else
        _retl(1);       /* Return logical .T. back to Clipper */
}

Avoiding Unresolved Externals

As we have already seen, a common problem plaguing the programmer 
interfacing C functions with Clipper programs is Unresolved Externals. 

The following example C function called s_print()  will not link with 
Clipper. 

#include <nandef.h>
#include <extend.h>
#include <stdio.h>

CLIPPER s_print()
{
    char *x;

    x = _parc(1);

    printf("\nI received %s from Clipper.\n",x);

    _ret();
}

The linker gives you the following reply;

Microsoft (R) Overlay Linker  Version 3.65   
Copyright (C) Microsoft Corp 1983-1988.  All rights reserved.

M:SLIB.LIB(IOERROR) : error L2025: __doserrno : symbol defined more than once
 pos: 16C6F Record type: 53C6

LINK : error L2029: Unresolved externals:



__RealCvtVector in file(s):
 M:SLIB.LIB(REALCVT)
_abort in file(s):
 M:SLIB.LIB(CVTFAK)

There were 3 errors detected

The error L2025 'symbol defined more than once' can in this case be 
ignored. However, the unresolved externals 'RealCvtVector' and 'abort' 
cannot be ignored. These two functions are referenced by the function 
printf() which has been included in the C function. The answer is to 
use as few of the compiler's run time library functions as possible, 
use ROM calls instead with INT86() and INTDOSX() etc. 

Adding High Resolution Graphics To Clipper With C 

The most annoying omission from Clipper, in my opinion, is the lack of 
high resolution graphics facilities. The following functions, written 
in Turbo C, provide high resolution graphics to Clipper. 

First we require a means to change the video display mode to a high 
resolution graphics mode, and back to text mode. The IBM PC BIOS 
provides the means for this and can be called from C as follows; 



/*         Servile Software Library For Clipper             */

#include <nandef.h>
#include <extend.h>
#include <dos.h>

CLIPPER s_smode()
{
    /* Set Video Mode */
    /* From Clipper use s_smode(mode) */

    union REGS inreg,outreg;

    inreg.h.al = _parni(1);
    inreg.h.ah = 0x00;
    int86 (0x10, &inreg, &outreg);


/*  1 40x25 colour text
    2 40x25 bw text
    3 80x25 colour text
    4 320x200 4 colour graphics
    5 320x200 4 colour graphics colour burst off
    6 640x200 2 colour graphics
    etc
*/
    _ret();
}

Having set the computer into graphics mode, how about setting pixels 
to a specified colour? 

/*         Servile Software Library For Clipper             */

#include <nandef.h>
#include <extend.h>
#include <dos.h>

CLIPPER s_plot()
{

    union REGS inreg,outreg;

    /* Sets a pixel at the specified coordinates to the specified 
colour. */ 

    inreg.h.bh = 0x00;
    inreg.x.cx = _parni(1);
    inreg.x.dx = _parni(2);
    inreg.h.al = _parni(3);
    inreg.h.ah = 0x0C;
    int86(0x10, &inreg, &outreg);
}

Line drawing and circles are handled by these two functions;

/*         Servile Software Library For Clipper             */

#include <nandef.h>
#include <extend.h>
#include <dos.h>

CLIPPER s_line()
{

    union REGS inreg,outreg;

    /* Draws a straight line from (a,b) to (c,d) in colour col */

    int a;
    int b;
    int c;
    int d;
    int col;
    int u;
    int v;
    int d1x;
    int d1y;
    int d2x;
    int d2y;
    int m;
    int n;
    int s;
    int i;

    a = _parni(1);
    b = _parni(2);
    c = _parni(3);
    d = _parni(4);
    col = _parni(5);

    u = c - a;
    v = d - b;
    if (u == 0)
    {
        d1x = 0;
        m = 0;
    }
    else
    {
        m = abs(u);
        if (u < 0)
            d1x = -1;
        else
            if (u > 0)
                d1x = 1;
    }
    if ( v == 0)
    {
        d1y = 0;
        n = 0;
    }
    else
    {
        n = abs(v);
        if (v < 0)
            d1y = -1;
        else
            if (v > 0)
                d1y = 1;
    }
    if (m > n)
    {
        d2x = d1x;
        d2y = 0;
    }
    else
    {
        d2x = 0;
        d2y = d1y;
        m = n;
        n = abs(u);
    }
    s = (m / 2);

    inreg.h.al = (unsigned char)col;
    inreg.h.bh = 0x00;
    inreg.h.ah = 0x0C;
    for (i = 0; i <= m; i++)
    {
        inreg.x.cx = (unsigned int)(a);
        inreg.x.dx = (unsigned int)(b);
        int86(0x10, &inreg, &outreg);
        s += n;
        if (s >= m)
        {
            s -= m;
            a += d1x;
            b += d1y;
        }
        else
        {
            a += d2x;
            b += d2y;
        }
    }
}

This circle drawing function uses in-line assembler to speed up the 
drawing process. It can easily be replaced with inreg and outreg 
parameters as in the other functions, or the other functions can be 
changed to in-line assembler. Both methods are shown to illustrate 
different ways of achieving the same result. 

/*         Servile Software Library For Clipper             */

#include <nandef.h>
#include <extend.h>
#include <dos.h>


void plot(int x, int y, unsigned char colour)
{
    asm mov al , colour;
    asm mov bh , 00;
    asm mov cx , x;
    asm mov dx , y;
    asm mov ah , 0Ch;
    asm int 10h;
}

int getmode()
{
    /* Returns current video mode  and number of columns in ncols */

    asm mov ah , 0Fh;
    asm int 10h;
    return(_AL);
}


CLIPPER s_circle()
{
    int x_centre;
    int y_centre;
    int radius;
    int colour;
    int x,y,delta;
    int startx,endx,x1,starty,endy,y1;
    int asp_ratio;

    x_centre = _parni(1);
    y_centre = _parni(2);
    radius = _parni(3);
    colour = _parni(4);

    if (getmode() == 6)
        asp_ratio = 22;
    else
        asp_ratio = 13;

    y = radius;
    delta = 3 - 2 * radius;

    for(x = 0; x < y; )
    {
        starty = y * asp_ratio / 10;
        endy = (y + 1) * asp_ratio / 10;
        startx = x * asp_ratio / 10;
        endx = (x + 1) * asp_ratio / 10;

        for(x1 = startx; x1 < endx; ++x1)
        {
            plot(x1+x_centre,y+y_centre,colour);
            plot(x1+x_centre,y_centre - y,colour);
            plot(x_centre - x1,y_centre - y,colour);
            plot(x_centre - x1,y + y_centre,colour);
        }

        for(y1 = starty; y1 < endy; ++y1)
        {
            plot(y1+x_centre,x+y_centre,colour);
            plot(y1+x_centre,y_centre - x,colour);
            plot(x_centre - y1,y_centre - x,colour);
            plot(x_centre - y1,x + y_centre,colour);
        }

        if (delta < 0)
            delta += 4 * x + 6;
        else
        {
            delta += 4*(x-y)+10;
            y--;
        }
        x++;
    }
    if(y)
    {
        starty = y * asp_ratio / 10;
        endy = (y + 1) * asp_ratio / 10;
        startx = x * asp_ratio / 10;
        endx = (x + 1) * asp_ratio / 10;
        for(x1 = startx; x1 < endx; ++x1)
        {
            plot(x1+x_centre,y+y_centre,colour);
            plot(x1+x_centre,y_centre - y,colour);
            plot(x_centre - x1,y_centre - y,colour);
            plot(x_centre - x1,y + y_centre,colour);
        }

        for(y1 = starty; y1 < endy; ++y1)
        {
            plot(y1+x_centre,x+y_centre,colour);
            plot(y1+x_centre,y_centre - x,colour);
            plot(x_centre - y1,y_centre - x,colour);
            plot(x_centre - y1,x + y_centre,colour);
        }
    }
}

The Clipper facilities for displaying text on the screen, @....SAY and 
? do not work when the monitor is in graphics mode. You then need the 
following function to allow text to be displayed in a graphics mode; 

/*         Servile Software Library For Clipper             */

#include <nandef.h>
#include <extend.h>
#include <dos.h>

int sgetmode(int *ncols)
{
    /* Returns current video mode  and number of columns in ncols */

    union REGS inreg,outreg;

    inreg.h.ah = 0x0F;
    int86(0x10, &inreg, &outreg);
    *ncols = outreg.h.ah;
    return(outreg.h.al);
}

void at(int row, int col)
{
    asm mov bh , 0;
    asm mov dh , row;
    asm mov dl , col;
    asm mov ah , 02h;
    asm int 10h;
}


CLIPPER s_say()
{
    char *output;
    int p = 0;
    unsigned char page;
    unsigned char text;
    int n;
    int r;
    int c;
    int attribute;

    output = _parc(1);
    r = _parni(2);
    c = _parni(3);
    attribute = _parni(4);

    asm mov ah , 0Fh;
    asm int 10h;
    asm mov page, bh;

    sgetmode(&n);

    at(r,c);

    while (output[p]) 
    {
        text = output[p++];
        asm mov bh , page;
        asm mov bl , attribute;
        asm mov cx , 01h;
        asm mov ah , 09h;
        asm mov al , text;
        asm int 10h;
        c++;
        if (c < (n-1))
            at( r, c);
        else
        {
            c = 0;
            at(++r,0);
        }
    }
}

When drawing graphs, it is often required to fill in areas of the 
graph in different patterns. This is a graphics function to fill 
boundered shapes with a specified hatching pattern providing a means 
to achieve more usable graphs; 


/*         Servile Software Library For Clipper             */

#include <nandef.h>
#include <extend.h>
#include <dos.h>

int pixset(int x, int y)
{
    /* Returns the colour of the specified pixel */

    asm mov cx ,x;
    asm mov dx ,y;
    asm mov ah ,0Dh;
    asm int 10h;
    return(_AL);
}

CLIPPER s_fill()
{
    /* Fill a boundered shape using a hatch pattern */

    int mode;
    int xa;
    int ya;
    int bn;
    int byn;
    int x;
    int y;
    int col;
    int pattern;
    int maxx;
    int maxy;
    int hatch[10][8] = { 255,255,255,255,255,255,255,255,
                        128,64,32,16,8,4,2,1,
                        1,2,4,8,16,32,64,128,
                        1,2,4,8,8,4,2,1,
                        238,238,238,238,238,238,238,238,
                        170,85,170,85,170,85,170,85,
                        192,96,48,24,12,6,3,1,
                        62,62,62,0,227,227,227,0,
                        129,66,36,24,24,36,66,129,
                        146,36,146,36,146,36,146,36};

    /* Patterns for fill, each integer describes a row of dots */

    x = _parni(1);
    y = _parni(2);
    col = _parni(3);
    pattern = _parni(4);

    mode = getmode();

    switch(mode)
    {
        case 0:
        case 1:
        case 2:
        case 3: break;
        case 4:
        case 9:
        case 13:
        case 19:
        case 5: maxx = 320;
                maxy = 200;
                break;
        case 14:
        case 10:
        case 6: maxx = 640;
                maxy = 200;
                break;
        case 7: maxx = 720;
                maxy = 400;
                break;
        case 8: maxx = 160;
                maxy = 200;
                break;
        case 15:
        case 16: maxx = 640;
                 maxy = 350;
                 break;
        case 17:
        case 18: maxx = 640;
                 maxy = 480;
                 break;

    }

    xa = x;
    ya = y;  /* Save Origin */

    if(pixset(x,y))
        return;

    bn = 1;
    byn = 0;

    do
    {
        if (hatch[pattern][byn] != 0)
        {  /* If blank ignore */
            do
            {
                if ((bn & hatch[pattern][byn]) == bn)
                {
                    asm mov al , col;
                    asm mov bh , 00;
                    asm mov cx , x;
                    asm mov dx , y;
                    asm mov ah , 0Ch;
                    asm int 10h;
                }
                x--;
                bn <<= 1;
                if (bn > 128)
                    bn = 1;
            }
            while(!pixset(x,y) && (x > -1));

            x = xa + 1;
            bn = 128;

            do
            {
                if ((bn & hatch[pattern][byn]) == bn)
                {
                    asm mov al , col;
                    asm mov bh , 00;
                    asm mov cx , x;
                    asm mov dx , y;
                    asm mov ah , 0Ch;
                    asm int 10h;
                }
                x++;
                bn >>=1;
                if (bn <1)
                    bn = 128;
            }
            while((!pixset(x,y)) && (x <= maxx));
        }
        x = xa;
        y--;
        bn = 1;
        byn++;
        if (byn > 7)
            byn = 0;
    }
    while(!pixset(x,y) && ( y > -1));

    /* Now travel downwards */

    y = ya + 1;

    byn = 7;
    bn = 1;
    do
    {
        /* Travel left */
        if (hatch[pattern][byn] !=0)
        {
            do
            {
                if ((bn & hatch[pattern][byn]) == bn)
                {
                    asm mov al , col;
                    asm mov bh , 00;
                    asm mov cx , x;
                    asm mov dx , y;
                    asm mov ah , 0Ch;
                    asm int 10h;
                }
                x--;
                bn <<= 1;
                if (bn > 128)
                    bn = 1;
            }
            while(!pixset(x,y) && (x > -1));

            /* Back to x origin */
            x = xa + 1 ;
            bn = 128;

            /* Travel right */
            do
            {
                if ((bn & hatch[pattern][byn]) == bn)
                {
                    asm mov al , col;
                    asm mov bh , 00;
                    asm mov cx , x;
                    asm mov dx , y;
                    asm mov ah , 0Ch;
                    asm int 10h;
                }
                x++;
                bn >>=1;
                if (bn <1)
                    bn = 128;
            }
            while((!pixset(x,y)) && (x <= maxx));
        }
        x = xa;
        bn = 1;
        y++;
        byn--;
        if (byn < 0)
            byn = 7;
    }
    while((!pixset(x,y)) && (y <= maxy));
}

