Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Design

Titanium and the NTC Ship Manager Application


Dr. Dobb's Sourcebook July/August 1997: Titanium and the NTC Ship Manager Application

Evan, a lead developer for Nautical Technology Corp., can be contacted at [email protected].


Nautical Technology Corp. develops vessel-management software for the marine industry. Our NTC Ship Manager software includes modules for inventory control, requisitioning and purchasing, maintenance and service ordering, preventive maintenance, invoicing and financial analysis, crew management, and crew payroll. All of these modules operate on a single, integrated database, so that, for example, in the purchasing module, you record purchases of spare parts for the same machinery items whose maintenance is tracked in the preventive-maintenance module.

The system is typically installed in one or more offices of the client company, as well as on ships. Since the high cost of satellite communications precludes ocean-going vessels from tapping directly into shoreside databases, a separate database is installed at each site (vessel or office). Our Replication Manager software ensures that data entered or changed at each site is transmitted and redistributed to all the other sites as applicable.

In short, NTC Ship Manager is a highly database-intensive transaction-processing application, incorporating over 300 different types of database transactions covering a diverse range of application areas. At the heart of the system is Micro Data Base Systems (MDBS) Titanium database engine (and its precursors -- MDBS III and MDBS IV). When NTC Ship Manager development began in 1984, we selected MDBS/Titanium because of its speed, flexibility in allowing complex data interrelationships to be accurately represented, and support for C. Since then, MDBS has continued to enhance the engine's features with support for ODBC and stored procedures, providing us a growth path for the continuing development of NTC Ship Manager.

Titanium Engine Overview

Titanium is a multithreaded database management system designed for complex applications, including those requiring rehosting of nonrelational database applications.

The system is scalable from stand-alone DOS and Windows 3.x/95/NT systems, to client/server networks running NetWare NLM, Windows 95/NT, OS/2, and UNIX servers. Version 6.1 provides interfaces for C, C++, Cobol, Visual Basic, Delphi, and includes drivers for 32-bit ODBC database access. It also provides TCP/IP protocol support. Titanium API calls are identical across all operating systems, allowing for the migration of applications without code changes.

Titanium implements a unique indexing construct called "Dynamic Pointer Arrays" (DPAs) that permits queries to be performed without joins, thereby minimizing disk I/O and providing performance benefits for operations involving two or more tables. DPA technology also addresses complex data modeling problems by directly supporting one-to-many, many-to-many, and recursive relationships without intersect tables or redundant data.

Ship Manager Overview

Our experience building NTC Ship Manager showed that there are a number of different factors that go into developing a high-quality commercial application:

  • You must have a thorough understanding of the application area and of your users' requirements.
  • For a database-oriented application, you must develop an appropriate data model.
  • You must also select appropriate development tools.

NTC Ship Manager was originally developed with the MDBS III and MDBS IV database managers, precursors to today's Titanium DBMS. In our 13 years of experience with this family of database tools, we have accumulated a number of tricks and techniques which we will share with you in this article. Some of these we learned from other MDBS developers; some we developed on our own, whether by inspiration or sheer necessity. All of them are hands-on, practical techniques that work in the real world.

Database Functionality, Encapsulation, and Error Handling

The Titanium API functions use a shared set of possible return values called DMS (Data Management System) error codes. Generally, a return value of 0 indicates that the API function completed with the intended result, while a nonzero value indicates an error or special condition. Currently, there are approximately 200 such codes, most of which can be returned by more than one API function. For any given API function, there might be on the order of one or two dozen possible return values, signifying problems ranging from program logic errors to environmental issues (disk read/write errors, and so on).

Handling all these possible return values presents a potentially overwhelming burden. If you tried, for every Titanium API call in your application, to individually handle every possible return value (or even just the common ones), you would be programming for a long time. There must be a better way -- and there is. The solution lies in creating a single entry point through which all Titanium API calls in your application must pass. The specific techniques for doing this will depend on the programming language your application uses, but we will illustrate how we accomplished it in C.

We created a function called dms_call(), which takes a number of arguments. We then created a header file (see Listing One, which redefined all the Titanium API calls as macros, each of which expands to a call to dms_ call() with specific arguments. When the source code is recompiled with this header file, all calls to the Titanium API are converted to calls to dms_call(). In this way, we are able to encapsulate all NTC Ship Manager's Titanium database access into the dms_call() module.

The dms_call() function is illustrated in Listing Two. The arguments to dms_ call() include a function number that indicates the Titanium API function we are "really" trying to call. dms_call() calls the appropriate Titanium API function, and passes it the intended arguments (which were also passed as arguments to dms_ call()). It then examines the value returned by Titanium. Values of zero (indicating success) or 255 (indicating a "no more records" condition, also a "normal" result) are passed back to the caller of dms_call(). On any other values, dms_call invokes our DMS error handler, which displays a screen containing diagnostic information, allows the user to type in supplemental information for an error report, and terminates the application. This scheme allows us to write a single generic DMS error handler and attach it by default to all our Titanium API calls.

We have also left room for further fine tuning. If, in a particular situation, we want to trap and handle a particular Titanium error number ourselves, we can call another function to add the error number to an array of error numbers, which dms_ call() should ignore and pass through to its caller. For example, if we are inserting a record into a Titanium set (index), we may want to ignore DMS error 11, which means that the record is already included in the set. In this case, we precede our call to the record insertion with a call to passerr(11) to temporarily add Error 11 to the list of error codes that will not trigger the DMS error handler, then call passerr(-11) afterwards to remove Error 11 from this list; see Listing Three.

Record Creation Functions

Titanium provides a transaction-based architecture with full online commit and abort functionality, permitting the encapsulation of the creation and/or modification of records together with all related processing necessary to preserve integrity. This is important for NTC Ship Manager, since most of the approximately 100 concepts (record types or "tables" in relational parlance) in our system have specific integrity constraints; in some cases, there are multiple such constraints involving a substantial amount of complexity. For example, a newly created record may need to be inserted into a dozen or more sets, often conditionally depending on one or more parameters.

In structuring our program code, we were careful to separate out into distinct functions the Titanium API calls needed for creation of new records. These functions, which we have come to refer to as "crs-functions" after the old MDBS crs() (Create Record and Store) function used to create database records, typically handle all back-end processing associated with creating new records, including starting and committing/aborting transactions, creating the new record, inserting the new record into sets as needed, and updating any associated records. Any and all user-interface code is rigorously excluded from these functions. In doing this, we have essentially produced our own well-defined API for creating new NTC Ship Manager records.

The great benefit of this approach is reliability and robustness. In many cases, there are numerous different places in the program where a new record of a particular type could be created. For example, a new machinery part record can be typed into an input screen provided for that purpose, or imported from an external system via an Xbase file. In both cases, the ntc_crs_part() function is called to do the actual creation.

User Indicators

Titanium provides the concept of "user indicators" that can be used by the application program to "remember" specific database records. A record held in a user indicator can optionally be locked by the application. This gives you great flexibility in devising a record-locking/concurrency-control strategy.

We took advantage of these facilities by building our own record locking/unlocking API on top of them. Our ntc_lock_ record() function allocates the next available user indicator from a pool of user indicators that it manages, and assigns this user indicator to the current record. It then calls the Titanium API again to lock the record. Finally, it returns to the caller an integer handle to the user indicator, which can later be passed to ntc_unlock_ record() to unlock the record and free the user indicator for reuse. This scheme allows for nesting record locks, without requiring the locks necessarily to be released in the order that they were initiated. Because management of the pool of available user indicators is built into our API routine, the various parts of our application do not need to be concerned with what records have already been locked, or with what the user indicator handles by other portions of the application.

Global Variables for Field Names and Set Names

In the first years of NTC Ship Manager's existence in the mid-1980s, the DOS-based C compilers we used (Lattice C and, later, Microsoft C) had a limit of 64 KB on the total size of compiled-in static data. As time passed and our programs grew larger, this started to become problematic for us, because most of the database API functions took one or more string parameters denoting database field names or set names, which counted toward the 64-KB limit. The early Lattice compiler at least had a feature whereby multiple occurrences of a particular string (such as "remarks") in a single source file would generate multiple references to a single copy of the string in the object code. When we switched to the Microsoft compiler in 1988, this useful but nonstandard feature went away, and our static-data size problems became acute. At one point, we determined that 15 percent of the total size of our executable program consisted of static data from field names and set names alone!

Our solution was to do away with the practice of using quoted strings for field name and set name arguments, and to define a variable for each field name and set name that could be referenced for this purpose. We wrote a program to scan our Database Definition Language (DDL) file and parse out the name of every field and set in the database. For each unique field name used anywhere in the database ("remarks," for example), this program created a new source file, remarks.c, consisting of a single line of code:

char f_remarks[] = "remarks";.

Additionally, the program created a single header file, fldnames.h, containing an entry like the following for each field name:

extern char f_remarks[];.

We compiled all the field-name .C files into a library called FLDNAMES.LIB. We then went through our code and replaced references in our source code to "remarks" with the new variable f_remarks, so that gfc("remarks"); became gfc(f_remarks);.

When we linked our executable with the FLDNAMES.LIB, we now had no more than one actual instance of each field-name string linked into the program! Applying a similar solution to set names yielded even bigger savings, and our static-data size problems were licked for good.

You may be wondering why we went to the trouble of writing a program to parse the DDL file instead of doing it manually. The answer is that, from time to time, we modify the database schema, adding new fields and/or sets. When this happens, all we have to do is rerun the DDL parser and recompile using the new header file and .C files it generates.

The compilers nowadays can accommodate more than 64 KB of static data if need be, so this issue is not the show stopper it once was. Still, that's a lot of data adding to the size of your executable program for no good reason. While it might not justify a retrofit of an existing application, a little bit of advance planning will allow you to reap the benefits of this technique for new projects.

Redundant Sets

Database theory discusses third-normal-form representation of data, with no redundancy whatsoever, but real-world developers know that in practice, exceptions to this ideal are necessary, most commonly for reasons of expediency or performance. We have taken advantage of an opportunity to use redundancy to our advantage in our inventory-control module with respect to the classic parts-explosion (bill of material) problem.

Each piece of shipboard machinery, such as a main engine, generator, or pump, includes a list of parts. For some large and complex equipment, such as a main engine, there may be a number of assemblies and subassemblies, with several levels of breakdown, before reaching the smallest parts. The total number of parts in such equipment can be approximately 2000. For smaller machinery items, such as a pump, the total of approximately 30 parts can be represented in a one-tiered flat list. Titanium's support for recursive sets allows us to provide an accurate data representation of the multilevel parts explosion. The equipment owns the top-level parts (assemblies), which may then own subassemblies, which may, in turn, own parts. (More than three levels may be needed in practice; NTC Ship Manager allows up to 10.)

The aforementioned scheme is the most economical, third-normal-form data representation. But it did not lend itself well to the on-screen, scrolling listings of parts (complete with hierarchical levels indicated by indentation) that we wanted to provide to our users. Programming our listing feature to navigate the various levels of the recursive set would have been cumbersome, and did not fit in well with the generic listing routine we had developed (which could list a group of records, provided they were all members of the same owner record). Our solution was to tinker with the data.

We defined in the database a new set linking the owner equipment record directly to all the member parts, through all levels. We created an algorithm to insert newly created parts into this set, with the order of their appearance being the same order that you would see if you were to traverse the tree from top to bottom, following each node down to the bottom recursively before continuing with the next node. Figure 1 illustrates the original recursive set and the additional redundant set linking the equipment record directly to all parts. At the cost of a bit of tricky programming (and some storage for the new set indexes), we now had a single, complete, fast, easy-to-access set for building our onscreen hierarchical listings. These hierarchical listings have, for years, been the backbone of our inventory-control and purchasing modules and are key to their ease of use.

Performance Characteristics

You will want to "stress test" your application to monitor the kind of performance it provides, especially in processing-intensive areas. There are a number of ways to approach performance issues in Titanium, each with its own applicability and tradeoffs.

One fundamental item you will want to pay close attention to is database page buffering. With Titanium, you can specify the amount of RAM that is allocated to the DBMS for database page buffering to reduce disk accesses and increase performance -- often dramatically. This performance benefit is relatively easy to achieve. In client-server Titanium environments, you specify the number of page buffers to be allocated at the database server in a command parameter file. In stand-alone Titanium environments, your application code must specify to the DBMS the number of page buffers to be used. The most flexible way to do this is to read the desired value from a user-definable configuration file. In both cases, you (or your user) must make sure there is enough RAM to support the desired number of buffers. The number of page buffers required to deliver good performance depends on what your application is doing, as well as your hardware, software, and network environment, and is best determined by experimentation. One thousand page buffers is typically a good starting point. Although page buffering is more of an implementation issue than a development issue, it is one of the first things you want to look at, since it provides significant benefits that are easy to achieve.

One of the first performance issues we wrestled with was in developing the hierarchical parts listings described previously. Each part was displayed on one line, together with its primary part number. Some explanation is in order here. NTC Ship Manager allows multiple part numbers to be entered for each part; each part number is linked to a reference describing what kind of part number it is (usually, this is the name of a vendor or the title of the equipment manual that the part number was published in). This design, while highly flexible and useful, required that the part number be stored in a different record type from the part, and related by a set. When we ran our hierarchy listing, we found it to be noticeably slow. Upon investigating, we found that the performance problem was caused by thrashing; the part number records were apparently being stored in a location on the hard disk that was physically distant from the part records. Once we understood the problem, the fix was not difficult. We modified our DDL specification to indicate that part number records should be clustered around their owner parts. This directive causes the Titanium database manager to store new part number records on the same database page as their owner part records wherever possible. As a result, the thrashing stopped, and the hierarchical listings ran much faster.

A performance issue of a different kind surfaced when we were developing our first client-server implementation of NTC Ship Manager in 1991. A frequently used screen listing all ship records in the database, which had always performed perfectly well in our stand-alone implementation, ran unacceptably slow in a client-server configuration using the Netbios protocol. With the help of a protocol analyzer, we determined that the bottleneck was caused by a large number of data packets being exchanged between the client and server. When we started experimenting, we found that we could improve performance dramatically by replacing the "Get current record" command we were using to retrieve the ship record from the server with three "Get field from current record" commands to read just the three fields we needed. This was contrary to what we had expected -- we had anticipated that one get-record command would be faster than three get-field commands, in view of the associated per-command overhead. But the point was that the ship record was one of the largest records in our database schema, and the database manager was breaking it up into multiple data packets to transmit across the network. The single get-record command was producing more network traffic -- in data and acknowledgments -- than the three get-field commands! We changed the code, and solved the problem; and we changed our programming approach to favor get-field commands over get-record commands, especially when dealing with large records. But the important lesson here is that it's worthwhile to try these sorts of things both ways, because it is hard to predict in advance which way is going to be faster; the answer is dependent on the specifics of your environment and what the application is trying to do.

Conclusion

The last, but far from least, piece of advice I have for you in preparing to develop your application is that, if you hope to take full advantage of Titanium's technology, surround yourself with the best developers you can find. To extract the high performance that Titanium can deliver requires a corresponding degree of technical sophistication on the part of the designer and programmer. At NTC, our good fortune in assembling and retaining a long-tenured team of strong developers has been crucial to our product's success.

DDJ

Listing One

/********************************  This file redefines all the MDBS functions as calls to the Command
*  Shell.  This shell uses the function dms_call(), declared as follows.
*      int dms_call (fptr, arg1, arg2, fnum, filename, linenumb)
*            int (far pascal  *fptr)();
*            char             *arg1, *arg2;
*            int              fnum;
*            char             *filename;
*            unsigned int     linenumb;
*   fptr           is a pointer to the MDBS function
*   arg1 and arg2  are pointers to the arguments which get passed to fptr
*                  (If there are fewer than 2 arguments, a pointer to bl_p,
*                  which is defined as  " ",  pads the unnecessary arguments.)
*   fnum           is a unique number assigned to each MDBS function
*                  (for debugging)
*   filename       is a pointer to the filename returned by the preprocessor
*                  (This string is only included when DEBUG is defined, in
*                  order to save space; otherwise the null pointer is passed.)
*   linenumb       is the line number within the file (as determined by the
*                  preprocessor)
*    N.B.:  Because all MDBS functions use the pascal calling convention,
*          it was necessary to reverse the 2nd and 3rd arguments in dms_call
*          (i.e.,  "a, NULL"  becomes  "NULL, a")  so that functions which
*          expect only a single argument will find it in the correct place
*          on the stack.
*******************************/


</p>
#ifndef NTC_DMS_FILENAME
static char *sourcefilename = NULL;
#endif


</p>
typedef  ushort                 UI;     // user indicator
#define  DMS_INVALID_OWNER      5       // DMS error for invalid owner
#define  DMS_INVALID_MEMBER     6       // DMS error for invalid member


</p>
//NULL,             0      NULL,      NULL,
#define DMS_AMM            1
#define DMS_AMO            2
#define DMS_AOM            3
#define DMS_AOO            4
#define DMS_AUI            5
#define DMS_CCU            6
#define DMS_CRA            7
#define DMS_CRS            8
#define DMS_DBCLS          9
#define DMS_DBCLSA        10
#define DMS_DBENV         11
#define DMS_DBOPN         12
#define DMS_DBOPNA        13
#define DMS_DBSAVE        14
#define DMS_DBSTAT        15
#define DMS_DRC           16
#define DMS_DRM           17
#define DMS_DRO           18
#define DMS_FDRK          19
#define DMS_FFM           20
#define DMS_FFO           21
#define DMS_FFS           22
#define DMS_FLM           23
#define DMS_FLO           24
#define DMS_FMI           25
#define DMS_FMSK          26
#define DMS_FNM           27
#define DMS_FNMI          28
#define DMS_FNMSK         29
#define DMS_FNO           30
#define DMS_FNOI          31
#define DMS_FNOSK         32
#define DMS_FNS           33
#define DMS_FOI           34
#define DMS_FOSK          35
#define DMS_FPM           36
#define DMS_FPO           37
#define DMS_FRK           38
#define DMS_DBGETC        39
#define DMS_GETM          40
#define DMS_GETO          41
#define DMS_GFC           42
#define DMS_GFM           43
#define DMS_GFO           44
#define DMS_GMC           45
#define DMS_GOC           46
#define DMS_GTC           47
#define DMS_GTM           48
#define DMS_GTO           49
#define DMS_IMS           50
#define DMS_IOS           51
#define DMS_MAU           52
#define DMS_MCC           53
#define DMS_LGFILE        54
#define DMS_LGFLSH        55
#define DMS_LGMSG         56
#define DMS_NCI           57
#define DMS_PFC           58
#define DMS_PFM           59
#define DMS_PFO           60
#define DMS_PIFD          61
#define DMS_DBPUTC        62
#define DMS_PUTM          63
#define DMS_PUTO          64
#define DMS_RMS           65
#define DMS_ROS           66
#define DMS_RSM           67
#define DMS_RSO           68
#define DMS_SCM           69
#define DMS_SCN           70
#define DMS_SCO           71
#define DMS_SCU           72
//NULL,            73      NULL,      NULL,
#define DMS_SMC           74
#define DMS_SME           75
#define DMS_SMM           76
#define DMS_SMN           77
#define DMS_SMO           78
#define DMS_SMU           79
#define DMS_SOC           80
#define DMS_SOE           81
#define DMS_SOM           82
#define DMS_SON           83
#define DMS_SOO           84
#define DMS_SOU           85
#define DMS_SUC           86
#define DMS_SUM           87
#define DMS_SUN           88
#define DMS_SUO           89
#define DMS_SUU           90
#define DMS_TCT           91
#define DMS_TMT           92
#define DMS_TOT           93
#define DMS_TRABT         94
#define DMS_TRBGN         95
#define DMS_TRCOM         96
#define DMS_XMM           97
#define DMS_XMO           98
#define DMS_XOM           99
#define DMS_XOO          100
#define DMS_MCF          101
#define DMS_MCP          102
#define DMS_MRTF         103
#define DMS_MRTP         104
#define DMS_MSF          105
#define DMS_MSP          106
#define DMS_TCN          107
#define DMS_TMN          108
#define DMS_TON          109
#define DMS_DBCNV        110
#define DMS_ALTEOS       111
//NULL,           112      NULL,      NULL,
//NULL,           113      NULL,      NULL,
//NULL,           114      NULL,      NULL,
//NULL,           115      NULL,      NULL,
//NULL,           116      NULL,      NULL,
//NULL,           117      NULL,      NULL,
//NULL,           118      NULL,      NULL,
#define DMS_TUN          119
#define DMS_FPMI         120
#define DMS_FPOI         121
#define DMS_FPMSK        122
#define DMS_FPOSK        123
#define DMS_OFM          124
#define DMS_OFO          125
#define DMS_OLM          126
#define DMS_OLO          127
#define DMS_OMSK         128
#define DMS_ONM          129
#define DMS_ONMSK        130
#define DMS_ONO          131
#define DMS_ONOSK        132
#define DMS_OOSK         133
#define DMS_OPM          134
#define DMS_OPO          135
#define DMS_ODRK         136
#define DMS_ORK          137
#define DMS_OPMSK        138
#define DMS_OPOSK        139
#define DMS_OMI          140
#define DMS_ONMI         141
#define DMS_OPMI         142
#define DMS_OOI          143
#define DMS_ONOI         144
#define DMS_OPOI         145
#define DMS_SDC          146
#define DMS_SCD          147
#define DMS_MRID         148
#define DMS_GDC          149
#define DMS_GVLC         150
#define DMS_GII          151
#define DMS_GKI          152
#define DMS_GRI          153
#define DMS_GLRV         154
#define DMS_GHRV         155
#define DMS_GSI          156
#define DMS_GTT          157
//NULL,           158      NULL,      NULL,
//NULL,           159      NULL,      NULL,
#define DMS_GFLI         160
#define DMS_GAI          161
//NULL,           162      NULL,      NULL,
//NULL,           163      NULL,      NULL,
//NULL,           164      NULL,      NULL,
//NULL,           165      NULL,      NULL,
#define DMS_GAFN         166
//NULL,           167      NULL,      NULL,
#define DMS_TRSYNC       168
#define DMS_MCM          169
#define DMS_MRTM         170
#define DMS_MSM          171
#define DMS_MMU          172
#define DMS_NAL          173
#define DMS_FRM          174
#define DMS_FRO          175
#define DMS_GFS          176
#define DMS_DBXPND       177
#define DMS_GEI          178
#define DMS_DBSEL        179
#define DMS_VRDEF        180
#define DMS_VROF         181
#define DMS_VRON         182
#define DMS_GVII         183
#define DMS_GVRI         184
#define DMS_VRDEL        185
#define DMS_DBMEMU       186
#define DMS_TRBGNA       187
//NULL,           188      NULL,      NULL,
//NULL,           189      NULL,      NULL,
//NULL,           190      NULL,      NULL,    //"GKV",     gkv,
#define DMS_DMSSJP       191
#define DMS_DBFILE       192
#define DMS_GXERR        193
#define DMS_GXSTR        194
#define DMS_DBALIGN      195
#define DMS_DBAU         196
#define DMS_DBFU         197
#define DMS_DBSU         198
#define DMS_DBAVAIL      199
#define DMS_DBSRV        200
#define DMS_DBSTART      201
#define DMS_DBSTOP       202
#define DMS_DBUSR        203
//NULL,           204      NULL,      NULL,    //"SDM",     scm,
//NULL,           205      NULL,      NULL,    //"SDO",     sco,
//NULL,           206      NULL,      NULL,    //"SMD",     smd,
//NULL,           207      NULL,      NULL,    //"SOD",     sod,
#define DMS_DBEMS        208
#define DMS_FDB          209
#define DMS_IDB          210


</p>
#define  amm(a)     dms_call(NULL, a,    DMS_AMM,    sourcefilename, __LINE__)
#define  amo(a)     dms_call(NULL, a,    DMS_AMO,    sourcefilename, __LINE__)
#define  aom(a)     dms_call(NULL, a,    DMS_AOM,    sourcefilename, __LINE__)
#define  aoo(a)     dms_call(NULL, a,    DMS_AOO,    sourcefilename, __LINE__)
#define  aui(a)     dms_call(NULL, a,    DMS_AUI,    sourcefilename, __LINE__)
#define  ccu(a)     dms_call(NULL, a,    DMS_CCU,    sourcefilename, __LINE__)
#define  cra(a,b)   dms_call(a,    b,    DMS_CRA,    sourcefilename, __LINE__)
#define  crs(a,b)   dms_call(a,    b,    DMS_CRS,    sourcefilename, __LINE__)
#define  dbcls()    dms_call(NULL, NULL, DMS_DBCLS,  sourcefilename, __LINE__)
#define  dbclsa(a)  dms_call(NULL, a,    DMS_DBCLSA, sourcefilename, __LINE__)
#define  dbenv(a)   dms_call(NULL, a,    DMS_DBENV,  sourcefilename, __LINE__)
#define  dbopn(a)   dms_call(NULL, a,    DMS_DBOPN,  sourcefilename, __LINE__)
#define  dbopna(a,b)dms_call(a,    b,    DMS_DBOPNA, sourcefilename, __LINE__)
#define  dbsave()   dms_call(NULL, NULL, DMS_DBSAVE, sourcefilename, __LINE__)
#define  dbstat(a)  dms_call(NULL, a,    DMS_DBSTAT, sourcefilename, __LINE__)
#define  drc()      dms_call(NULL, NULL, DMS_DRC,    sourcefilename, __LINE__)
#define  drm(a)     dms_call(NULL, a,    DMS_DRM,    sourcefilename, __LINE__)
#define  dro(a)     dms_call(NULL, a,    DMS_DRO,    sourcefilename, __LINE__)
#define  fdrk(a,b)  dms_call(a,    b,    DMS_FDRK,   sourcefilename, __LINE__)
#define  ffm(a)     dms_call(NULL, a,    DMS_FFM,    sourcefilename, __LINE__)
#define  ffo(a)     dms_call(NULL, a,    DMS_FFO,    sourcefilename, __LINE__)
#define  ffs(a)     dms_call(NULL, a,    DMS_FFS,    sourcefilename, __LINE__)
#define  flm(a)     dms_call(NULL, a,    DMS_FLM,    sourcefilename, __LINE__)
#define  flo(a)     dms_call(NULL, a,    DMS_FLO,    sourcefilename, __LINE__)
#define  fmi(a,b)   dms_call(a,    b,    DMS_FMI,    sourcefilename, __LINE__)
#define  fmsk(a,b)  dms_call(a,    b,    DMS_FMSK,   sourcefilename, __LINE__)
#define  fnm(a)     dms_call(NULL, a,    DMS_FNM,    sourcefilename, __LINE__)
#define  fnmi(a,b)  dms_call(a,    b,    DMS_FNMI,   sourcefilename, __LINE__)
#define  fnmsk(a,b) dms_call(a,    b,    DMS_FNMSK,  sourcefilename, __LINE__)
#define  fno(a)     dms_call(NULL, a,    DMS_FNO,    sourcefilename, __LINE__)
#define  fnoi(a,b)  dms_call(a,    b,    DMS_FNOI,   sourcefilename, __LINE__)
#define  fnosk(a,b) dms_call(a,    b,    DMS_FNOSK,  sourcefilename, __LINE__)
#define  fns(a)     dms_call(NULL, a,    DMS_FNS,    sourcefilename, __LINE__)
#define  foi(a,b)   dms_call(a,    b,    DMS_FOI,    sourcefilename, __LINE__)
#define  fosk(a,b)  dms_call(a,    b,    DMS_FOSK,   sourcefilename, __LINE__)
#define  fpm(a)     dms_call(NULL, a,    DMS_FPM,    sourcefilename, __LINE__)
#define  fpo(a)     dms_call(NULL, a,    DMS_FPO,    sourcefilename, __LINE__)
#define  frk(a,b)   dms_call(a,    b,    DMS_FRK,    sourcefilename, __LINE__)
#define  dbgetc(a)  dms_call(NULL, a,    DMS_DBGETC, sourcefilename, __LINE__)
#define  getm(a,b)  dms_call(a,    b,    DMS_GETM,   sourcefilename, __LINE__)
#define  geto(a,b)  dms_call(a,    b,    DMS_GETO,   sourcefilename, __LINE__)
#define  gfc(a,b)   dms_call(a,    b,    DMS_GFC,    sourcefilename, __LINE__)
#define  gfm(a,b)   dms_call(a,    b,    DMS_GFM,    sourcefilename, __LINE__)
#define  gfo(a,b)   dms_call(a,    b,    DMS_GFO,    sourcefilename, __LINE__)
#define  gmc(a,b)   dms_call(a,    b,    DMS_GMC,    sourcefilename, __LINE__)
#define  goc(a,b)   dms_call(a,    b,    DMS_GOC,    sourcefilename, __LINE__)
#define  gtc(a)     dms_call(NULL, a,    DMS_GTC,    sourcefilename, __LINE__)
#define  gtm(a,b)   dms_call(a,    b,    DMS_GTM,    sourcefilename, __LINE__)
#define  gto(a,b)   dms_call(a,    b,    DMS_GTO,    sourcefilename, __LINE__)
#define  ims(a)     dms_call(NULL, a,    DMS_IMS,    sourcefilename, __LINE__)
#define  ios(a)     dms_call(NULL, a,    DMS_IOS,    sourcefilename, __LINE__)
#define  mau(a)     dms_call(NULL, a,    DMS_MAU,    sourcefilename, __LINE__)
#define  mcc(a)     dms_call(NULL, a,    DMS_MCC,    sourcefilename, __LINE__)
#define  lgfile(a)  dms_call(NULL, a,    DMS_LGFILE, sourcefilename, __LINE__)
#define  lgflsh()   dms_call(NULL, NULL, DMS_LGFLSH, sourcefilename, __LINE__)
#define  lgmsg(a)   dms_call(NULL, a,    DMS_LGMSG,  sourcefilename, __LINE__)
#define  nci()      dms_call(NULL, NULL, DMS_NCI,    sourcefilename, __LINE__)
#define  pfc(a,b)   dms_call(a,    b,    DMS_PFC,    sourcefilename, __LINE__)
#define  pfm(a,b)   dms_call(a,    b,    DMS_PFM,    sourcefilename, __LINE__)
#define  pfo(a,b)   dms_call(a,    b,    DMS_PFO,    sourcefilename, __LINE__)
#define  pifd(a)    dms_call(NULL, a,    DMS_PIFD,   sourcefilename, __LINE__)
#define  dbputc(a)  dms_call(NULL, a,    DMS_DBPUTC, sourcefilename, __LINE__)
#define  putm(a,b)  dms_call(a,    b,    DMS_PUTM,   sourcefilename, __LINE__)
#define  puto(a,b)  dms_call(a,    b,    DMS_PUTO,   sourcefilename, __LINE__)
#define  rms(a)     dms_call(NULL, a,    DMS_RMS,    sourcefilename, __LINE__)
#define  ros(a)     dms_call(NULL, a,    DMS_ROS,    sourcefilename, __LINE__)
#define  rsm(a)     dms_call(NULL, a,    DMS_RSM,    sourcefilename, __LINE__)
#define  rso(a)     dms_call(NULL, a,    DMS_RSO,    sourcefilename, __LINE__)
#define  scm(a)     dms_call(NULL, a,    DMS_SCM,    sourcefilename, __LINE__)
#define  scn()      dms_call(NULL, NULL, DMS_SCN,    sourcefilename, __LINE__)
#define  sco(a)     dms_call(NULL, a,    DMS_SCO,    sourcefilename, __LINE__)
#define  scu(a)     dms_call(NULL, a,    DMS_SCU,    sourcefilename, __LINE__)
#define  smc(a)     dms_call(NULL, a,    DMS_SMC,    sourcefilename, __LINE__)
#define  sme(a)     dms_call(NULL, a,    DMS_SME,    sourcefilename, __LINE__)

#define  smm(a)     dms_call(NULL, a,    DMS_SMM,    sourcefilename, __LINE__)
#define  smn(a)     dms_call(NULL, a,    DMS_SMN,    sourcefilename, __LINE__)
#define  smo(a)     dms_call(NULL, a,    DMS_SMO,    sourcefilename, __LINE__)
#define  smu(a,b)   dms_call(a,    b,    DMS_SMU,    sourcefilename, __LINE__)
#define  soc(a)     dms_call(NULL, a,    DMS_SOC,    sourcefilename, __LINE__)
#define  soe(a)     dms_call(NULL, a,    DMS_SOE,    sourcefilename, __LINE__)
#define  som(a)     dms_call(NULL, a,    DMS_SOM,    sourcefilename, __LINE__)
#define  son(a)     dms_call(NULL, a,    DMS_SON,    sourcefilename, __LINE__)
#define  soo(a)     dms_call(NULL, a,    DMS_SOO,    sourcefilename, __LINE__)
#define  sou(a,b)   dms_call(a,    b,    DMS_SOU,    sourcefilename, __LINE__)
#define  suc(a)     dms_call(NULL, a,    DMS_SUC,    sourcefilename, __LINE__)
#define  sum(a,b)   dms_call(a,    b,    DMS_SUM,    sourcefilename, __LINE__)
#define  sun(a)     dms_call(NULL, a,    DMS_SUN,    sourcefilename, __LINE__)
#define  suo(a,b)   dms_call(a,    b,    DMS_SUO,    sourcefilename, __LINE__)
#define  suu(a)     dms_call(NULL, a,    DMS_SUU,    sourcefilename, __LINE__)
#define  tct(a)     dms_call(NULL, a,    DMS_TCT,    sourcefilename, __LINE__)
#define  tmt(a)     dms_call(NULL, a,    DMS_TMT,    sourcefilename, __LINE__)
#define  tot(a)     dms_call(NULL, a,    DMS_TOT,    sourcefilename, __LINE__)
#define  trabt()    dms_call(NULL, NULL, DMS_TRABT,  sourcefilename, __LINE__)
#define  trbgn()    dms_call(NULL, NULL, DMS_TRBGN,  sourcefilename, __LINE__)
#define  trcom()    dms_call(NULL, NULL, DMS_TRCOM,  sourcefilename, __LINE__)
#define  xmm(a)     dms_call(NULL, a,    DMS_XMM,    sourcefilename, __LINE__)
#define  xmo(a)     dms_call(NULL, a,    DMS_XMO,    sourcefilename, __LINE__)
#define  xom(a)     dms_call(NULL, a,    DMS_XOM,    sourcefilename, __LINE__)
#define  xoo(a)     dms_call(NULL, a,    DMS_XOO,    sourcefilename, __LINE__)
#define  mcf()      dms_call(NULL, NULL, DMS_MCF,    sourcefilename, __LINE__)
#define  mcp()      dms_call(NULL, NULL, DMS_MCP,    sourcefilename, __LINE__)
#define  mrtf(a)    dms_call(NULL, a,    DMS_MRTF,   sourcefilename, __LINE__)
#define  mrtp(a)    dms_call(NULL, a,    DMS_MRTP,   sourcefilename, __LINE__)
#define  msf(a)     dms_call(NULL, a,    DMS_MSF,    sourcefilename, __LINE__)
#define  msp(a)     dms_call(NULL, a,    DMS_MSP,    sourcefilename, __LINE__)
#define  tcn()      dms_call(NULL, NULL, DMS_TCN,    sourcefilename, __LINE__)
#define  tmn(a)     dms_call(NULL, a,    DMS_TMN,    sourcefilename, __LINE__)
#define  ton(a)     dms_call(NULL, a,    DMS_TON,    sourcefilename, __LINE__)
#define  dbcnv(a)   dms_call(NULL, a,    DMS_DBCNV,  sourcefilename, __LINE__)
#define  alteos()   dms_call(NULL, NULL, DMS_ALTEOS, sourcefilename, __LINE__)
#define  tun(a)     dms_call(NULL, a,    DMS_TUN,    sourcefilename, __LINE__)
#define  fpmi(a,b)  dms_call(a,    b,    DMS_FPMI,   sourcefilename, __LINE__)
#define  fpoi(a,b)  dms_call(a,    b,    DMS_FPOI,   sourcefilename, __LINE__)
#define  fpmsk(a,b) dms_call(a,    b,    DMS_FPMSK,  sourcefilename, __LINE__)
#define  fposk(a,b) dms_call(a,    b,    DMS_FPOSK,  sourcefilename, __LINE__)
#define  ofm(a,b)   dms_call(a,    b,    DMS_OFM,    sourcefilename, __LINE__)
#define  ofo(a,b)   dms_call(a,    b,    DMS_OFO,    sourcefilename, __LINE__)
#define  olm(a,b)   dms_call(a,    b,    DMS_OLM,    sourcefilename, __LINE__)
#define  olo(a,b)   dms_call(a,    b,    DMS_OLO,    sourcefilename, __LINE__)
#define  omsk(a,b)  dms_call(a,    b,    DMS_OMSK,   sourcefilename, __LINE__)
#define  onm(a,b)   dms_call(a,    b,    DMS_ONM,    sourcefilename, __LINE__)
#define  onmsk(a,b) dms_call(a,    b,    DMS_ONMSK,  sourcefilename, __LINE__)
#define  ono(a,b)   dms_call(a,    b,    DMS_ONO,    sourcefilename, __LINE__)
#define  onosk(a,b) dms_call(a,    b,    DMS_ONOSK,  sourcefilename, __LINE__)
#define  oosk(a,b)  dms_call(a,    b,    DMS_OOSK,   sourcefilename, __LINE__)
#define  opm(a,b)   dms_call(a,    b,    DMS_OPM,    sourcefilename, __LINE__)
#define  opo(a,b)   dms_call(a,    b,    DMS_OPO,    sourcefilename, __LINE__)
#define  odrk(a,b)  dms_call(a,    b,    DMS_ODRK,   sourcefilename, __LINE__)
#define  ork(a,b)   dms_call(a,    b,    DMS_ORK,    sourcefilename, __LINE__)
#define  opmsk(a,b) dms_call(a,    b,    DMS_OPMSK,  sourcefilename, __LINE__)
#define  oposk(a,b) dms_call(a,    b,    DMS_OPOSK,  sourcefilename, __LINE__)
#define  omi(a,b)   dms_call(a,    b,    DMS_OMI,    sourcefilename, __LINE__)
#define  onmi(a,b)  dms_call(a,    b,    DMS_ONMI,   sourcefilename, __LINE__)
#define  opmi(a,b)  dms_call(a,    b,    DMS_OPMI,   sourcefilename, __LINE__)
#define  ooi(a,b)   dms_call(a,    b,    DMS_OOI,    sourcefilename, __LINE__)
#define  onoi(a,b)  dms_call(a,    b,    DMS_ONOI,   sourcefilename, __LINE__)
#define  opoi(a,b)  dms_call(a,    b,    DMS_OPOI,   sourcefilename, __LINE__)
#define  sdc(a)     dms_call(NULL, a,    DMS_SDC,    sourcefilename, __LINE__)
#define  scd(a)     dms_call(NULL, a,    DMS_SCD,    sourcefilename, __LINE__)
#define  mrid(a)    dms_call(NULL, a,    DMS_MRID,   sourcefilename, __LINE__)
#define  gdc(a)     dms_call(NULL, a,    DMS_GDC,    sourcefilename, __LINE__)
#define  gvlc(a,b)  dms_call(a,    b,    DMS_GVLC,   sourcefilename, __LINE__)
#define  gii(a,b)   dms_call(a,    b,    DMS_GII,    sourcefilename, __LINE__)
#define  gki(a,b)   dms_call(a,    b,    DMS_GKI,    sourcefilename, __LINE__)
#define  gri(a,b)   dms_call(a,    b,    DMS_GRI,    sourcefilename, __LINE__)
#define  glrv(a,b)  dms_call(a,    b,    DMS_GLRV,   sourcefilename, __LINE__)
#define  ghrv(a,b)  dms_call(a,    b,    DMS_GHRV,   sourcefilename, __LINE__)
#define  gsi(a,b)   dms_call(a,    b,    DMS_GSI,    sourcefilename, __LINE__)
#define  gtt(a,b)   dms_call(a,    b,    DMS_GTT,    sourcefilename, __LINE__)
#define  gfli(a)    dms_call(NULL, a,    DMS_GFLI,   sourcefilename, __LINE__)
#define  gai(a,b)   dms_call(a,    b,    DMS_GAI,    sourcefilename, __LINE__)
#define  gafn(a,b)  dms_call(a,    b,    DMS_GAFN,   sourcefilename, __LINE__)
#define  trsync(a)  dms_call(NULL, a,    DMS_TRSYNC, sourcefilename, __LINE__)
#define  mcm()      dms_call(NULL, NULL, DMS_MCM,    sourcefilename, __LINE__)
#define  mrtm(a)    dms_call(NULL, a,    DMS_MRTM,   sourcefilename, __LINE__)
#define  msm(a)     dms_call(NULL, a,    DMS_MSM,    sourcefilename, __LINE__)
#define  mmu(a)     dms_call(NULL, a,    DMS_MMU,    sourcefilename, __LINE__)
#define  nal()      dms_call(NULL, NULL, DMS_NAL,    sourcefilename, __LINE__)
#define  frm(a,b)   dms_call(a,    b,    DMS_FRM,    sourcefilename, __LINE__)
#define  fro(a,b)   dms_call(a,    b,    DMS_FRO,    sourcefilename, __LINE__)
#define  gfs(a,b)   dms_call(a,    b,    DMS_GFS,    sourcefilename, __LINE__)
#define  dbxpnd(a,b)dms_call(a,    b,    DMS_DBXPND, sourcefilename, __LINE__)
#define  gei(a)     dms_call(NULL, a,    DMS_GEI,    sourcefilename, __LINE__)
#define  dbsel(a)   dms_call(NULL, a,    DMS_DBSEL,  sourcefilename, __LINE__)
#define  vrdef(a,b) dms_call(a,    b,    DMS_VRDEF,  sourcefilename, __LINE__)
#define  vrof(a,b)  dms_call(a,    b,    DMS_VROF,   sourcefilename, __LINE__)
#define  vron(a,b)  dms_call(a,    b,    DMS_VRON,   sourcefilename, __LINE__)
#define  gvii(a,b)  dms_call(a,    b,    DMS_GVII,   sourcefilename, __LINE__)
#define  gvri(a,b)  dms_call(a,    b,    DMS_GVRI,   sourcefilename, __LINE__)
#define  vrdel(a)   dms_call(NULL, a,    DMS_VRDEL,  sourcefilename, __LINE__)
#define  dbmemu(a)  dms_call(NULL, a,    DMS_DBMEMU, sourcefilename, __LINE__)
#define  trbgna()   dms_call(NULL, NULL, DMS_TRBGNA, sourcefilename, __LINE__)
#define  dmssjp(a,b)dms_call(a,    b,    DMS_DMSSJP, sourcefilename, __LINE__)
#define  dbfile(a)  dms_call(NULL, a,    DMS_DBFILE, sourcefilename, __LINE__)
#define  gxerr(a)   dms_call(NULL, a,    DMS_GXERR,  sourcefilename, __LINE__)
#define  gxstr(a)   dms_call(NULL, a,    DMS_GXSTR,  sourcefilename, __LINE__)
#define  dbalign()  dms_call(NULL, NULL, DMS_DBALIGN,sourcefilename, __LINE__)
#define  dbau()     dms_call(NULL, NULL, DMS_DBAU,   sourcefilename, __LINE__)
#define  dbsu(a)    dms_call(NULL, a,    DMS_DBSU,   sourcefilename, __LINE__)
#define  dbfu()     dms_call(NULL, NULL, DMS_DBFU,   sourcefilename, __LINE__)
#define  dbavail(a) dms_call(NULL, a,    DMS_DBAVAIL,sourcefilename, __LINE__)
#define  dbsrv(a)   dms_call(NULL, a,    DMS_DBSRV,  sourcefilename, __LINE__)
#define  dbstart(a) dms_call(NULL, a,    DMS_DBSTART,sourcefilename, __LINE__)
#define  dbstop(a)  dms_call(NULL, a,    DMS_DBSTOP, sourcefilename, __LINE__)
#define  dbusr(a,b) dms_call(a,    b,    DMS_DBUSR,  sourcefilename, __LINE__)
#define  dbems(a)   dms_call(NULL, a,    DMS_DBEMS,  sourcefilename, __LINE__)
/* Eof. DMSFUNC.H */

Back to Article

Listing Two

/*************************** Module Name: DMSCALL.c
**************************/
#define NTC_DMS_FILENAME
static char sourcefilename[] = "DMSCALL";


</p>
#include "mmsys.h"
#include "codedef.h"


</p>
/*
** The next two constants define the number of times the error-64-handler
** will retry the call before asking the user whether to continue, and 
** the number of 10ths of a second between retries.
*/
#define MAX_ERR64_RETRIES 15
#define ERR64_RETRY_INTERVAL 20


</p>
#define DOT_SYMBOL '+'


</p>
extern short dmserrs[];
void dms_error(int, int, char *, unsigned int, BOOL, BOOL);


</p>
/******************************
* DESCRIPTION:
*   This function replaces all calls to MDBS functions.  Macros redefine
*   the DMS calls to call this function instead.
*   For example,
*   #define crs(a,b) dms_call(crs,(char *)a,(char *)b,8,__FILE__,__LINE__)
*   the argument fnum is MDBS's unique number for the function.
*   filename and linenumb are the name of the file and the line number as
*   determined by the preprocessor and set as replacements for __FILE__ and
*   __LINE__, respectively.
*   Benign command status values are returned to the calling function.  More
*   serious values are given to dms_error() which displays the information
*   and exits to DOS.
******************************/
int NTC_PASCAL dms_call (arg1, arg2, fnum, filename, linenumb)
    char           *arg1, *arg2;
    int            fnum;
    char           *filename;
    unsigned int   linenumb;
{ 
    int         error, nwindow;
    int         i, ntries;
    BOOL        dbbusy_window_open;
    char        dots_array[80];
    int         last_dot;
    char        context_buffer[80], logmsg[100];
    struct lock_msg_struct msgbuf;
    static char context_fmtstr[] =
                "(status: %d, fn: %d, file: %s %u)";
    MSG msg;
    /*
    **  ntries is set at MAX_ERR64_RETRIES, in the case that an error 64
    **  occurs.  If it is error 64, ntries will be decremented each time
    **  the function is retried.
    */
    ntries = MAX_ERR64_RETRIES;
    /*
    **  dbbusy_window_open  is used to track whether the "DB Busy" window
    **  has been opened or not.
    */
    dbbusy_window_open = FALSE;
    /*
    ** last_dot is an index to the array of dots which increments with each
    ** retry.  Used with error 64.
    */
    last_dot = 0;
    /*
    ** nwindow indicates how many windows are opened.
    ** It has to be initialized here because in the case of error 64 it
    ** is necessary to close the DB BUSY window if that window is open and on
    ** the retry an error other than 64 occurs.
    ** In this case it is necessary that we
    ** close no windows if the error 64 case has not opened any.
    */
    nwindow = 0;


</p>
    do  {
        /*
        ** Call the DMS function, and return immediately if 
        ** the command status is 0.
        */
        error = (*(get_dmsfptr(fnum)))(arg1, arg2);
        /*
        ** See note for nwindow above.
        */
        if ((nwindow) && (error != 64)) {
            ntc_close_window (nwindow, &nwindow);
            spi_flush_screen();
        }
        if (error == 0) {
            /*
            ** Close DB Busy window if it is open.
            */
            if (dbbusy_window_open) {
                ntc_close_window (nwindow, &nwindow);
                spi_flush_screen();
            }
            return (0);
        }
        /*
        ** Check the array of allowable errors.  Return if the non-zero
        ** error from *fptr is in the array; else go to the switch below.
        ** The variable pass_array is a global array.
        */
        for (i = 0; dmserrs[i]; ++i)
        {
            if (error == dmserrs[i]) {
                if ((dbbusy_window_open)) {
                    ntc_close_window (nwindow, &nwindow);
                    spi_flush_screen();
                }
                return (error);
            }
        }
        switch (error) {
            case 255:
                /*
                **  End-of-set condition.  This is not really an error at all.
                **  Return the end-of-set result code to the caller.
                */
                if (dbbusy_window_open) {
                    ntc_close_window (nwindow, &nwindow);
                    spi_flush_screen();
                }
                return(255);


</p>
            case 62:
            case 63:
            case 68:
                /*
                **  Record-locking conflict
                */
                clearstruct(msgbuf);
                sprintf(msgbuf.msg[0], "Status: %d fn: %d File: %s Line: %d",
                     error, fnum, filename, linenumb);


</p>
                /* Give user option of retrying or aborting */
                switch (retry_dms_msg("xusertry", &msgbuf)) {
                    case RETRY_YES:
                        break;
                    case RETRY_NO:
                      dms_error(error, fnum, filename, linenumb, FALSE, TRUE);
                      break;  // dms_error should never return.
                }
                break;
            case 64:
                /*
                **  Our operation was rejected because another user has an
                **  abortable transaction in progress.
                */
                if (! dbbusy_window_open) {
                    ntc_open_std_window ("xdbbusy", 5, 13, &nwindow);
                    spi_string_tofield_byname("fil", filename);
                    spi_int_tofield_byname("lin", linenumb);
                    spi_flush_screen();
                    dbbusy_window_open = TRUE;
                }
                else {
                    /*
                    ** If the xdbbusy window is already open, write a dot to
                    ** the window.
                    */
                    dots_array[last_dot] = DOT_SYMBOL;
                    dots_array[++last_dot] = '\0';
                    spi_string_tofield_byname("dbbusy", dots_array);
                    spi_flush_screen();
                }
                spi_keyhit(ERR64_RETRY_INTERVAL);  // Wait 2 seconds.
                if (ntries--)
                    continue;
                last_dot = 0;
                *dots_array = '\0';
                sprintf (context_buffer, context_fmtstr,
                        error, fnum, filename, linenumb);
                switch (retry_dms ("xdb64ask", context_buffer)) {
                    case RETRY_YES:
                        ntries = MAX_ERR64_RETRIES;
                        continue;
                    case RETRY_NO:
                      dms_error(error, fnum, filename, linenumb, FALSE, TRUE);
                      break;  // dms_error should never return.
                }
                break;
            default:
                if (dbbusy_window_open) {
                    ntc_close_window (nwindow, &nwindow);
                    spi_flush_screen();
                }
                dms_error(error, fnum, filename, linenumb, TRUE, TRUE);
                break;  /* dms_error should never return. */
        }  /* end of switch (error) */
    } while (TRUE);  /* end of while; will always loop again */
}

Back to Article

Listing Three

/******************* Insert the current record into the specified set.
* Handles the case where the record is already a member of the set.
* Return Values:
*    0  Record inserted into set successfully.
*    1  Record was already a member of the set.
******************/
void InsertRecord(char *setname)
{
    int ret;
    passerr(11);         /* Tell the DMS error handler not to trap error 11 */
    ret = ims(setname);  /* Attempt insertion of record into set */
    passerr(-11);        /* Restore default trapping of DMS Error 11 */
    return((ret == 11) ? 1 : 0);
}


</p>

Back to Article


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.