Home page  
Help > SDK Help > Client Interface Components >
Thunderbolt Engine API Version 4.0
Version 7.11

Preliminary: January 30, 2002

 1.0 Introduction
     1.1  Thunderbolt Engine API Overview
     1.2  Thunderbolt Engine API Interface Conventions

     1.3  Writing Applications using Thunderbolt Engine API Interface

              1.3.1 Calling from a C++ Program

     1.4  Thunderbolt Engine API Concepts
              1.4.1 The Thunderbolt Paradigm
              1.4.2 Tables and Fields
                       1.4.2.2 Data Types
              1.4.3 Indexes
              1.4.4 Collections
              1.4.5 Creating a Database
                       1.4.5.1 Database Definition Language
              1.4.6 The Application Interface
              1.4.7 Record Descriptions
              1.4.8 Display Formats
     1.5  Thunderbolt Registry Variables and Environment Variables
     1.6  Thunderbolt Error Handling and Error Logging
     1.7  Thunderbolt Debugging Tools
     1.8  Thunderbolt Logging Functions
     1.9  Thunderbolt Stored Procedures
     1.10  Thunderbolt Error Codes
             1.10.1  Thunderbolt Symbolic Error Codes
     1.11  TB API CONTROL{} Structure Definition
     1.12  TB API System Variables

2.0 API Function Descriptions
      2.1  ddlAdviseCallback() - post advisory callback
      2.2  ddlAttach() - attach to another Thunderbolt database
      2.3  ddlBegin() - create a TB API context
      2.4  ddlCalc() - single CALC/SET/IF command
      2.5  ddlCancelCallback() - post cancel callback
      2.6  ddlCatRD() - concatenate two RDs
      2.7  ddlChange() - change records in a collection
      2.8  ddlChangeEx() - change records in a collection (parameterized)
      2.9  ddlChangeV() - change records in a collection (vectorized)
      2.11 ddlClear() - clear a collection
      2.12 ddlCloneConnection() - initialize a duplicate of a CONTROL{} structure
      2.13 ddlClose() - close database
      2.14 ddlCommand() - Command-style interface to select TB API functions
      2.15 ddlCompileAI() - Compile AI from symbolic file
      2.16 ddlCompileDBD() - Compile DBD from symbolic file
      2.17 ddlCompileAIMem() - Compile AI from symbolic memory image
      2.18 ddlCompileDBDMem() - Compile DBD from symbolic memory image
      2.19 ddlCompileRdMem() - Compile RD from memory image
      2.20 ddlConstruct() - create structure for keyed field(s)
      2.21 ddlCopyData() - copy the records in a collection
      2.22 ddlCreateCalcObject() - create a calc object to be performed by ddlGetCalc()
      2.23 ddlCreateColl() - create a collection dynamically at run-time;
      2.24 ddlCreateFieldAlias() - create an alternate name for a field
      2.25 ddlCreateTempTable() - create a temporary table
      2.26 ddlCreateTokenMapping() - create a mapping from SQL-Compatible names to TB-Compatible names
      2.27 ddlDataErrorCallback() - post the Data Error Callback
      2.28 ddlDelete() - delete record(s)
      2.29 ddlDeleteB() - delete record
      2.30 ddlDestroyCalcObject() - destroy a calc object created by ddlCreateCalcObject()
      2.31 ddlDestroyColl() - destroy a collection created by ddlCreateColl()
      2.32 ddlDestroyFieldAlias() - destroy the alternate name for a field
      2.33 ddlDestroyRdMem() - Destroy a compiled RD from memory
      2.34 ddlDestroyTempTable() - destroy the temp table that was created by ddlCreateTempTable()
      2.35 ddlDestroyTokenMapping() - destroy the mapping from SQL-Compatible names to TB-Compatible names
      2.36 ddlDestroyTrigger() - unregister a trigger
      2.37 ddlDestruct() - destroy structure for keyed field(s)
      2.38 ddlDetach() - detach from one or all Thunderbolt databases
      2.39 ddlEnd() - terminate a TB API context
      2.40 ddlFetch() - read one or more fields from a record
      2.41 ddlFill() - fill a collection
      2.42 ddlFind() - isolate records in a collection
      2.43 ddlFindCb() - special FIND with Callback
      2.44 ddlFindEx() - isolate records in a collection (parameterized)
      2.45 ddlFindV() - isolate records in a collection (vectorized)
      2.46 ddlFindCalc() - isolate records in a collection using Calc-Object
      2.47 ddlFindCxData() - extract structure data to user array
      2.48 ddlFindX() - isolate records in a collection
      2.49 ddlFindXY() - isolate records in a collection
      2.50 ddlGetCalc() - get data resulting from executing calc object
      2.51 ddlGetIndexData() - get data from index
      2.52 ddlGetIndexDataNext() - get data from index
      2.53 ddlGetIndexDataPrevious() - get data from index
      2.54 ddlGetMemo() - read a memo field
      2.55 ddlGetMemoByTxtPtr() - read a memo field
      2.56 ddlGetType() - get type info for DBD field
      2.57 ddlGetRDType() - get type info for RD field
      2.58 ddlGetRDInfo() - get extended info for RD field
      2.59 ddlGetRDHandle() - get RD handle
      2.60 ddlGetRDTypeArray() - get type info for all fields of an RD in an array of structures
      2.61 ddlGetRDTypeByIndex() - get type info for RD field by index
      2.62 ddlGetRDInfoByIndex() - get extended type info for RD field by index
      2.63 ddlGetRec() - read record from database (collection)
      2.64 ddlGetRecB() - read record from database (record number)
      2.65 ddlGetRecno() - copy record numbers from collection to memory array
      2.66 ddlGetRecReset() - reset ddlGetRec() pointer
      2.67 ddlInfo()  - INFO command
      2.68 ddlInitRec()  - init record image
      2.69 ddlInvertIndex() - convert an index back into table form
      2.70 ddlLimit() - manage the number of data base users
      2.71 ddlLoad() - load records into database
      2.72 ddlLoadMem()/ddlLoadMemEx() - load records from a memory array
      2.73 ddlLookRec() - get information about the next record in a collection
      2.74 ddlMatch() - isolate additional records into collection
      2.75 ddlMatchEx() - isolate additional records into collection
      2.76 ddlMatchM() - isolate additional records into collection
      2.77 ddlMode() - MODE
      2.78 ddlMpick() - multiple pick
      2.79 ddlOpen()  - open database
      2.80 ddlOrder()  - define the hierarchy for a collection
      2.81 ddlPick() - specified records into collection
      2.82 ddlPrintCallback() - post print callback
      2.83 ddlPutMemo() - record to database
      2.84 ddlPutRec() - write record to database
      2.85 ddlRaze() - discard all data in one or all tables
      2.86 ddlReduce() - remove duplicate records from a collection
      2.87 ddlRelate() - arrange hierarchical data
      2.88 ddlRelateEx() - arrange hierarchical data
      2.89 ddlRequestDataStrategy() - compute optimum strategy for data resolution
      2.90 ddlRereadRec() - reread the current record
      2.91 ddlReturn() - combined ddlCreateCalcObject() & ddlGetCalc()
      2.92 ddlReverseRec() - reverse byte order of binary data types
      2.93 ddlRewriteRec() - rewrite record to database
      2.94 ddlRewriteB() - rewrite record to database
      2.95 ddlRpr() - raw print
      2.96 ddlSaveColl() - save a collection to a file
      2.97 ddlSetChangeCb() - set Change Callback
      2.98 ddlSetFetchDataCb() - set Fetch Data Callback
      2.99 ddlSetSortCb() - set Sort Callback
      2.100 ddlSetTriggerCb() - register a trigger and set callback function
      2.101 ddlShellSort() - Shell Sort an Array of Data
      2.102 ddlShow() - show tree values
      2.103 ddlSort() -  arrange non-hierarchical data
      2.104 ddlStructure() -  create structure for keyed field(s)
      2.105 ddlStructureMem() -  create structure for UDI indexes(s)
      2.107 ddlTally() -  count records in collection
      2.108 ddlTrackerCallback() - post tracker callback
      2.109 ddlUdiStruct() - indirect update User Defined Index (UDI)
      2.110 ddlUnload() - unload records from database
      2.111 ddlUnlog() -  disconnect a data base user
      2.112 ddlUsers() -  users report
      2.113 ddlVerify() -  housekeeping functions
      2.114 ddlWrite() - write records
      2.115 ddlWriteEx() - write records (parameterized)
      2.116 ddlWriteV() - write records (vectorized)

3.0 Additional API Function Descriptions
      3.1  DdGetFetchInfo() - ddlSetFetchDataCb() batch mode function
      3.2  DdGetVarCharData() - ddlSetFetchDataCb() batch mode function
      3.3  DdGetMetaphoneMapping() - get Metaphone phonetic translation for a string
      3.4  DdGetPhonicMapping() - get Phonic phonetic translation for a string
      3.5  DdGetPorterStem() - get Porter stem for a string
      3.6  DdGetSoundexMapping() - get Soundex phonetic translation for a string
      3.7  DdEncodeNumericDate() - encode binary date components into a single 32 bit integer
      3.8  DdDecodeNumericDate() - decode binary date components from a single 32 bit integer
      3.9  DdEncodeNumericTime() - encode binary time components into a single 32 bit integer
      3.10  DdDecodeNumericTime() - decode binary time components from a single 32 bit integer
      3.11  DdTxtDecodeParams() - decode MEMO pointer parameters
      3.12  DdTxtEncodeParams() - encode MEMO pointer parameters

--- 1.0 Introduction

1.1 Thunderbolt Engine API Overview

Thunderbolt Engine API is the lowest level interface available for Thunderbolt Database applications. Thunderbolt Engine API provides functions to perform operations like opening and closing databases, data definition (create, alter, and drop tables, create views etc.), data manipulation (insert, update, and delete), and queries. Applications that need the power, performance and flexibility provided by the Thunderbolt Query Language (TQL) use this API. This is the API that underlies the Thunderbolt Native API Driver.

Note: Because the Thunderbolt Engine API derives from the Thunderbolt Rapid Application Development system (RAD), and its predecessor 'D' the Data Language (DDL), some remnants of this history remain. For example, all of the API function names are in the form ddlxxx(). So, you may find some references to 'D'. Also, most API functions correspond to a particular 'D' command. For example, 'ddlFind()' API function corresponds to 'FIND' command. So, 'ddlFind()' API function document refers to the 'FIND' command description.


1.2 Thunderbolt Engine API Interface Conventions

Thunderbolt Engine API has a set of interface functions for various operations, and a structure called CONTROL structure. CONTROL structure is a placeholder for database connection handle and other information like errors and responses. The first argument in calls to all API functions is a pointer to a control structure. A call to ddlOpen() API function fills the CONTROL structure with a handle to the opened database. All subsequent calls to that database use this CONTROL structure.

typedef struct CONTROL{
      int ddlHandle;             /* context handle; do not change */
      int Status;                /* return 0 or error code */
      char ErrorMessage[300];    /* return error string if Status is non-zero */
      char ErrorMessage2[300];        
      char Response[300];        /* message in response to API call*/
      char Response2[300];
      long Ans1;                 /* return numeric Answer1 */
      long Ans2;                 /* return numeric Answer2 */
      long Ans3;                 /* return numeric Answer3 */
      char Ans1str[300];         /* return string Answer1 */
      char Ans2str[300];         /* return string Answer2 */
      char Ans3str[300];         /* return string Answer3 */
}CONTROL;


In case of errors, all functions return a non-zero integer error code in Control.Status, plus an ASCII string describing the error in Control.ErrorMessage.

Each API function can return up to three answers through the CONTROL structure. These answers can be either binary values or character strings. The answer pairs are:

 
   long Ans1;   char Ans1str[300];      /* Answer1 */
   long Ans2;   char Ans2str[300];      /* Answer2 */
   long Ans3;   char Ans3str[300];      /* Answer3 */


In the general case, (i.e., not knowing which type of answer to expect), the calling program should check Control.AnsXstr[] for a non-null string; then check AnsX for non-zero value.

The definition of the control structure is contained in an include file named "ddl_incl.h".

Thunderbolt Engine API also includes a file called 'diag' with a list of diagnostic codes and their corresponding messages. Thunderbolt Engine uses this file to translate internal error codes in to messages understandable by users.

1.3 Writing Applications using Thunderbolt Engine API Interface

Following are the steps involved in using Thunderbolt Engine API:

       1. Initialize Thunderbolt Engine API Driver

       2. Open database connection

       3. Do your operations on opened database

       4. Close database connection

       5. Unload Thunderbolt Engine API Driver

When you open a database, a connection handle will be returned in the CONTROL structure. You use this CONTROL structure for subsequent operations on the database. 

For reading or writing a record, you have to define a buffer that matches the length of the record and pass it as argument to appropriate API calls.

1.3.1 Calling from a C++ Program

Here is an example C/C++ program showing opening a database, querying and reading the result records, writing a record to the database, and closing the database.

   // Include Thunderbolt Engine header file 
   #include "ddl_incl.h"

   /*
     This is the RD for this test program:

     RD  CUSTOMER
       cust#       x(6)
       name        x(20)
       street      x(15)
       city        x(12)
       state       x(2)
       zip         x(5)

   */

   // define a structure to hold CUSTOMER record
   // This structure is passed as a buffer to Thunderbolt API to read and write customer data

   struct CUSTOMER{
      char  custID[6];
      char  name[20];
      char  street[15];
      char  city[12];
      char  state[2];
      char  zip[5];
   };

   void main(int argc, char *argv[])
   {
      // Declare a control structure
      // This holds your database handle
      static struct CONTROL ctl;
      struct CUSTOMER cust; // declare a customer buffer to read and write customer records
      char*  dbName = "C:\WhamTech\DatabasePath\DatabaseName"; // fill database path + name

      // Initialize driver
      ddlBegin(&ctl);

      if(ctl.Status!=0) printf("\n\nBEGIN ERROR: %s\n\n",ctl.ErrorMessage);

      // Open Database connection

      ddlOpenR(&ctl,"/W", dbName);

      if(ctl.Status!=0){
         printf("\n\nOPEN ERROR: %s\n",ctl.ErrorMessage);
         exit(1);
      }

      // Query for customers with name starting with 'B'
      ddlFind(&ctl,"/","A CUSTOMERS WHERE NAME SV \'B\'");

      if(ctl.Status!=0) printf("\n\nFIND ERROR: %s\n\n",ctl.ErrorMessage);

      // sort results by name
      ddlSort(&ctl,"/U","A CUSTOMERS BY NAME");

      if(ctl.Status!=0) printf("\n\nSORT ERROR: %s\n\n",ctl.ErrorMessage);

      // read results
      if(ctl.Response[0]!=0){
         printf("\n-->Sort %s %%ANSWER1=%ld",ctl.Response,ctl.Ans1);

         for(int i=0;i< ctl.Ans1;++i){
             ddlGetRec(&ctl,"/","A CUSTOMERS CUS_REC",(char*)&cust);
             if(ctl.Status!=0){printf("\n\nREAD ERROR: %s\n\n",ctl.ErrorMessage); break;}
             printf("\n%-6.6s %-20.21s %-15.16s %-12.13s %-2.2s %-5.5s",.cust,cust.name,cust.street,cust.city,cust.state,cust.zip);
         }
      }

      //
      // Write a customer record
      //   

      // Initialize cust buffer
      ddlInitRec(&ctl,"/","customers",(char*)&cust);

      if(ctl.Status!=0){printf("\n\nINIT ERROR: %s\n\n",ctl.ErrorMessage); break;}

      // Set customer data to write
      char* myName = "MyName";
      char*  myStreet = "MyStreet";
      char*  myCity = "MyCity";
      char*  myState = "MyState";
      char*  myZip = "MyZip";
     
      memcpy(cust.custID,"123456",6);
      memcpy(cust.name myName,strlen(myName));
      memcpy(cust.street, myStreet,strlen(myStreet));
      memcpy(cust.city,myCity,strlen(myCity));
      memcpy(cust.state,myState,strlen(myState));
      memcpy(cust.zip,myZip,strlen(myZip));

      // Write a record
      ddlPutRec(&ctl,"/","CUSTOMERS CUSTOMERS",(char*)&cust);

      if(ctl.Status!=0){printf("\n\nWRITE ERROR: %s\n\n",ctl.ErrorMessage); break;}

      // Close Database connection
      ddlCloseR(&ctl);

      if(ctl.Status!=0){printf("\n\nCLOSE ERROR: %s\n\n",ctl.ErrorMessage); exit(1);}

      // Unload driver 
      ddlEnd(&ctl);
   }

Thunderbolt Engine API can be called from Visual Basic Versions 5.0 and above. However, each entry point ddlXxx() has a special VB entry point ddlSXxx() which uses the stdcall calling sequence required by VB 5 and above. For example, ddlOpen() is called ddlSOpen() in Visual Basic.

Refer to the file vbsample.bas in the samples folder for definitions of the CONTROL structure and function prototypes.

1.4 Thunderbolt Engine API Concepts

1.4.1 The Thunderbolt Paradigm 

See the Thunderbolt Paradigm document.

1.4.2 Tables and Fields 

See the Tables and Fields document.

1.4.3 Indexes 

See the Indexes document.

1.4.4 Collections 

See the Collections document.

2.7 ddlCatRD() - concatenate two RDs

Prototype:

   void ddlCatRD(
      CONTROL *ctl,
      char *OptnStr,
      char *ArgList);


Permitted values in OptnStr: NA

Form of ArgList:
      <rdname1> <rdname2> <rdname3>
            <rdname1> is the name of the first source record description
            <rdname2> is the name of the second source record description
            <rdname3> is the name of the target record description

Description:
Creates a new record description <rdname3>, which is the logical concatenation of <rdname1> and <rdname2>.

2.28 ddlDeleteB() - delete record

Prototype:

   void ddlDeleteB(
      CONTROL *ctl,
      char *OptnStr,
      char *ArgList);


Permitted values in OptnStr: NA

Form of ArgList:
      <dgname> <rec#>

Description:
Deletes data record specified all key index structures associated with that record.

See the DELETE command.

Value returned in Control.Ans1:
      Number of records deleted [1 or 0]

2.48 ddlFindX() - isolate records in a collection

Prototype:

   void ddlFindX(
      CONTROL *ctl,
      char *OptnStr,
      char *ArgList,
      float *Array,
      int Length);


Permitted values in OptnStr: NA

Form of ArgList:
      <cx> [<dgname>] <field>

Array is an array of values whose type depends on the type of <field>
    <field> type     Array type
           I                 short int
           ID               long int
           F                 float
           FD              double

Length is the number of floating point values in Array.

Description:
Performs FIND <cx> [<dgname>] where
<field> EQ Array[0] OR <field> EQ Array[1] OR ...

Values returned in Control Structure:
Control.Ans1 Total number of records selected

2.49 ddlFindXY() - isolate records in a collection

Prototype:

   void ddlFindXY(
      CONTROL *ctl,
      char *OptnStr,
      char *ArgList,
      float *Array1,
      char *Arg2,
      float *Array2,
      int Length);


Permitted values in OptnStr: NA

Form of ArgList:
      <cx> [<dgname>] <field1>

Array1 is an array of values whose type depends on the type of <field1>
    <field1> type     Array1 type
           I                 short int
           ID               long int
           F                 float
           FD              double

Form of Arg2:
      <field2>

Array2 is an array of values whose type depends on the type of <field2>
    <field2> type     Array2 type
           I                 short int
           ID               long int
           F                 float
           FD              double

Length is the number of floating point values in each array.

Description:
Performs FIND <cx> [<dgname>] where
(<field1> EQ Array1[0] and <field2> EQ Array2[0]) OR
(<field1> EQ Array1[1] and <field2> EQ Array2[1]) OR
...
Values returned in Control Structure:
Control.Ans1 Total number of records selected

2.59 ddlGetRDHandle() - get RD handle

Prototype:

   void ddlGetRDHandle(
      CONTROL *ctl,
      char *OptnStr,
      char *ArgList);


Permitted values in OptnStr: NA

Form of ArgList:
      <rdname> [<piPathName>]
            <rdname> is the ASCII name of the record description that describes Image;
            <piPathName> is the path and name of the .pi filefor an external RD

Description:
Searches for the requested RD.

Values returned in Control Structure:
Control.Ans1 RD Handle
Control.Ans2 Number of fields in RD
Control.Ans3 Length of record

2.64 ddlGetRecB() - read record from database (record number)

Prototype:

 
   void ddlGetRecB(
      CONTROL *ctl,
      char *OptnStr,
      char *ArgList,
      char *Image);


Permitted values in OptnStr: NA

Form of ArgList:
      <dgname> <rdname> [<piPathName>] <rec#>

Form of Image:
Image must reflect exactly the record described by rdname.

Description:
Reads a record from the open database. The contents of the record are stored into a record image whose form is described by the record description <rdname>.

ddlGetRecB() does not support data type TEXT.

When an external RD in specified [by (piPathName)], the RD is automatically cached for possible future use.

Each call to ddlGetRecB() causes a physical read of the database to ensure that the latest record is read.

Values returned in Control Structure:
Control.Ans1 0 - Invalid record number; or requested record is deleted
1 - Record was read successfully

2.70 ddlLimit() - manage the number of data base users

Prototype:

   void ddlLimit(
      CONTROL *ctl,
      char *OptnStr,
      char *ArgList);


Permitted values in OptnStr:

   /X - suppress calls to Print Callback

Form of ArgList: See Description

Description:
See the LIMIT command.
Note: All printing performed by the ddlLimit() function is achieved thru a user callback, as specified in the ddlPrintCallback() function.

2.90 ddlRereadRec() - reread the current record

Prototype:

 
   void ddlRereadRec(
      CONTROL *ctl,
      char *OptnStr,
      char *ArgList,
      char *Image);


Permitted values in OptnStr: NA

Form of ArgList:
      <cx> <dgname> <rdname> [<piPathName>]

Form of Image:
Image must reflect exactly the record described by rdname.

Description:
Rereads the last record read.

Each call to ddlRereadRec() causes a physical read of
the database to ensure that the latest record is read.

Values returned in Control Structure:
Control.Ans1 1 - record read successfully
Control.Ans2 record number of record

2.92 ddlReverseRec() - reverse byte order of binary data types

2.93 ddlRewriteRec() - rewrite record to database

Prototype:

 
   void ddlRewriteRec(
      CONTROL *ctl,
      char *OptnStr,
      char *ArgList,
      char *Image);


Permitted values in OptnStr: NA

Form of ArgList:
      <cx> <dgname> <rdname> [<piPathName>]

Form of Image:
Image must reflect exactly the record described by rdname.

Description:
Modifies the last record read or written in the open database. The contents of the modified record are contained
in a record image whose form is described by the record
description <rdname>.

The record image must be initialized to ASCII
blanks for X() data types, to ASCII zeros for N()
data types, and to binary zeros for binary data
types. This can be done easily with the ddlInitRec()
function.

X() and N() data types should never contain
binary zeros.

For fields in the database which are data type N(), it is suggested that the corresponding field in the record description be data type X(), with a size that allows for required sign and/or point.

ddlRewriteRec() does not support data type TEXT.

When an external RD in specified [by (piPathName)], the RD is automatically cached for possible future use.

Values returned in Control Structure:
Control.Ans1 1 - record written successfully
Control.Ans2 record number of record

2.94 ddlRewriteB() - rewrite record to database

Prototype:

 
   void ddlRewriteB(
      CONTROL *ctl,
      char *OptnStr,
      char *ArgList,
      char *Image);


Permitted values in OptnStr: NA

Form of ArgList:
      <dgname> <rdname> [<piPathName>] <rec#>

Form of Image:
Image must reflect exactly the record described by rdname.

Description:
Rewrites the specified record.
The contents of the modified record are contained
in a record image whose form is described by the record
description <rdname>.

The record image must be initialized to ASCII
blanks for X() data types, to ASCII zeros for N()
data types, and to binary zeros for binary data
types. This can be done easily with the ddlInitRec()
function.

X() and N() data types should never contain
binary zeros.

For fields in the database which are data type N(), it is suggested that the corresponding field in the record description be data type X(), with a size that allows for required sign and/or point.

ddlRewriteB() does not support data type TEXT.

When an external RD in specified [by (piPathName)], the RD is automatically cached for possible future use.

Values returned in Control Structure:
Control.Ans1 1 - record written successfully
Control.Ans2 record number of record

2.106 ddlSuperMatch() - create pre-computed collections

2.111 ddlUnlog() - disconnect a data base user

Prototype:

   void ddlUnlog(
      CONTROL *ctl,
      char *OptnStr,
      char *ArgList);


Permitted values in OptnStr:

   /X - suppress calls to Print Callback

Form of ArgList: See Description

Description:
See the UNLOG command.
Note: All printing performed by the ddlUnlog() function is achieved thru a user callback, as specified in the ddlPrintCallback() function.

Copyright © 2019 , WhamTech, Inc.  All rights reserved. This document is provided for information purposes only and the contents hereof are subject to change without notice. Names may be trademarks of their respective owners.