# CHAPTER 4: 標準インターフェイス

## 4.1 はじめに

問い合わせ言語コマンドは、`mscall`ルーチンを使って C 言語プログラム中に組み込むことができます。 このルーチンは、引数としてデータベースディレクトリと 問い合わせ言語コマンドを指定するだけです。

       mscall (database_directory, query_language_statement);

データベースディレクトリと問い合わせ言語コマンドの両方は文字列として 指定する必要があります。また、文字列は、アプリケーションプログラムによって 作成することができます。 このルーチンは、SQL ステートメント実行に成功した場合、"0"を返し、 失敗した場合は "0"以外の値を返します。 (失敗の原因として、問い合わせ言語コマンドの構文エラーまたは 無効なテーブルおよびアトリビュート名の指定が考えられます。)

## 4.2 ヘッダーファイル mscc.h

ヘッダーファイル `"mscc.h"`は、`mscall`を使用するすべての C 言語アプリケーションでインクルードする必要があります。 このファイルには、`mscall`を含む、すべてのホスト言語インターフェイス ルーチンによって使用する定数およびデータタイプが定義されています。 ヘッダーファイルの内容を以下に示します。

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

       #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 */

`mscc.h`ファイルはバージョンによって異なることに注意してください。 このファイルは、Empressディレクトリの下の以下の場所に置かれます。

- UNIX の場合は、`include/mscc.h`になります。
- Windows NTの場合は,、`include\mscc.h`になります。

## 4.3 初期化とクリーンアップ

`mscall`を使ってプログラムを作成するには２つの方法があります。

1. 通常の`main`プロシージャを`msmain`とすることができます。 これを使用する場合、初期化が不要でデフォルトの`main`プロシージャが 含まれます。また、引数は通常の`main`と同様、`argc`および `argv`を持っています。同様に通常の`C exit`プロシージャを `msexit`とします。

2. ユーザー自身で`main` プロシージャを書くことができ、 はじめに`msinit`をコールし、 終了で`msexit`をコールします。 こられの２つのプロシージャはさまざまな初期化およびクリーンアップ処理を実行します。

`msclean`ルーチンは、 プログラムを終了する直前にコールするプロシージャをスタック上に登録するために 使用することができます。

## 4.4 コンパイルとリンク

`mscall`を使用したプログラムは、`"cc"`ではなく、 `"empcc"`を使用してコンパイルする必要があります。 これはプログラムのコンパイル時に適切なEmpressライブラリに リンクすることを確実にします。 この点を除けば、`"empcc"`と`"cc"`は同じ働きをします。

## 4.5 標準インターフェイスを使用した例

以下の例のデータベース名は、`repairs`いう名前の論理データベースです。 データベースの物理位置は、システム上のどこに置いても構いません。

例 1

以下のプログラム`"names.c"`は、 `personnel`テーブルから名前を検索します。

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

例 2

プログラム`"phone.c"`は、 1人の従業員名を指定し、電話番号を検索します。

       #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);
       }

例 3

プログラム`"newphone.c"`は、 指定した名前が１つだけであるかをチェックします。

       #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);
       }

例 4

次の例は、phone プログラムの対話的なバージョン`"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");
                 }
       }

例 5

プログラム`"employees.c"`は、 `personnel`テーブルのすべての従業員の名前と電話番号を検索し、 適切なヘッダーとともにカウント数を出力します。

       #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");
       }

例 6

C プログラム `"loans.c"`は、 指定された各従業員のローン残高の詳細を出力します。

       #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);
       }

このプロシージャは、これに対応するシェルプログラムと全く同じ処理結果を返します。

例 7

プログラム `"menu.c"`は、単純なメニュー操作を行うプログラムで、 標準 C インターフェイスによってEmpress 問い合わせ言語を実行し、 結果を表示します。 選択可能なコマンドは、以下になります。

1. 従業員番号
2. 電話番号
3. クレジット制限
4. 従業員レコード全体
5. ローン残高の総額

       #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 検索した値の利用

問い合わせ言語よりも複雑な方法でアトリビュート値を扱う場合、 問い合わせ言語によって作成されたダンプファイルのアトリビュート値の ポインタを取得するために `msgvinit`および`msgval`ルーチンを使用します。 `msgvline`ルーチンは、カレントレコード番号を返し、 `msgpline`ルーチンは、ダンプファイルのカレント行数を返します。

`msgvinit`ルーチンは、 `fopen`によって返されたEmpressダンプファイルへのポインタを その引数として使用し、このファイルから値を取得するための初期化を行います。 常にこのルーチンは値の取得の前にコールしなくてはなりません。

`msgval`ルーチンは引数を必要とせず、文字列のアトリビュート値を返します。 ファイルの終了では`(char *)0`を返し、行の終了 (`LIST DUMP`コマンドによってダンプファイルを作成した場合は 通常、レコードの終了になります。) では、`(char *)-1`を返します。

`msgval`を連続的にコールすると、それごとにアトリビュート値が返され、 すべての値が検索されるまで、アトリビュートごと、またレコードごとに ダンプファイルを通じて処理されます。 例えば、`loans`テーブルから全レコードを検索し、利息を加えた総額を出力する場合、 1 レコードのすべての値を検出した時に行の終了をチェックし、 必要なアトリビュートごとに`msgval`をコールし、 `msgval`が、`(char *)0`を戻すまで実行するためのループが必要になります。

`msgvline`は、引数を必要とせず、 `LONGINTEGER`型としてダンプファイル のカレント行数を返します。 通常、`DUMP`コマンドによってファイルを作成した場合、 レコード数のカウントになり、 `DUMP LIST`コマンドによってファイルを作成した場合、 アトリビュート数のカウントを返します。

`msgpline`は、引数を必要とせず、使用された場合、 `DUMP`および`LIST DUMP`コマンドに関わらず `LONGINTEGER`型としてダンプファイル のカレントレコード番号を返します。

例 1

プログラム`"statement.c"`は、 ローンの利息計算を行い、指定された従業員の月別データの単純な出力をします。 dollar型アトリビュート値をfloat型で返すための有益な`dollcvt`ルーチンを この例では含んでいます。

    #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 = $%-.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      = $%-2.f\n", limit);
             printf ("Total Debts       = $%-.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));
    }