# CHAPTER 4: The Standard Interface

## 4.1 Introduction

Query Language commands may be embedded in a C program with the routine `mscall`. The routine takes the database directory and the Query Language command as its arguments.

       mscall (database_directory, query_language_statement);

Both the database directory and the Query Language statement should be strings. The strings may be built by the application program. The routine returns zero (0) if the statement succeeds, and a non-zero value if it fails. (A failure may be caused by a syntax error in the Query Language command, or an invalid attribute or table name.)

## 4.2 The Header File mscc.h

The header file `mscc.h` must be included in all C applications using `mscall`. It contains definitions of constants and data types used by all the Host Language Interface routines, including `mscall`. The header file is listed below.

       /***********************************************************************
        *      (c) Copyright   Empress Software Inc. 1983, 2006
        ***********************************************************************/

       #ifndef __MSCC_H

       #define __MSCC_H

       #ifdef __cplusplus
       extern "C" {
       #endif

       #include        <stdio.h>
       #include        "c.h"

       #include        <misc/public.h>
       #include        <usrmp/msdsqldt.h>
       #include        <usrfns/dtparvar.hx>
       #include        <api/mrapi.hx>
       #include        <api/msapi.hx>
       #include        <api/mxapi.hx>
       #include        <misc/init.hx>
       #include        <misc/mscc_com.h>
       #include        <misc/mxe.h>
       #include        <misc/mxtrig.h>

       #ifdef __cplusplus
       }
       #endif

       #endif  /* __MSCC_H */

Note that, the `mscc.h` file differs from version to version. For an exact reference, the file is located in the following Empress directory:

- For UNIX, it is `include/mscc.h`
- For Windows NT, it is `include\mscc.h`

## 4.3 Initialization and Cleanup

There are two ways to organize a program using `mscall`.

1. You can name the regular `main` procedure `msmain`. This will include a default `main` procedure which does the necessary initializations. `msmain` may have the parameters `argc` and `argv` as usual. Similarly, name the regular `C exit` procedure `msexit`.

2. You can write your own `main` procedure, calling `msinit` to begin and `msexit` to end. These two procedures carry out various initialization and cleanup tasks.

The routine `msclean` allows you to put a procedure on the stack to be called immediately before the program exits.

## 4.4 Compiling and Linking

Programs using `mscall` should be compiled using `empcc`, rather than `cc`. This ensures that the appropriate Empress library routines are included when the program is compiled. In all other respects, `empcc` is identical to `cc`.

## 4.5 Examples Using the Standard Interface

The database name in the following examples is a logical database named `repairs`. The physical location of the database could be anywhere on the system.

Example 1

The following program, `names.c`, selects names from the `personnel` table.

       #include <mscc.h>
       #define DATABASE "repairs"
       msmain ()
       {
            mscall (DATABASE, "select name from personnel");
       }

Example 2

The program `phone.c` looks up a telephone number given a single employee name.

       #include <mscc.h>

       #define DATABASE "repairs"
       #define BUFSIZE  512

       msmain (argc, argv)
            int   argc;
            char  *argv[];
       {
            char *name, command[BUFSIZE];
            name = argv[1];
            sprintf (command,"
            select phone from personnel dump where name =\
                 '%s'", name);
            mscall (DATABASE, command);
       }

Example 3

The program `newphone.c` checks that only one name is given:

       #include <mscc.h>

       #define DATABASE  "repairs"
       #define  BUFSIZE  512

       msmain (argc, argv)
            int   argc;
            char  *argv[];
       {
            char   command[BUFSIZE], *name;

            if  (argc != 2)
            {
                 fprintf (stderr, "Usage: phone \
                      employee_name\n");
                 msexit (1);
            }
            name = argv[1];
            sprintf (command,
                      "select phone from personnel\
                      dump where name =\
                      '%s'", name);
                 mscall (DATABASE, command);
       }

Example 4

An interactive version of the phone program, `getphone.c`:

       #include <mscc.h>

       #define     DATABASE   "repairs"
       #define     NOTHING    ""
       #define     BUFSIZE    512
       msmain()
       {
            char  name[BUFSIZE], command[BUFSIZE];

            printf ("\nFIND phone number GIVEN employee's\name.\n");
            printf ("To stop, enter \".q\"\n\n");
                 for  ( ; ; )
                 {
                      do
                      {
                           printf ("Employee name: ");
                           gets (name);
                           if  (strcmp (name, ".q") == 0 )
                                msexit (0);

                      } while  (strcmp (name, NOTHING) == 0 );
                      printf ("\n");
                      sprintf (command,
                      "select phone from\personnel dump\
                           where name = '%s', name);
                      mscall (DATABASE, command);
                      printf ("\n");
                 }
       }

Example 5

The program `employees.c` selects the names and phone numbers of all employees in the `personnel` table, counts their number and prints suitable headings:

       #include <mscc.h>
       #define   DATABASE   "repairs"
       msmain()
       {

            printf ("Current Employees\n\n");
            mscall (DATABASE, "select name, phone from\personnel");
            printf ("Total Number of Employees = ");
            fflush (stdout);
            mscall (DATABASE, "select count from personnel\dump");
            printf ("\n");
       }

Example 6

The C program `loans.c` prints the details of loans outstanding for each given employee:

       #include <mscc.h>
       #define  DATABASE    "repairs"
       #define  BUFSIZE     512

       msmain (argc, argv)
            int   argc;
            char  *argv[];
       {
          char  command[BUFSIZE], newcommand[BUFSIZE],*name;
          int        i;

          if (argc < 2 )
          {
            print f("USAGE: %s employee's name\n", argv[0]);
            exit (1);
          }
          for (i = 1 ; i < argc ; i++ )
          {
             name = argv[i];
             printf ("Loans currently outstanding to\
                  %s:\n\n", name);
             fflush (stdout);
             sprintf (command,
                  "select date, amount from loans where \
                  name = '%s'", name);
             mscall (DATABASE, command);
             printf ("\n");
             printf ("Total amount owing for %s: ", name);
             fflush (stdout);
             sprintf (command,
                  "select sum (amount) from loans dump \
                  where name = '%s'", name);
             mscall  (DATABASE, command);
             printf ("\n\n");
          }
          name = argv[1];
          sprintf (command,
                 "select sum (amount) from loans dump where \
                 name = '%s'" name);
          for  (i = 2 ; i < argc ; i++ )
          {
             name = argv[i];
             sprintf (newcommand, " or name = '%s'", name);
             strcat (command, newcommand);
          }
          printf ("\n\ntotal amount outstanding over ");
          printf ("all of the above:");
          fflush (stdout);
          mscall (DATABASE, command);
       }

This produces exactly the same results as the corresponding shell program.

Example 7

The program `menu.c` shows a simple menu-driven program for invoking Empress Query Language commands via the Standard C Interface. It enables you to select:

1. the personnel number for any employee;
2. the telephone number;
3. the credit limit;
4. the whole personnel record;
5. all loans outstanding.

       #include <mscc.h>
       #define   DATABASE  "repairs"
       #define   YES       '1'

       #define   NO        '0'
       #define   BUFSIZE   512

       #define   NAMESIZE  32 /* max length of ATTR names +1 */
       #define   NOTHING   ""
       #define   NUMBER    "n"
       #define   PHONE     "p"
       #define   CREDIT    "c"
       #define   RECORD    "r"
       #define   LOANS     "l"
       #define   QUIT      "q"

       char name[BUFSIZE];
       char clause[BUFSIZE];

       msmain ()
       {
            char   showmenu;
            char   command[BUFSIZE],
                 attribute[NAMESIZE];
            char   table[NAMESIZE],
                 query[BUFSIZE];

            printmenu ();

            for  ( ; ; )          /* loop through repeated queries */
            {
                 strcpy (attribute, NOTHING);
                 strcpy (clause, NOTHING);
                 strcpy (table, "personnel");

                 showmenu = NO;
                 printf ("\nCommand: ");
                 gets (command);
                 if  (strcmp (command, NUMBER) == 0)
                 {
                      strcpy (attribute, "number");
                      findname ();
                 }
                 else
                      if (strcmp (command, PHONE) == 0)
                      {
                           strcpy (attribute, "phone");
                      findname ();
                 }
                 else
                      if (strcmp (command, CREDIT) == 0)
                      {
                           strcpy (attribute, "credit_limit");
                           findname ();
                 }
                 else
                      if (strcmp (command, RECORD) == 0)
                      {
                           /* do nothing yet */
                      }

                      else
                           if (strcmp (command, LOANS) == 0)
                           {
                                strcpy (table, "loans");
                           }
                      else
                           if (strcmp (command, QUIT) == 0 )
                           {
                                msexit (0); /* normal termination */
                           }
                           else
                           {
                                showmenu = YES;
                                briefmenu ();
                           }
                      if  (showmenu == NO) /* continue with query */
                      {
                           sprintf (query, "select %s from %s %s",
                                attribute, table, clause);
                           mscall (DATABASE, query);
                      }
            }
       }
       printmenu ()
       {

            printf ("\n\n To select:\n\n");
            printf ("An employee's personnel number  %s\n", NUMBER);
            printf ("An employee's phone number      %s\n", PHONE);
            printf ("An employee's credit limit      %s\n", CREDIT);
            printf ("All personnel records           %s\n", RECORD);
            printf ("All loans outstanding           %s\n", LOANS);
            printf ("To leave the program            %s\n\n", QUIT);
       }
       briefmenu ()
       {
            printf ("\nPersonnel number  (%s), phone (%s), ",
                 NUMBER, PHONE);
            printf ("credit limit  (%s), all records (%s), \n", CREDIT,
                 RECORD);
            printf ("all loans  (%s), or quit  (%s)\n",
                 LOANS, QUIT);
       }
       findname ()
       {
            do
            {
                 printf ("Enter employee name: ");
                 gets (name);
            } while  (STRCMP (name, NOTHING) == 0);
            sprintf (clause, "where name = '%s'", name);
       }

## 4.6 Using Retrieved Values

To manipulate attribute values in more complex ways than the Query Language permits, use the routines `msgvinit` and `msgval` to obtain pointers to attribute values in a dump file produced via a Query Language command. The routine `msgvline` returns the current record number. `msgpline` returns the current line number of the dump file.

The routine `msgvinit` takes as its argument a pointer to an Empress dump file returned by `fopen`, and initializes retrievals from that file. It must always be called before values are retrieved.

The routine `msgval` takes no arguments. It returns an attribute value as a string, `(char *)0` on end-of-file, and `(char *)-1` on end-of-line (which is normally the end of a record, unless the dump file has been produced by a `LIST DUMP` command).

Successive calls to `msgval` return successive attribute values, stepping though the dump file attribute-by-attribute, record-by-record, until all values have been retrieved. Thus, to retrieve all the records from the `loans` table and print the amounts plus interest, for example, requires a loop which runs until `msgval` returns `(char *)0`, with a call to `msgval` for every attribute value expected, and a check for the end of the line when all values from one record have been found.

`msgvline` takes no arguments, and returns the current line number in the dump file as a `LONGINTEGER`. This will give a count of the number of records seen if the file was produced by a regular `DUMP` command, and a count of the number of attributes seen if the file was produced by a `LIST DUMP` command.

`msgpline` takes no arguments, and returns the current file record number in the the dump as a `LONGINTEGER` regardless of whether or not `DUMP` or `LIST DUMP` commands where used.

Example 1

The program `statement.c` calculates interest on loans and prints a simple monthly statement for the named employees. A useful routine, `dollcvt`, which turns a dollar attribute value into a float value, is included in this example.

    #include  <mscc.h>
    #define   DATABASE   "repairs"
    #define   TEMP       "msdump"
    #define   RATE       0.02
    #define   BUFSIZE    128

    extern    double     dollcvt ();

    msmain (argc, argv)
       int   argc;
       char  *argv[];
    {
       FILE   *file;
       int    loan_no, counter;
       char   *name, *value;
       char   command[BUFSIZE];
       double amount, interest, total, sum, limit;

       for (counter = 1; counter < argc; counter++)
       {
          name = argv[counter];
          sprintf (command, "select credit_limit from \
               personnel dump into '%s' where \
               name = '%s'", TEMP, name);

          mscall (DATABASE, command);
          file = fopen (TEMP, "r");

          msgvinit (file);
          value = msgval ();
          fclose (file);

          if (value  == (char *)0)
          {
             printf ("\n\n*****  Cannot find any");
             printf ("record of employee '%s' in the database *****\n",
                  name);
          }
          else
          {
             limit = dollcvt (value);
             sprintf (command, "select date, amount from loans dump\
                  into '%s' where name = '%s'",
                  TEMP, name);
             mscall (DATABASE, command);
             printf ("\n\n\n");
             printf ("Monthly Statement for %s\n", name);
             printf ("*********************************\n\n");
             printf ("Date     Amount   Interest Total\n\n");
             file = fopen (TEMP, "r");msgvinit (file);
             value = msgval ();/* get first date */
             if (value == (char *)0)
          {
             printf ("No loans outstanding\n\n");
             printf ("Credit limit = $%-8.2f\n",limit);
             continue;
          }
          loan_no = sum = 0;
          for ( ; value != (char *)0 ; )
          {
             printf ("%-7s", value);  /* print date */
             value = msgval (); /* get amount */
             printf (" $%-9s", value); /* print amount */
             amount = dollcvt (value);
             interest = amount * RATE;
             total = amount + interest;
             printf ("$%-8.2f $%-8.2f\n", interest, total);
             sum = sum + total;
             value = msgval (); /* check end-of-line */
             if (value == (char *)-1)
             {
                  loan_no++;
                  value = msgval (); /* get next date */
                }
             }
             fclose (file);
             printf ("\nNumber of loans = %d \n", loan_no);
             printf ("Credit limit      = $%-8.2f\n", limit);
             printf ("Total Debts       = $%-8.2f\n", sum);
             if (sum >= limit)
             {
       printf ("*****  Please note: \ your credit limit is");
                printf ("exceeded *****\n");
             }
            }
         }
         unlink (TEMP);
    }
    double dollcvt (string)
         char    *string;
    {
         extern   double   atof ();
         char     c, buffer [BUFSIZE], *buf_ptr;

         for (buf_ptr = buffer ;
              (c =  *string++) != '\0' ;
            )
         if (c != '$' &&
             c != '*' &&
             c != ' ' &&
             c != ',')
             *buf_ptr++ = c;

         *buf_ptr = '\0';
         return (atof (buffer));
    }