An Introduction to this Help File

This Help File is a reference for the DP4 C library and tools for application developers. The Introduction to DP4 Programming and Overview sections, particularly the information on LIBMAKE and the DP4 Database API, should be of wider interest, in particular to programmers using a C++ development environment such as MFC or similar, who want to access DP4 databases.

A basic knowledge of DP4 and C is assumed and experience of DP4, including fields, tables, roles, key fields, indexes, maps and other database concepts, is necessary to benefit fully from the information provided by this manual.

  • To learn about the functionality of DP4, read An Introduction to the DP4 ADE

This Section contains the following topics:

  • How this Help File is Organised

  • What's New

  • How to Comment on this Help File


How this Help File is Organised

The help file is organised as follows:

  • Introduction to DP4 Programming A look at the different ways of programming for a DP4 database, including alternatives to the native DP4 APIs, and a summary of the key features of the DP4 API

  • Overview of DP4 Programming An Introductory Guide to the DP4 Programming Tools and Libraries. Includes an introduction to the DP4 Database APIs and underlying concepts, and a similar look at the DP4 User Interface, as well as other parts of the library. Also contains a Tutorial and Reference for the LIBMAKE utility. This section of the manual has been expanded considerably comapred with earlier releases, as it supersedes the DP4 C Developers Guide, which has been withdrawn. It focusses on the "traditional C" version of the DP4 interfacem but the sections on Databases and LIBMAKE should be of general interest.

  • Programming with DP4DBAPI introduces the newer thread-safe version of the DP4 API. These are the APIs underlying the new DP4 C++ interface DP4DBASE, so the material in this section will be of interest to anyone wanting better to understand that interface, or to customise it.

  • Installing and Using DP4 C Libraries and Tools Tells you how to install and start using the DP4 C Libraries and Tools. Includes information on how to compile and link C programs that use the DP4 C library

  • Header Files Describes the C header files supplied for use in DP4 programs

  • Global Variables Describes the global variables defined in the DP4 CCOWN library and used in DP4 C programs

  • Macros Describes a number of useful macros defined in the DP4 header files

  • Functions An alphabetic reference to the functions in the DP4 C libraries

  • Error handling in DP4 Programs explains how DP4 generates System and FAIL errors, and how to recover from them.

  • Appendix 1 An index that allows you to look up System Variables by their pre-4.500 names

  • Appendix 2 An index that allows you to look up functions by their pre-4.500 names

  • The SALESORD Database A plan of the tables on the SALESORD database used in most of the sample code and other examples in this manual.


What's New in the DP4 C Reference Manual

This page will tell you about recent changes to this manual. The most recent changes are mentioned first.

New in 4.624

  • 2 May 2013 Documentation of show_resource() improved.

New in 4.623

  • 16 Jul 2007 New function trm_run_program_ex().

  • 28 Jun 2006 Documented new variable app_server_name.

  • 10 Jun 2005 Docmentation of database functions has been updated to cover DP4DBAPI in more detail. See the new DP4DBAPI section

  • 14 Apr 2005 Improved and updated documentation for show_special()

New in 4.622

  • 14 Apr 2005 Added documentation for srv_connect() and associated functions used to obtain and release connections to the DP4 database manager.

  • 7 Apr 2005 Added documentation for tf_get_file_timestamp() and tf_set_file_timestamp() and sf_ equivalents.

  • 13 Aug 2004 DP4 APIS for file access now exist in both 32 bit and 64 bit versions. Refer to file DP4CAPI.H, or use the search facility in the CHM version of this manual and enter the word file_pointer

New in 4.620

  • 10 Nov 2003 Information on trm_control_subclass() corrected and expanded, and trm_get_hwnd() documented

  • 29 Sep 2003 Information on DP4 Version control added (based on information previously in DP4 Developer's Manual)

  • 26 Jun 2003 A new section on Error Handling in DP4 Programs has been added, and the app_error() function has been documented.

  • 30 Jan 2003 tail_read() behaves differently in 4.620 from earlier releases.

  • 29 Nov 2002 A new introductory section has been added, and the Overview section has been almost completely rewritten and much expanded.

    The Overview section replaces the information formerly held in the DP4 C Developers Guide. There have been many many corrections throughout the manual, though probably much more remains to be done.

  • 17 Oct 2002 What's New Page Added


How to Comment on this Help File

Our aim is to provide you with top quality documentation. In order to do that, we need your help. Feedback from you is the best way we have of measuring the quality of our documentation, so comments, criticisms and suggestions on our manuals are greatly appreciated.

If you are familiar with HTML you can use the view source option to get an editable copy of each page. You are welcome to e-mail us with edited versions of pages of this or any other DP4 manual. You can contact us at CustomerCare@itim-ts.com.


An Introduction to DP4 Programming

This section is intended as a brief introduction to DP4 programming using programming languages such as C,C++,Visual Basic or Delphi.

Apart from DP4's own specialised programming tools (QAB, Report Writer, and Importer), or similar third party tools (such as Crystal Reports) there are three basic approaches to writing programs for DP4 systems, each of which has its own advantages and disadvantages, discused on the following pages:

  1. Programming without using the DP4 API

  2. Programming using the DP4 Database API only

  3. Programming using the "pure" DP4 API

Of course it is also possible to mix and match approaches in different parts of a program. For example you may choose to write most of a large program using the "pure" DP4 API with a view to future portability, but to include some Operating System specific code for things that cannot be easily programmed otherwise.

Next some key features of the DP4 API are introduced.


Programming without the DP4 API

If you are using a language such as Delphi or VB that has built in support for databases, you can completely ignore the native DP4 APIs, and simply treat DP4 database as ODBC database sources or as ADO record sets etc. This has some important advantages:

  • You (and the programmers that come after you) do not have to learn a proprietary API.

  • You can use sophisticated data aware objects designed to work with any database.

  • You can easily switch to using another supplier's database if you should choose to do so.

On the other hand you will lose out in the following ways:

  • The native DP4 API makes database programming extremely simple in any language - database fields and records are tied directly to variables declared directly in your program, so there is actually very little to learn, and it is very easy to write maintainable code: there are no potentially slow and error prone stages such as parsing of SQL, binding of columns to variables etc. In fact the DP4 API is so simple to use that it is not uncommon for DP4 users to use it in preference to SQL for accessing Oracle or other databases.

  • The native DP4 API makes certain very commonly required database operations very easy, which are almost impossible to perform in standard SQL.

  • The native DP4 API is VERY fast, and native DP4 programs usually have a very small footprint

  • You may lose out on program portablity - your program will be restricted to the platforms supported by your chosen programming tools.

If you take this approach to programming you will not need to use this manual - you will only need the DP4 developers manual, to learn how to create your DP4 database in the first place, and the DP4 SQL Products guide to find out how to access DP4 databases using ODBC


Programming using the DP4 database API only

The second approach to DP4 programming is to use the native DP4 API for accessing databases, but to ignore the "user interface" related parts of the DP4 API, and to use some other set of tools for manging the user interface . This approach also has advantages:

  • You can use the most fashionable programming tools to create the most modern user interface for your programs, but you get the benefit of the native DP4 database API in terms of ease of use and raw performance.

On the other hand:

  • You still may lose out on program portablity - your program will be restricted to the platforms supported by your user interfce.

  • You probably won't get the benefit of DP4's small footprint.

If you take this approach to programming you will only need to refer the parts of this manual that discuss the LIBMAKE program, and the parts that describe the DP4 database APIs


Programming using the pure DP4 API

The third approach to programming with DP4 is to write "pure" DP4 program. In a pure DP4 program you use the DP4 database API and user interface API. The only other APIs you use are those that are part of the standard for the language you are using (though in C other files are usually created and accessed using DP4's Unix-like file I/O functions, and not ANSI C functions). Here are some of the advantages of this approach:

  • The DP4 API is supported on a very wide range of platforms, from Windows CE right up to AIX and HP/UX. In future it can be ported to any platform that comes with an ANSI C compiler and a sufficiently powerful operating system. DP4 programs written many years ago for MS-DOS can be recompiled to run on the latest version of Windows or Linux.

  • The DP4 user interface API is very abstract - a program is not concerned with things such as the size of the screen, whether or not there is a mouse or other pointing device, what language the user interface appears in. The user interface is contained in "maps", stored on a DP4 database. These are edited separately from the program, and you can make significant changes to the user interface without making any changes to the executable part of your program. This level of abstraction brings its own benefits:

    • The same DP4 executable can run with either a text based or a full GUI user interface ( the text based user interface allows your program to be run through Telnet or a similar protocol).

    • The user interface of a DP4 program may not even be running on the same computer as your program - using the DP4 thin client a Unix program can appear to a user of a Windows computer exactly like a regular Windows application.

    • DP4 programs can interact in a device independent way with specialised hardware such as MSRs, specialised printers, customer displays.


Key Features of Programming with the DP4 API

  • DP4 programs access databases using types declared directly within the program. For example a customer record would be accessed using a variable declared as struct CUSTOMER in C, as class CDP4Customer in C++, as class TCustomer in Delphi and so on. These variables contain members that are in one to one correspondence with the fields on the database. The declarations needed for each table are prepared by the DP4 utility LIBMAKE, which in many cases also generates the code needed to establish a connection to the DP4 database manager and terminal manager. The data independence mechanism built into the DP4 database manager ensures that programs continue to work even if the database structure is changed after the program has been built. (There are limitations to this facility however, and it is important that you familiarise yourself with these when planning to make changes to a live system.)

  • When you call functions to update a DP4 database you are not actually changing the database at that time. You are adding pending requests to a queue of updates for the current transaction. The transaction will be completed when you call one of the functions that commit your changes. Either all the updates in a single transaction will be applied at the same time, or they will all be rejected, if there is a contention error, or a constraint violation. It is important that changes are only committed when it is logically appropriate to do so.

  • Unlike most databases, it is possible, and indeed usual, to bypass most constraint checking when you commit changes to a DP4 database. The rationale behind this is that application programs typically make all necessary checks before updating the database in the first place, so that for the database manager to make the checks again is unnecessary. However this puts an onus on the programmer to make sure the program is correct.

  • There are two sets of libraries and support files for programming using the DP4 API.

    1. The "traditional" DP4 API using the ccown library (the actual name depends on the platform). This is the library used by the vast majority of DP4 utilities, and the primary focus of this manual. This wraps the interface-level DP4 functions with a collection of C functions used to implement high level functions for doing things like managing dialogs and list boxes, creating files that implement views of the database, and so on. This library is primarily intended for C programs, though it can also be used from C++. The library makes extensive use of global variables. For single-threaded programs that access only one or two databases this library is very convenient to use. However, if you are planning to use multiple threads or open many databases it may not be the best choice.

    2. The interface level DP4 API is available from release 4.620, though some parts of it were available previously via the DP4DYN interface. The interface level functions are essentially a subset of the C functions described in this manual. However, most of these functions require extra parameters, to avoid any need for using global variables. For C++ this library has been "wrapped" in the CDP4Database and related classes.


Overview

This section gives a brief overview of writing programs using the DP4 API. New users should aim to familiarise themselves with this section first as it introduces most of the key DP4 functions and other aspects of DP4 programming. Although it primarily deals with C programming it will be useful to anyone programming using the native DP4 API, whatever language is in use.

  • DP4 C Program Structure The bare bones of a DP4 C program

  • Using Databases introduces some important concepts in DP4 and the functions used to access DP4 databases

  • Using LIBMAKE to Generate Declarations and Code tells you how to use LIBMAKE to automatically generate the declarations and initialisation code that makes a DP4 database accessible to your C program.

  • Using the DP4 User Interface Tools and Functions introduces maps - the most important feature of the DP4 User Interface - and how to use them to create menus and dialogs or when printing or creating text files.

  • Memory Management and Caching introduces functions that you can use to improve the performance of your DP4 programs

  • Extracting and Sorting Data outlines how to use extract files to create reports sorted in ways that cannot be handled using regular database indexes.


DP4 C Program Structure

The code below shows a skeleton DP4 C program.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18

   /* Template for DP4 C programs */

   /*
   #c
   #db SALESORD
   #private
   #tables CUSTOMER,
           REGION;
   #end
   */

   #include "dp4capi.h"
   #include "template.h"

   void process()
   {
     /* Your code goes here! */
   }

The first thing you will notice is there appears to be (almost) no code! However the comment beginning at line 3 contains lines that are ignored by the C compiler, but processed by the DP4 LIBMAKE program, to generate the include file template.h . These lines are called directives, and always start with a hash sign. Lines 4-9 of the template above contain 5 LIBMAKE directives:

  1. #c tells LIBMAKE to generate an include file for the C language. LIBMAKE also supports C++, Delphi, VB, and several other languages.

  2. #db salesord selects the database used to generate the include file. In this case the include file will also contain initialisation and termination functions that open and close the database.

  3. #private enables the DP4 "private database" facility discussed elsewhere.

  4. #tables introduces a list of DP4 tables for which appropriate structures (and variables) are declared in the template.h include file.

  5. #end tells LIBMAKE there are no more DP4 directives in the file.

You can learn more about LIBMAKE and these directives in Using LIBMAKE to Generate Code and Declarations.

After the LIBMAKE directives, two standard #include statements appear. The first one is: #include "dp4capi.h"
DP4CAPI.H (formerly DATAFIT.H) is provided as part of DP4 and must be accessible to your C program as an include file. It contains definitions of all the functions and global variables mentioned in this manual. Older programs may include the file DP4.H instead of DP4CAPI.H. DP4.H enables some old names for various DP4 APIs and variables.

The second #include statement specifies the file that LIBMAKE generates. This will have the same name as your program, but with the file extension .h (at least for C programs). For example, if your program is called enquiry.c, use the statement: #include "enquiry.h" The contents of this file are described here. You can, of course, use the #include statement for any other include files. We recommend that standard include files such as stdlib.h are included before any other include files, using the <> form of the #include statement. This minimises any potential problems with clashing declarations.

Following the include files you can include any other declarations required by your program. Although C and C++ permit great flexibility in the order of declarations we recommend that you adhere to some well-defined standard such as putting #define pre-processor directives first, then any typedefs, then variable declarations, and then forward declarations of the functions in this source module arranged in order of definition (and we recommend functions are ordered top down rather than bottom up).

You must also provide a function called process() that contains the main loop of the program.

Where did main() go?

DP4 C programs typically begin with process() rather than main() (or WinMain()) because the DP4 C library has a main() that loads and enables the DP4 database API and the DP4 User interface API. A variant of this main(), which only enables the database API, is supplied in the file NOTRM.C. The process() function can be declared as void process(int argc,char **argv) if desired, though you won't usually need to do this as the DP4 C API also contains functions for processing command line options that can be called from anywhere in a program.

If you really want to you can provide your own main() function, but in this case you will need to include your own code to load and enable DP4 APIs. You can adapt the code contained in the source file CCMAIN.C or NOTRM.C in this case.

If you want to pass an exit code back to the operating system when your program terminates you can call the function dp4_halt() which is analogous to the standard C exit() function, and in fact calls it as its last operation. You should not call exit() directly because this will bypass the normal DP4 termination code. (In order to minimise its use of the ANSI C runtime library DP4 does not itself use the atexit() function). If a program exits by returning from process() it will return exit code 0.

If you wish you can declare process() with two parameters like this:

void process(int argc,char **argv)

This may be useful if you want to perform your own command line analysis, or adapting a previously written program that did not use DP4. However, it is more usual to use the DP4 tail_() functions to do this, and these don't need the argc or argv variables.


Using Databases

This section shows you how to write C programs that operate on records from a database. Using the SALESORD database, you can follow simple examples that show you how to do the following:

  • Fetch records that match a key
  • Fetch records in sequence
  • Fetch records with multi-part keys by using depth
  • Use foreign keys
  • Write records in different ways
  • Delete records
  • Control multi-user systems that use a common database

This section is divided into the following topics:

  • Opening and Closing Databases
  • Reading Information from a Database
  • Writing Information to a Database
  • Deleting Information from a Database
  • Contention Control
  • Database Access Summary
  • Database functions Quick Reference


Opening and Closing Databases

A database must be opened before it can be accessed. You open a database with the db_open() or dp4_db_open() functions. In DP4 C applications you will often only need the call to db_open() that LIBMAKE generates for you.

When you have finished using a database, you must close it to ensure that it is shut down properly. Another C function, db_close(), is available for closing a database. In C programs LIBMAKE generates a call to close the database in the finish() function, which is always called when the application is terminating.

The Example Code Generated by LIBMAKE contains the lines shown here:

37
38
39
40
41
42
43
44
45
46

void init()
{
  df_libtype = 1;
  db_open("SALESORD",0,SHARED+PRIVATE_DB);
  map_load("TEMPLATE");
}
void finish()
{
  db_close();
}

When any DP4 C program using the standard main() function is run, the function init() is called before process(). It contains the following call to db_open on line 40: db_open("SALESORD",0,SHARED+PRIVATE_DB);

  • The first parameter, "SALESORD", specifies the database that is to be opened.
  • The second parameter, 0, is the generation number. (It will usually be a much bigger number)
  • The third parameter contains the flags SHARED and PRIVATE_DB. The SHARED flag means that others users can use the SALESORD database at the same time. The PRIVATE_DB flag means that if you read a record, DP4 scans your pending updates before it scans the actual database. So, you only see the database as it will be when you commit your changes.

When a DP4 C program finishes, the function finish is called last. Therefore, the call to db_close() shown in line 45 closes the SALESORD database. You can open and close databases at any point in your program.


Reading Information from a DP4 Database

Reading Information from a Database

There are several functions for reading records from a DP4 database. All of them are very similar in operation.

  • rec_fetch() and dp4_rec_fetch() read records one at a time directly from the DP4 database interface.

  • blk_fetch() and dp4_blk_fetch() read multiple records from the database. If a program is processing multiple records this is always more efficient, and over a network it is very efficient. Unfortunately these functions are rather inconvenient for the average program to use directly.

  • bf_fetch() and dp4_bf_fetch() read records one at a time from a buffer that the DP4 interface fills as necessary by calling blk_fetch() as necessary. bf_fetch() is almost as easy to use as rec_fetch(), but performs almost as fast as blk_fetch(). It is usually the best function to use when you are going to be reading many consecutive records from a table. There are examples on this page,or you can find out more about bf_fetch() in Buffered Fetch Functions.

With all these functions you specify the record to be fetched by doing the following:

  1. Assign the value of any key fields that have known values.
  2. Call the appropriate fetch function specifying an index table and a number of keys to match and the index to be used. In C if, as will often be the case, you are searching using a table's primary index you can use the function rec_fetch_main() instead of rec_fetch(), to avoiding having to specify all the rec_fetch() parameters explicitly. In C++ you will often be able to rely on default parameter values to save you from having to pass all parameters explicitly.

The remainder of this section illustrates various aspects of using the fetch() functions.

  • Fetching a Record with a specific key
  • Reading records sequentially
  • Multi-Part Keys - Fetching a Group of Related Records

Fetching a record with a Specific Key

To fetch the customer record whose customer_number is 10000 you could use code like the following:

customer.customer_number = 10000;
if (rec_fetch_main(EQUAL,&customer.l) > 0)
  printf("Hurray\n");
else
  printf("Sorry - I can't find customer 10000\n");

If a program will be frequently looking up records with a specific key, and they are not going to change while the program is running it may be beneficial to use a cache to reduce the number of database accesses. DP4 C programs can take advantage of the cache_() family of functions to do this.

On the other hand if you know the customer name is SMITH you might write code like this:

WIPE(customer.name); /* same as memset(customer.name,' ',sizeof(customer.name)); */
memcpy(customer.name,"SMITH",5);
if (rec_fetch(EQUAL,&customer.l,_INDEX_CUST_NAME,0) > 0)
  printf("Hurray\n");
else
  printf("Sorry - I can't find customer SMITH\n");

Note that you need to know the index that corresponds to the field whose value you know when fetching a record with a known key value. If you assign to the wrong fields for the index you specify in the call to a fetch function, you won't get any warnings or errors - you will just get the "wrong" record returned.

Using rec_fetch() you can very easily find the record with the key "nearest" to a given value:

customer.customer_number = 10000;
if (rec_fetch_main(EQUAL|NEXT,&customer.l) > 0)
  printf("Customer %f is the first customer with number >=10000",customer.number);
else
  printf("Sorry - I can't find any customers with customer number >=10000\n");

NEXT on its own would find the first customer with a custom number > 10000 rather than >=. This type of fetch is very difficult using SQL, especially if there are several key fields.

Reading Records Sequentially

To fetch all the customers in customer number sequence you could use a loop like the following:

  if (rec_fetch_main(FIRST,&customer.l))
    do
      printf("Found customer %f",customer.number);
  while (rec_fetch_main(NEXT,&customer.l);

To improve performance you could replace this with calls to bf_fetch() as follows:

{
  BF_HANDLE h = bf_open(&customer.l,BF_SEQUENTIAL,0,0);
  if (bf_fetch(h,FIRST,&customer.l,0,0))
    do
      printf("Found customer %f",customer.number);
    while (bf_fetch(h,NEXT,&customer.l,0,0);
  bf_close(h);
}

If we want to fetch in name sequence all we have to do is change the fourth parameter in the calls to bf_fetch() from 0 to _INDEX_CUST_NAME.

We could fetch the records in reverse sequence by starting from the LAST record and then calling bf_fetch(PREV) repeatedly. However this is usually a little bit slower than fetching in the forward direction.

You don't have to start with the FIRST or LAST record - you can start off a sequential scan through a table using an EQUAL or an EQUAL|NEXT search. On a primary index you can also start with a NEXT or a PREV search. However this is not safe for non-unique secondary indexes - in rare cases this can cause one record to be skipped - and you should always include one of the "terminal" flags FIRST or EQUAL or NEXT in the flags.

Multi-part keys

Let's change the last example to read the ORDER_LINE table instead of the customer table.

{
  BF_HANDLE h = bf_open(&order_line.l,BF_SEQUENTIAL,0,0);
  if (bf_fetch(h,FIRST,&order_line.l,0,0))
    do
      printf("Found order_line %ld %d",order_line.order_number,order_line.line);
    while (bf_fetch(h,NEXT,&order_line.l,0,0);
  bf_close(h);
}

This reads all the order lines for all the orders. If we only want to read the order lines for order 1000 we use the DEPTH flag and specify a fixed order number as follows:

{
  BF_HANDLE h = bf_open(&order_line.l,BF_SEQUENTIAL,0,0);
  order_line.order_number = 1000;
  if (bf_fetch(h,FIRST+DEPTH,&order_line.l,0,0,1))
    do
      printf("Found order_line %ld %d",order_line.order_number,order_line.line);
    while (bf_fetch(h,NEXT+DEPTH,&order_line.l,0,0,1);
  bf_close(h);
}

Note the additional final parameter passed to bf_fetch() to specify the DEPTH value. This tells the DP4 database manager that the first part of the two part key of the order line table is fixed at the current value.

This is one time when the dp4_ versions of the DP4 API are somewhat different from the traditional C versions - in the C version the final parameter is optional so you have to specify the DEPTH flag to indicate that it is present. In the dp4_ version of the fetch functions the final parameter is always present (the C++ wrappers will default it to 0 for you), so you don't have to specify the DEPTH flag and the interface will add it to the flag automatically when every you pass an explicit non zero value for the final parameter.

What if we want to fetch the first order line for each order? Can we use a similar bit of code?
The answer is NO because if we specify a depth of 2 matching is done on both order_number and line.

There are two possible solutions - we can define another index where the order of the keys is reversed, or, probably better, we can change the key value after each fetch() to miss out the next records. In this case we mustn't use bf_fetch() because we are no longer reading consecutive records. So we might end with code a bit like this:

{
#define MAX_LINE 32767
  if (rec_fetch(FIRST,&order_line.l,0,0) > 0)
    do
    {
      printf("Found order_line %ld %d",order_line.order_number,order_line.line);
      order_line.line = MAX_LINE;
    }
    while (rec_fetch(NEXT,&order_line.l,0,0);
}

With the native DP4 API you cannot fetch() a record using an arbitrary group of fields as you can in SQL using a WHERE clause. You can only specify a complete index or a leading subset of the keys of an index. The upside to this seeming restriction is that you can be very sure that any rec_fetch() you do call will complete very quickly, and you are free to define as many index tables as are necessary for your programs.


Writing Information to a Database

As well as finding records from a database, you can also add new records to a database, and change or delete existing ones.

  • The functions rec_post(), rec_post_with_flags() or dp4_rec_post() are used to insert or update records.
  • The function rec_kill() or dp4_rec_kill() is used to delete records.
  • The function db_update() or dp4_db_update() is used to commit pending updates. db_commit() and db_checkpoint() are a short-hand form of the most common calls to db_update()
  • The function db_decommit() is used to throw away (rollback) pending updates.

This topic is divided into the following sub-topics:

  • Creating Transactions

  • Posting and Updating Records

  • Reading Updates

  • Committing, Flushing and Checkpointing


Creating Transactions

When you post a record to a database (with the function rec_post() for example), you place it in a queue that holds records that are to be written to the database until the function db_update() is called.

A set of records posted to the queue is called a transaction. When the database is updated (with db_update), DP4 either writes the complete transaction to the database or rejects the whole of it. The next transaction starts the next time a record is successfully fetched from the database or when another update function is called.


Inserting and Updating Records

A single function is used for both inserting and updateing records. The most common form of the function is rec_post() which may be called as follows:

rec_post(&customer.l);

The only parameter is a C structure that corresponds to the table containing the record that is to be posted to the queue. In this case, the CUSTOMER table is to be updated.

The function rec_post() does not specify any checking on the database. To specify checking, use the function rec_post_with_flags().

rec_post_with_flags(&customer.l, CHECK_PARENT);

Again the first parameter is the C structure that corresponds to the table containing the record that is to be posted to the queue. The CUSTOMER table is to be updated.

The second parameter is a set of flags detailing the checks to be performed at commit time, in this case the CHECK_PARENT flag. is specifed This means that the database is not updated unless a parent record exists for foreign keys marked for parent checking. In this case the only such parent table is the REGION table, and so the user cannot enter a new or modified CUSTOMER record unless it includes a valid region code.

If you use the CHECK_PARENT flag with rec_post_with_flags, the new record is still posted to the queue even if it does not have a parent. This is because the checking is not done until db_update is called to write the transaction to the database. You can find out more about the possible checks in the documentation for the rec_post_with_flags(),dp4_rec_post() function.

These functions may be called several times to create a single transaction that contains several updates. When db_update() is called, the complete. This is discussed further in Committing, Flushing and Checkpointing.

The function db_update returns a non zero (and negative) value if it detects a fail condition, the return value corresponds to the error. For example if a contention error is detected the return value is -COMMIT_FAILURE. In the C library version of the function the fail_code variable is used to store the asame information, but this time with a positive value.

If a transaction is rejected because a check specified in a preceding call to rec_post_with_flags() fails, fail_code is set to the same value as the flag, for example if a parent check fails db_update() returns -PARENT_CHECK and fail_code is set to PARENT_CHECK.


Deleting Information from a Database

Records are deleted from a DP4 database using the rec_kill() function. rec_kill() works in a similar way to rec_post(), i.e. the record is not deleted from the database immediately, but only once the current transaction is committed (using db_update(),db_commit() or db_checkpoint())

Flags passed to rec_kill() can cause the deletion to fail if the record is a parent of other records still on the database, or automatically delete the child records (this is known as a cascaded delete in SQL).


Committing, Flushing and Checkpointing

When you call db_update(), the current transaction is written to the transaction log file as well as to the database, (assuming you have enabled transaction logging). The transaction log file is a record of transactions written to the database that can be used for recovery if there are problems with the database. Records may also have been locked during the transaction when they were read from the database (see Record Locking ).

The function db_update() has one parameter, which specifies a set of possible actions to perform in addition to updating the database.

  • You can choose to unlock all the records that were logged during the transaction, or to leave them locked. You can also choose only to unlock records if the transaction succeeded.

  • You can choose to checkpoint, or secure, the changes made by the transaction or not.

Committing Records

The COMMIT flag guarantees that the database is updated in the sense that the changes are visible to other users, but not necessarily that they have been made permanently. In the event of a power failure or similar catastrophe the recovery process that the database then undergoes may cause the transaction to be rolled back and hence lost. If this happens the transaction will be lost completely - it is not possible for partial transactions to remain, or for other transactions that were not rolled back to reflect updates made by the lost transaction.

In the absence of other flags locked records remain locked when the COMMIT flag is used.

db_update(COMMIT) is equivalent to db_commit() .

Flushing Records

You can force the transaction to be written permanently if you pass the FLUSH_LOG flag to db_update(). Once db_update(FLUSH_LOG) returns, the transaction is secure even if the database needs to be recovered because of a subsequent system or power failure.

Flushing records is slower than committing them to a database, but it is safer because it allows you to recover a database if you need to.

Checkpointing Records

db_update(CHECKPOINT) is similar to db_update(FLUSH_LOG) in that it secures the transaction. In addition it unlocks any records that the program had locked.

db_update(CHECKPOINT) is equivalent to db_checkpoint() .

You should take care to write critical transactions securely, but avoid indiscriminate use of CHECKPOINT or FLUSH_LOG commits. These operations are expensive, particularly prior to release 4.620, and using them excessively is a common cause of poor performance.


Contention Control

So we have not discussed how DP4 behaves when two programs attempt to update the same record at the same time, or how to prevent this situation from arising.

Database systems can take three approaches to contention:

  • They can be single user, and only allow one program access to the database at a time.
  • They can use a locking scheme that force programs to lock any records they wish to update.
  • They can use optimistic contention (or concurrency) control. In this scheme programs are allowed to update any record without needing to lock it, but if two programs try to update the same record at the same time one will encounter an error condition.

DP4 supports all three approaches to contention some extent. The preferred approach is usually the third. DP4 was one of the first databases to use optimistic concurrency control. You can find out more about each approach to contention control on the following pages:

  • Using Exclusive Access to Prevent Contention
  • Record Locking in DP4
  • Optimistic Contention Control


Using Exclusive Access to Prevent Contention

A DP4 program can acquire exclusive access to a database by specifying the EXCLUSIVE flag when db_open() is called. If other programs are using the database already the call to db_open() will fail. Assuming it succeeds however, no other programs will be able to open that database.

When a program has exclusive access to a database, (and to a lesser extent when a program happens to be the only program using a database), DP4 can bypass some of the checks that are normally necessary when db_update() is called. Therefore for batch programs that can be run when a system is not in use by nromal users, it is worth considering using exclusive access, as it may improve performance. Additionally a program with exclusive access to a database does not need to have any code for dealing with contention.


Record Locking

Database systems may allow programs to lock records. While a record is locked only the program that has the lock may update it. If another program tries to read the record, it may hang until the record is unlocked, or perhaps the calll to read the record returns an error code.

Record locking has the advantage that once a record is locked the program can be sure it will not get a contention error when it updates it. Users of a program are not going to be happy if they spend a long time entering data only for the transaction to be rejected because of a contention problem.

On the other hand record locking has several disadvantages:

  • Locking records involves work which may well prove to be unnecessary most of the time. The database system always has to create a lock, even though most of the time no other program will actually try to update the record.

  • It is not clear how to use record locking to prevent two programs from both inserting a new record with the same key value

  • It is possible for deadlock or "deadly embrace" situations to arise where two or more programs each have locks that one of the other programs requires, but won't give up their lock until they can lock the other record. Database systems may be able to detect this situation. If they do they usually cure the problem by "rolling back" one of the programs. However, this destroys any confidence a program can have in its locks.

Record Locking in DP4.

In DP4 a record is locked when it is fetched with the LOCK flag. If a program succeeds in locking a record no other DP4 program can update or delete it until it is unlocked. A locked record becomes unlocked when it is explicitly unlocked by the program that locked it (by fetching it again with the UNLOCK flag), or when the program performs a db_update() operation, and specifies either the CHECKPONT flag or one of the UNLOCK flags (UNLOCK_ALWAYS or UNLOCK_IF_OK). A fetch with UNLOCK becomes effective immediately, so you will normally only use this when you are sure you will not want to update the record.

A DP4 program is not prevented from reading a record that is locked by another program. However, a C program can tell that a record is locked by testing the locked variable as in the sample code below:

while (rec_fetch_main(EQUAL + LOCK, &order_header.l))
{
  if (locked)
  {
    /* Display Error Message */
    printf("Sorry another user this order locked\n");
    ...
  }
  else
    printf("Hurray! You locked the order\n");
}

The locked variable is set whether or not a program is trying to lock the record itself.

If a program tries to update a record that is locked by another program it will get a LOCK_FAILURE error when it tries to commit the transaction.

There are limitations to record locking in DP4:

  • DP4 does not detect deadlock situations. You have to design your system so that they cannot arise.

  • You cannot lock new records in DP4, only existing ones.

We strongly recommend that you use record locking only sparingly if at all. In many cases careful design of a system can eliminate many possible causes of contention.


Optimistic Contention Control

Optimistic Contention/Concurrency Control

Optimistic Concurrency Control gets its name from the fact that the program "optimistically" takes the view that no other program will interfere with its updates, so that it can save itself the effort of locking the records it needs to update. The program only needs to worry about contention when it actually happens.

Optimistic Contention Control in DP4

Optimistic Contention Control (sometimes called change-verify contention control) is built into DP4. When you post a set of records to the queue, they form a transaction. Whole transactions are written to the database (applying the specified checks) by db_update in one operation. This means that only complete transactions are written to the database, and so multiple record updates made by different programs remain in step. One of the checks the DP4 manager makes is that none of the records to be updated have already been updated since the user last read the record, or since the transaction began. (These are not necessarily the same thing, of which more later)

The Basics

Suppose program A and program B both read the same record. They each change the record, and call rec_post() to place the updates in the queue. If program A is the first to call db_update() and assuming the call to db_update() succeeds (returning 0), a subsequent call to db_update() made by program B fails and will return -COMMIT_FAILURE.

Program B will usually avoid the contention error if it reads the problem record after program A has already commited its change to it, even if it had previously read the old state of the record earlier in the transaction. However a contention error is guaranteed if Program B tried to update the record based on its old state even if it subsequently overwrites the update with another call to rec_post().

The Details

Internally DP4 contention control works like this:

Each record on a database has a timestamp, set when the record is first created and updated each time the record is updated. All the records updated by a single transaction get the same timestamp - the current database timestamp.

Whenever a record is read from a database its timestamp is returned to the calling program. Additionally each transaction has a timestamp - the database timestamp at the time the transaction begins. A transaction begins when a program first calls a fetch() function that returns data (unsuccessful fetches don't count), or when it calls rec_autoinc(),rec_verify(),rec_post() or rec_kill()and ends when it calls db_update() or an equivalent function.

When a program calls rec_post() the database manager checks whether the update is an insert or a replace, and remembers this. If the update is a replace and the record has been updated since the transaction began, and the timestamp of the posted record does not match the actual timestamp of the record, a commit failure is guaranteed.

When a program calls db_update() the database manager checks the timestamps of the records in the update queue against the current state of the records on the database. An update of an individual record would be allowed if either of the following apply:

  • The record posted is supposed to be an insert and the record does not exist already.

  • The record posted is supposed to be a replace and the record exists already, and the timestamp of the current record is either older than the transaction start, or matches the timestamp of the record in the post file, indicating the program "saw" the updated record anyway.

  • The record posted is supposed to be a deletion, and the record either never existed, or the timestamps match for a replace

  • The record posted is a verify() and the record exists and the timestamps match as for a replace.

The Pitfalls

There are two important things to watch out for with DP4 contention control

  1. Because DP4 does not force you to read a record before updating it, and because a transaction only lasts until you call db_update() it is possible to unintentionally overwrite another program's changes to a record with the earlier state of the record . This can happen as follows:

    Program A Program B
    Read Record 1
    Change and Post Record 1
    Commit changes     		Do nothing
    Do nothing Read Record 1
    Change and Post Record 1
    Commit changes
    Change and Post Record 1
    Commit changes     		Do nothing

    Program A did not read record 1 again, because it had already read it. But to the DP4 database manager it looks as though program A simply did not care about the state of record 1, and this type of update is permitted, because it is very useful when importing data. (It would probably be a good idea if there were a flag for db_open() that a program could pass to indicate that it did not want to be allowed to update the database like this, but currently there is not.)

    Obviously it is possible to come up with many possible scenarios. The solution in all similar cases is for a program never to rely on the previous state of records it read before its last call to db_update(), but always to read the record again.

  2. If records are read via a DP4 ADC, the timestamp is usually set to 0. This means that updates are only permitted if the record has not been updated since the start of the transaction, because the real database manager never "sees" the fact that a program actually read a more recent state of the record. This can cause "spurious" commit failures to arise, for example when a transaction was begun a long time ago (perhaps through something as innocent as a map record being read from the database by the DP4 terminal manager), whereas the program has in reality been idle for a long time and a transaction is only "really" beginning now. The cure for this type of problem is to insert extra calls to db_update() when a program leaves an idle state.

  3. If you are copying records from one database to another, and you do not zero the timestamp, there is slight possibilty that you might fortuitously avoid a commit failure where you should get one, through a timestamp from one database matching a later timestamp from another database.


Designing out Contention

The best method of contention control is to design it out of your database. Here are a few ideas.

  • Avoiding generating numeric keys for new records based on the highest number already used unless one the following is true:

    • You use the rec_autoinc() function to generate the key.

    • The key of the record also includes a component that is guaranteed to be different for another instance of the program. For example an order entry program can use the highest key so far technique for order line numbers if the order header number is allocated via rec_autoinc(). Another common technique, espcially if you intend to use a client-server configuration, is to add a "workstation_id" key to tables where contention between workstations might be a problem.

  • Avoid doing things like maintaining stock-totals in order entry or sales programs. If you do this two instances of the program will contend when they happen to update the same stock record. Instead get a background program to process new orders to do the stock update. If necessary include a status field in the order line table that indicates the stock needs updating, and update this when the line has been processed. To avoid the inefficiency of constantly polling a table for changes you can use an ADC (Auxiliary Database Controller) that "wakes up" the background program as necessary.


Using More than One Database

You saw on the C Program Structure page how LIBMAKE automatically generates a call to db_open(). When db_open is called, the global variable db_nr is set to the number of the database that you open, and another global variable, db_generation, is set to the generation number specified in the call to db_open().

The values held in db_nr and db_generation are used to determine which database to use by all the database access functions.

If you wish to open a second database, and your program will switch between the two databases, these global variables must be saved before you make the second call to db_open, and you will have to set the variables as necessary to access each database.

Suppose that you create a copy of the SALESORD database called SALES2. If your program uses LIBMAKE to open the SALESORD database, you can open SALES2 as follows:

int salesord_dbnr,salesord_generation,sales2_db_nr,sales2_db_generation;
salesord_db_nr = db_nr;
salesord_db_generation = db_generation;
db_open("SALES2", 11, SHARED + PRIVATE_DB);
sales2_db_nr = db_nr;
sales2_db_generation = db_generation;

If you later wish to use database functions on the SALESORD database, you must first reset db_nr and db_generation as follows:

db_nr = salesord_db_nr;
db_generation = salesord_db_generation;
rec_fech_main(EQUAL,&order_header.l); /* goes to salesord */
To switch back to using the SALES2 database, reset db_nr and db_generation again:

db_nr = sales2_db_nr;
db_generation = sales2_db_generation;
rec_fech_main(EQUAL,&order_header.l); /* goes to sales2 */

It is very easy to forget to set the db_generation variable. If you do forget this, you may be lucky and encounter a system error such as system error 80. If you are unlucky you may end up with mangled data on your database, because the data independence mechanism relies on the value of db_generation being correct.

Accessing multiple databases - A Better Way

From 4.620, If you want to access more than one database, and especially if you want to have more than one connection to DP4 (for instance, because you are accessing multiple servers using srv_connect()) you may find it more convenient to use the functions contained in dp4dbapi.h rather than the C functions this manual concentrates on. In fact the dp4dbapi.h functions are the "fundamental" functions in 4.620, and the C library functions just call them. These functions are also intended to replace the dp4dyn interface functions. There is a one to one mapping between the C functions described in this manual and the functions in dp4dbapi.h. The only differences are as follows:

  • The names of the functions in dp4dbapi.h are almost all the same as the C functions described here, except they are prefixed with dp4_. Thus rec_fetch() becomes dp4_rec_fetch(). About the only exception is that dp4_rec_post() equates to rec_post_with_flags() and not rec_post(). The information in the alphabetic function reference is gradually being updated to include information about the dp4_ functions. The dp4_ part of the name is ignored - each function is described together with the corresponding "traditional" function.
  • Almost all the dp4dbapi.h functions take a handle as the first parameter. This is either a connection handle to the DP4 interface, or a connection handle to a specific database or file. These handles point to structures maintained inside the DP4 interface, that maintain the inforamtion that used to be in global variables. Because you do not have access to these structures you cannot make serious programming errors such as unintentionally using a database handle obtained on one connection with a different connection.
  • In fetch() functions the record_area parameter precedes the search_flags parameter, so that read and update functions are consistent.
  • There are no optional parameters. All parameters must be passed.
  • The DEPTH flag need not be set explicitly. The DEPTH flag is set when the corresponding parameter is set to a non zero value.
  • The dp4_ functions do not make any use of DP4 global variables.
  • Almost all functions that have a void return type in the C library are BOOLEAN in dp4dbapi.h. For such functions the return value will always be TRUE, unless a system error or fail error was generated in the cause of the function execution. Most of the void functions are functions such as rec_post() or rec_kill(). The return value in dp4dbapi.h does not imply anything about the eventual success or failure of the transaction of which these calls form a part.

The dp4dbapi.h functions are also similar to the functions available in dp4dyn, but with the following differences:

  • The high level functions for accessing the dictionary are not available in dp4dbapi.h. The source code of these functions is available on request (though it is not very good), and it is planned to make most of the functions used by DP4 utilities available in due course.
  • dp4dyn only implemented a subset of the C DB API. dp4dbapi.h implements the whole DP4 Database interface.
  • In DP4dyn many functions required two connection handles, so that you could easily pass a database connection and a server connection that were unrelated by mistake. Almost all dp4dbapi.h functions only require one connection handle.

Going back to the example in the previous section, we could now access the two databases together very easily, for example:

DCONN *salesord_conn = dp4_db_open(conn,"salesord",11,SHARED+PRIVATE_DB,0,0,0);
DCONN *salesw_conn = dp4_db_open(conn,"sales2",11,SHARED+PRIVATE_DB,0,0,0);
BOOLEAN found;
for (found = dp4_rec_fetch(salesord_conn,&order_header.l,FIRST,0,0,0) > 0;found;
     found = dp4_rec_fetch(salesord_conn,&order_header.l,NEXT,0,0,0) > 0)
   if (dp4_db_fetch(sales2_conn,&order_header.l,EQUAL,0,0,0) <= 0)
     printf("Order %n is on salesord but not sales2\n",order_header.order_nr);

This code would be no more complicated even if the two databases were on two different servers! (apart from the fact that you would have to open a connection to the other server)

You can use a mixture of the new and old style functions in a single program, but you should not mix calls to the two sets of functions on the same connection. In particular you must not call dp4_db_open() on the global connection handle used by the DP4 C library, otherwise you will not get a new connection handle, but the existing global connection will be reused.

For C++ programmers, the dp4dbase files, contained in the dp4dyn subdirectory, should be a convenient way of using the dp4dbapi version of the DP4 database functions. dp4dbase.h and dp4dbase.cpp are a source code compatible replacement of the old dp4class interface.

Anyone planning to use dp4dbapi directly from a C program should note that it is not intended that you mix calls to the dp4dbapi library and the traditional C library. This has not been tested and there might be some problems with doing this - although the C library is now implemented as a wrapper around dp4dbapi, there are some differences in how the data used internally by dp4dbapi is created when it is used from the DP4 C library.


Database Access Summary

To access DP4 databases at all you have to call three functions: srv_load_srv(), srv_connect() or srv_get_process(), and srv_enable(). For C programs these functions are normally for you by the supplied main() function.

These functions are directed to the database server. With the exception of the System database, to access a database you must first open it with a call to db_open() or open_database() if your program can work with any database and the user should choose the database to work on. Both functions allow you to specify various modes to control how the database is opened.

To fetch records from the database, use rec_fetch() on the main index or on the secondary index or for reading data sequentially use bf_fetch() . The same functions are also used to LOCK records.

To add or update records to the database, use the rec_post() function, and to delete records use use rec_kill() function. You can get DP4 to generate keys for new records automatically for auto-increment tables using rec_autoinc()

To write the records to the database, make a call to db_commit(), db_checkpoint() or db_update().

When a program has finished with a database it should close it with db_close().

If a program wants to access more than one database it needs to save and restore the db_nr and db_generation variables associated with each database. Failure to do this correctly can cause data to be corrupted./p>


Database Functions Quick Reference

The table below is not intended as an exhaustive list. Refer to the documentation of each function for full details

Function | Flag | Meaning
rec_fetch()
dp4_rec_fetch
rec_fetch_main()
bf_fetch()
dp4_bf_fetch()
blk_fetch() dp4_blk_fetch() 		EQUAL This searches the table for a record that exactly matches on all keys
FIRST | This finds the first record in the logical sequence of the index
NEXT | This finds the next record in the logical sequence of the index. It depends on the current value of the data record
LAST | This finds the last record in the logical sequence of the index
PREV | This finds the previous record in the logical sequence of the index. It depends on the current value of the data record
DEPTH | Indicates the number of parts of a multi-part key that are to be used for matching. Only needed in C. You can "or" DEPTH into flags to be passed to a fetch function, or you can use one of the pre-defined value: FIRST_DEPTH, NEXT_DEPTH, LAST_DEPTH PREV_DEPTH
LOCK | If a matching entry is found and is not locked by another user, it is marked as locked and the global variable locked is set to TRUE
UNLOCK | If the record is locked, it is unlocked immediately (instead of at the next checkpoint operation)
rec_post_with_flags
dp4_rec_post() 		NO_CHECK No parent checking is performed. rec_post(&rec.l) is equivalent to rec_post_with_flags(&rec.l,NO_CHECK)
CHECK_PARENT | The next commit or checkpoint operation fails for this record if the parent is missing in any of the indexes marked for parent checking
VERIFY_PARENT | The next commit or checkpoint operation fails for this record if the parent is either missing, or has been updated since the start of the transaction in any of the indexes marked for parent verification
CHECK_CHANGE | The next commit or checkpoint operation fails for this record if the record is an amendment of an existing record, and any of the secondary indexes marked for change prevention have been changed
db_update()
dp4_db_update() 		COMMIT This writes the transaction to the database, so its changes are visible to all users but does not definitely secure it in the event of a system failure. . Locked records remain locked. db_commit() is the same as db_update(COMMIT)
CHECKPOINT | This writes the transaction to the database, so its changes are visible to all users and guarantees that it will be secure in the event of a system failure. Locked records are unlocked. db_checkpoint() is the same as db_update(CHECKPOINT)
FLUSH_LOG | This writes the transaction to the database, so its changes are visible to all users and guarantees that it will be secure in the event of a system failure. Locked records are NOT unlocked.
rec_kill
dp4_rec_kill() 		ORPHAN_CHECK This prevents a record from being deleted if it has one or more child records on the database. This check is only applied if any children exist in any of the indexes that you marked for orphan checking when you set up the database. The check is also made for any of the children which would be deleted by the flag CHILD_DELETE
CHILD_DELETE | This has the effect of automatically deleting any children that belong to the record that you wish to delete. All further levels of children are also deleted
IGNORE_CHILD | This disables all orphan checking, and suppresses all child deletion


Using LIBMAKE to Make C Program Items

Using LIBMAKE to Generate Code and Declarations

This topic is divided into the following sub-topics:

  • Running LIBMAKE

  • Specifying the Programming Language

  • Specifying the Database Name

  • Specifying how to open the database

  • Defining the Database Tables to be Used

  • Generating Indexes for Record Fetching

  • Extracting and Sorting Records

  • To Mark the End of Directives

  • Example of Code Generated by LIBMAKE

  • LIBMAKE Directives Reference


Running LIBMAKE

Lines 4-9 of the skeleton program contain directives to LIBMAKE that it uses to generate an include file. To run LIBMAKE, type the command:

libmake template.c

You will see LIBMAKE process the source file on the screen, and display messages if it encounters any errors as it runs. If there are no errors, the include file TEMPLATE.H is generated. In fact LIBMAKE will run much faster if you run it with the -quiet option like this:

libmake -quiet template.c

In this case LIBMAKE does not echo the files it is reading or creating - only any error messages are displayed.

The LIBMAKE directives that the examples in this manual use are described in the following sub-sections. The complete set of directives is given in LIBMAKE Directives Reference


Specifying the Programming Language

You must specify the programming language that LIBMAKE should use when generating include files using one of the following directives:

#c
#c#
#cpp
#vb32
#delphi32
#pascal
#cobol
#cbasic

With some of these directives there are additional directives that modify the generated code in some way or other. Notably with C++ the following directives are relevant:

#public makes the members of the table structures public. By default the members are only accesible via Get and Set methods, some of which use MFC specific classes. We strongly recommend using #public.

#inline Usually LIBMAKE generates a C structure (identical to the normal C declaration for the table) and then wraps it in a C++ class. If you specify #inline there is no separate C structure, and the fields of the record are generated directly inside the class.

#dp4time. If you do not want to use the #public toggle, but wish to be able to access DP4 date and time fields without having to use the MFC CTime class specify this toggle.

#hpp. If you do not specify the #inline directive then LIBMAKE generates two header files for a module: C structures in filename.h and C++ classes in Cfilename.h. If you specify #hpp the C++ classes are generated in filename.hpp instead.

#nodp4class. Stops LIBMAKE generating a call to include dp4class.h. From 4.620 you can build C++ programs using dp4dbase.h, which is an enhanced version of dp4class.h. Or you may want to write your own version of the base C++ classes used in the C++ code. In either case you probably won't want dp4class.h included.


Specifying the Database Name

You specify the database to be uses with the #db directive:

#db dbname

where dbname is the name of the database that you wish to use.

If you use the -db dbname command tail when you run LIBMAKE, the database specified with the #db directive will be ignored. This is sometimes useful if you have two different generations of a database present and would like to use LIBMAKE to compare the layout of fields within the tables on the two databases.


Specifying Flags for db_open()

You can use a number of LIBMAKE directives to control how the init() function generated for C programs opens the database:

#exclusive

LIBMAKE passes the EXCLUSIVE flag in the generates call to db_open().Your program will only run if no other programs are using the database, and will prevent other programs opening the database while it is running.

#no_logging

This directive adds the value NO_LOGGING to the flags with which db_open() is called. This flag only works for programs that also specify #exclusive

#private

This directive adds the value PRIVATE_DB to the flags with which db_open() is called, and enables the private database facility. The private database facility can impact performance, especially for programs that create very big transactions, but allows programs to consistently see the database as it will be if committing the current transaction succeeds.

#quit

This directive adds the value QUIT_ON_FAILURE to the flags with which db_open is called.

Using this directive means your program will always terminate if a commit failure condition is encountered. It should only be used if commit failures should not arise in practice, or when prototyping a program.

#read_only

This directive adds the value READ_ONLY to the flags with which db_open is called.

#shared

This directive adds the value SHARED to the flags with which db_open() is called, enabling multiple programs to open the data simultaneously. In fact SHARED has the value 0 so there is no need to specify this directive explicitly.


Defining the Database Tables to be Used

You must specify each table in the database that your program uses with the #table directive:

#table tablename,
       tablename,
       ...
       tablename;

where each tablename is the name of a database table, for example CUSTOMER or REGION. You don't have to put each table name on a new line, only the comma to separate table names is important.

If you are using LIBMAKE with #C and you need more than one copy of a table to be be declared globally you can use the following syntax for a table instead of a simple table name:

tablename.rolename

For example:

#table customer.cust1,customer.cust2,material;

would declare one record for the material table, and two for the customer table. cust1 and cust2 are often called table roles, by analogy with the field roles that allow the same field to appear more than once in a DP4 table. You can specify any valid identifier as a table role name.

In your C code you would distriguish between the two copies like this:

  rec_fetch(EQUAL,&customer.cust1.l);
  rec_fetch(EQUAL,&customer.cust2.l);

The tablename.rolename syntax does not work with the #cpp directive, and should not be required -you can easily create as many instances of your CDP4Customer class object as your require.


Generating Indexes for Record Fetching

You can generate indexes for use in calls to the function rec_fetch with the #index directive:

#index index_name,
       index_name,
          ...
       index_name;

where index_name is the name of an index table or role that you wish to use with rec_fetch() or a similar function .

For example, if you use the directive:

#index supplier,
       principal,alternative;

You could call rec_fetch() to access materials by princial supplier number as follows:

if (rec_fetch(FIRST, &material.l, _SUPPLIER, _PRINCIPAL) > 0)
{
  depth = 0;
  do
  {
     map_draw(1,SCROLL);
     if (depth==0)
        showi(1,1,material.principal.supplier_number);
     showc(1,2,material.material_code);
  }
  while (rec_fetch(NEXT, &material.l, _SUPPLIER, _PRINCIPAL) > 0);
}

and by alternative supplier number like this:

if (rec_fetch(FIRST, &material.l, _SUPPLIER, _ALTERNATIVE) > 0)
{
 ...
  while (rec_fetch(NEXT, &material.l, _SUPPLIER, _ALTERNATIVE) > 0);
}

You can only use indexes that are shown with the Update property set in SETLINK. In fact the indexes shown in these examples are not turned on, so the code would fail with a system error.


Extracting and Sorting Records

For C programs LIBMAKE can generate declarations for "extract" files, which can be used to present views of the database. An example of using these functions is given in the extract_open() documentation. One of the chief reasons for using the extract file routines is that they can use a sophisticated sort facility that is capable of sorting millions of records using highly effcient algorithms.

To declare records for an extract file, use the #extract directive:

#extract fieldname_1 ,
fieldname_2 ,
…
fieldname_n ;

where fieldname_x is the name of a database field, for example region_code or name.

You can also specify a fieldname that does not appeaer on the database. In this case you need to specify the datatype of the field as well, using a datatype letter (as used in MAKEDB), the number of occurrences (which defaults to 1), and, for character fields, the length. For example local_address:C:4:30 would result in the extract record containing a field char local_address[4][30], and local_number:I would declare short local_number.

To define the sort order for the extract file, use the #sorton directive:

#sorton fieldname_1 ,
fieldname_2 ,
…
fieldname_m ;

where each fieldname_x is one of the fieldnames given in the #extract directive.


To Mark the End of Directives

LIBMAKE does not process any directives after the #end directive:

#end


LIBMAKE Directives Reference

The Syntax of the LIBMAKE command is:

libmake -quiet filenames

The filenames is the name of one or more source files containing LIBMAKE directives. The command tail -quiet is optional. It suppresses output of the generated C to the screen. It is a good idea to use this command tail.

The complete set of LIBMAKE directives are given below.

Language specifiers

You must specify the language you wish LIBMAKE to generate declarations in. It should be noted that for some languages not all the other directives are applicable. In particular, for C++,Visual Basic and Delphi, only the #table and #index directives are relevant (apart from C++ specifc directives) - for these languages LIBMAKE does not generate initialisation code to open the database, or support extract files.

Directive | Language | Initialisation Code Generated?
#c | C | Yes
#cbasic | IBM 4680/4690 Basic | Yes
#cpp | C++ | No
#delphi32 | 32 Bit Delphi | No
#vb32 | 32 Bit Visual Basic | No

In fact LIBMAKE still supports a number of other languages including various dialects of Cobol and Pascal, and 16 bit versions of Delphi or VB, but we no longer supply programming libraries or header files for these.

Other Directives

#db

You must specify which database that you wish to use. Do this with the #db directive: #db database where database is the name of the database that you wish to use.

#dp4time

(For #cpp only) Stops LIBMAKE from using the MFC CTime class for accessing date and time fields. Only needed if you do not use #public

#end

LIBMAKE stops scanning the source file for directives when it sees the #end directive.

#exclusive

(For #c and #cbasic only) LIBMAKE passes the EXCLUSIVE flag in the generates call to db_open().Your program will only run if no other programs are using the database, and will prevent other programs opening the database while it is running.

#external

(#c only) Use the #external directive to generate a separate file filename.e containing extern versions of the declarations in the .h file. This may be useful if your program has more than one source file.

#extract

(#c only) Please refer to the section Extracting and Sorting Records for information about this directive.

The #extract directive is used in conjunction with the #sorton directive.

#filename

(#c only) If you use the #extract directive, the extract file is given the name EXTRACT by default. To change the name of the extract file, use the #filename directive: #filename filename

where filename is the name that you wish the extract file to have.

#index

To generate indexes for use in calls to the function rec_fetch(), use the #index directive:

#index indexname,
       indexname,
       ...
       indexname;

where indexname is the name of an index table or role to use with rec_fetch().

For example, if you use the directive: #index index_date_time,index_cust_name; You could then call rec_fetch with the INDEX_DATE_TIME index or INDEX_CUST_NAME indexes as follows:

rec_fetch(NEXT, &order_header.l, _INDEX_DATE_TIME, 0);

rec_fetch(NEXT, &customer.l, _INDEX_CUST_NAME, 0);

If an index uses a role name as well as a table name you can can specify the index roles in this directive as well or you can have a #ROLE section using the same syntax (LIBMAKE does not actually care whether a name specified in these sections is an index table name or a role name).

#inline

(For #cpp only) Causes LIBMAKE to generate the record area for tables directly inside C++ classes. Without this directive LIBMAKE creates separate include files containing C Structures and C++ classes that use them.

#libtype

(For #c and #cbasic only) Causes LIBMAKE to set the df_libtype variable. If not specified df_libtype is set to 1. df_libtype affects the behaviour of the DP4 field input functions. See df_libtype in the C/C++ Programmers Reference. The recommended value is 5

To specify the libtype, use the #libtype directive: #libtype n

where n is the value to be assigned to df_libtype.

#mapname

(For #c and #cbasic only) LIBMAKE generates a call to map_load() that loads a mapset with the base name of the source file.

To use a different mapset, use the #mapname directive: #mapname mapsetname

where mapsetname is the name of the mapset to use in your program.

#noinit

(For #c only) LIBMAKE generates two functions called init and finish. To prevent these functions from being generated in the header file, use the #noinit directive:

If you use the #noinit directive, no code is generated to perform the following tasks:

  • Opening the database with db_open()
  • Loading the mapset with map_load()
  • Closing the database with db_close()

You will then have to write your own code to open and close the database or load maps.

#no_logging

(For #c and #cbasic only) This directive adds the value NO_LOGGING to the flags with which db_open() is called.

#nodp4class

(For #cpp only) This directive stops LIBMAKE including dp4class.h in the generated code.

#nopadding

When LIBMAKE generates types, by default it declares padding fields where empty space is left in a record to ensure correct data alignment.

To disable this feature, and rely on compiler generated padding instead, (not recommended) use the #nopadding directive.

#pointer

(For #c only)

This makes LIBMAKE generate table declarations that are pointers to structures rather than actual structures. This is usually used when creating QAB C exits.

#private

(For #c and #cbasic only)

This directive adds the value PRIVATE_DB to the flags with which db_open() is called, and enables the private database facility.

#public

(For #cpp only) If you use this flag the members of your C++ classes corresponding to fields on the database will be declared public. If you do not use this flag the members will be private and only accessible via Get and Set functions. As these functions do not do any checking you are recommended to use the #public directive.

#quit

This directive adds the value QUIT_ON_FAILURE to the flags with which db_open is called.

Using this directive means your program will always terminate if a commit failure condition is encountered.

#read_only

(For #c and #cbasic only) This directive adds the value READ_ONLY to the flags with which db_open is called.

#shared

(For #c and #cbasic only) This directive adds the value SHARED to the flags with which db_open() is called, enabling multiple programs to open the data simultaneously. In fact SHARED has the value 0 so there is no need to specify this directive explicitly.

#sorton

Please refer to the section Extracting and Sorting Records for information about this directive.

The #sorton directive is used in conjunction with the #extract directive.

#table

Please refer to Defining the Database Tables to be Used.


Example of Code Generated by LIBMAKE

If you run LIBMAKE on Example 1, it generates the include file shown below.

Example 2 Include file generated by LIBMAKE:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

/* C structs for database SALESORD at generation 0*/
#define SALESORD_GENERATION 0

#define _CUSTOMER 4107
#define _REGION 4104
struct CUSTOMER
{
  short l;
  short typenr;
  short generation;
  short flags;
  long timestamp;
  double customer_number;
  char region_code[3];
  char _p1000;
  char address[3][30];
  double credit_limit;
  double balance;
  char name[30];
  boolean credit_customer;
  char _p1001;
};
struct REGION
{
  short l;
  short typenr;
  short generation;
  short flags;
  long timestamp;
  char region_code[3];
  char _p1002;
  short employee_number;
  char name[30];
};
struct CUSTOMER customer = {sizeof(struct CUSTOMER),4107};
struct REGION region = {sizeof(struct REGION),4104};
void init()
{
  df_libtype = 1;
  db_open("SALESORD",0,SHARED+PRIVATE_DB);
  map_load("TEMPLATE");
}
void finish()
{
  db_close();
}

Lines 3-36 arise from the #tables directive in the example code, Lines 4, 6-22 and 35 from specifying customer. These lines of code enable you to use the C structure called customer to use records from the CUSTOMER table.

For example, the CUSTOMER_NUMBER field on the database corresponds to the C structure element customer.customer_number.

The other lines in this section allow you to use records from the REGION table similarly.

LIBMAKE also generates two functions, init() and finish(). When any DP4 C program runs, the function init() is called first. You will see that the function init contains a call to another function, db_open().

The function db_open() is discussed in Opening and Closing Databases. In this case, it opens the SALESORD database as a private database, because the example contains the #private directive.

Also, a call to the function map_load() appears on line 41 of the example above. This defines the mapset that you use within the program as being of the same name as your program. A mapset of the correct name must exist within the database defined with the #db directive. If you want to use a different mapset you can specify the #mapname directive, or you can call the map_load() function at any point in your program.


Using the DP4 User Interface Tools and Functions

The DP4 User Interface is implemented using the Terminal manager, which is responsible for providing almost all the functions DP4 programs need that are not database related (and database functions that need a user interface).

The functions used most often are those that relate to providing programs with a user interface. Most of these functions depend on maps. Maps contain information about the text, edit boxes, buttons and images a program needs to display to provide the user interface. For an introduction to the user interface related parts of the terminal manager you may like to consult the User Interface section of the Introduction to the DP4 ADE.

The DP4 user interface does not use the "event driven" model which became popular with the advent of C++ and Windows. Instead it uses a traditional top down approach to programming. A DP4 C program will typically start off by asking the user to select an option from a menu, then invoke one or more dialogs or do some work, and then return to the menu to ask the user what to do next. We think this makes for more maintainable programs, as the program logic usually closely reflects the business logic - rather than being obscured by state tables or a mass of functions to enable and disable parts of the user interface. Perhaps surprisingly it is very easy to make a program written this way behave in a very responsive way. For example you can arrange for clicking on part of the screen to automatically send your program the required input that will cause it to navigate to the required part of the code. The program will only fail to arrive there if there is some error condition that the user must correct first, and typically the program will display the appropriate message more or less automatically.

The following pages introduce some of the main groups of user interface related functions:

  • Introduction to Maps
  • Menus
  • Choosing From a List of Records
  • Data Entry and Display
  • Printing

The terminal manager also contains a group of file related functions, and numerous low level functions, that a program can use to remain independent of calls to the operating system. Most of these have names beginning with tf_() and trm_(). You will have to consult the alphabetic function reference to find out about these functions.


Introduction to Maps

Maps are similar to Windows Dialogs, or HTML forms - they generally consist mainly of map fields, which can be used to edit or display data, and headings ("static" or "text" controls) that describe the map fields. Like dialogs and forms they may also contain "buttons" of various types, and images. Some types of maps may play a multi-media sound when they are displayed to alert the user to an error condition. Each control or field in a map has an associated field number. Field numbers are very important, as they are used for several different purposes:

  • Field numbers set the "Tab order" of the fields within the dialog

  • Programs refer to fields using these same numbers. For example when a program uses a map tas a menu it will discover the option chosen from its field number, and when it wants to display information in a field it passes the field number to identify the control to use. (Programs usually use these numbers directly: there is not often much benefit in assigning symbolic names to them, as there is usually very little need to change them, and in any case a program will probably only have one or two lines of source code that need to refer to any one field.)

  • Field numbers also determine the "geometry" of a map. If a user moves out of a field using the down arrow key, DP4 does not use the physical positions of the controls to determine which field to move to, but the field numbers. The method used to number fields so that arrow keys behave as the user would expect is explained in "Cursor Movement in Maps" in the Mapedit manual.

Maps are much more general purpose than HTML forms or Windows dialogs - DP4 uses maps to display menus and popup windows (list boxes), and to format output to be written to files or printed. Maps are stored on a DP4 database, and maps for more than one language can be stored on the same database - allowing the same executable to be used for more than one language. Maps are keyed on the mapname, used to group a set of related maps together, the language code, and a map number. Programs usually refer to maps by their number within a mapset, though it is also possible to assign and use "handles" to allow multiple copies of the same map to be used simultanously. There is no built in facility to assign symbolic names to maps, though it is probably good practice to do this yourself, especially for maps that are used in several different source modules. Maps can also store context sensitive help for menus or dialogs. Maps are created using the Mapedit program. In 4.5xx it is also quite easy to create maps directly with a text editor - the best way to find out how to do this is to use MAPEDIT to print out a few maps in MAPPOST format.

C Functions

The two main functions for placing a map on screen are map_draw() and map_attach(). map_draw() tries to position maps in an empty part of the screen, usually underneath the last map. There are some rather complicated rules for how DP4 will clear or move other maps around to make room for a new map, depending on the flags passed to map_draw(), but usually you won't need to worry much about these.

You can attach one map to another with map_attach() to build up complex more displays.

To clear a map and all maps displayed after it, use map_clear_from().

map_clear_after() removes all the maps displayed after it, but not the map itself, and map_clear() (implemented as a macro based on map_change()) clears a map and its attached maps, but not other maps drawn after the map. You can also use map_change() to hide or reveal a map (amongst other things).

You can clear the whole screen with map_clear_all(), or by passing the CLEAR flag in the second parameter of map_draw(). You can use map_new_window() and map_old_window() to hide and restore all the maps on a screen. While the maps are hidden like this,

Data can be displayed in fields on the map using the show_x() functions, though usually you will only need to call this functions for "output only" fields.

Map Types

Maps are drawn on the screen are split into types, such as menu maps, data maps, remark maps, error maps, help maps and so on, several of which have their own function that can be used to display them: map_menu(), map_datamap(), and map_remark(). High level functions that display maps use one of these functions. This is intended to help you build a consistent interface, using uniformi colours and display positions: all the functions are supplied in source code form, allowing you to tailor the functions to meet your own needs, either by replacing them, or by setting the flags they use to display maps (in global variables menu_flags, data_flags, and rmk_flags

and controlling their positioning via map_new_position()


Menus

Maps used as menus of options are displayed with the map_menu(), and field_choose(), is used to select an option. map_choose() or map_g_choose() are used to display, select an option from and finally clear a static menu to be used as a popup.

Menus with options that can be enabled or disabled are supported with the analogous map_g_choose() or field_eg_choose() functions. Disabling options in a menu is usually referred to as "Greying Out", in DP4 documentation.

Use map_bar_choose() to create a menu bar with pull down menus.

You can pick an option from a menu while the cursor is in another map with either the askm_special() function or the enumerated inpm_e() or inpm_ge() functions. inpm_e() is a very useful function that may allow you to avoid creating some small static data tables on your database, and can save you from the sin of hard coding an association between characters and program options.

You can find out mode about creating and using menu maps on the following pages:

  • Creating and Using a Simple Menu
  • Greying-Out (Disabling) Menu Options
  • Menu Bars with Pull Down Menus


Choosing Options from a Menu

Creating a Simple Menu

To create a menu that a user can move around using the mouse or cursor keys, set up a map with the required number of options:

  • If you are using a 4.6xx release of Mapedit you just need to create text controls with a field number: you use field number 1 for the text corresponding to option 1, field 2 for option 2 and so on. You can set a mnemonic for each option by putting an & before the appropriate character in the option text.

  • In 4.5xx releases you can't directly assign numbers to text items like this. Instead you simply type in the text, and then add a field 1 coinciding with option 1, field 2 with option 2, and so on. In this version of MAPEDIT you set up by mnemonics by using the mnemonic option in mapedit and moving the cursor to the appropriate point.

    On 4.5xx it is also very easy to use a text editor to create menus, and to post them to the database with MAPPOST. Most maps for DP4 utilities are actually created this way! An example is shown below.

With either release it is important to set up the map properties as a popup menu with as many rows as there are options, and 1 column.

#sysdb
#mapname  DFSETUP
#optleft '['
#optright ']'
#window 2 8 1 map_flags=2051
_________________________________________|
 DP4 SYSTEM SETUP  User Preferences

 [1 Date and time input/display        ]

 [2 Number/character/menu input/display]

 [3 Colours and miscellaneous cosmetics]

 [8 Exit... (X)                        ]
________________________________________
#fields 1: select='D',
        2: select='N',
        3: select='C',
        8: select='X';
Here is one of the maps used by the DFSETUP utility in MAPPOST format

Calling map_choose()

Use the function map_choose to obtain the option from the user. For example:

if (map_choose(3, &option))
       return FALSE;
     else
       switch (option)
       {
         ...

This displays the menu, and obtains the option, whether the user selects it by navigation, number, mnemonic or by function key.

If the user selects an option, map_choose returns 0 and sets variable pointed to by the second parameter to the field number of the chosen option. If the user presses <Esc>, it returns the value ESCAPE (defined in dp4uiapi.h). The return value is also stored in the global variable abort_code.

map_choose() clears the menu from the screen after an option has been selected.


Greying-Out Menu Options

To grey-out menu options, use the function map_g_choose, for example:

if (map_g_choose(3, &option, banned))
  return FALSE;

This function is like map_choose(), except that it has a third parameter which is a pointer tonts to (an array of) short, containing the list of options to be greyed-out. This list must be zero terminated, for example:

short banned[4];
banned[0] = 2;
banned[1] = 3;
banned[2] = 4;
banned[3] = 0;

Real code to fill up the list would probably be more complicated. An alternative to filling up the list each time, is to pass a pointer to a different part of the list. For example, passing &banned[0] (== banned) to map_g_choose would disable options 2,3 and 4, but passing &banned[2] would only disable option 4.


Action-Bars and Pull-Down Menus

Menu Bars and Pull-Down Menus

Creating a menu bar with Pull-Down menus is a little bit more complicated than creating a Simple popup menu. The main reason is that this type of menu contains several maps: if the menu bar is map n and there are m options on the bar, then maps n+1 though n+m are used for the pull downs.

You should use mapedit to create this type of menu. In release 4.6xx MAPEDIT has a special editor for editing menus. You should find this fairly easy to use, and it is not described here. Unfortunately it does not allow you to create secondary pull downs (pull downs on pull down), though these are supported.

To set up a menu bar menu with pull-down menus using 4.5xx, use MAPEDIT to create a map with the options on a horizontal line. Use the Pull-Down option to attach pull-down menu maps to each option. Set the map properties for each pull down as a popup menu with as many rows as there are options, and 1 column.

You can set up mnemonics and function keys for the action-bar options, or for the options on any of the pull-down menus.

Calling map_bar_choose()

An important difference from map_choose() is that with map_bar_choose() you must display the menu bar before calling map_bar_choose. The rational for this is that the menu bar is normally left on the screen permanently, so it is inappropriate for map_bar_choose() to draw and then clear it.

Here is a typical call to map_bar_choose() and part of the following switch statement (taken from the code for QABUTIL)

    map_draw(230,MENUMAP|RETAIN);
     if (map_bar_choose(230,&action,HAS_PULL_DOWN,0,banned_list))
        action = 0x1f;
    switch (action)
    {
      case 0x11:
      case 0x12:
      case 0x13:
      case 0x14:
      case 0x15:
      case 0x16:
      case 0x17:
      case 0x18:
      case 0x19:
        export(action & 0xf,0);
        loadmainmap();
        break;
      case 0x1a:
        do_qabpost();
        loadmainmap();
        break;
      case 0x1b:
        transfer_options();
        break;

Pull-down menu options are represented by the value:

16*X + Y

Where:

X = Number of Action-Bar Field

Y = Number of Pull-Down Menu Option

It is convenenient to use hexadecimal when working with pull-down options, for example the value 0x41 represents option 1 from the pull-down menu for action-bar field 4.

You can grey-out options with an array as with map_g_choose, for example:

banned[0] = 1;
banned[1] = 0x31;
banned[2] = 0;


Data Entry and Display

The terminal manager functions that are used for data entry and display are perhaps the most difficult functions to give a brief over-view of.

To display a map to be used for data entry or displaying data, you can use map_datamap(), though you will not usually need to call this function explicitly, because you will call a higher level routines that perorm other tasks at the same time.

To display a map temporarily, (such as a warning about an entered value) use map_remark(), or map_pause() if you require the user to acknowledge the message.

To display data in fields, you can use the show_x() functions. There is one of these for each different data type. (One day we'll get round to providing an overloaded show(),askf() and inpm() to keep C++ programmers happy.)

Data entry from a field can be done with askf_x() - where only one field is being used, but usually you shoud use the inpm_x() functions to allow the user to move freely through all the data entry fields and buttons in a dialog.

Usually the best way to do data entry and display data fields is with map_get_inputs(). This is function that drastically simplifies the writing of data entry functions. It takes as one of its parameters a function pointer - a callback function you write which calls an input or display function for each field and does any necessary validation beyond the built in validations supported by the field input functions.

For very simple dialogs, that only require an acknowledgment or a yes or no answer from the user, a range of functions are available: map_yes(); map_askf_y(); sys_del_confirm(); and sys_upd_confirm().

You can find out more about various aspects of using the functions associated with data entry on the following pages:

  • Displaying and Formatting Data
  • Field Input
  • Managing a Complete Dialog with map_get_inputs()


Displaying and Formatting Data

The basic function for displaying data in a map field is called show(). To call this function you specify the map and field to display into,a pointer to the data to be displayed, the data type: for example 'C','I', or 'N', the size of the data, and flags that format the data (for example you can choose to have numbers displayed in a fixed length with leading asterisks - this might be used for printing cheques).

As it would get rather tedious to have to call this function many times in a program, you can use the show_x() functions instead. These only have three parameters - the map and field number, and a third parameter which is either a pointer to data, or sometimes a data value. These functions don't have a parameter to format the data.

However, you can set the format using the global variable show_flags for all the show_x() functions except show_d(). You use the global variable showd_flags for the format of dates that you display with show_d().

The values that these global variables may take are documented with the show_x() function. The global variables show_flags and showd_flags are initially set to DEFAULT. It is usually a good idea to set the SKIP_MISSING_FIELD flag which means that DP4 will not generate a DP4 system error if the field cannot be found in the map. This means that a field that is no longer required can be removed from a program without the need for recompilation. show_flags and showd_flags may be set to any combination of the permitted values, for example:

show_flags = LEFT_JUSTIFY + BLANK_IF_ZERO +SKIP_MISSING_FIELD;

On the whole it is best not to specify format flags in a program, It is better to specify the format flags in the map instead. If a map does specify format flags these over-ride the ones in the program. One exception is that using LEFT_JUSTIFY is probably a good idea if you want to line up a lot of output data without having to set up an explicit format for every field in a map, and there is no point lining up numbers on the (possibly implied) decimal point.There are some aspects of formatting that can only be handled in a map. For example you can associate a "unit" with numeric fields. This is most often used to display a currency symbol, but you could also use it to display abbreviations for distances or weights.

The field input functions (askf_x() and inpm_x()) do not use show_flags or showd_flags. Instead they have their own global variables, scrn_flags and scrnd_flags. These take all the same flags as show_flags and showd_flags, and they take some additional flags detailing how the program should respond to certain special keys, as it described innext.

Displaying Text

Sometimes you may want to display some text in a program that is not database data. For example you might need to display the words "Credit" or "Debit" next to a money amount. If you want your application to be able to work in any language you must avoid hard-coding such words into your program. DP4 has a function show_attach() which can be used to display text from one map into a field on another map. Another common technique for handling this situation is to take the text from small "static data" tables on the database. On the SALESORD database the REGION and PAYMENT_MEDIA tables fit into this category. A disadvantage of storing descriptive text on the database is that it will cause problems if the system is to be run in multi-lingual countries such as Belgium or Switzerland.

Displaying Composite Fields

It is quite common for a system to need fields which are stored as several fields on the database, but which should look like a single field to the user. A similar situation is where a field is stored as something like AAABBBBCCC, but should be displayed as AAA-BBBB-CCC or similar. Part numbers might be a typical example of either type of field. DP4 does not yet support such fields as well as it might, but one way to deal with them is to write your own show function that uses map_attach() to attach a small map with the several fields needed to display the composite field, then displays the fields into the attached map, and finally "glues" the attached map onto the attached field. This type of processing is also common when displaying popup windows using the pick_record() function, to be discussed later.

In 4.6xx an alternate technique is to use "embedded data" which allows you to insert data into part of a text control.


Field Input

DP4 has two main families of functions for getting input from the user for a specific field. Both sets of functions use and/or set, a considerable number of global variables. There are functions spcific to each data type supported by the DP4 database. The functions ensure that the input value is valid and, when the input is associated to a field on the database,within the range of values allowed for the field. They can also simplify things like resetting the values in a dialog to the initial "default" values, providing pop up lists for a user to select from, and returning focus to the appropriate point if the user inputs a value that additional validation in your code disallows.

  • The askf_x() functions are used where the user is not allowed to navigate away from the current field. In practice this means they are rarely used.

  • The inpm_x() functions are used for collecting input in a dialog where the user can roam around the fields with the cursor keys or by clicking with a mouse.

Global Variables

df_libtype

A variable called df_libtype determines certain global behaviours of the DP4 Terminal manager. It is something that should rarely if ever be touched (it can be set by a libmake directive). Basically it is a set of bit flags. Very occasionally, when we think "Wouldn't it be nice if _______ (insert name of function) worked like this,instead of the way we first thought of?". If the answer is yes we use up one of the bits in df_libtype, to enable the new behaviour without upsetting existing programs.

abort_code

Both families of field input functions store information about the key used to navigate away from the field in the abort_code variable. This is usually, but not always, also the return value from input functions. The return value from input functions should be used in the following way:

  • If an input function returns a non zero value, then the user is navigating away from the field in a way that means any validations in your program for the field should not be performed at this point.
  • If the input function returns 0 then the user is navigating away from the field normally, and your program should perform any necessary validations on the field. If the input is invalid you should display a message, and request input in that fields again.

Skipping Input with jump

The input functions do not necessarily stop and ask for input. If the user presses an OK button or the Enter key to complete a dialog, input into remaining fields in the dialog is skipped. This is done by setting the variable jump to TRUE. The function is still called, in case the data is invalid. If it is the user will not be allowed to close the dialog, and will have to correct the offending value(s). jump is a very important variable, as you have to remember to set it to FALSE if a validation in your code fails and you reset the field number. Failure to set jump FALSE in this situation is a frequent cause of "jump loops", which result in a hung program.

Default Values - clearfirst

Two more global variables control what input functions do with the initial value passed to them. Usually you will want to display this as the initial value to the user. However you may want to reset the field to the "default" value. This is often, but by no means always, 0,blank etc. Dates and times are often defaulted to the system clock for example.

A field will only be reset to the default value if the corresponding field in the map has the clearfirst property set, and the global variable clearfirst is set to a non zero value (usually 2). In addition the variable range must indicate that this is the first time the field is being visited. Usually range is initialised to 1 at the start of a dialog and DP4 looks after it from then on. It is usual to set clearfirst to 2 when a dialog is being used to insert a new record, and to 0 if it the same dialog is used to amend an existing record.

Detecting Change

Two more variables changed and selected tell you something about what a user has done to a field. If you set changed FALSE before calling an input function, and it comes back TRUE then the user changed the value of the field from the initial (but possibly clearfirsted) value presented to him. selected is very obscure, but it is used inside some of the functions supplied as source code, as it can tell you whether certain abort_code values were generated as a result of a function key being pressed, or through some other method. changed is often more trouble than its worth, and certainly should not be relied on for more than one call to an input function (a user might change a value and then change it back again leaving changed TRUE when nothing is different from the initial state).

Format and responding to Special Keys

Finally a variable called scrn_flags can be used to control the display format of input fields, and it also determines what special keys fields respond to. Date fields use scrnd_flags instead. Responding to special keys is discussed in more detail next.


Responding to Special Keys

The data entry functions (inpm_x(), and askf_x()) always return when the user moves away from the field. The return value usually indicates how this was done by returning values such as LEFT, RIGHT and so on, defined in the file dp4uiapi.h. (These particular values would only be returned by the inpm_x() family, because the askf_x() functions don't allow the user to move away from the current field like this.)

By default only the keys used by DP4 for moving around fields cause inpm_x() to lose focus and return. Frequently applications work by allowing users to enter a value and then press a function or other special key. It is common in retail systems for keyboards to have one hundred or more such special keys. These are normally mapped (using the keyboard configuration option in DFSETUP) to generate function keys such as F170 F97 and son on.

There are two ways to enable your input fields to respond to special keys like this:

  • You can set the FUNC_KEYS flag in the variable scrn_flags or scrnd_flags as appropriate.
  • You can set the flag on a field by field basis using MAPEDIT.
The first technique is more appropriate if you want all input fields to respond to function keys, the second if you only want to respond to function keys at specific points in the application. Note that your application will be notified of all function keys when this flag is set (the return value from inpm_x() will be a positive number corresponding to the function key, for example pressing function key 97 will cause inpm_x() or askf_x() to return 97, and set abort_code to the same value). If the user presses a function key that has no meaning you can either display an error message or beep, or simply call the interrupted input function again.

If you want to take special action when one of the following four special keys is pressed : <Page Up>,<Page Down>, <Insert>, <Delete>, then you must set the CURS_KEYS flag instead of or as well as the FUNC_KEYS flag. The CURS_KEYS flag is used by functions like pick_record() to allow the user to move to another "page" of a pop-up window or list box.


Prompt- Choosing Values From a List

Users expect not to have to manually key in all the data entry fields in a dialog, but to be able to bring up a list of the possible choices for each field in a pop-up window (opening the list box part of a combo box in Windowsese). The pick_record() and pick_list() functions are desgined to help you provide this kind of facility in your own programs. A range of records can be scanned through by the user, where only a few record details are displayed at one time. In the DP4 API, this type of pop up window is usually called Prompt.

In fact pick_record() and pick_list() are not just useful inside dialogs. It is possible to combine the windows used by these functions with a menu bar, so that a whole prorgam is controlled from a call to pick_record() or pick_list(). An example of this is the BROWSER utility.

pick_record() solves the problem of using a list box with a big database. Windows list boxes are usually populated with every single possible option. On a big database this can use a prohibitive amount of time and memory. pick_record() only reads enough records to populate a listbox a page at a time. Both pick_record() and pick_list() can also dynamically update other information on screen as the user moves round the list of choices.

These functions have a lot of parameters, and can be rather intimidating for a novice user of DP4. They are well worth persevering with however. Both are provided in source code form, so you can adapt them if required.

The map_glue() function usually comes in handy when creating the callback functions needed by pick_record() and pick_list().

Visual Hint that Prompt is Available

In 4.6xx You can inform the user that prompt is available for a field by setting (with MAPEDIT) the prompt flag for the edit control used for entering the field . This causes the control to appear with a prompt button next to it. (This is a button with a downward pointing arrow on it). Clicking the mouse on the button while the program is calling the fields input function will cause the input function to return. The input function returns 0 and the field number is set to the next input field, but the value in abort_code is set to SPACE_BAR (you may like to add your own definition: #define PROMPT_REQUESTED SPACE_BAR). You arrange for your validation code to test for this value and brong up the prompt window when it is detected. If the use selects a record form the list, then you set the input field to the appropriate value and resume processing at the next input field. If the user cancels the window then you should reset the field number and ask for input again.

In 4.5xx you can provide a similar arrow next to an input field to indicate prompt is available by pressing Alt+P with the caret in the appropriate position when in the text entry mode of the DP4 MAPEDIT program. However you have to also set the flag PROMPT_AVAILABLE in scrn_flags or scrnd_flags.

Users can also request prompt by pressing a key that has been programmed as the "prompt key" using the keyboard customisation option of DFSETUP. In the default configuration pressing <Ctrl+P> generates this key.

If the PROMPT_AVAILABLE flag is set in scrn_flags, but you haven't code prompt for the field, pressing <Ctrl+P>, or clicking the Prompt button, will move to the next field. This is confusing for the user. This means that if some fields have prompt, and others don't you could be faced with having to constantly change the value of scrn_flags. To avoid this the inpm_x() functions actually have two variants: inpm_x() is the normal version, used for fields with prompt (or without prompt if scrn_flags does not have PROMPT_AVAILABLE set), and inpm_x_np(), which are used for fields without prompt in programs where scrn_flags has PROMPT_AVAILABLE set.


Managing a Dialog with map_get_inputs()

Using map_get_inputs() helps ensure your programs' dialogs work with a consistent user interface. It also reduces the amount of clutter in your code.

Before discussing map_get_inputs() it will help if you understand more about how dialogs work in DP4:

  • In DP4 programs validation of entry fields is usually performed as soon as the user moves away from the field in a forward direction. We think it is less annoying for users to do validation like this rather than validating all the fields at the end, (so that the user does not find out about mistakes until they think they hoped they had finished), as is common with other ADEs that manage dialogs.

  • A dialog always processes all its fields in order.Programs can rely on fields entered earlier in the dialog being correct. If a user uses the mouse or a cursor key to move away from the current field in a forward direction, missing out several intervening fields, your program will still get a chance to validate all the intervening fields. Occasionally it may mean the user does not end up where he expects, but usually he will, and the logic of your program is much simpler than it would be if you had to write a separate validation routine.

  • Internally DP4 manipulates variables like jump, input_field, and (possibly) clearfirst, and remembers the history of the dialog. For example uses the cursor end key to move to the last field on a row, it might turn out that the last field was disabled, in which case DP4 will set the field number to point to the previous field on the row. On the other hand if the user then tabs forward from that field, hitting the disabled last field again, DP4 will then decide to move to the first field on the next row. How DP4 determines the field a cursor key should move to is explained in Cursor Movement in Maps and how navigation proceeds round the fields of a map is explained in detail in Field Navigation During Data Entry. You don't need to fully understand the latter. A good principle to remember is that to you the programmer, it will usually look as though the user is moving round the dialog in a sensible way, but to the user it will look as though he can, within reason, move round the dialog in any way he chooses.

Overview of map_get_inputs()

map_get_inputs() is usually responsible for displaying the map a dialog will use, initialising various global variables ready for the dialog and then calls your callback function to process each field in turn.

Your callback function may be used in two distinct phases:

  • An initial (optional) predisplay phase displays the initial values for each field in the dialog map.

  • A second "real" pass through the fields is used to collect input from the user.

When the user completes or cancels the dialog map_get_inputs() will return, after performing various (optional) cleaning up operations, such as clearing the dialog map and restoring global variables.

An example of using map_get_inputs() together with pick_record() can be found next.


An Example Dialog in DP4

This example is based on the code of QABUTIL. It shows part of the code used to implement the "Options" dialog in the Transfer pull-down menu. The code has been slightly modified to show the actual map numbers used. All the map numbers refer to maps in the PROGMAKE mapset on the SYSTEM database. This is quite a nice example as it illustrates most of the key aspects of using map_get_inputs() and pick_record():

  • Setting jump FALSE and resetting the field number after a validation failure

  • How to deal with fields that are sometimes omitted, depending on previous input

  • How to use the PROMPT facility, including a nice reuse of a fetch callback function to do validation.

This code is called with the global variables set as follows:

show_flags = SKIP_MISSING_FIELD|LEFT_JUSTIFY;
scrn_flags = SKIP_MISSING_FIELD|LEFT_JUSTIFY|PROMPT_AVAILABLE;
df_libtype = 5;

#define MAX_HISTORY 6
extern union WK
{

DP4 utilities often declare a single wk variable and put all the save areas need for calls to pick_record() there. It can save a lot of space. Of course it would probably be a better idea to just use a temporary area, but at the time pick_record() was originally written putting this much data on the stack would have been a very bad idea, and using a heap was not an option.

  struct SAVE_HISTORY
  {
    short pm_number;
    char pm_name[sizeof(pm_name_table.pm_name)];
    datetype date;
    char headtext[sizeof(db_history.headtext)];
  } save_history[MAX_HISTORY+1];
} wk;

void transfer_options(void);
  static void option_inner(int m,int *f);
    static int pick_history(void);
      boolean fetch_history(int direction);
      static void show_history(int mapnr,int fieldnr);

void transfer_options()
{
  struct OPTION save_option;

It is good practice to ensure that a dialog that is cancelled does not change any values, so we save the variables the dialog changes first. Althoughh not needed here the saved structure can also be used for testing if anything has been changed - this is much safer than relying on the changed variable.

  option.quiet = quiet;
  option.doing_maps = doing_maps;
  save_option = option;
  if (map_get_inputs(340,1,option_inner,PREDISPLAY|CLEAR|GI_GLOBALS)!=0)
  {

If the dialog is cancelled restore the saved value of options

    option = save_option;
  }
  quiet = option.quiet;
  doing_maps = option.doing_maps;
}
/**/

static void option_inner(int m,int *f)
{
  int fieldnr = *f;

  switch (fieldnr)
  {
    case 1:
      inpm_e(&option.option,341);
      break;

    case 2:
      if (option.option != 2)
      {

Dialogs often contain fields that are not always relevant.If a particular field is not relevant or is not used for input you should miss it out by calling inpm_omit() (to just skip it) or inpm_ban() (to skip it and provide a visual hint that it is unavailable). Never just set the field number to the next field, or return without doing anything: if you do your program will hang.

        inpm_ban();
        break;
      }

      if (inpm_c(option.pm_name))
        break;

This field has validation. Break if inpm_x() indicates we should not do it.

      MOVE(pm_name_table.pm_name,option.pm_name);
      pm_name_table.wordtype = VERSIONNAME;

If the user entered an invalid value or requested prompt allow him to choose from a list. Calling our a pick_record()'s fetch callback function is a nice way of validating the input, and makes sure a following call to the pick_record() is properly initalised. We can't pass EQUAL, because that has a special meaning to the fetch function. But we can pass EQUAL|DEPTH or EQUAL|BUT.

      if ((!fetch_history(EQUAL|DEPTH) || abort_code) && pick_history())
      {

If we get here the user was offered prompt, but cancelled without making a choice. So we need to reset the field number in this case, and must set jump FALSE

        jump = FALSE;
        *f = fieldnr;
        break;
      }
      MOVE(option.pm_name,pm_name_table.pm_name);
      showc(m,fieldnr,option.pm_name);

The value displayed in field 2 needs to be updated because the user may have chosen the value from prompt. Even if he entered a value, it may need to be changed because the name uses type S, for which case and whitespace are not significant.

      option.timestamp = db_history.timestamp;
      option.export_era = pm_name_table.pm_number;
      break;
    case 3:
      if (option.option != 3)
      {
        inpm_ban();
        break;
      }

      if (old_dbnr > 0 && old_dbnr != main_dbnr)
      {
        db_nr = old_dbnr;
        db_close();
        db_nr = main_dbnr;
        old_dbnr = 0;

Some nasty manipulation needed to make sure we don't unintentionally leave databases open, or accidentally close the main database or the system database.

      }
      if (inpm_unp(option.dbname))
        break;

      /* strip any trailing spaces off dbname */
      {
        register int i;
        for (i = 0;option.dbname[i]!=' ' && option.dbname[i];i++)
          ;
        option.dbname[i] = 0;
      }
      old_dbnr = db_open(option.dbname,0,READ_ONLY+MISSING_OK);
      db_nr = main_dbnr;
      if (old_dbnr < 0)
      {

User specified an invalid database, so we complain and reset the field number.

        jump = FALSE;
        map_draw(1,SYSTEMMAP|IN_BOX|REMARK|ERRORMAP);
        *f = fieldnr;
        break;
      }
      break;
    case 4:
      inpm_y(&option.doing_maps);
      break;
    case 5:
      inpm_y(&option.compact_format);
      break;
    case 6:
      inpm_y(&option.no_macros);
      break;
    case 7:
      inpm_y(&option.quiet);
      break;
    case 8:
      inpm_y(&option.controlled);
      break;

A few easy fields there! field 9 is similar to field 2

    case 9:
      if (!option.controlled)
      {
        inpm_ban();
        break;
      }

      if (inpm_c(option.post_pm_name))
        break;
      MOVE(pm_name_table.pm_name,option.post_pm_name);
      pm_name_table.wordtype = VERSIONNAME;
      if ((!fetch_history(EQUAL|DEPTH) || abort_code) && pick_history())
      {
        jump = FALSE;
        *f = fieldnr;

Actually the real code of QABUTIL set the field number to 8 here, and used 8 in the call to show_c() below - it did not use the fieldnr variable, so this field did not work properly!.

This bug was probably introduced when a new field was added to the map at some point. Using the saved value of the initial field number helps protect against this kind of bug, as can be seen here.

        break;
      }
      MOVE(option.post_pm_name,pm_name_table.pm_name);
      show_c(m,fieldnr,option.post_pm_name);
      option.control_stamp = db_history.timestamp;
      option.post_era = pm_name_table.pm_number;
      break;
    case 10:
      inpm_y(&option.ignore_errors);
      break;
    case 11:
      inpm_y(&option.test_only);
      break;
    default:
      inpm_omit();
      break;

Calling inpm_omit() in default: is very important in your data_inner() callback function:

  • Even if you explicitly process all the fields in the map, having the case will keep your program working if another field is added to the map later on. Without it your program will hang if control reaches a field number that is neither input nor omitted.

  • impm_omit() is used to process fields that do not require explicit processing, notably buttons. Although your data_inner() function will not often be called when the current field number belongs to a button, it is certainly not impossible.

  }
}

/**/

static int pick_history()
{
  return pick_record(492,0,0,fetch_history,show_history,wk.save_history,
                   sizeof(wk.save_history[0]),MAX_HISTORY,DEFAULT);
}

/**/

boolean fetch_history(direction)
{
  if (direction == EQUAL)
  {

pick_record() passes the direction==EQUAL flag to indicate that we need to reset our position in the table. That is why the calls to fetch_history() in the options_inner() function pass EQUAL|DEPTH - they don't want to end up here. Resetting involves getting the key out our save structure and calling rec_fetch_main(EQUAL) regardless of what index we are fetching on. If we wanted to improve performance we could use the bf_() (buffered fetch) functions.

    db_history.internal.pm_number = wk.save_history[0].pm_number;
    rec_fetch_main(EQUAL,&db_history.l);
    pm_name(VERSIONNAME,db_history.internal.pm_number);
  }
  else
  {
    while (rec_fetch_main(direction|DEPTH,&pm_name_table.l,1))
    {

We are putting a filter on the data here. The manipulation of direction is typical. We have to test against EQUAL because we want fetch_history(EQUAL|DEPTH) to return FALSE when there is no match.

      db_history.internal.pm_number = pm_name_table.pm_number;
      if (rec_fetch_main(EQUAL,&db_history.l) || direction & EQUAL)
        break;
      if (direction & FIRST)
        direction = NEXT;
      if (direction & LAST)
        direction = PREV;
    }

    if (there)
    {
      MOVE(wk.save_history[0].pm_name,pm_name_table.pm_name);
      MOVE(wk.save_history[0].headtext,db_history.headtext);
      wk.save_history[0].pm_number = db_history.internal.pm_number;
      wk.save_history[0].date = db_history.date;
    }
  }
  return there;
}

/**/

static void show_history(int mapnr,int fieldnr)
{
  if (fieldnr == 1)
    show_over(mapnr,101,496);

A not quite typical pick_record() show callback. The callback is ensuring some non list item related data is also displayed in the popup. This could probably have been done better another way.

  map_attach(mapnr,fieldnr,494,DEFAULT);
  show_c(494,1,wk.save_history[fieldnr].pm_name);
  show_h(494,2,wk.save_history[fieldnr].pm_number);
  show_d(494,3,&wk.save_history[fieldnr].date);
  show_c(494,4,wk.save_history[fieldnr].headtext);
  map_glue(mapnr,494);
}


Printing

In DP4 the APIS for printing information are used for far more than just printing to a printer: they are often used for creating files - for example the DP4 ReportWriter uses the printing APIS to create "export files" used to send data to mainframes or other remote systems. DP4 OSDIs, used to control special hardware such as POS (Point of Service) devices like cash drawers,customer displays and receipt printers are controlled using the DP4 print APIs as well.

The DP4 printing APIs are generally similar to the APIs used to display information on screen, and use maps created in exactly the same way, using MAPEDIT or MAPPOST. Maps are often called forms in the context of printing.

To use the DP4 printing APIs a program has to open an output device (with dvc_choose() or dvc_open()), which establishes one or more destinations for your output and creates a buffer for formatting the output. Then the program formats data in the print using the print APIs (form() to output a map, and prn_x() to print fields in the form), and periodically outputs the buffer to the output device(s) with the prn_print() function. Finally the program releases the print device with dvc_close().

More information on each stage of printing is contained in the following topics:

  • Selecting a Print Device
  • Sending Data to the Print Buffer
  • Output from the Print Buffer
  • Releasing the Print Device


Selecting a Print Device

Before you can print any information from a DP4 C program, you must open a print device, using either the dvc_choose() (if you want to allow the user to select the output device, or specify a filename) or dvc_open(), if you want to hard code the device to be opened, or you are going to manage device selection yourself. The opened device can have three distinct destinations: file,"printer" (the actual device, which may not actually be a printer), and screen.

A program can only have one set of print devices open at a time. However if you are printing to a device controlled by an OSDI you can use the dvc_set_subdevice() function to switch between the different component devices it controls. For example you might switch between a journal and a receipt printer on a POS device. There are also various other functions used only with OSDI devices. These are covered in more detail in the OSDI Developers Guide here.

Here is a typical call to dvc_choose():

dvc_choose(SCREEN + DP4_FILE + FT_DEFAULT, 1, "PRINT", ".OUT");

The first parameter specifies the output devices. In this case, SCREEN and DP4_FILE are used to direct output by default to the screen and to a file, and not in fact to the printer.

The value FT_DEFAULT allows you to specify (as a fourth parameter) a default file extension to be used if the user specifies a filename without an extension. If FT_DEFAULT is not used the default file extension is .PRN

The second parameter is the number of the printer that you wish to use. Device 1 is a pre-defined device, "DP4", which can be used for creating files readable by the DP4 utilities DP4ED and VIEW. Usually you would not hard code a number here directly, but would use a symbolic constant such as MY_PRINTER. The DP4 utility DFSETUP has an option to create an include file with pre-processor definitions based on the devices on the system database. You also use DFSETUP to set up new printers and devices, if necessary.

We recommend that programs doing general purpose printing should print to the DEFAULT device, and that at installation time you arrange for this device to use a suitable print device. If you are using DP4 version 4.6xx on a on a Windows based computer you may choose to configure (via DFSETUP) the pre-defined DEFAULT_PRINTER device to use DFPRINT. This can then print to any printer supported by the Windows OS wiith full support for fonts,color and bitmaps. One drawback of doing this is that files created using this device are not readable by other software, so you may prefer to print to DFPRINT when doing real printing, and some other device when printing to a file. You can also use the -device command tail to specify the DP4 print device on the command line.

The third parameter is the default value for the filename (without extension). The filename can be changed by the user through the dialog dvc_choose() creates. If this filename exists, a second screen is displayed that allows the user to delete the file, append output to it, or to cancel output to the file and return to the device selection dialog.

If dvc_choose() succeeds one of two values is returned:

  • The value 1 is returned if a real printer is opened, or the call to dvc_choose() did not specify the PRINTER flag.

  • The value 0 is returned if the program specified the PRINTER flag and printing will be via a spooler, such as the DFPRINT.

If the function fails, because the user cancelled the dialog, or because a file could not be opened etc, then the value ESCAPE is returned.

It should be noted that at present a DP4 program can only have one set of print devices open at a time. If a program calls dvc_choose() or dvc_open() a second time any existing print device is released.


Sending Data to the Print Buffer

When dvc_choose() has set up the print buffer, you are ready to send information to it. You do this by using the functions form(), which is analogous to map_draw() and perhaps form_beside(), and form_over(). (There is no equiavlent of map_attach() currently).

Data is printed into the form using the prn_x() functions, which work exactly like the show_x() functions do for screen based maps, and use show_flags and showd_flags in the same way. Versions of the functions without an underscore, prnx() also exist. These have an additional parameter that allows field by field specification of format flags. The latter functions are only really retained for compatibility with very old versions of DP4 - normally you won't specify formatting information within the program, but directly in the map.

Programs that print need to manage their own pagination. Your program should monitor the number of lines printed so far by testing the linenr variable. When this is close to the maximum number of lines allowed on a page, as given in the page_depth variable, your program will call prn_print() to print the current contents of the print buffer as described next. Normally you will allow a few blank lines. Programs often print a page header and footer on each page. There are no special routines for this, but you can discover the amount of room your maps take up by calling the map_get_depth() function.

Programs sometimes print to pre-printed stationery, though this is not very common nowadays. If you do need to do this, the form_test() function may be useful to allow the user to line the paper up properly.


Output from the Print Stream

When you have sent some data to the print stream, you can use the DP4 function prn_print to print it out on the printer. It is also displayed on the screen or written to the output file if these output devices have been selected. (When printing to DFPRINT the output is not displayed immediately - instead a progress indicator is displayed, and the DFPRINT program is invoked to view or print the output when printing is completed.)

prn_print(FALSE);

This function has only one parameter that is set to TRUE to send a form feed to the printer after the contents of the print buffer have been printed, so as to start a new page. It is set to FALSE if a form feed is not required.

If you are printing to a device supported by an OSDI you should call prn_print() before selecting a different subdevice, as otherwise you may unintentionally send buffered print output to the wrong subdevice of the OSDI.

It is also possible to discard the contents of the print buffer with the prn_abort() function.


Releasing the Printer

When you have completed all the printing that you wish to do, call the DP4 function dvc_close() to release the printer for use by other programs.

dvc_close(TRUE);

The only parameter to dvc_close specifies whether or not to send a form feed to the printer before dvc_close releases it. Use the value TRUE for a form feed, or FALSE for no form feed.


Extracting and Sorting Data

You may sometimes need to create a report that cannot efficiently be created using the indexes built into the database. For example you might need to produce a report sorted by some derived value not present on the database.

Assuming you do not decide to add the information and a suitable index to the database, you will be faced with having to sort the data. DP4 makes this easy for you with the concept of extract files.

  • To define the format of the data for an extract file, use the #extract LIBMAKE directive.

  • To specify how the extract file is to be sorted, use the #sorton LIBMAKE, directive.

Both of these are described in Extracting and Sorting Records in the LIBMAKE documentation.

A program that uses extract files usually follows this pattern:

  1. After initialising the program trawls through the database calling the function extract_write() to write unsorted entries to the extract file

  2. When all the necessary data has been read the extract file is sorted by calling extract_sort(). Depending on the size of the file and the degree of order already present this may take just a second or two, or perhaps many minutes or even hours if the file is extremely large. However, so far as we know, the sort algorithm we use would be very hard to improve upon.

  3. Once the sort has completed your program reads the now sorted extract file by calling extract_read() repeatedly, and uses the information to create the required report.

The extract file routines are supplied in source code form (in ccextrct.c), so you can create a customised version of them if required. The source code of the sort program is not distributed.


Memory Management and File Handling

The DP4 library contains a number of memory management functions that can give improved performance. They fall into three categories which are described in this Section:

  • Heap Management Functions

    The heap management functions implement a 'cheap', efficient heap. Except on legacy platforms there is little reason to use these functions directly, although the cache and buffered fetch functions use them internally- the ANSI heap routines will work just as well or better.

  • Caching Functions

    The caching functions allow frequently used records to be retrieved from memory rather than from the database server

  • Buffered Fetch Functions

    The buffered fetch functions read multiple records when accessing access to the database server. Because these functions markedly reduce the number of application calls to the DBMS, they are well suited to achieve performance gains on networked systems. In fact they can considerably improve performance even when the database is accessed locally, partly by reducing the amount of IPC between the application and the DBMS, and partly because the DBMS can optimise certain operations when it knows it has to fetch multiple records at the same time.

File handling functions

DP4 also has two families of functions for reading and writing files. They differ only in whether the file in question is accessed via the database manager , hence possibly on a remote machine, (the sf_() familiy of functions) or locally via the terminal manager, the (tf_() familiy). These functions use DP4's own file caching mechanism in those programs, which can improve performance (though this is less of a consideration on modern operating systems.)

Traditionally several of these functions used or returned quantities that were of type long, and then unsigned long, which meant they did not work with very large files or disks. As of release 4.622 both 32 bit and 64 bit versions of all these functions are available. In this manual such parameters or return types are all described as file_pointer. You can enable the 64 bit version of the functions by including the following #define in your C code becore including dp4capi.h:

#define USE_DP5_FILEHANDLING 1


Heap Management Functions

The DP4 C library contains a set of functions for heap management. These functions can be used in place of the standard C library functions malloc() and free(). Unless you define the symbol NO_ANSI_POLLUTE dp4capi.h also contains #define that map malloc() and free() to the replacement DP4 functions mallocp() and freep(). The heap functions were originally added to the DP4 library to enable programs using malloc() and free() to be linked with the DP4 module that enabled object code portability. They have been retained, because it is not unknown for the official implementation of the functions to have bugs, and because they are usually smaller than the CRT implementation. Windows programmers should note that, like most other functions in the DP4 C library, they are not thread safe.

The heap management functions are provided as source code, which is memory-model and operating-system independent.The functions are grouped together by module:

  • CCNHEAP1.C and CCNHEAP2.C implement the core heap functions and the replacements for the standard C library functions malloc() and free()
  • CCNHEAP3.C implements memory reallocation
  • CCNHEAP4.C provides functions allowing an existing heap to be completely replaced with a different temporarty one (this is really just for QAB)
  • CCHEAPI.C provides code for initialising the heap. You should refer to the documentation of the heap_init() function to find out how this is done, before using the DP4 heap in your own programs.

In addition, the Least Recently Used (LRU) modules, CCLRU.C, CCLRU1.C and CCLRU2.C provide functions for managing doubly linked lists, used by the cache() functions to discard cached data on a least recently used basis. These functions are defined in LRU.H and not dp4capi.h. This code is used, for example, in the implementation of the caching functions. There are functions for:

  • Incorporating objects into the linked list
  • Creating an item that can be put in the list
  • Locking (prevent discarding) of an item in the list
  • Unlocking with promotion/demotion of an item in the list

It is your responsibility to intialise the heap. You can write a function to handle memory exhaustion, or use the lru_handler from the LRU modules. There is no internal consistency checking - assigning to discarded pointers and other program errors will cause unpredictable results. If you experience problems, you might consider debugging the program using the heap routines supplied with your compiler, or recompiling the supplied source code with DEBUG defined. DP4 programs initially have no heap, but it can be acquired in one of three ways:

  • You can call heap_init().
  • You can declare an array which is not used and call freex(array,sizeof(array))
  • You can allocate memory from the operating system or use compiler-supplied memory allocation and call freex().


Caching Functions

Often database tables will be accessed randomly using rec_fetch_main(EQUAL) or the equivalent call to rec_fetch(). If the data in a table is essentially read-only as far as a particular program is concerned and you expect records to be accessed frequently, a program can use the cache functions to reduce the number of calls made to the DBMS. This is done as follows:

  • Identify a table to be cached
  • Call cache_describe() at the beginning of the program, to set the key size. The key size is usually the total size of the primary key

Note

Records cannot be cached directly in either of these circumstances:

  • The table has non-key fields embedded within key fields
  • Record access is through a secondary index

Replace calls to:

rec_fetch_main(EQUAL,&table.l,0)

with:

if (!cache_find(&table.l) && rec_fetch_main(EQUAL, &table.l,0))
  cache_post(&table.l);
  • Operational Restrictions
  • Operational Refinements


Cache Function Limitations

In order to make proper use of the caching functions, keep in mind the following limitations:

  • The timestamp field of cached records is not preserved, nor the generation and flags fields. Therefore, data records retrieved from a cache cannot in general be posted to the database.

  • The database number is not included in the cache - so records with the same TYPENR from different databases cannot be cached together.

  • Only use the cache to store data that can be recreated (or fetched from the database easily) because cached data may be discarded without warning.

  • Take care when caching key fields of type 'S' and 'U'. Such fields should always be converted to a fixed format before adding to, or looking in, the cache. To illustrate this restriction, type 'S' data that is cached with a key of 'MYNAME' is not found when searching on the key 'MY_NAME'.


Cache Function Refinements

The cache functions are independent of the DBMS. That is, you can create your own tables and cache them. Two useful techniques are:

  • To truncate the data being cached to store only the fields in which you are interested in the cache. This allows more records to be cached in the same amount of memory.

  • To create tables that have changed keys from on the database. For example, you may wish to access a table by name whose key is a number. In this case, to allow it to be cached, you would declare a table where the name was the first field. If, for instance, you have a database in which orders are linked to customer records through a customer number, to display the customer name, you would replace:

     customer.cust_nr = order.cust_nr;
 rec_fetch_main(EQUAL,&customer.l);

    with:

    customer.cust_nr = order.cust_nr;
if (!cache_find(&customer.l) && rec_fetch_main(EQUAL, &customer.l))
  cache_post(&customer.l);

    Depending on the amount of heap available and the ratio of orders to customers, this can greatly reduce the number of DBMS accesses. This kind of code is, for example, used by PROGCOMP to reduce the number of calls to the database manage when looking up names within the formula compiler.


Buffered Fetch Functions

DP4 provides block fetch functions which allow multiple records to be read from the database at a time. This gives improved performance over the normal scheme where records are requested from the DBMS one at a time. However, the block fetch functions are inconvenient to use, particularly in interactive routines like pick_record().

The performance advantage of using block functions is most significant over a network. DP4 network programs used to attempt to optimise searches automatically - but this was of limited benefit because in many situations a buffer must be discarded in case the buffered record is no longer correct.

The buffered fetch family of functions provide a convenient encapsulation of the block fetch functions, allowing you to write code which is almost as straight forward as using regular fetch functions, but with all the performance advantage of block fetches. All the buffered fetch functions are prefixed by bf_. To use them you must open a buffer with bf_open(), read records from it with bf_fetch(), and finally close it with bf_close(). Interactive programs may need to call bf_reset() to change their position within the buffer from time to time.

In application programs, you should replace code like this:

rec_fetch_main(FIRST,&customer.l);
while (there)
{
   rec_fetch_main(NEXT,&customer.l);
}

with:

buff_customer = bf_open(&customer.l,0,0,BF_SEQUENTIAL);
bf_fetch(buff_customer,FIRST,&customer.l,0,0);
while (there)
{
   bf_fetch(buff_customer,NEXT,&customer.l,0,0);
}
bf_close(buff_customer);

In a pick_record() situation you would replace:

boolean fetch_customer(direction)
{
   if (direction == EQUAL)
   {
      customer.cust_nr = wk.save_cust[0].cust_nr;
      return rec_fetch_main(EQUAL,&customer.l);
   }
   if (rec_fetch(direction,&customer.l,_INDEX_NAME,0))
   {
      wk.save_cust[0].cust_nr = customer.cust_nr;
      MOVE(wk.save_cust[0].name,customer.name);
   }
   return there;
}

with:

boolean fetch_customer(direction)
{
   if (direction == EQUAL)
   {
      customer.cust_nr = wk.save_cust[0].cust_nr;
      return bf_reset(buff_customer,&customer.l);
   }
   if (bf_fetch(buff_customer,direction,&customer.l,_INDEX_NAME,0))
   {
      wk.save_cust[0].cust_nr = customer.cust_nr;
      MOVE(wk.save_cust[0].name,customer.name);
   }
   return there;
}

In this case, you open the buffer before the call to pick_record() and close it afterwards. The call to bf_open() looks like this:

buff_customer= bf_open(&customer.l,sizeof(customer.cust_nr),0,
    BF_INTERACTIVE);

In interactive programs where the user may be be paging up and down a window of records, the bf_ functions need to be able to change their position within a buffer. This means that bf_open() has to know where the key is in a record.


DP4DBAPI functions

This page documents the functions and macros that are implemented in the thread safe DP4 database API, contained in DP4DBAPI.H. The functions are mostly similar to the C functions that this documentation focuses on, but the interfaces do not rely on global variables and are implemented inside a DLL or shared library. The header file contains a few other functions, but these are reserved for internal use, are provided for compatibility with older DP4 releases only.

If you are familiar with the traditional C library, you should find it very easy to get used to the DP4DBAPI functions. You will find the information contained in Accessing multiple databases - A Better Way a useful background.

Programs that use DP4DBAPI should inculde dp4dbapi.h and be linked using CCOWN. However, assuming you have used only DP4DBAPI functions, your program will not bring in any of the C global variables, or the traditional OWN functions, but will link only the DP4 interface, which is exported from either SYSLIBx or USRLIBx depending on the platform. On Windows platforms you can check for this using link -dump -imports to see what libraries your program is actually using, and we will be happy to send you an import library for the API if you prefer.

Server Connection Related Functions

Connection Initialisation

BOOLEAN dp4_srv_load_srv(int argc,char **argv,dp4_name_string version_info);
CONN * dp4_srv_connect(dp4_name_string server_name,
                       int nr_system,
                       int rw_access,
                       dp4_error_callback error_handler,
                       int channel,
                       void *reserved);
BOOLEAN dp4_error_status(const CONN *conn,int *error_level,
                         int *error_nr,int *error_p0,int *error_p1);
BOOLEAN dp4_srv_enable(CONN *conn,int file_only);
/* function for use in ADC initialisation */
void dp4_adc_set_channel(CONN * conn,int channel);

Connection Termination

BOOLEAN dp4_srv_disable(CONN *conn,int file_only);
#define dp4_srv_disconnect(conn) dp4_srv_end_process(conn)
void dp4_srv_end_process(CONN *conn);

Utility Macros

#define dp4_dconn_conn(dconn)  (* (CONN**) dconn)
#define dp4_fconn_conn(fconn)  (* (CONN**) fconn)

These two macros allow you to determine the server connection handle from a DCONN or FCONN handle. This might be useful if you are implementing a library in which you expect to be passed a handle to a database or file connection, and then want to open another file or database on the same server

Database Functions

Connection Functions

DCONN * dp4_db_open(CONN * conn,dp4_name_string string_db_name,
                    int generation,int flags,int reserved,
                    dp4_swapper swapper,struct DP4_SWAP swap_table[]);
BOOLEAN dp4_db_close(DCONN * dconn);

If you require a (read-only) connection to the system database, then call the following function which is not otherwise documented in this manual.

DCONN * dp4_db_system_connection(CONN * conn,dp4_swapper swapper,struct DP4_SWAP swap_table[]);
If you want to update the system database that it must be opened in the usual way using dp4_db_open()

Transaction Control

BOOLEAN dp4_db_decommit(DCONN * dconn);
int dp4_db_update(DCONN * dconn,int flags);
int dp4_db_checkpoint(DCONN * dconn);
int dp4_db_commit(DCONN * dconn);

Legacy/Internal functions

These functions are primarily used by DP4 utilities, to access information relavent to the backup/maintenance cycle of a database. Mostly the same information is now available via the @DB_STATUS table, and you are recommended to use that instead of these APIS if you want to access this information for your own purposes.

int dp4_db_get_flag(DCONN * dconn,int flag_nr);
int dp4_db_get_generation(DCONN * dconn);
int dp4_db_get_status(DCONN * dconn);

Record Level Functions

Read Functions

Unbuffered Reads

These functions are suitable for reading records from the database one at time. Although you can also use them for accessing records sequentially you are recommended to use the buffered read functions for that purpose instead. If you are using the C++ DP4DBASE class wrapper, you will require only two additional lines of code to make your sequential database accesses use buffered instead of unbuffered reads.

BOOLEAN dp4_rec_fetch(DCONN * dconn,short * datarec,
                                      int flags,int index,int index_role,
                                      int match_keys);
BOOLEAN dp4_rec_fetch_main(DCONN * dconn,short * datarec,int flags,int match_keys);
Buffered/Block Reads

These functions are intended for accessing a number of consecutive records in a table. Use these functions in preference to the unbuffered functions, as they improve performance and scalability of your application.

BF_HANDLE dp4_bf_open(short * datarec,int keysize,int count_wanted,int strategy);
void dp4_bf_close(BF_HANDLE buffer);
BOOLEAN dp4_bf_fetch(DCONN *dconn,BF_HANDLE buffer,
                     short * datarec,int flags,
                     int index,int role,int match_keys);
BOOLEAN dp4_bf_reset(DCONN *dconn,BF_HANDLE buffer,short * datarec,int flags);

Internally, the buffered fetch functions use the following "block fetch" function. However the block fetch function is much less convenient

int dp4_blk_fetch(DCONN * dconn,int number,short * datarec,
                  int flags,int index_rec,int index_role,
                  int match_keys);

Write/Update functions

BOOLEAN dp4_rec_autoinc(DCONN * dconn,short * datarec);
BOOLEAN dp4_rec_kill(DCONN * dconn,short * datarec,int flags);
BOOLEAN dp4_rec_post(DCONN * dconn,short * datarec,int flags);
BOOLEAN dp4_rec_verify(DCONN * dconn,short * datarec,int flags);

Updates only become effective when you commit your transaction

Legacy Functions

These functions are provided for compatibility reasons. You are recommended to avoid them.

int dp4_rec_parent_copy(DCONN * dconn,short * parent,const short * child,int role);
int dp4_rec_child_copy(DCONN * dconn,short * child,
                                       const short * parent,int role);
BOOLEAN dp4_rec_retrieve(DCONN * dconn,short *datarec);

OS level API

These functions provide many of the facilities typically provided by an operating system. The purpose of implementing them in DP4DBAPI is that they allow DP4 utilities to perform this type of work across network connections.

Date/Time Functions

BOOLEAN dp4_sf_get_date_time(CONN *conn,datetype * date,timetype * time);
BOOLEAN dp4_sf_set_date_time(CONN *conn,
                             const datetype * date,
                             const timetype * time);

File Functions

DP4DBAPI has a range of APIS that can be used to create, modify and delete files on the computer where the DP4 database manager is running. These functions may be very convenient if you need to access files remotely as part of your DP4 application, but at the same time present a possible security hazard. In some cases these functions may be disabled on the server computer.

All the functions that take parameters or return values that are filek sizes or pointers, or disk space exist in two different variants: one using 32 bit values for such parameters, and one using 64 bit values. The 64 bit variants have the same name, but with the sf_ part of the name prefix replaced by sf64. The easiest way to use the 64 bit functions is to compile code with USE_DP5_FILEHANDLING defined to be 1. Local variables needed for file pointers and sizes can be declared using file_pointer, which maps either to dp4_file_pointer (unsigned long) or dp5_file_pointer (long long/__int64) as appropriate.

int dp4_sf_check(CONN *conn,dp4_name_string filenane);
BOOLEAN dp4_sf_close(FCONN * fconn);
dp4_name_string dp4_sf_default(CONN * conn,char * path_buffer,size_t buffer_size);
BOOLEAN dp4_sf_delete(CONN * conn,dp4_name_string filename);
BOOLEAN dp4_sf_flush(FCONN * fconn);
BOOLEAN dp4_sf_get_file_timestamp(FCONN * fconn,
                                  datetype * date,
                                  timetype * time);
dp4_file_size dp4_sf_get_size(FCONN * fconn);
dp4_file_pointer dp4_sf_get_position(FCONN * fconn);
FCONN * dp4_sf_open(CONN * conn,dp4_name_string filename,int mode);
dp4_file_size dp4_sf_read(FCONN * fconn,void * buffer,dp4_file_size nr_bytes);

BOOLEAN dp4_sf_read_record(FCONN * fconn,short * datarec);
BOOLEAN dp4_sf_rename(CONN * conn,dp4_name_string newname,
                      dp4_name_string oldname);
dp4_file_pointer dp4_sf_seek(FCONN * fconn,
                             dp4_file_pointer posn);
BOOLEAN dp4_sf_set_file_timestamp(FCONN * fconn,
                                  const datetype * date,
                                  const timetype * time);
dp4_file_size dp4_sf_space(CONN * conn,dp4_name_string filename);
BOOLEAN dp4_sf_write(FCONN * fconn,const void * buffer,
                     dp4_file_size nr_bytes);
BOOLEAN dp4_sf_write_record(FCONN * fconn,
                            const short * datarec);

BOOLEAN dp4_sf_read_sysdata(CONN * conn,
                            struct SYSDATA * sysdata,
                            size_t size_sysdata);

C Compatibility Functions

int dp4_conn_process_nr(CONN * conn);
int dp4_conn_server_handle(CONN * conn);
int dp4_db_nr(const DCONN * dconn);

The three functions above can be used to find values suitable for setting into the df_process, server_handle, and db_nr global variables of CCOWN. You might need to do this if you are writing new code using DP4DBAPI, but need to call previously written code that uses the CCOWN version of the DP4 interface. For example the 4.6xx MAPEDIT program uses this, because the DLL which is responsible for maintaining maps and controls on databases was written using the traditional DP4 C interface.


Error Handling with DP4DBAPI

In traditional DP4 C programs there is usually very little in the way of error handling. It is assumed that a database can always be opened, that a fetch will either succeed or return information that there is no record. The reason this is OK is that the database manager will set error status information when something untoward happens, and the interface code checks this status information after every call and generates a system or fail error if an error has arisen. Typically these errors terminate the program more or less gracefully, displaying an appropriate error message first. If the program survives the error (for example because it filtered the system or fail error using the app_error() facility) then the return code of the function will usually be set to some safe value

With DP4DBAPI you are responsible for handling all errors that might arise. There are two possible techniques for doing so:

  • You can pass the address of a valid error callback function as the fourth parameter to dp4_srv_connect(). If you choose this method you are handling errors at the first available opportunity, and you can choose which errors you want to treat as fatal and which as recoverable. For example, if a program is collecting data from many servers, you might want to treat system error 87 (corrupt database) as being recoverable: you would simply stop collecting data from the affected machine, and move on the next. On the other hand, if a program is accessing only one database, then almost any system error reported against that database should be non recoverable.

    A basic error handler of this type might look like this:

    void DP4API my_dp4_error(CONN *conn,int flags,int error_level,
                                 int error_nr,int p0,int p1)
{
  if (flags & DP4_ERR_REPORT & error_level > 1)
    printf("DP4 Error type %d Number %d Additional info %d %d\n",error_level,error_nr,p0,p1);
  if (flags & DP4_ERR_HALT || errorlevel > 1)
    exit(error_level);
}

    Four error levels are defined: error level 3 and error level 4 correspond to system and fail errors, and are the most serious. Error level 1 messages are informational, and can be ignored - the most common such message is message 67 ("Log file nearly full"). The most problematical messages are those belonging to error level 2 - these are messages that require interaction from the user, but which do allow the program to continue execution. The reason they are problematical is that in some cases the API that encountered the problem will be retried inside the interface, so if your program simply ignores such an error your program may go into a tight loop in the DP4 interface. Most such error messages relate to loading and unloading log disks, and only apply if you choose to keep the database log files on removable media. If you do not do this, the only other message of this type which is at all likely to appear are ones that warn you that the database has been opened in read-only mode when write access was requested. So you may choose to treat all such errors as fatal.

    The flags parameter to the error callback indicates whether an error report is required, and whether the application is advised to terminate. The DP4_ERR_REPORT flag will almost always be set for DP4DBAPI calls, when an error is encountered. The reason for this flag is that the DP4 C library uses a common error handler for both database and user interface APIs, and that user interface APIs have typicallt already been reported by the time the error handler is called. The DP4_ERR_HALT value will be set for all errors of level 3 and 4, but is advisory only. The one exception to this, is that if you get encounter error level 4 number 9 you must exit. This error is only generated when the DP4 service has failed, and once you encouter it you will not be able to call DP4 successfully again from this application.

  • You can choose not to set up an error callback function. In this case your program must call the dp4_error_status() function after every call to a function where the return value indicates an error has occurred, or for any return code where the particulat function being called does not define an error return value. This technique should probably only be used if you are creating your own wrapper API for DP4. Used in an ordinary application you would be vulnerable to leaving an error unhandled and thus misinterpretation of the functions results.

    The error information returned to dp4_error status should be treated in more or less the same way as has already been described.

    Reporting Error messages

    On Windows platforms, the best way to accurately report errors returned by DP4 is to include the header file dp4error.h and global.h in your code and to call the dp4_xerror_report() function via the dp4_error_report() macro. This will produce an appropriate message for each error. It is the function now used by the DP4 terminal manager when reporting errors, but it does not itself use the terminal manager.

    This function requires a window handle as the parent of the message box that will be displayed. You can use HWND_DESKTOP, but this may sometimes cause problems with Windows activation, so a real window handle is to be preferred.

    void dp4_xerror_report(int flags,
                       HWND hWnd,int error_type,int error_nr,
                       const void *error_p0,const void *error_p1,
                       const char * filename,
                       const char * src_file,int linenr,
                       gmem_type * gptr);
#define dp4_error_report(flags,hWnd,error_type,error_nr,p0,p1,filename) \
  dp4_xerror_report(flags,hWnd,error_type,error_nr,p0,p1,\
                    filename,__FILE__,__LINE__,gmem_ptr)

    A typical call to dp4_error_report() would look like this:

      dp4_error_report(DP4ERR_NO_SOURCE,pWnd->m_hWnd,m_level,m_msgNr,&m_para0,&m_para1,0);

    If you want to take responsibility for displaying the error message yourself, but would still like to get the error text you can call dp4_message_get() instead.

    LPTSTR dp4_xmessage_text_get(int flags, int error_level,
                             int error_nr,
                             const void * error_p0,
                             const void *error_p1,
                             const char *filename,
                             gmem_type * gptr);

#define dp4_message_text_get(flags,level,nr,p0,p1,filename) \
  dp4_xmessage_text_get(flags,level,nr,p0,p1,filename,gmem_ptr)


Installing and Using DP4 C Tools and Libraries

This section tells you how to install and start using the DP4 C tools and Libraries. It is divided into the following topics:

  • C/C++ Compilers Supported by DP4
  • Setting Up the DP4 C Environment
  • Building Programs

Additional information on building programs for Legacy platforms can be found on this pages:

  • Compiling Legacy (16 bit) DP4 C Programs
  • Linking Legacy DP4 C Programs

DP4 supports "object code portability" which allows DP4 programs written for one operating system to be converted to run on another operating system, without recompilation or relinking them. This is covered in Conversion Utilities.


C Compilers Supported

Compilers Supported

DP4 libraries work with the Microsoft,Intel and GNU C/C++ compilers on Win32 platforms and the GNU or manufacturer supplied compiler on Unix/Linux platforms. On legacy platforms the Microsoft and Metaware compilers are supported in a range of memory models. Support for other compilers may be available on request.

For details of how to install the supported compilers, refer to the manufacturer's instructions. When you have successfully installed the compiler of your choice, you must install the appropriate set of C libraries and the DP4 include files as described in Setting up your DP4 C Environment.


Setting up your DP4 C Environment

Header Files

The files you need for building C/C++ programs are all contained on the DP4 CD ROM. The C include files are in the include subdirectory for each release. Unix format versions are contained in the ccunx.tar far file in the unix subrectory, (they are identical to the regular versions apart from using Unix newlines instead of the usual CRLF line termination). They can be installed using the DP4 Install program for your version of DP4, or you can simply copy them directly from the CD ROM, either all at once, or as you find you need them.

You will need to ensure that your compiler knows where these files are. You can usually do this in two ways:

  1. Most compilers support the INCLUDE environment variable (Metaware C uses IPATH instead). If you are using Visual C++ then you can set the directories used in the directory Tab of the Tools/Options Dialog.

  2. Most compilers also allow you to specify include file directories using the -I command line option.

Library Files

The DP4 C library files are slightly more complicated. The DP4 C library is usually called ccown.lib or ccown.a, and can be found either in the appropriate TAR file (for Unix and Linux - see the Unix package list for details) or it will be installed as part of the C programming support when you run the DP4 install program. On Legacy platforms the library is called xc own y .lib where xc depends on the compiler and y depends on the memory model. The library for Windows 9x/NT etc is called ntown.lib. All these files are contained in appropriate locations on the DP4 CD ROM and can be installed with the DP4 Install program or copied manually. The two versions of the QAB library are named in the same way, so that xcowny.z becomes xcqaby.z or xcqabdy.z (so ntqab.lib and ntqabd.lib are the release and debug versions of the QAB libraries on Windows 9x/NT; these become ccqab.lib and ccqabd.lib on Windows CE, and ccqab.a and ccqabd.a on Unix and Linux).

As with header files the C compiler (or linker) needs to know where your libraries are to be found. This is usually done using the LIB environment variable. On Unix libraries are usually named libsomename and all stored in the same directory (possibly using symbolic links). You can use mv to use the same scheme for the DP4 libraries, or you can simply specify the file name on the cc command line (which is what we do).

Tools

LIBMAKE is normally installed as part of the base DP4 installation as are the conversion tools used for converting legacy programs between the supported operating systems, and KENTCURS, which can be used to set version information into files and apply patches of various types to executables that have already been built.


Building Programs

For Legacy verions of C batch files are supplied for compiling and linking programs. These are described here.

If you are using Microsoft Visual C++ on any Win32 platform you can build your programs using a Visual Studio project - but in this case you must be careful that the compiler options selected are suitable. In particular you should specify the Zp1 option, or else use #pragama pack(1) round the include files generated by LIBMAKE LIBMAKE did not generate the necessary pragmas itself until 4.620. You should also check that WIN32 is defined (it almost certainly will be). We have never been able to persuade Visual Studio to run LIBMAKE as part of the build process, though probably we just didn't try hard enough.

Simple DP4 programs can be built directly with the compiler on most platforms, for example on Win9x you can use the following command line to a build a single module program:

cl -DWIN32 -Zp1 -Oxs progname.c -link ntown.lib -entry:mainCRTStartup
The -Oxs option is usually the best choice of optimisation flags for any program. To build a debug version of your program use -Od -Zi instead.

On Unix or Linux you would use something like:

$(CC) -o progname $(CFLAGS) progname.c ccown.a

This assumes that CC and CFLAGS are environment variables that are appropriately defined, for example CC=cc or CC=gcc and CFLAGS=-Dunix -O2 -I.

A very good way to build programs on any platform is using make files. A simple template make file that works with Microsoft NMAKE for building DP4 Windows programs, dp4c.mak, is included on the CD ROM. It is also not too difficult to develop a cross platform make file that will work anywhere. Most of DP4 is built like this. We will gladly send you sample make files on request, but as DP4 is or has been built for around twenty different platforms, you may find them over-complicated for your needs.


Windows-Specific Issues

Resource Files

You will normally need to supply each Windows application with its own resource file (to give it an icon at least). The DP4 Terminal manager will automatically interpret certain resources within your application. You can, for example, specify the following resource items:

  • A program icon. This requires a resource statement of the form: ICON ICON filename.ico
  • A string table used to hold an application title (displayed on the application window's title bar) and message strings for application termination:

    STRINGTABLE
BEGIN
1    Title text
100  Application Terminated Normally Message
101  Application Terminated Abnormally Message

    Other resources can be declared in the resource file, as required by your application. (For example, all standard DP4 utiltiies have a version resource so that version information can be displayed in Explorer). Graphics (either resources or files) can be displayed inside your maps using the DP4 C API show_resource(), or (in 4.6xx) as static bitmaps and icons referenced by your maps. You can also display images via bitmap or icon handles using the DP4 API show_graphic() API.

    DP4RES

    A special resource named DP4RES32.DLL (DP4RES.DLL for 16 bit DP4) is supplied with DP4 for Windows set. This contains all the icons used by standard DP4 programs. You may find this useful when creating DP4 menus using MENUEDIT or within QAB programs. If you like, and have access to a program to extract the icons already present, you can build a replacement version of this DLL.


Trapping System Menu Close Requests

A user may attempt to quit your application via the System Menu Close option or by pressing Alt F4. You may wish to intercept this action if, for example, you have unsaved changes that you want to prompt the user to handle.

You can intercept the close by providing the function app_close(), for example:

BOOLEAN app_close(int retcode)
{
  /* Your code here... */

  return TRUE;  /* To allow termination */
 return FALSE;  /* To prevent termination */
}

The parameter retcode is the code that has been passed to dp4_halt() (which in turn calls app_close()). It will have one of the following values:

0 | Normal termination - the program has completed
1 | The program has called halt() to terminate
2 | The user has terminated the program with <Ctrl+S> <Ctrl+C>, or has chosen Close from the System Menu under Windows, or pressed escape in sys_request()
3 | The program is terminating after a DP4 system error
4 | The program is terminating after a DP4 fail error

In the case of a Windows system menu close action, the retcode will be 2.


Compiling Legacy (16 bit) DP4 C Programs

Compiling DP4 Legacy (16 bit) C Programs

Microsoft C (Ver 6, 7 & 8)

For Microsoft C we supply a batch file MC.BAT for each memory model supported (small, medium and large), showing the options we suggest you use.

If your C source is in a file called filename.c, issue the command:

MC filename or
MC filename.C

Any errors detected are found in the file C.ERR. If the compilation is successful, you can proceed to link it as an executable program, as described next.

Metaware High C

For Metaware C for each memory model we supply a compiler profile HC.PRO that configures the Metaware compiler appropriately (these work equally well with the FlexOS version of the compiler). Small, Medium,Big and Large Memory models are supported.

If your C source is in a file called filename.c, issue the command:

hc.exe -c filename.c or if you use hc.bat
hc.bat filename

If the compilation is successful, you can proceed to link it as an executable program, as described next.


Linking Legacy (16-bit) DP4 C Programs

For each compiler and memory model supported we supply a file called dp4link.bat. This can link your application for a variety of operating systems or versions. The options available depend on the compiler and memory model selected. The syntax of the DP4LINK batch file is:

DP4LINK [program type] progname [/m]

If program type is not specified dp4link will choose a default based on the current operating system. The possible program types are as follows:

Type | Meaning
/c | Generates DOS small model convertible programs. Requires the root module MCROOT2.OBJ or HCROOT2.OBJ
/7 | Generates OS/2 small model programs linked with the root module MCROOT7.OBJ or HCROOT7.OBJ
/8 | Generates Windows small model programs linked with the root module MCROOT8.OBJ or HCROOT8.OBJ
/10 | Generates OS/2 Presentation Manager small model programs linked with the root module MCROOTP.OBJ or HCROOTP.OBJ
/r | Generates DOS (real mode) programs (this is the default)
/p | Generates OS/2 programs
/pm | Generates OS/2 Presentation Manager programs
/w | Generates Windows programs

The /c,/7,/8,/10 options are only available when linking small model programs.

You can find out exactly which options are available for your chosen compiler and memory model by running dp4link -help

DOS programs generated with the option /c may be converted to run under other operating systems with the conversion utilities described in Conversion Utilities.

In addition to the options above, the option /m may be given to generate a map file.

Additional information on linking 16 bit Windows programs can be found here.

If a Metaware version of DP4LINK detects that it is being run on FlexOS it automatically links a FlexOS program. You can use /o as a program type to tell DP4LINK to link to TRM5.OVR in place of TRM5.286. This is recommended.

When linking your program DP4LINK will look for a file named progname.lnk. If this exists it is assumed to contain input for the linker, normally a list of filenames separated with spaces. If DP4LINK cannot find such a file it assumes your program consists of a single object module progname.obj and tries to link that.

Other Batch Files

As well as DP4LINK, other similar batch files are supplied for linking specific program types:

  • QABLINK is used for linking QAB C Exits
  • QABLINKS is used for linking QAB C Exits with various memory saving features.
  • AUXLINK is used for linking ADCS


More about 16 bit DP4 Windows Applications

Linking a Convertible DP4 Windows Application

If you choose to make a convertible application, you will not be able to call the Microsoft Windows functions directly, although you can call those contained in the DP4 file WINCALL.H if you use Metaware C. We strongly recommend that you build programs that call Windows functions as native Windows applications rather than convertible applications.

A DP4 DOS executable can be converted to a Windows application using EXE2OS2:

EXE2OS2 -WINDOWS application name

This will produce a file with the extension .WIN. This distinguishes it from the pre-conversion .EXE file. Windows requires a .EXE extension, so copy or rename the .WIN file to a file with a .EXE extension. You will have to run the resource compiler against to make it executable

In summary, you can use the DP4 facilities as follows (with either Metaware or Microsoft C):

DP4LINK /c progname (link convertible program)
EXE2OS2 -windows progname (convert to Windows format)
RC -k progname (add resources)

You can use any version of the Microsoft linker. Your application is linked with the DOS version of the libraries when using Microsoft C.

You should build an .RC file for each program, as described later. You can eliminate the need to run RC by specifying an additional command tail, −FONT, to EXE2OS2.

MS-DOS Executables linked prior to DP4 version 4.500 must be relinked because the root code used then is not compatible with MCUMMY.WIN or HCDUMMY.WIN.

Building a Native 16 bit Windows Application

There are two methods of building a native DP4 Windows application:

  • Link with MCROOT8 or HCROOT8. Using this method with Microsoft C you do not necessarily need the SLIBCAW.LIB library. You may call Windows functions directly, but only in Microsoft C (unless you are capable of adding inline assembler to restore the es register) . Furthermore, your executable can be processed by the DP4 utilities KENTCURS and PROGDATE. You are restricted to small model for both Metaware and Microsoft.

    You must have a Windows or OS/2 linker (a segmented executable linker). The DP4 linker batch files should be able to produce windows programs like this if you specify the /8 option.

  • Linking using the /w option on DP4LINK (or QABLINK). The advantage is that you are not restricted to small model, although in Microsoft C you must have ?LIBCAW.LIB or ?LIBCEW.LIB.

    You must have a Windows or OS/2 linker (a segmented executable linker).

    You will need to invoke the RC program as above or run MARK.

    Note that large model Microsoft programs or big model Metaware programs may allow only one instance of themselves to be run, particularly if you are linking with the QAB libraries. There are also other pitfalls which can cause your program to generate protection faults if compiled in large model. For example, using showc() or prnc() on literal strings which are not the same length as field in which they are being shown.

The Module-Definition File

DP4 supplies a Microsoft module-definition file named DP4WIN.DEF as part of its Programming Tools Pack. It is used when linking a native Windows program using the DP4-supplied batch files QABLINK.BAT or DP4LINK.BAT. If you wish to use a different module-definition file, you should note the following:

  • A DP4 application will make calls on the DP4 Terminal Manager. This is implemented as a Dynamic Link Library (DLL) in Windows. You must include the IMPORTS section of DP4WIN.DEF in your own module-definition file


Conversion Utilities

DP4 programs linked using the /c program type (see information on linking) can be converted to run on othere operating systems, provided they meet the following criteria:

  • The program must be linked with the correct DP4 root module (the /c option on the link batch files ensures this)

  • The program must not contain relocations. It will contain relocations if it refers to far data or functions

  • The program must not contain any calls that are specific to either the operating system that it was developed under, or to the hardware running the operating system (unless protected by testing os_number variable). Usually your program will either fail to link or contain relocations in these cases, so it is unlikely a program that can be converted will have problems.

The full syntax of all the conversion utilities is given on the following pages, together with an example of each kind of conversion.

The following utilities are described:

  • EXE2CMD (MS-DOS to FlexOS)
  • EXE2OS2 (MS-DOS to OS/2,Windows,or Presentation Manager)
  • EXE2UNIX (MS-DOS to Unix)
  • CMD2EXE (FlexOS to MS-DOS)


EXE2CMD

EXE2CMD is used to convert MS-DOS programs to FlexOS (including 4690 OS) programs. The reason it is not called EXE2286 is that the same program used to be used for creating CDOS programs. The syntax of EXE2CMD is:

EXE2CMD options programnames

programnames specifies one or more .EXE files to be converted. The following options are available with EXE2CMD:

−EXE pathname\ | Specifies where to find the DOS .EXE files
−CMD pathname\ | Specifies where to place the converted files
−DUMMY pathname\ | Specifies where to find the dummy files needed for the conversion
−286 | Specifies conversion to .286 files. (You should always specify this)
−BIND | Specifies that the converted program should load the overlay TRM5.OVR instead of using TRM5.286 (recommended)
−BIND2 | Specifies that the converted program should load the overlay TRM5.OVR instead of using TRM5.286, but using a more modern overlay loading technique. Programs converted like this may not run on all FlexOS variants.

If you use the −EXE,−CMD or −DUMMY options it is important to put a trailing backslash after the pathname, unless it just refers to the current directory on another drive (e.g. −cmd d: )

Example

The command:

EXE2CMD -286 -BIND DBASE.EXE −EXE C:\WORK\ −CMD C:\FLXWORK\ −DUMMY C:\DP4\DUMMY\

converts the DOS program C:\WORK\DBASE.EXE to the FlexOS program C:\FLXWORK\DBASE.286


EXE2OS2

EXE2OS2 is used to convert MS-DOS programs to OS/2 (including presentation manager) and Windows programs. The syntax of EXE2OS2 is:

EXE2OS2 options programnames

programnames specifies one or more .EXE files to be converted. The following options are available with EXE2OS2:

−EXE pathname\ | Specifies where to find the DOS .EXE files
−OS2 pathname\ | Specifies where to place the converted files
−DUMMY pathname\ | Specifies where to find the dummy files needed for the conversion
−PM | Specifies conversion to Presentation Manager programs (with .PM extension)
−WINDOWS | Specifies conversion to Windows programs (with .WIN extension)
−FONT | Can be used with -windows to make a program execuable direcly after conversion. Normally you have to run RC against the program to provide it with resources such as an icon and Title, as described here.
−DLL | Specifies conversion to a DLL rather than a program (with .DLL extension)

If you use the −EXE,−OS2 or −DUMMY options it is important to put a trailing backslash after the pathname, unless it just refers to the current directory on another drive (e.g. −OS2 d: )

OS/2 programs are created with .OS2 extension. These, and .WIN and .PM files need to be renamed to .EXE to run on the target operating system.

Example

The command:

EXE2OS2 DBASE.EXE −EXE C:\WORK\ −OS2 C:\OS2WORK\ −DUMMY C:\DP4\DUMMY\

converts the DOS program C:\WORK\DBASE.EXE to the OS/2 program C:\OS2WORK\DBASE.OS2.


EXE2UNIX

EXE2UNIX can be used to convert MS-DOS programs to run on Unix (or even Linux). However, most recent versions of Unix/Linux come without support for 16 bit programs. This has to be installed as an addiitional package, and it is often buggy. We no longer recommmend using it.

The syntax of EXE2UNIX is:

EXE2UNIX options programnames

programnames specifies one or more .EXE files to be converted. The following options are available with EXE2UNIX:

−EXE pathname\ | Specifies where to find the DOS .EXE files
−UNIX pathname\ | Specifies where to place the converted files
−DUMMY pathname\ | Specifies where to find the dummy files needed for the conversion
−SUFFIX | Specifies that converted files should be given a .UNX suffix. Normally the converted files are not given a suffix
−BIND | Specifies that the converted program should hace the terminal manager "bound" in. This will make the program run faster, especially if it is a batch program that calls terminal manager functions intensively (for example a Report Program with a lot of output), but it will take up a lot more space on disk.

If you use the −EXE,−UNIX or −DUMMY options it is important to put a trailing backslash after the pathname, unless it just refers to the current directory on another drive (e.g. −UNIX d: )

Example

The command:

EXE2UNIX DBASE.EXE −EXE C:\WORK\ −UNIX C:\UNIXWORK\ −DUMMY C:\DP4\DUMMY\ −SUFFIX

converts the DOS program C:\WORK\DBASE.EXE to the UNIX program C:\UNIXWORK\DBASE.UNX.


CMD2EXE

CMD2EXE converts FlexOS programs to MS-DOS programs. The syntax of CMD2EXE is:

CMD2EXE options programnames

programnames specifies one or more .286 files to be converted. The following options are available with CMD2EXE:

−CMD pathname\ | Specifies where to find the DOS .CMD files
−EXE pathname\ | Specifies where to place the converted files
−DUMMY pathname\ | Specifies where to find the dummy files needed for the conversion
−286 | Specifies conversion from .286 files. (You should always specify this)

If you use the −CMD,−EXE or −DUMMY options it is important to put a trailing backslash after the pathname, unless it just refers to the current directory on another drive (e.g. −EXE d: )

Example

The command:

CMD2EXE -286 DBASE.286 −CMD C:\WORK\ −EXE C:\EXEWORK\ −DUMMY C:\DP4\DUMMY\

converts the FlexOS program C:\WORK\DBASE.CMD to the MS-DOS program C:\EXEWORK\DBASE.EXE.

If you really want to you can then convert the generated MS-DOS program to run on another operating system with EXE2UNIX or EXE2OS2!


Version Control

For the purposes of support it is important that all the files issued as part of an application should have a readily identifiable version.

DP4 provides several facilities that allow you to add and view version information in a consistent way across all your C/C++ programs and the standard DP4 components:

  • You can use the KENTCURS utility to add version information to executables and many other file types such as batch files.

  • You can use the PROGDATE utility to view and save to file program version information.

From release 4.621 DP4 includes a batch file named DP4VER.BAT that will quickly display the version information for all installed DP4 executable files. The information is saved in a file named version.prn. If you require support from Itim Technology Solutions on a DP4 related problem, sending this file as part of the problem report will often help get your problem resolved more quickly. The contents of DP4VER.BAT are shown below:

dir /a-d /b >xx *.exe *.dll *.w32
del version.prn
progdate -full -read xx -file .\version.prn
view version.prn

DP4 version information can often be determined in other ways as well:

  • from 4.620, if DP4 error logging is turned on, and the debug_version is 1, the DP4 version of all components in use is written to the errog log window and the debug.out file. Earlier releases of DP4 also report some version information, but this is not reliable.

  • The vs_verstring() function, (declared in verstamp.h) returns a string containing version information suitable for inclusion in an About box or similar.

  • On most version of Microsoft Windows, you can check the version information of Windows DP4 Executables by right clicking on the file in Explorer and choosing Properties. The Version Tab will display the DP4 version information.


Header Files

There are a large number of DP4 C header files. However, most programs will only need to include dp4capi.h. DP4 programs written before 4.620 will use datafit.h (which is the name for old versions of dp4capi.h). Very old DP4 programs may include dp4.h instead. Although the header files are primarily designed for C they also work with C++. In fact parts of the 4.620 C library are written using C++, so there should not be any problems with this.

Namespace Issues

Owing to the fact that the DP4 C API has existed for many years (parts of it predate the 1989 ANSI C standard), there are one or two minor conflicts with the standard. For example the file dp4macro.h defines abs() as a macro that does the same thing as the ANSI C abs() function but it works with any numeric type. Unfortunately it usually evaluates its arguments twice (unless there is a compiler intrinsic _abs()), and this may potentially cause problems. dp4capi.h also #defines malloc() to call the DP4 equivalent mallocp(). If you don't want any namespace pollution put #define NO_ANSI_POLLUTE before including any DP4 header files. On Win32 platforms there are also one or two clashes with the windows.h. These have never caused any problems so far as we know. Compilation with an option that enforces strict ANSI conformance helps (-Za with Microsoft compilers), though you will probably need to fix Microsoft's header files to make them work with this option!

Summary of Header Files

The table below gives a brief description of each DP4 header file. Most files conditionally include any other files they require, so you can include any of them in any order.

bcdreal.h | Contains definitions for internal real number structure used for number to character conversion
bitset.h btrees.h | These two header files are used by DBMS programs. You only need to use them if you write programs that access database files at this level. As an indication of how unlikly this is, none of the data dictionary maintenance programs, and none of QAB need these files.
ccown.h | Contains private functions and variables used by the DP4 library routines
cdata.h | Contains locale information about the character set being used by DP4. DP4 utilities rely on this file rather than the vagaries of the manufacturer supplied equivalent
cexits.h | File to be included in QAB C exit modules.
database.h | File containing type definitions for DP4 data dictionary tables. These definitions are different from what LIBMAKE would produce for these tables. You only need to include this if you are manipulating these tables.
datafit.h | Legacy file - now just includes dp4capi.h
dbflags.h | Contains various definitions relevant to internal features of DP4 databases. Most of the information formerly in this file has been moved to datafit.h
dp4.h | File containing defines and prototypes for the 'old name' dp4 functions. If you are used to using these old names then you should include this file rather than datafit.h.
dp4capi.h | The header file for the DP4 C library. This file includes some ANSI C header files and several other DP4 header files. You can find out more about this header file, and how it differs from the old datafit.h here .
dp4cc.h | Compiler recognition file. It also defines things like cdecl pascal etc, appropriately
dp4ci.h | Contains internal details of the DP4 interface
dp4clib.h | Header file used by DP4 C library routines.
dp4dbapi.h | Header file prototyping the interface level DP4 database API. If you only want to use the DP4 database API, and no other parts of the DP4 API, you only need to include this file. If you are want to use the interface with the same #define names as were used with DP4DYN rather than the C library versions then you also need to include dp4flags.h.
dp4dbflg.h | Header file containing flags and used by the DP4 database API functions using names from the "traditional" DP4 C library.
dp4far.h | File containing various macros and functions to allow programs that need to use mixed memory model features to be written in a portable way.
dp4macro.h | File containing various allegedly "useful" macros. Included by dp4capi.h
dp4pal.h | File containing internal details of the palette used by the 4.6xx terminal manger
dp4port.h | File containing byte swapping macros for portability between different processor architectures.
dp4types.h | File containing definitions of base types used by DP4 programs. Regrettably a few of these clash with Microsoft Windows definitions, mainly because we got there first!
dp4uiapi.h | Contains the interface level terminal manager functions (exported directly from the DP4 terminal manager on Windows)
interact.h | Legacy file - just includes dp4.h
interfce.h | Another private file used by the C library. Most of the interesting stuff in this file is now in dp4ci.h
lru.h | Implements functions to do with doubly linked lists used by some functions in the DP4 C library
mapcon.h | Functions that programs that want low level access to the 4.6xx Terminal manager can use
mapedlk.h | Used by programs that want to call the 4.6xx Map Editor.
maps.h | Declares 4.6xx or 4.5xx map data structures.
maputil.h | Functions for manipulating 4.6xx maps
network.h | This file is used by DBMS in networking environment.
newmenu.h | File containing definitions used by dp4 program. The only likely cause for needing this file is if you wish to write your own program to maintain the @menu_user table. If you do this you will have to be prepared to update this program with each new version of DP4, as new preferences are added almost continually. [There is no kit for building a replacement DP4 menu program, though help in this area is available if required]
oldmaps.h | File containing type definitions for DP4 4.5xx map tables. These definitions are different from what LIBMAKE would produce for these tables. You only need to include this if you are manipulating these tables.
printers.h | This contains the structures of the printers and device tables as set up by DFSETUP. You only need this file if you are modifying these records. You do NOT need it just because you are using print functions
proghead.h | This file contains definition used by KENTCURS PROGDATE and OS conversion programs such as EXE2CMD.
sysdata.h | You need to include this file if you want to reference file locations stored in the DP4 licence file, in which case you need to call tf_read_sysdata() first. [This will be done for you if you are writing QAB C exits.].
userdata.h | You need to include this file if you want to reference fields from userdata.sys, in which case you need to call trm_readuser() first. [This will be done for you if you are writing QAB C exits.]
verstamp.h | Declares functions that DP4 programs use to print their own version information and secure information added using kentcurs
cdos286.h msdos.h os2call.h unix.h wincall.h | These files are used by DP4 OS dependent modules.


Global Variables

The following global variables are declared as extern in dp4capi.h and can be used in all DP4 C programs:

abort_code
app_nr_system
app_rw_access
app_server_name
app_start_date
app_start_time
app_tailc, app_tailv
_broken, broken
changed
choose_any_option
clearfirst
data_flags
db_generation
db_is_open
db_name
db_nr
db_open_flags
depth
df_libtype
 df_process
fail_code
input_field
input_map
jump
locked
mallocx_handler
map_db_nr
map_name
menu_flags
os_number
page_depth
page_width
pk_chosen_field
pk_more_choices
pk_range
pr_device
 pr_err_mode
pr_filename
print_error
pr_line
pr_page
pr_started
pr_streams
pr_sub_device
range
realpass
rmk_flags
selected
scrnd_flags
scrn_flags
showd_flags
show_flags
there

There are a few other global variables, either declared in other header files, or intended for internal use only. See Other Global Variables for further information.

Note
The names of some System Variables were changed with effect from v 4.500. For a table of the name changes see Appendix 1


abort_code

Description | How user exited from input function
Old Name | #define abort abort_code
Syntax | int abort_code;
Remarks | The abort_code variable indicates how the user has exited from a DP4 input function. In general, it has the value 0 for a normal exit and ESCAPE when the user has pressed <Esc>. Refer to the individual Functions Section for details of how abort_code is affected in particular circumstances.


app_nr_system

Description | Number system used by program
Old Name | #define _nr_system app_nr_system
Syntax | int app_nr_system;
Remarks | The app_nr_system variable indicates what number system is being used by the program. It should never be altered in normal operation. This variable has nothing to do with the number system being used in the database. The valid values of app_nr_system are: (0) N_IEEE (5) N_LONGINT (1) N_MTPLUS (6) N_4680 (2) N_BASIC (7) N_VAX (3) N_COBOL In most cases, app_nr_system is N_IEEE.


app_rw_access

Description | Read/write access level
Old Name | #define _rw_access app_rw_access
Syntax | int app_rw_access;
Remarks | The app_rw_access variable indicates the read write access used in database accesses. This variable is not initialised until trm_read_user(), or a function that calls trm_read_user(), is called. You can also initialise the variable by called trm_get_access(). At one time this was done for you prior to the call to init(). This has not been the case since at lease release 4.515.


app_server_name

Description | Value of -server_name command tail, if specified
Syntax | const char * app_server_name;
Remarks | If the user specifies -server_name command tail on command line, then this variable contains the name they specified. Note, that the value will not be used unless DP4 is loaded in a configuration that supports named connections. app_server_name does not contain the server_name if the application is connected to the default server (the one specified in the command line to the TCP network requester). This variable was added for 4.623


app_start_date

Description | Date the application started
Old Name | #define today app_start_date
Syntax | datetype app_start_date;
Remarks | The app_start_date variable holds the date when the application started. It is set, through a call to the tf_get_date_time() function, before the init() function is called.


app_start_time

Description | Time the program started
Old Name | #define now app_start_time
Syntax | timetype app_start_time;
Remarks | The app_start_time variable holds the time the program was started. It is set (through a call to tf_get_date_time() ) before the init() function is called.


app_tailc, app_tailv

Description

Tail variables

Old Name

#define _tailc app_tailc
#define _tailv app_tailv

Syntax

int app_tailc;
char** app_tailv;

Remarks

The app_tailc and app_tailv variables are used by the tail_get_option() and tail_read() functions. You generally need not refer to these variables. They are initialised by the DP4 main() function. If you write your own main() function and want to use the tail_() functions you should initialise these variables from the argc and argv parameters passed to main().


broken, broken

Description

Network broken status

Syntax

int _broken;
int broken;

Remarks

The broken variable is used in networking applications to test whether the network is broken. See the DP4 networking documentation for details on using this variable in applications.


changed

Description | Input data has been changed
Syntax | int changed;
Remarks | The changed variable is used in DP4 input functions and indicates whether the user has changed the data in an input function. You need to reset changed to FALSE before calling the input function to make this check. Rather than use this variable, it is better to keep two copies of the record and to compare them after some editing has been done to see whether changes have been made.


choose_any_option

Description | All options available status
Old Name | #define _any_option choose_any_option
Syntax | int choose_any_option;
Remarks | The choose_any_option is used to indicate in functions that support greying out that all options are accessible. It can be used as a parameter in a function which supports greyed-out fields where no greying out is required.


clearfirst

Description | Clear field before input variable
Syntax | int clearfirst;
Remarks | The clearfirst variable governs whether a field is cleared before input is taken from the user. It can be set to 2 for entering new records and then reset to 0. Fields are only cleared if the corresponding field in the map also has the clearfirst property and the range variable indicates the field has not already been processed. The map_get_inputs() function will manage this variable for you so generally, there is no need to use this variable.


data_flags

Description | Flags used for map_datamap()
Syntax | int data_flags;
Remarks | The data_flags variable is used in the map_datamap() function. Its initial value is RETAIN. Library functions always display all data maps using this function rather than map_draw(mapnr,RETAIN); The possible values that can be used are a combination of the following together with DATAMAP.
Value | Meaning
NO_SCROLL_OFF | Don't Scroll off the screen
RETAIN | Place as if it were a datamap
IN_BOX | Draw box around map

You can also control where the map is placed by using the map_new_position() function.


db_generation

Description

Generation number used to access currently active database

Old Name

#define _generation db_generation

Syntax

int db_generation;

Remarks

The db_generation variable holds the generation number to be used in making database accesses. This value is passed to the db_open() function. For a program using LIBMAKE generated header files it is vital that the value passed to db_open corrsponds to the generation number of the database used to generate the header file. If you pass an incorrect value the data independence mechanism will "mangle" data incorrectly, and you may create a database with bad data on it. With the open_database() functions the database is opened at generation 0. To access the database at the latest generation, use: db_generation=db_get_generation() afterwards. In this case you MUST destermine the database structure dynamically by querying the dictionary records.

If more than one database is used in a program this variable needs to be saved when the database is opened and then reset together with db_nr as necessary. You can avoid the need for this by using the DCONN based calls in dp4dbapi.h instead of the traditional DP4 C API


db_is_open

Description | Indicates whether the database is still open.
Old Name | #define _db_is_open db_is_open
Syntax | int db_is_open;
Remarks | The db_is_open variable indicates whether the current database is open. It is set to TRUE by a successful call to db_open() and reset to FALSE by db_close(). Generally there is no need to use this variable, and indeed it cannot be relied upon if your program opens and closes several databases.


db_name

Description

Name of database

Old Name

#define dbname db_name

Syntax

char db_name[32];

Remarks

The db_name variable holds the name of the database last opened with a call to db_open() or open_database(). It is zero-terminated.

Note the db_nr variable controls which of the open databases is used in a database access. So, when several databases are in use, the db_name variable will generally not name the active database.


db_nr

Description

Database number

Old Name

#define _dbnr db_nr

Syntax

int db_nr;

Remarks

The db_nr variable identifies the active database. This the database to be used in database access calls. db_nr is returned when the db_open() or open_database() function is called. If the program is going to open more than one database, save this variable and use it to move between the databases. The variable's initial value is 0, corresponding to the SYSTEM database which is always open.

If you are opening many databases you may find it easier to use the dp4dbapi.h functions instead of the traditional DP4 C API functions.


db_open_flags

Description | Database flags
Old Name | #define _db_flags db_open_flags
Syntax | int db_open_flags;
Remarks | The db_open_flags variable indicates whether the database is opened in shared or exclusive mode. It corresponds to the third parameter passed to db_open().


depth

Description | Depth to use in database accesses
Syntax | int depth;
Remarks | The depth variable indicates the number of keys that matched when fetching records. It is set with the appropriate value by the rec_fetch_main(), ftch2nd() and rec_fetch() functions.


df_libtype

Description

Indicates global behaviours of certain DP4 functions

Old Name

#define libtype df_libtype

Syntax

int df_libtype;

Remarks

The df_libtype variable controls various aspects of the behaviour of the DP4 user interface as follows:

  • Bit 0 (1) should always be set. If this bit is not set field_input functions do not work as documented in this manual.

  • If bit 1 (2) is set there is an implied call to trm_refresh() after every call to a function that outputs to the screen. Normally the screen is only painted when you explicitly call trm_refresh(), a map is changed to DEAD, or the program is waiting for input from the user. You may use this value as a "quick and dirty" way of ensuring that in batch programs that all output is seen by the user. However it is better to insert the necessary calls to refresh instead.

  • If bit 2 (4) is set then input functions that update the current field number (inpm_x() and scrn_map() etc) do not return if the user leaves the field in such a way that the current field is unchanged. This helps prevent the entered value being lost in certain circumstances. See libtype 5 for more information.

  • If bit 3 (8) is set then an out of range value is never copied into a field as a result of calling a field input function. Usually the user's entered value is always copied to the variable passed as the field value parameter. Usually the user cannot enter an out of range value in any case.

  • If bit 15 (32768) is set then the behaviour of the terminal manager in the event of a system or fail error is changed. Usually if such an error is generated inside the terminal mananager, the terminal manager reports the error itself before control returns to the user app_error() function. You can prevent this, and so take over responsibility for reporting errors by setting this bit. This is not recommended.


df_process

Description | Process number.
Old Name | #define _procnr df_process
Syntax | int df_process;
Remarks | The df_process variable holds the program's process number, which distinguishes it from other programs. It is set through the server before the init_application function is called. This variable may be useful when performing diagnostics. Do not change its value.


fail_code

Description | Failure code in call to server
Old Name | #define failure fail_code
Syntax | int fail_code;
Remarks | The fail_code gives the error code when a call to the server has failed.


input_field

Description | Current field being processed
Old Name | #define _fieldnr input_field
Syntax | int* input_field;
Remarks | The input_field variable is used in the inpm_x() functions to keep track of the field being processed. Its value is set by map_use() and must point to an integer variable.


input_map

Description | Map used in the current data entry form
Old Name | #define _mapnr input_map
Syntax | int input_map;
Remarks | The input_map variable is used by the inpm_x() functions to determine which map owns the current field to process. It is set by a call to map_use(). Generally, you need not refer to this variable because most input functions pass this value.


jump

Description | Controls stopping for input
Syntax | int jump;
Remarks | In most cases, the jump variable controls whether input functions stop for user input. Pressing <Tab> sets this variable to TRUE when the program is running. Functions that are not affected by this variable include the menu functions which always halt for user input. For more details look up the particular functions in question.


locked

Description | Indicates whether records are locked
Syntax | int locked;
Remarks | This variable is set after a call to the server and will be TRUE if another user has locked the record by passing the LOCK flag when fetching the record.


mallocx_handler

Description

Provides an error handler for mallocx()

Syntax

handler DP4D mallocx_handler;

Parameters

handler ANY DP4P (* handler)()

Remarks

If this variable is set to point to an error-handling function, it is called when mallocx() is about to return 0 as a result of heap exhaustion. For example, a simple but effective error handler is:

ANY PTR out_of_memory()
{
 map_pause(OUT_OF_MEMORY);
 halt(); // stops program
 return 0; // to satisfy compiler
}

This avoids you having to check the return code of mallocx(). A more sophisticated version is as follows:

ANY PTR find_some_more_memory(size)
{
 if (find_some_more_memory(size)== OK) /* user-written
 function which gets memory from somewhere*/
 return ptr;
 map_pause(OUT_OF_MEMORY);
 halt();
 return 0;
}

To prevent infinite recursion where mallocx() is called from the handler, set mallocx_handler to 0 temporarily before the handler function is called and restore it afterwards. If you set mallocx_handler again within the handler routine, it is your responsibility to control recursion.


map_db_nr

Description | Current database used for maps
Old Name | #define _map_dbnr map_db_nr
Syntax | int map_db_nr;
Remarks | The map_db_nr variable holds the database number which holds the mapset currently being used for screen display. It is set by the map_load() function. map_load() loads the mapset determined by the currently active database. This is normally the application database in use but can also be the SYSTEM database.


map_name

Description | Current mapset being accessed
Old Name | #define _map_name map_name
Syntax | char map_name[10];
Remarks | The map_name variable holds the mapset passed to the last call to map_load(). It is zero-terminated. It is set by a call to the map_load() function.


menu_flags

Description | Flags parameter for menu
Syntax | int menu_flags;
Remarks | The menu_flags variable is used in the map_menu() function. Its default value is MENUMAP. All the library functions display menus using this function rather than map_draw(mapnr,MENUMAP). The possible values that can be used are a combination of the following together with MENUMAP.
Value | Meaning
NO_SCROLL_OFF | Do not scroll off the screen
RETAIN | Place as if it were a data map
IN_BOX | Draw box around map
POP_UP_WINDOW | Place as if it were a popup map. Under Windows any map displayed with this flag is independently movable. Free maps acquire this flag automatically when displayed with the map_menu() function as long as RETAIN or BOTTOM are not specified. Attached maps only have this flag when it is passed explicitly

You can also position where the map is placed by using the map_new_position() function.


os_number

Description | Describes operating system in use
Old Name | #define _os_version os_number
Syntax | int os_number;
Remarks | The possible values that os_number can take are given in DBFLAGS.H. The common values are: OS_MS-DOS, OS_286, OS_UNIX, OS_WINDOWS, OS_OS2 If you need to add hardware-dependent code in your program, you can code a version for each operating system using this variable.


page_depth

Description | Page depth of printer
Old Name | #define pagedepth page_depth
Syntax | int page_depth;
Remarks | The page_depth variable indicates the number of lines the printer uses for a page. It is set when a printer is selected. You use this with pr_line to format the printer output correctly.


page_width

Description | Page width of printer
Old Name | #define pagewidth page_width
Syntax | int page_width
Remarks | The page_width variable indicates the width of each line for the printer. It is set when a printer is selected.


pk_chosen_field

Description | Field selected by the user in the choice window
Old Name | #define _option pk_chosen_field
Syntax | int pk_chosen_field
Remarks | In the window of choices, this is the number of the field selected by the user. It is set by pick_record() and pick_list(). You must save and restore this variable if you want to make use of it.


pk_more_choices

Description | Flags variable for displaying the "more available" messages
Old Name | _more pk_more_choices
Syntax | int pk_more_choices
Remarks | This variable holds a value in the range 0 to 3 that determines the "more available" message displayed. Its value is added to 180 to display the following SYSTEM database maps:
Map 180: * All items shown * | Map 181: * More below *
Map 182: * More above * | Map 183: * More above and below *

It is set by pick_record() and pick_list(). You must save and restore this variable if you want to make use of it.


pk_range

Description | Range of records on screen.
Old Name | #define _count pk_range
Syntax | int pk_range
Remarks | This variable holds the number of choices on the screen. It is set by pick_record() and pick_list(). You must save and restore this variable if you want to make use of it.


pr_available

Description | Indicates if printer is available.
Syntax | int pr_available
Remarks | The pr_available variable indicates whether the selected printer is available. It is set by the dvc_choose() and dvc_open() functions. It is initially set to -1, but is set to 0 when the printer is unavailable and spooling is possible.


pr_device

Description | Current printer number
Old Name | #define print_nr pr_device
Syntax | int print_nr;
Remarks | The pr_device variable holds the logical print number of the current printer. It is set when either dvc_open() or dvc_choose() is called.


pr_err_mode

Description | Print error mode
Syntax | int pr_err_mode;
Remarks | The pr_err_mode variable determines the behaviour when an error has occurred. If it is zero the program is terminated after displaying an error message, otherwise the program can continue.


pr_filename

Description | Print filename
Syntax | char pr_filename[64];
Remarks | The pr_filename contains the zero-terminated filename being used in printing. pr_filename is initially set to an empty string. To determine whether file output is taking place, test pr_streams & FILE.


print_error

Description | Print error
Syntax | int print_error;
Remarks | The print_error variable holds the value of any error that may occur during printing. It is only set when a device driver is being used.


pr_line

Description | Current line number
Old Name | #define linenr pr_line
Syntax | int pr_line;
Remarks | Indicates the current line number in printing procedures. Every time the prn_print() function is called pr_line is reset to 0.


pr_page

Description | Current page number
Old Name | #define pagenr pr_page
Syntax | int pr_page;
Remarks | Indicates the current page number in printing procedures. Every time the dvc_choose() function is called pr_page is set to 1.


pr_started

Description | Indicates printing has started.
Old Name | #define _print_started pr_started
Syntax | int pr_started;
Remarks | The pr_started variable indicates whether printing has started. It is set to TRUE once a successful call to open the printer has been made. dvc_close() resets this variable to FALSE.


pr_streams

Description | Current output device
Old Name | #define op_device pr_streams
Syntax | int pr_streams
Remarks | The pr_streams variable holds details of the current output device. This variable gets set appropriately by the dvc_open() and dvc_choose() functions. While printing you can test this variable to see which combination of the printer, screen and file outputs is in use. For example, pr_streams & PRINTER is non-zero when the printer is in use.


pr_sub_device

Description | Device number
Old Name | #define _l_dvc_nr pr_sub_device
Syntax | int pr_sub_device;
Remarks | The pr_sub_device contains the logical device number of the selected device. It corresponds to the l_dvc_nr key field of the l_devices structure. pr_sub_device is initially equal to the pr_device variable, which is set by the dvc_open() or dvc_choose() function. It is subsequently changed by calls to dvc_set_subdevice(). You may find pr_sub_device useful when handling printer errors in programs that make use of DDIs.


range

Description | Field access restraint variable
Syntax | int range;
Remarks | The range variable is used together with the variable selected to control the movement of the cursor by the user in data entry forms. In the older 'libtype 0' scheme, the user could not access fields using the cursor with a higher number than range. With the new 'libtype 1' scheme, there is no restriction on cursor movement. range holds the highest field value currently processed.


realpass

Description | Indicates a genuine data entry pass
Old Name | #define mainpass realpass
Syntax | int realpass;
Remarks | The realpass variable is set to TRUE when a genuine pass is being made. It is used in the map_get_inputs() and map_predisplay() functions. Refer to these functions if you want to know how to use this variable.


rmk_flags

Description | Flags parameter for remark maps
Syntax | int rmk_flags;
Remarks | The rmk_flags variable is used in the map_remark() function. Its default value is REMARK. All the library functions display remark maps using this function rather than map_draw(mapnr,REMARK). The possible values that can be used are a combination of the following together with REMARK.
Value | Meaning
BOTTOM | Place map at bottom of screen. Under Windows, maps displayed with this flag are always positioned at the bottom of the window whether it is sized or scrolled
NO_SCROLL_OFF | Do not scroll off the screen
RETAIN | Place as if it were a datamap
IN_BOX | Draw box around map
POP_UP_WINDOW | Place as if it were a popup map. Under Windows any map displayed with this flag is independently movable. Free maps acquire this flag automatically if displayed with the map_remark() function as long as RETAIN or BOTTOM are not specified. Attached maps only have this flag if it is passed explicitly

You can also position where the map is placed by using the map_new_position() function.


selected

Description | Indicates the user has entered data in the field rather than cursored past it
Syntax | int selected;
Remarks | The selected variable is used in data entry forms to indicate that the user has entered data in a field or has pressed <Enter> and has not cursored past the field. (Also useful in pick_record() function where there is a mouse and mnemonics menu.)


scrnd_flags

Description | Input screen flags variable for dates
Syntax | int scrnd_flags;
Remarks | The scrnd_flags variable is used in input screen functions like inpm_d() and askf_d(). The scrnd_flags variable takes the same values as showd_flags with certain changes; refer to the askf_d function for further details. It is initialised to DEFAULT.


scrn_flags

Description | Screen flags variable
Syntax | int scrn_flags;
Remarks | The scrn_flags variable is used in input screen functions like inpm_x() and scrnx_map(). The scrn_flags variable takes the same values as show_flags with certain changes; refer to the askf_x() function for further details. It is initialised to DEFAULT.


showd_flags

Description | Show values for dates
Syntax | int showd_flags;
Remarks | The showd_flags variable governs the parameters used in the screen show() functions for output of dates, such as show_d(). It is initialised to DEFAULT.


show_flags

Description | Controls parameters of the screen show() functions
Syntax | int show_flags;
Remarks | The show_flags variable governs the parameters used in the screen show() functions, such as show(), show_x(), and show_over(). Output of dates is controlled by the showd_flags parameter. Refer to the show_x() functions for details of possible values.


there

Description | Indicates whether last database access was successful
Syntax | int there;
Remarks | The there variable indicates the success of the last database access call by one of the rec_fetch_main(), ftch2nd() and rec_fetch() functions.


Other Global Variables

srv_data_address

For internal use only.

user

This is linked in when trm_read_user() is used to read the USERDATA.SYS file. You should include the USERDATA.H header file when you want to access fields within USERDATA.SYS.

sysdata

This is linked in when tf_read_sysdata() is used to read the DATAFIT.SYS system file. You should include the SYSDATA.H header file when you want to access fields within DATAFIT.SYS.


Macros

The DP4 header files contain the following useful macros, all of which are available when you include dp4capi.h.

• abs | • rec_comp
• COPY | • SAME
• make_tempname | • showb
• max | • sys_pause
• min | • WIPE
• MOVE | • ZERO
• NR_ELEMENTS | • zero_rec


abs

Description

Finds the absolute value of a numeric

Definition

abs(x) ((x)<0 ? -(x) : (x))

This macro evaulates the argument more than once. It is also inconsistent with the ANSI C abs() function. From 4.620 you can stop it being defined by defined by defining NO_ANSI_POLLUTE before including dp4.h or dp4capi.h. Please note that this macro works with double variables, whereas the ANSI C function does not!


COPY

Description | Copies a number of characters from one place to another
Syntax | If Metaware is being used without UNIX it is defined as: COPY(to,from,length) _move(from,to,length) (where _move is an Metaware intrinsic function.) Otherwise it is defined as: COPY(to,from,length) memcpy(to,from,length) or memmove(target,from,length), depending on which we think is faster.
Remarks | This is used to build other macros.


make_tempname

Description | Gets a unique name for a temporary file
Syntax | make_tempname(name) ((name)[6] = df_process/16+'A',(name)[7] = df_process%16+'A')
Parameter | name base name to make unique
Remarks | The macro creates temporary files that do not clash with those created by other processes. The base name should be an array of the form "NAME00AA", where the last two letters, "AA", are changed using the df_process variable.


max

Description | Finds the maximum of two numerics
Syntax | max(x,y) ((x)>(y) ? (x) : (y))


min

Description | Finds the minimum of two numerics
Syntax | min(x,y) ((x)<(y) ? (x) : (y))


MOVE

Description

Copies characters from one array to another

Syntax

If Metaware is being used without UNIX, MOVE is defined as:

MOVE(to,from) _move(from,to,sizeof(to))
(_move is a Metaware intrinsic function)

Otherwise MOVE is defined as:

MOVE(to,from) COPY(to,from,sizeof(to))

Remarks

to is the array to copy to. (It cannot be a structure or the pointer to one.) from is the array to copy from. (It cannot be a structure or the pointer to one.) sizeof(to) is an intrinsic C function that gives the number of characters to transfer. Note sizeof(to) is used rather than sizeof(from) to avoid overwriting the target area.


NR_ELEMENTS

Description

Gets number of rows in a matrix

Syntax

NR_ELEMENTS(a) (sizeof(a)/sizeof(a[0]))

Remarks

Useful macro to get the number of rows in a matrix to avoid hard coding it, for example:

char NAMES[][64]
{
 ...
} names;
...
while (i < NR_ELEMENTS(names))
{
 ...


rec_comp

Description | Compares two DP4 records
Syntax | rec_comp(x,y) (memcmp((char *)(x)+HEADER_SIZE, (char *)(y)+HEADER_SIZE,*(short *)(x)-HEADER_SIZE))
Remarks | Returns 0 if the records are identical. The records should belong to the same table. Note that the initial header fields in the structures are ignored.

See also SAME.


SAME

Description | Compares two arrays
Syntax | SAME(x,y) (memcmp(x,y,sizeof(x))==0)
Remarks | It returns TRUE when the two arrays are the same. The x and y parameters must be arrays of the same size.

See also rec_comp.


showb

Description | Shows a bit flag
Syntax | showb(m,f,flag,bit) showy(m,f,((flag) & (bit))!=0)
Remarks | This macro is useful when an integer is used to hold several Boolean flags.


sys_pause

Description

Pauses the screen

Syntax

sys_pause()
(map(0,SYSTEMMAP|IN_BOX|REMARK|NO_SCROLL_OFF),
wait_field(0,3,SKIP_MISSING_FIELD))

Remarks

This macro is provided as an aid to debugging. It displays the 'Press any key' message at the bottom of the screen.


WIPE

Description | Fills an array of characters with spaces
Syntax | If Metaware is being used WIPE is defined as: _fill_char(x,sizeof(x),' ') (_fill_char is a Metaware intrinsic function) Otherwise it is defined as: WIPE(x) memset(x,' ',sizeof(x))
Remarks | Note this is not for use with pointers. sizeof(x) must give the size of the space to be filled in.


ZERO

Description | Fills an array with zeros
Syntax | If Metaware is being used ZERO is defined as: ZERO(x) _fill_char(x,sizeof(x),0) (_fill_char is a Metaware intrinsic function) Otherwise it is defined as: ZERO(x) memset(x,'\0',sizeof(x))
Remarks | Note this is not for use with pointers. sizeof(x) must give the size of the space to be filled in.


zero_rec

Description | Fills a DP4 record with zeros
Syntax | If Metaware is being used zero_rec is defined as: zero_rec(x) (_fill_char((char *)(x)+HEADER_SIZE, *(short *)(x)-HEADER_SIZE,0)) (_fill_char is a Metaware intrinsic function) Otherwise it is defined as: zero_rec(x) (memset((char *)(x)+HEADER_SIZE,0, *(short *)(x)-HEADER_SIZE))
Remarks | Only the database fields should be filled in and not the L, TYPENR fields and so on. HEADER_SIZE gives the amount of space taken up by the header fields in the DP4 record.


Functions

This section describes the functions held in the standard DP4 C library, <xx>own<y><.ZZZ>. The functions are ordered by their 4.500 names and presented in a template format.

To provide a more logical naming scheme, most functions were given new names in release 4.500. Function names in general consist of a prefix which is common to functions in the same family. For example, the functions which use maps have names beginning with map_.

If you are familiar with the old, pre-4.500 names, you can still use them to refer to functions. The header file DP4.H has defines for the old names. In this help system, refer to Appendix 2 which lists the pre-4.500 names with the new names.

For ease of handling, functions have been divided alphabetically into the following sub-sections:

  • Functions A-C
  • Functions D
  • Functions E-F
  • Functions G-L
  • Functions M
  • Functions N-Q
  • Functions R
  • Functions S
  • Functions T-V
  • Functions W-Z


Functions A-C

• app_close() | • blk_fetch()
• askf() | • cache_describe()
• askf_password() | • cache_find()
• askf_special() | • cache_lock()
• askf_x() | • cache_post()
• askm_radio() | • cache_start()
• askm_special() | • cache_unlock()
• bf_close() | • copy2child()
• bf_fetch() | • copy2parent()
• bf_open() | • cycle()
• bf_reset() |


app_close()

Purpose

User-defined function controlling whether a DP4 program terminates

Syntax

boolean app_close(retcode)
{
 /* Whatever code you like */
 return TRUE; /* to allow termination */
 return FALSE; /* to prevent termination */
}

Parameters

int retcode

the termination code passed to dp4_halt() usually in the range 0 through 6

Description

DP4 programs are normally terminated through a call to the dp4_halt() function. This function calls the finish function that is written by LIBMAKE to close the database.

To terminate a program from within your code call dp4_halt(<n>) where <n> is the appropriate operating system return code. This function calls app_close, which you write yourself to control whether the program is allowed to terminate, and whether any other processing is done before allowing the program to terminate.
The retcode passed to dp4_halt can take one of the following values:

Value | Meaning
0 | Normal termination - the program has completed
1

The program terminated through a call to dp4_halt(1). This is the value typically used to signal an application error condition.

2

The user terminated the program in one of the following ways:

  • With <Ctrl+S> followed by <Ctrl+C>
  • By choosing Close on the Windows system menu
  • By pressing <Esc> in the sys_request() function. For example, when asked to load a log disk or warned a program is in read-only mode
3 | The program generated a DP4 system error through the sys_error() function
4

The program generated a DP4 fail error through the sys_fail() function

5

An IPC error occurred while the application was attempting to communicate with DP4

6

The application is being run via the DP4 terminal server, and the terminal session on the client machine has dropped.

There is no need to use app_close unless you want to intercept program termination. An example of a program which uses app_close is the DP4 utility DP4ED, which prompts users to save their changes if they try to exit the program.

Intercepting program termination is most useful where the user terminated the program.

The app_close function can be called by the system at any time, so the same code could be used where the user chooses:

  • To exit the application
  • To open a file that is already open


app_error()

Purpose

User-defined function to over-ride standard handling of DP4 system errors, FAIL errors. and other standard DP4 messages.

Syntax
void app_error(int DP4P flags,int DP4P type,int DP4P error_nr,
               int DP4P error0,int DP4P error1)
{
 /* Whatever code you like */
}
Parameters

int DP4P flags

if DP4ERR_REPORT is set then the error has not yet been reported by TRM and needs reporting. Clearing this flag stops the error message from being displayed or written to syslog.prn

if DP4ERR_HALT is set then the error is a system error or fail error and the program will terminate unless the flag is cleared

int DP4P type

Value | Meaning
1 | The message is a system message (from the SYSTEM mapset) but not an error. A typical message is the 'Log file nearly full' message
2 | The message is a system request (from the SYSTEM mapset) but not an error. A typical message is the 'Load log disk' message. With these messages the user is invited to press a key to continue. Pressing Enter continues the program normally. Pressing Esc will result in a call to dp4_halt().
3 | the message is a system error
4 | the message is a fail error

int DP4P error

This is the number of the error message that should be displayed.

int DP4P error0
int DP4P error1

These are additional parameters giving further information about the error.
Description

Here is an example app_error() function:

void app_error(int DP4P flags,int DP4P type,int DP4P error_nr,
               int DP4P error0,int DP4P error1)
{
  if (*flags & DP4ERR_REPORT)
  {
    /* Report error ourself */
    printf("DP4 Error Type %d Number %d paras %d %d\r\n",type,error_nr,error0,error1);
    *flags &= ~DP4ERR_REPORT;
  }
  if (type == 3 && error_nr == 31) /* Database needs backup */
  {
    *flags &= ~DP4ERR_HALT;
    /* program will test return from db_open() and run back up code */
  }
}

The default app_error() function does nothing.


askf()

Purpose

Generalised input function

Old name

#define scrn_field askf

Syntax

boolean askf(mapnr, fieldnr, data, dtype, size, flags)

Parameters

int mapnr

Number of the map containing the field

int fieldnr

Number of the field from which to input

ANY* data

Pointer to the data which is to hold the result of the field input

int dtype

The data type of the field input

int size

The internal size of the parameter dtype

int flags

Specifies the display format, acting like the global variable scrn_flags

Description

This is a generalised input function, used internally to construct the askf_x() functions. It is available to programmers who wish to construct their own input functions.

The data is assumed to be of the type specified by the parameter dtype, and of the size specified by the parameter size. The size is the internal storage size, not the display size.

Return values

Returns the value of the global variable abort_code

See also

askf_x()

Example

askf(2,6,&orderline.quantity,2,'I',DEFAULT);


askf_password()

Purpose | Obtains the password from the user
Old name | #define scrn_password askf_password
Syntax | int askf_password(mapnr, fieldnr, password)
Parameters | int mapnr | Number of map containing the field
 | int fieldnr | Field from which to get password
 | char* password | Pointer to an array to hold the entered password

Description

The askf_password() function is used to obtain a character string from the user which normally represents a password that must not be echoed on the screen. Instead the '#' character is echoed as a guide to the number of characters entered.

On first entering the function, a row of dots is displayed, one per character, in the field. If the <Backspace> key is pressed, the display is cleared and the input restarts from the beginning of the field. (In version 4.6xx the display is initially blank and asterisks are used instead)

The map must already be on the screen when the function is called.

Earlier versions of this function had a restriction on the number of characters that the field could contain. This has now been removed.

Return values

Returns the value of the global variable abort_code which is zero or ESCAPE if the user has pressed the <Esc> key

Example

/*
 C Functions Reference Manual
 ----------------------------
 Filename: ASKF_PAS.C
 Example: askf_password
 Purpose: Program gets a password before user can
          proceed
*/

/*#db salesord
#c
#end*/
#include "dp4.h"
#include "askf_pas.h"
/* generate this file by running LIBMAKE */

boolean valid_password(char *);
void start_prog(void);

void process()
{
 char password[8];
 map_draw(1, RETAIN);
 for (;;)
 {
  if (askf_password(1, 1, password))
   /* if Escape pressed */
   dp4_halt(1);   /* exit the program */
  if (valid_password(password))
   /* check password valid */
   break;
 }
 start_prog();  /* start main part of program */
}
/* dummy functions follow */
boolean valid_password(char * password)
{
 /* checks password is valid */
 return TRUE;
}
void start_prog()
{
 /* starts main part of program */
 return;
}


askf_special()

Purpose

Waits in a map field

Old names

#define wait_field askf_special
#define pause(mapnr,fieldnr) askf_special(mapnr,fieldnr,scrn_flags)

Syntax

int askf_special(mapnr, fieldnr, flags)

Parameters

int mapnr

Number of map in which to wait

int fieldnr

Number of field in which to wait

int flags

Parameter specifying display format

The parameter flags can take any of the values as for the global variable scrn_flags when used with the askf_x() functions

Description

The askf_special() function positions the cursor in the field passed as a parameter, and waits for any key to be pressed. The display format is specified by the parameter flags. The map must already be on the screen.

The global variables jump and clearfirst affect the behaviour of this function. If clearfirst is 0 and jump is TRUE, the function does not stop for user input. If clearfirst is 1, jump is set to FALSE and the program waits for user input.

If the user presses <Tab>, jump is set to TRUE and the function returns.

If the key pressed is <Esc>, the global variable abort_code is set to non-zero, otherwise it is set to zero.

Return values

Returns the value of the global variable abort_code

See also

askf_special(), askm_special()

Example

askf_special(6,1,BLANK_IF_ZERO);


askf_x()

Purpose

Field input functions

Old name

#define scrnx_field askf_x

Syntax

int askf_c(mapnr, fieldnr, data)
int askf_d(mapnr, fieldnr, data)
int askf_h(mapnr, fieldnr, data)
int askf_i(mapnr, fieldnr, data)
int askf_l(mapnr, fieldnr, data)
int askf_n(mapnr, fieldnr, data)
int askf_s(mapnr, fieldnr, data)
int askf_t(mapnr, fieldnr, data)
int askf_u(mapnr, fieldnr, data)
int askf_y(mapnr, fieldnr, data)

Parameters

int mapnr

Number of map containing the field

int fieldnr

Number of field to input data from

<type> data

Variable or pointer to variable in which to place input data

The <type> of the parameter data depends on which askf_x() function is used, as shown below. Also shown is the corresponding DP4 datatype for each of the askf_x() functions:

Function | Type | DP4 datatype
askf_c | char* | C
askf_d | datetype* | D
askf_h | short* | I
askf_i | int* | I
askf_l | long* | L
askf_n | double* | N
askf_s | char* | S
askf_t | timetype* | T
askf_u | char* | U
askf_y | boolean * | Y

Description

These functions are the screen input functions. The data is accepted from the screen in the position of the specified field.

These functions are generally used when there is only one field in the map, or the input sequence is fixed and previous input values cannot be corrected. If the user is allowed to cursor through the fields the scrnx_map() or inpm_x() functions should be used.

The detailed action of the askf_x() functions depends on the global variables clearfirst and jump.

The global variable clearfirst controls whether the field should appear as it does before any input, or set to the default value.

The global variable jump gives you a mechanism for skipping the remaining fields in a sequence.

The global variables clearfirst and jump are most commonly used as follows:

  • clearfirst = 0, jump = TRUE
    With these settings, the data is displayed as for a call to the corresponding show_x() function. The global variable abort_code is set to zero for the askf_x() function. The program does not stop for input from the user

  • clearfirst = 1
    In this case, the askf_x() function sets the global variable jump to FALSE. If the global variable clearfirst is 1 and the field is marked on the map as a clearfirst field, the data is set to the default on the database or zero, or if it is not a database field a blank value. The program stops for input from the user

If the value entered by the user differs from the previous value, the global variable changed is set to TRUE.

If the user presses <Tab> without altering the data, the global variable jump is set to TRUE, otherwise its value is not changed.

If the user presses <Esc>, the global variable abort_code is set to ESCAPE, otherwise it is set to zero.

The format in which the data is displayed is controlled by the global variables scrn_flags and scrnd_flags, which are initialised to the value DEFAULT. The global variables scrn_flags and scrnd_flags may take the same values as the global variables show_flags and showd_flags, as described for the show_x() functions, with the following differences:

Value

Meaning

SKIP_MISSING_FIELD

If the field does not exist then the program does not crash. A value of zero is returned

FUNC_KEYS

Allows the user to press one of the function keys, which will return with the global variable abort_code set to a non-zero value. In some configurations, 9 and 10 will be used for help and training mode keys and will not return a value

CURS_KEY

Changes the action of the <Ins>, <Del>, <Pg Up> and <Pg Dn> cursor keys. Instead of editing, they cause the function to return with the global variable abort_code set to a non-zero value

NULL_FORBIDDEN

Prevents the input of a null value

END_WITHOUT_CR

Causes input to terminate when the field is full, without the user having to press <Enter>

OVERTYPE_MODE

Prevents the function askf_c() destroying the existing contents of field as soon as the user presses a key

If the global variables scrn_flags and scrnd_flags are set to the value DEFAULT, a call to an askf_x() function will use the format flags that were set with MAPEDIT or MAPPOST.

If the global variable abort_code is set to a non-zero value on return, its numeric value can be used to determine which key was pressed. The possible values are:

Value

If the user pressed

ESCAPE
INSERT
DELETE
PAGEUP
PAGEDOWN
1,2,3,,...n

the <Esc> key
the <Ins> key
the <Del> key
the <Pg Up> key
the <Pg Dn> key
the function key <F1>, <F2>, …<Fn>

To input into multi-occurs fields, use the general askf() function, unless it is a character field when you can use the askf_c function.

Return values

Returns the value of the global variable abort_code

See also

scrnx_map(), inpm_x(), show_x(), scrn_flags, scrnd_flags

Example

if (!askf_c(1,2,customer.name))

{..

if (!askf_d(1,2,&order.date))

{..

if (!askf_t(1,2,&order.time))

{..

if (!askf_i(1,3,&orderline.quantity))

{..

if (!askf_l(1,3,&longdata))

{..

if (!askf_n(1,4,&material.price))

{..

if (!askf_u(1,1,customer.name))

{..

if (!askf_y(1,8,&job.complete))

{..
>


askm_radio()

Purpose

Generates radio buttons on a map. Obsolete from v 4.603 onwards

Syntax

askm_radio(int mapnr, int fieldnr, short *selected,
int low_field, int high_field, int flags);

Parameters

int mapnr

The number of the map to display the radio buttons

int fieldnr

The number of the current field

short *selected

Pointer to the number of the default selected radio button in the set; the first in the set is 0

int low_field

The lowest field number displayed as a radio button

int high_field

The highest field number displayed as a radio button

int flags

Specifies the display format, acting like the global variable scrn_flags

Description

The function askm_radio() creates a set of radio buttons in a DP4 map.

Each button in the set constitutes a field in the map. The low_field and high_field parameters determine the range of field numbers of the set.

One button must be specified as the default selection using the selected parameter. This is 0 if the lowest field is the default, 1 if the second and so on.

From v 4.603 the askm_radio() function has been discontinued. The terminal manager supports the use of radio-type buttons through its regular input routines, in which each button is assigned a field number.

Return values

None

Example

/*
 C Functions Reference Manual
 ----------------------------
 Filename: ASKM_RAD.C
 Example: askm_radio
 Purpose: Simple data entry using radio buttons
*/

/*
#mapname blob
#db system
#map 1 4 1 map_flags=19
________

| Radio One

| Radio Two

| Radio Three

Enter a name |_
________
#fields 1:select=0,2:select=t,3:select=h;
#end
*/

#include "dp4.h"

short radio = 0; /* Default activated radio button
                    is first in set  */
char name[2];

void process_inner(int m,int *f);
  int ask_mradio(int mapnr,int DP4P fieldnr,
     short DP4P data,int first,int last,
     int scrn_flags);

/**/

void process()
{
   libtype = 1;
   loadmaps("blob");
   while (!get_inputs(1,1,process_inner,
               CLEAR|PREDISPLAY))
     jump = FALSE;
}

/**/

void process_inner(m,f)
int *f;
{
   switch (*f)
   {
      /* First three fields on map are the radio
         button set */
      case 1:
      case 2:
      case 3:
        /* Solicit input in radio button set */
        askm_radio(m,f,&radio,1,3,0);
        break;
      case 4:
        /* Non-radio button field */
        inpm_cnp(name);
        break;
      default:
        skip();
        break;
   }
}


askm_special()

Purpose

Waits in a map field

Old names

#define select(mapnr,fieldnr)
askm_special(mapnr,fieldnr,scrn_flags,0)
#define wait_map askm_special

Syntax

int askm_special(mapnr, fieldnr, flags, mnem_map)

Parameters

int mapnr

Number of owning map

int* fieldnr

Pointer to field to wait in

int flags

Parameter specifying display format

int mnem_map

Number of a map which is usually displayed on the screen with mnemonic letters

The parameter flags can take any of the following values:

Value

Meaning

FUNC_KEYS

Allows the use of the function and number keys to select an option

NR_KEYS

Allows the number keys to select an option

CURS_KEYS

Causes the <Ins>, <Del>, <Pg Up> and <Pg Dn> keys to select the currently highlighted option, and return with the global variable abort_code set to the appropriate value

NO_WRAP

This makes the function askm_special() return with the value of the key pressed if the user tries to move outside the choice array

Description

The askm_special() function highlights a field and waits for the user to press a key. It is often used to allow the user to select an option from a menu which has already been displayed. This menu is passed as the parameter mnem_map. If the menu is not being used, the parameter mnem_map should be set to 0.

If a mnemonic letter is pressed, the function askm_special() returns the value of the global variable abort_code, which is set to the number of the field whose mnemonic letter was pressed.

The parameter fieldnr is set to where the next input should be. If a mnemonic key is pressed, fieldnr does not change at all, and the function inpm_omit() should be called if the field is part of a data entry function.

If the global variable realpass is FALSE, the function always returns RIGHT. This occurs when the function map_get_inputs() is called with the parameter flag set to the value PREDISPLAY, or the function map_predisplay() is called.

The function askm_special() is unlike the function inpm_e() because it does not check that the number chosen corresponds to a field on the menu.

Return values

Returns the value of the global variable abort_code. If a mnemonic letter is pressed, it returns the corresponding field number

See also

inpm_e()

Example

/*
 C Functions Reference Manual
 ----------------------------
 Filename: ASKM_SPE.C
 Example: askm_special
 Purpose: Simple data entry for new employees using
          a menu to get the grade
*/

/*
#db salesord
#c
#tables employee;
#end
*/

#include "dp4.h"
#include "askm_spe.h"
/* generate this file by running LIBMAKE */

/* prototypes */
void get_new_emp(void);
void init_new_emp(void);
void emp_inner(int,int*);
int pick_field(int,int*,int,int);
void process()
{
 trm_read_user(); /* only required if using
                     Microsoft C: a bug in the
                     linker causes an unresolved
                     external error in the DP4
                     library */
 map_datamap(1);
 get_new_emp();
}
/**/
void get_new_emp()
{
 /* initialise the new fields */
 init_new_emp();
 if (!map_get_inputs(2, 1, emp_inner, GI_GLOBALS))
 /* Note: in 4.42x, the flag ASK_CONFIRM should be
   passed to this function. In 4.500, Save/Cancel
   buttons should be added to the map */
 rec_post_commit(&employee.l);
}
/**/

void init_new_emp()
 /* initialise fields for a new customer */
{
 /* get last customer and add 1 */
 rec_fetch_main(LAST, &employee.l);
 employee.employee_number++;
 employee.gender[0] = 'M';
 memset(&employee.date_of_birth,0,
    sizeof(employee.date_of_birth));
 employee.grade[0] = 'W'; WIPE(employee.name);
}
/**/
void emp_inner(m, f)
 int m; int* f;
 {
 int field_nr = *f;
 switch (field_nr)
 {
  case 1:
    inpm_c(employee.name);
    break;
  case 2: /* show it only */
    show_h(m, 2, employee.employee_number);
    inpm_omit();
    break;
  case 3:
    {
      int option = employee.gender[0]=='M' ? 1 : 2;
      option = pick_field(m, f, 6, option);#
      if (!abort_code)
        employee.gender[0] = (option == 1) ?
          (char) 'M': (char) 'F';
     }
     break;
  case 5:
    inpm_d(&employee.date_of_birth);
    break;
  case 6: /* region code choose from a menu */
    {
      static char grades[4] = {'D','M','C','W'};
      int option = 4, i;
      for (i = 0; i < 3; i++)
        if (grades[i] == employee.grade[0])
          option = i + 1;
      option = pick_field(m, f, 10, option);
      if (!abort_code)
        employee.grade[0] = grades[option - 1];
    }
    break;
  default:
    inpm_omit();
    break;
 }
}
/**/
int pick_field(m, f, m1, option)
  int m; int* f; int m1; int option;
{
  int field_nr = *f;
  int old_option = option;
  if (realpass)
  {
    field_blank(m, field_nr + 1);
    show_over(m, field_nr + 1, m1 + option);
    map_menu(m1);
    askm_special(m, f, NR_KEYS|CURS_KEYS, m1);
/* if space bar enter the menu or the field does not exist,
   highlight_field returns TRUE if the field does not exist */
    if (abort_code == SPACE_BAR
     || (abort_code > 0 && !field_highlight(m1,
        abort_code > 0 ? (option = abort_code) :
        option)))
    {
      if (field_choose(m1, &option, DEFAULT))
      {
        *f = field_nr;
        jump = FALSE;
        map_clear_from(m1);
        abort_code = 0;
        return old_option;
      }
    }
    else
      if (abort_code > 0) /* mnemonic key pressed */
      {
        option = abort_code;
        inpm_omit();
      }
    map_clear_from(m1);
  }
  else
    inpm_omit();
  if (abort_code != ESCAPE)
  {
    field_blank(m, field_nr + 1);
    show_over(m, field_nr + 1, m1 + option);
    abort_code = 0;
  }
  else
  {
    jump = FALSE;
    *f = field_nr;
  }
  return option;
}
/**/


bf_close()

Purpose | Closes and Discards a fetch buffer previously created with bf_open()
Syntax (CCOWN) | void bf_close(BF_HANDLE buffer);
Syntax (DP4DBAPI) | void dp4_bf_close(BF_HANDLE buffer);
Parameters | BF_HANDLE buffer | Address of the buffer
Description | This function discards a buffer allocated by bf_open(). You must remember to call this function, otherwise the memory used by it is never returned to the heap
Example | See the example for cache_find()


bf_fetch()

Purpose

Retrieves a record from the buffer

Syntax (CCOWN)

BOOLEAN bf_fetch(BF_HANDLE buffer,int flags,short *datarec, int index,int role,ADDITIONAL)

Syntax (DP4DBAPI)

BOOLEAN dp4_bf_fetch(DCONN * dconn,BF_HANDLE buffer,short *datarec,int flags int index,int role,int match_keys)

Parameters

DCONN * dconn

Handle to database connection

BF_HANDLE buffer

Address of buffer

int flags

Search method as described below

short *datarec

Pointer to record variable

int index

Index number of Index used

int role

Role number of Index used

int match_keys

Indicates number of leading key fields which must match the initial values in the record. In the DP4DBAPI implementation you must pass a value for this parameter, even if it will be ignored. In the CCOWN implementation this parameter is optional, and the value is ignored unless you specify the DEPTH value in flags.

Description

This function fetches a record from the buffer using the specified index and role. A system error is generated when the buffer is invalid for the specified table.

The first call to the bf_fetch() function after the buffer is created, and subsequent calls where NO_FINDLIST is specified in flags:

  • Discard the current contents, if any, of the buffer
  • Automatically fill the buffer, using the contents of datarec as the key by calling the blk_fetch()
  • Return the data for the first record in the buffer

The buffer is ordered in the natural sequence implied by flags. In particular, the REVERSE flag is passed for PREV-type searches to stop the DBMS reordering the data.

Subsequent calls to bf_fetch() work like this:

  • If LAST or FIRST is passed (without NO_FINDLIST) and the buffer contains the appropriate record, that record is returned, otherwise processing continues as though NO_FINDLIST had been specified
  • If neither FIRST, LAST, NO_FINDLIST, NEXT nor PREV are passed in flags, the same record is returned regardless of the contents of datarec, the indexes or other flags specified
  • If neither FIRST, LAST nor NO_FINDLIST are passed in flags, but either NEXT or PREV are passed: the record adjacent to the record returned on the previous call is returned regardless of the contents of datarec, the indexes or other flags specified. If an adjacent record is returned but the buffer is exhausted, the buffer is refilled. bf_fetch() automatically determines whether a rec_fetch_main(EQUAL) is required before fetching records in the appropriate direction. This is the case when the buffer was previously filled in the opposite of the current direction

It should be noted that the initial contents of the data record and the index passed are ignored for the purposes of extracting a record from the buffer. However, programs using this function cannot rely on this behaviour because they do not know when bf_fetch() will go to the DBMS to read data. Note that with this strategy it is possible to switch the direction of searches with a minimal overhead.

For bf_fetch, the global variable depth is extracted from the generation field of records in the buffer; it is placed there by the blk_fetch() function, and there is also set to the return value of the function.

Because the index and role parameters are ignored except when the buffer is to be refilled and should always be the same, you may wonder why they are not passed to the bf_open() function and stored as part of the buffer information. The reason is that passing them explicitly to each call to the bf_fetch() function makes it easy to call the rec_fetch() function if no buffer could be allocated at the time the bf_open() function was called.

Return values

Returns TRUE if a record was fetched, and FALSE if there was no matching record or an error occured.

Example

See the example for the cache_find() function


bf_open()

Purpose

Initialises a buffer for subsequent use in buffered fetch operations

Syntax (CCOWN)

BF_HANDLE bf_open(short *datarec,int keysize,int count_wanted,int strategy)

Syntax (CCOWN)

BF_HANDLE dp4_bf_open(short *datarec,int keysize,int count_wanted,int strategy)

Parameters

short *datarec

Address of record

int keysize

Number of bytes to compare when bf_reset() required

int count_wanted

Number of records for the buffer

int strategy

Processing strategy

Description

You call this function to initialise a buffer for subsequent buffered fetch operations. It should be noted that all the buffered functions are designed to work even when a buffer cannot be allocated, and that special action is not needed if the allocation fails: in this case the fetch will be done using rec_fetch().

The first parameter is a pointer to the L field of the database record for which buffered fetches are required. The L and TYPENR fields must be correctly filled in. For example, if the record is variable in length you must ensure that the length is the maximum possible for the record. Note that for such records, the underlying block fetches will never return more than one record at a time. However, the bf_xxxx functions work regardless of the number of records retrieved by the underlying block fetches, and if the interactive strategy is selected the buffered fetch functions will build up a cache of records using as much as possible of the buffer.

The second parameter is the number of bytes to be used when comparing records for the bf_reset() function. It is normally the total size of the primary key, but may need to be greater if the prime key fields of the table do not precede all the other fields. You may pass 0 if the bf_reset() function is not required, which is usually the case except then bf_fetch() is being used with pick_recoed().

The third parameter is a suggested number of records for the buffer. If 0 is passed, the bf_open() function automatically creates the largest useful buffer possible.

The final parameter indicates a strategy to be used. If BF_SEQUENTIAL is passed, the bf_xxxx functions assume that each record returned is processed once. If BF_INTERACTIVE is passed, the bf_xxxx functions do not assume this. The strategy differences are that if BF_INTERACTIVE is passed the default buffer size is twice as large as the largest number of records block fetch can return in one go, whereas for BF_SEQUENTIAL the buffer size is just the largest size. BF_VAR_INTERACTIVE should be passed if the interactive strategy is required for variable length records.

Subsequently, whenever more records need to be read into the buffer:

  • If the BF_SEQUENTIAL strategy is selected the whole buffer is replaced
  • If the BF_INTERACTIVE strategy is selected only part of the buffer is replaced

Return values

Returns the address of the buffer or 0 if one cannot be allocated

Example

See the example for the cache_find() function


bf_reset()

Purpose

Repositions search for interactive record processing

Syntax (CCOWN)

BOOLEAN bf_reset(BF_HANDLE buffer,short * datarec,int flags);

Syntax (DP4DBAPI)

BOOLEAN dp4_bf_reset(DCONN *dconn,BF_HANDLE buffer,short * datarec,int flags);

Parameters

DCONN * dconn

Handle to database connection

BF_HANDLE buffer

Address of buffer

short * datarec

Pointer to record variable

int flags

Controls the search method

Description

During pick_record() type operations it is occasionally necessary to reposition the search on a record retrieved earlier. For example, when the user presses <Pg Up> after an earlier series of <Pg Dn>s.

To use the bf_reset() function it is necessary to fill in the prime key fields and any non-key fields embedded within these in datarec. bf_reset() can then be called. If a matching record exists within the buffer, it is copied from the buffer unless NO_READ is specified in the flags. Subsequent calls to bf_fetch() will proceed from the appropriate point in the buffer. You can pass NO_FINDLIST in the flags parameter to leave the existing search position as it is, when you want to use the buffer as a cache.

If the record is not found, the rec_fetch_main() function is called to read the record, unless NO_READ is passed in the flags. Furthermore, without NO_FINDLIST passed in the flags, the data in the buffer is discarded.

Return values

The return value is TRUE if the record was found in the buffer or through a call to rec_fetch_main(), FALSE otherwise. The value is also stored in there

Example

See the example for the cache_find() function


blk_fetch()

Purpose | Fetches more than one record at a time
Syntax (CCOWN) | int blk_fetch(int nr, int flags, short *datarec, int index, int role, ADDITIONAL)
Syntax (DP4DBAPI) | int dp4_blk_fetch(DCONN * dconn, int number, short * datarec, int flags, int index_rec, int index_role, int match_keys);
Parameters | DCONN * dconn | Handle to database connection
 | int nr | Number of records to fetch
 | int flags | Indicates the search method used, as for the functions rec_fetch()
 | short * datarec | Pointer to the L field of a database record to be fetched. Note that this field must be a pointer to an array of records, matching the number of records requested in the first parameter, otherwise blk_fetch() may write to other data areas
 | int index | Index number of the index used
 | int role | Role number of the index used
 | int match_keys | Number of leading keys to match for searches other than EQUAL. In the CCOWN version of the API this is the optional ADDITIONAL parameter, and is ignored unless you specify DEPTH as one of the flags.
Description | The blk_fetch() function is a generalised version of the function rec_fetch(), which allows more than one record to be read from the database at a time. In general, you are recommended to use the bf_fetch() function instead of blk_fetch().
Return values | Returns the number of records actually fetched.
See also |


cache_describe()

Purpose | Enables caching for a particular table
Syntax | int cache_describe(typenr, keysize)
Parameters | int typenr | Table number
 | int keysize | Size of the key to the record
Description | cache_describe() must be called to enable caching for a particular table. The DP4 cache_xxxx functions utilise a heap for caching. You must initialise a heap with the freex() or heap_init() function, otherwise there will be no room for a cache. Calling the cache_describe() function has no effect on the records already in the cache. The first call to cache_describe() makes a call to cache_start().
Return values | Return values are as follows:
Value | Meaning
-1 | There is no cache (insufficient memory)
0 | The table cannot be cached with the specified key size because it is already being cached with a different key size
1 | The table was successfully added to the list of cache tables
2 | The table was already being cached with this key size
See also | freex(), heap_init()
Example | See the example for the cache_find() function


cache_find()

Purpose Searches for a matching record in the cache
Syntax

BOOLEAN cache_find(short *datarec);

Parameters

short *datarec

Pointer to the 1st field of the record to be found
Description

The TYPENR and data in the record are used to search for a matching record in the cache. If the record is found in the cache, it is copied into datarec.
Return values

Returns TRUE if a record is found and FALSE otherwise. The return value is stored in the global variable there

Example
/*
 C Functions Reference Manual
 ----------------------------
 Filename: CACHEX.C
 Example: The following functions are illustrated:
 cache_describe, cache_find and cache_post;
 bf_open, bf_close, bf_reset and bf_fetch.
 Purpose: Program illustrates DP4 caching and
 buffered fetch functions.
*/

/*
#db salesord
#tables customer,
        order_header,
        order_line,
        material,
        index_cust_name;
#c
#end
*/
#include "dp4.h"
#include "cachex.h" /* generate this file by running    LIBMAKE */
#define LAYOUT_MAP      1
#define ORDER_MAP       2
#define PICK_LINE_MAP   4
#define DISP_LINE_MAP   6
#define NUM_ORDER_LINES 8
#define HEAPSZ      16384 /* 16K */
/* work area for pick_record */
static struct SAVE_ORDER_LINE
{
 long order_number;
 short line;
 char material_code[8];
 char description[20];
 double price;
 short quantity;
 double line_value;
}save_order_line[NUM_ORDER_LINES + 1];
char heap[HEAPSZ];
/* declare a 16K heap */
static int new_line;
static boolean new_record = FALSE;
static BF_HANDLE ord_line_buffer;

/* prototypes */
boolean get_order(void);
  void order_inner(int,int*);
    void disp_order(void);
    boolean select_order(void);
void get_order_cust(void);
void disp_order_cust(void);
void edit_order(void);
  boolean fetch_line(int);
  void show_line(int,int);
  void del_line(void);
  int pick_line(void);
  void edit_line(int);
    void line_inner(int,int*);
void process()
{
   trm_read_user(); /* only required if using
                      Microsoft C: a bug in the
                      linker causes an unresolved
                      external error in the DP4
                      library */
   /* this call sets up the heap array as the cache
      which will be used by the DP4 cache_xxxx
      functions note that the same heap will also be
      used by the buffered fetch functions */
   freex(heap,sizeof(heap));
   /* this call sets up caching for a particular
     table (MATERIAL) */
   if (cache_describe(_MATERIAL,
          sizeof(material.material_code)) <= 0)
   map_remark(11); /* cannot cache or already
        cached */
   scrn_flags |= PROMPT_AVAILABLE;
   map_draw(LAYOUT_MAP,RETAIN); /* header
     comment/layout map */
   for (;;)
   {
      map_attach(LAYOUT_MAP,1,ORDER_MAP,DATAMAP);
      /* put up order map */
      if (get_order() == TRUE) /* has the user
                     specified an order */
      {
         get_order_cust(); /* yes get and display
            it */
         disp_order_cust();
         edit_order();  /* process order */
      }
      else
        break;          /* no, break out of loop */
   }
}
boolean get_order()
{
  /* let the user specify an order */
  boolean retcode;
  new_record = FALSE;
  /* get order details */
  if (map_get_inputs(ORDER_MAP,1,order_inner,
    GI_GLOBALS|NO_DISP) == ESCAPE) retcode = FALSE;
    /* did not get order */
  else
  {
     if (new_record)
     {
        new_record = FALSE;
        rec_post_commit(&order_header.l);
        /* commit record to database */
     }
     retcode = TRUE; /* got the order */
   }
   return retcode;
}

void order_inner(m,f)
  int m; int* f;
{
  /* inner function used in get_order(...) */
  int field_nr = *f;
  boolean got_the_record = FALSE;
  switch (field_nr)
  {
     case 1:
     show_over(ORDER_MAP,9,15);
     /* message: Enter Order Nr. or scan */
     if (!inpm_l(&order_header.order_number))
     /* normal exit */
     {
        if (abort_code == SPACE_BAR)
        /* if <Enter> or <Ctrl+P> */
        {
           if (rec_fetch_main(FIRST,
                         &order_header.l))
           /* first record */
             disp_order();   /* display it */
           got_the_record = select_order();
           /* allow user to scan */
        }
        else /* user has specified a record */
        {
           if (rec_fetch_main(EQUAL,
                          &order_header.l))
           /* Found successfully */
           {
              disp_order();
              got_the_record = select_order();
              /* allow user to scan records */
           }
           else /* allow user to choose add new,
              search or no action */
           {
              int option = 1;
              if (!map_choose(13, &option))
              {
                 switch (option)
                 {
                    case 1:
                      new_record = TRUE;
                      break;
                    case 2:
                      got_the_record = select_order();
                      break;
                    case 8:
                    default:
                      got_the_record = FALSE;
                      break;
                 }
              }
           }
        }
        if (new_record == FALSE && got_the_record ==
                 FALSE)
        {
           /* return to this field if record not
              selected */
           *f = field_nr;
           jump = FALSE;
        }
     }
     break;
     case 2: /* Customer number */
       if (new_record == TRUE)
       /* only process this field if a new record */
       {
          if (!inpm_n(&order_header.
                                customer_number))
          {
             customer.customer_number =
                     order_header.customer_number;
             if (!rec_fetch_main(EQUAL,&customer.l))
               {
                  /* return to this field if
                         customer does  not exist */
                  got_the_record = FALSE;
                  *f = field_nr;
                  jump = FALSE;
               }
               else /* get and display the date and
                     time of order */
               {
                  tf_get_date_time(&order_header.
                        order_date,order_header.
                        order_time);
                     /* get current date and time */
                  show_d(m,13,&order_header.
                                        order_date);
                  show_t(m,14,&order_header.
                                        order_time);
               }
            }
            else
            {
               got_the_record = FALSE;
               new_record = FALSE;
               /* No processing on other fields */
               inpm_omit();
            }
         }
         else
           inpm_omit();
           /* No processing this field */
           break;
       case 4: /* delivery date field */
         if (new_record) /* only process this field
                if a new record */
           inpm_d(&order_header.delivery_date);
         else
           inpm_omit();
           /* No processing this field */
           break;
       default:
         inpm_omit();
         /* No processing on other fields */
         break;
   }
}

void disp_order()
{
   show_l(ORDER_MAP,1,order_header.order_number);
   show_n(ORDER_MAP,2,order_header.customer_number);
   show_d(ORDER_MAP,4,&order_header.delivery_date);
   show_n(ORDER_MAP,15,order_header.order_value);
   show_d(ORDER_MAP,13,&order_header.order_date);
   show_t(ORDER_MAP,14,&order_header.order_time);
}

boolean select_order()
{
   int flags;
   int field_nr = 1;
   boolean record_selected = FALSE;
   map_attach(ORDER_MAP,9,14,0);
   /* message: cursor keys to scan ... */
   /* let user to browse through the records */
   while (askm_special(14,&field_nr,CURS_KEYS,0)
        != ESCAPE)
   {
      switch (abort_code)
      {
         case 0:
           record_selected = TRUE;
           break;
         case LEFT:
         case UP_LINE:
         case PAGE_UP:
           flags = PREV;
           break;
         case RIGHT:
         case DOWN_LINE:
         case PAGE_DOWN:
         case SPACE_BAR:
           flags = NEXT;
           break;
         case LEFT_LINE:
           flags = FIRST;
           break;
         case RIGHT_LINE:
           flags = LAST;
           break;
         default:
           break;
      }
      if (!record_selected)
      {
         if (rec_fetch_main(flags,&order_header.l))
             /* find record */
           disp_order();
             /* display the record */
      }
      else /* record_selected == TRUE */
        break;
   }
   abort_code = NORMAL;
     /* prevent escaping several levels */
   map_clear_from(14); /* clear message map */
   return record_selected;
}

void get_order_cust()
{
   /* fetch the customer record for the selected
          order */
   customer.customer_number = order_header.
                                   customer_number;
   if (!rec_fetch_main(EQUAL,&customer.l))
     show_over(ORDER_MAP,9,16);
       /* message: Customer Details not found */
}

void disp_order_cust()
{
   show_c(ORDER_MAP,3,customer.name);
   show_c(ORDER_MAP,5,customer.address[0]);
   show_n(ORDER_MAP,7,customer.credit_limit);
   show_n(ORDER_MAP,10,customer.balance);
}
void edit_order()
{
   /* edit existing or new order line */
   boolean loop = TRUE;
   do
   {
      /* attach the pick map to layout map */
      map_attach(LAYOUT_MAP,2,PICK_LINE_MAP,
        RETAIN|MENUMAP);
        /* clear the messages field */
      field_blank(ORDER_MAP,9);
      /* message: Use cursor, Ins, Del and Enter
          keys */
      show_over(ORDER_MAP,9,8);
      switch (pick_line())
        {
           case 0:
             edit_line(pk_chosen_field > pk_range);
             break;
           case DELETE:
             if (map_yes(7,FALSE))
               del_line();
               break;
           case INSERT:
             edit_line(TRUE);
             break;
           case ESCAPE:
             if (map_yes(9,FALSE))
               loop = FALSE;
               break;
             default:
               break;
        }
          /* remove the pick line map from
                layout map */
        map_clear_from(PICK_LINE_MAP);
  /* at this point, you might re-calculate, display and post a new order total.
   Also, it may be a good idea to renumber the all order.line values,
   otherwise this will keep increasing and will have "holes" in
   the sequence.*/
   }while (loop);
}

int pick_line()
{
   /* pick orderline with pick_record */
   int retcode;

   /* set up prime key */
   order_line.order_number =
                order_header.order_number;
     /* open buffer for order lines */
   ord_line_buffer = bf_open(&order_line.l,
          sizeof(order_line.order_number) +
          sizeof(order_line.line),0,BF_INTERACTIVE);
     /* pick_record on the second key */
   retcode = pick_record(PICK_LINE_MAP,0,0,
     fetch_line,show_line,save_order_line,
     sizeof(struct SAVE_ORDER_LINE),NUM_ORDER_LINES,
     RETAIN|NO_DISP|ALLOW_NEW|ALLOW_DELETE, 0);
     /* discard order_line buffer */
   bfclose(ord_line_buffer);
   return retcode;
}

boolean fetch_line(flag)
  int flag;
{
   /* fetch function for order_line pick_record */
   boolean rec_fetched;
   /* copy key fields from element 0 */
   if (flag == EQUAL)
   {
      order_line.line = save_order_line[0].line;
      rec_fetched = bf_reset(ord_line_buffer,
                   &order_line.l,0);
      /* note: if not using bf_xxxx functions the
       following call to the Database would be made:
   rec_fetched = rec_fetch_main(flag,&order_line.l);
   */
   }
   else
      /* fetch the record with depth of 1 */
   rec_fetched = bf_fetch(ord_line_buffer,
         flag|DEPTH,&order_line.l,0,0,1);
      /* note: if not using bf_xxxx functions the
      following  call to the Database would be made:
      rec_fetched = rec_fetch_main(flag|DEPTH,
             &order_line.l,1);
      */
      if (rec_fetched)
      {
         save_order_line[0].line = order_line.line;
         MOVE(save_order_line[0].material_code,
                        order_line.material_code);
         /* set up material record key */
        MOVE(material.material_code,
                        order_line.material_code);
         /* look for the material record in the
             cache */
        if (!cache_find(&material.l) &&
                 rec_fetch_main(EQUAL,&material.l))
        {
           /* if not in cache, and got from
                          Database, put in cache */
           cache_post(&material.l);
        }
        MOVE(save_order_line[0].description,
                material.description);
        save_order_line[0].price = material.price;
        save_order_line[0].quantity =
                             order_line.quantity;
        save_order_line[0].line_value =
                  order_line.line_value;
   }
   return rec_fetched;
}

void show_line(m, fieldnr)
  int m, fieldnr;
{
   /* show function for order_line pick_record */
   if (fieldnr >= range)
     show_over(m,fieldnr,5);
   else
   {
      map_attach(m,fieldnr,DISP_LINE_MAP,DEFAULT);
      show_c(DISP_LINE_MAP,1,
           save_order_line[fieldnr].material_code);
      show_c(DISP_LINE_MAP,2,
           save_order_line[fieldnr].description);
      show_n(DISP_LINE_MAP,3,
           save_order_line[fieldnr].price);
      show_i(DISP_LINE_MAP,4,
           save_order_line[fieldnr].quantity);
      show_n(DISP_LINE_MAP,5,
           save_order_line[fieldnr].line_value);
      map_glue(m,DISP_LINE_MAP);
   }
}

void del_line()
{
   /* delete line from the list */
   rec_kill_ignore(&order_line.l);
   /* delete the record */
   db_checkpoint();
   /* commit the change */
}

void edit_line(new_item)
  int new_item;
{
   /* set global flag indicating new order_line or
     not */
   new_line = new_item;
   /* if new order_line, calculate next line number
       */
   if (new_line) order_line.line =
       rec_fetch_main(LAST|DEPTH,&order_line.l,1) ?
           order_line.line + 1 : 1;
   /* work out position of DISP_LINE_MAP and put up
          edit line map in-situ */
   map_attach(PICK_LINE_MAP,new_line ? pk_range + 1
         : pk_chosen_field,  DISP_LINE_MAP,DEFAULT);
   /* process the picked line fields */
   if (!map_get_inputs(DISP_LINE_MAP,1,line_inner,
        NO_DISP|(new_line ? 0: PREDISPLAY)))
     rec_post_commit(&order_line.l);
     /* commit record to database */
}

void line_inner(m,f)
  int m; int* f;
{
   int field_nr;
   field_blank(ORDER_MAP,9);
   /* clear the messages field */
   show_over(ORDER_MAP,9,18);
   /* message: order line selected */
   switch (field_nr = *f)
   {
      case 1: /* material code */
        if (new_line)
        {
           if (!inpm_u(order_line.material_code))
           {
               MOVE(material.material_code,
                      order_line.material_code);
               /* look for the material record in
                  the cache */
               if (!cache_find(&material.l))
               {
                  if (rec_fetch_main(EQUAL,
                                     &material.l))
                     cache_post(&material.l);
                     /* from Database, so put in
                          cache */
                  else
                    /* not valid material record so
                     return to this field */
                  {
                     show_over(ORDER_MAP,9,17);
                     /* message: cannot find
                          record */
                     map_draw(10,ERRORMAP|REMARK);
                     *f = field_nr;
                     jump = FALSE;
                  }
               }
            }
         }
         else
         /* don't allow editing of this field */
         {
            show_c(m,field_nr,order_line.
                   material_code);
            inpm_omit();
         }
         break;
       case 2:
         /* material.description: display only */
         show_c(m,field_nr,material.description);
         inpm_omit();
         break;
       case 3: /* material.price: display only */
         show_n(m,field_nr,material.price);
         inpm_omit();
         break;
       case 4: /* quantity */
         if (!inpm_i(&order_line.quantity))
         /* user has specified quantity */
         order_line.line_value =
             material.price * order_line.quantity;
         break;
       case 5:
         /* order line total value: display only */
         show_n(m,field_nr,order_line.line_value);
         inpm_omit();
         break;
       default: /* skip all other fields */
         inpm_omit();
         break;
   }
}


cache_lock()

Purpose

Gets a pointer to a record into the cache

Syntax

ANY * cache_lock(short* datarec)

Parameters

short* datarec

Pointer to the data

Description

This is a more specialised version of the cache_find() function. In this version, a pointer to the data is returned. The data is not copied into the record. The pointer points to the data directly, not to a data header because none is present. The cache_lock() function is faster than the cache_find() function; no copying is done. You must call the cache_unlock() function, passing the value returned by cache_lock(), when you have finished with the data, otherwise you cannot discard the data from the cache.

Note that it is not permissible to call any other cache_xxxx function on a particular record while that record is locked with cache_lock().

Return values

Returns 0 when the data is not found, otherwise a pointer to the data


cache_post()

Purpose

Adds a record to the cache

Syntax

BOOLEAN cache_post(short * datarec);

Parameters

short * datarec

Pointer to the record to be added

Description

The cache_post() function is called to add data to the cache. Note that you may do either of the following:

  • Repost changed data to the cache
  • Change the record length

Return values

Returns TRUE if the record is added to the cache and FALSE otherwise

Example

See the example for the cache_find() function


cache_start()

Purpose | Initialises or reinitialises the cache
Syntax | BOOLEAN cache_start(void);
Parameters | None
Description | When the cache_start() function is called all the existing records in the cache are discarded. There is no need to call this function explicitly, as the first call to cache_describe() causes it to be called.
Return values | Returns TRUE if there is enough memory for a cache and FALSE otherwise


cache_unlock()

Purpose | Unlocks a locked record
Syntax | void cache_unlock(any *data);
Parameters | ANY *data | Pointer to the locked record
Description | Unlocks data locked by the cache_lock() function. It is your responsibility to ensure a valid pointer is passed. Passing an invalid pointer will lead to significant problems later (possibly much later).
Return values | None


copy2child()

Purpose

Copies the fields of a parent record to the fields of its child record. This function is regarded as obsolete in 4.500

Syntax (CCOWN)

int copy2child(short * from_record, short * to_record, role)

Syntax (DP4DBAPI)

BOOLEAN dp4_copy2child(DCONN * dconn,short *to_record,short *from_record,const char *role);

Parameters

short DP4P from_record

Pointer to L field of parent record

short DP4P to_record

Pointer to L field of child record

const char DP4P role

Role of index

Description

The copy2child() function copies the key fields of the given parent's child index from the parent record to the child. This involves a call to the database manager to perform the field transcriptions so it is inefficient, particularly in networking applications. Normally, the transfer is explicitly done in the program.

This function has the advantage that it remains valid if the child index details of the parent are changed.

Note that the order of the from and to parameters is interchanged between the DP4DBAPI and OWN variants of this function.

Return values

Returns FALSE if the key fields are already the same, otherwise TRUE

See also


copy2parent()

Purpose

Copies the fields of a child record to the fields of its parent record. This function is regarded as obsolete in 4.500

Syntax

int copy2parent(child_rec, par_rec, role)

Parameters

short DP4P child_rec

Pointer to L field of child record

short DP4P par_rec

Pointer to L field of parent record

const char DP4P role

Role of index

Description

The function copy2parent() copies the key fields of the given parent's child index from the child record to the parent record. This involves a call to the database manager to perform the field transcriptions so it is inefficient particularly in networking applications. Normally the transfer is explicitly done in the program.

The function has the advantage that it remains valid if the database parent's child index details are changed.

Return values

Returns FALSE if the key fields are already the same, otherwise TRUE

See also


cycle()

Purpose

Cycle through parent's child records. This function is regarded as obsolete in 4.500 and from 4.620 is no longer included in the DP4 C library (the source is on the DP4 CD Rom if you require it for maintaining old programs.

Syntax

int cycle(child_rec, par_rec, role)

Parameters

short* child_rec

Pointer to child's L field

short* par_rec

Pointer to parent's L field

char* role

Role of parent - child index

Description

The cycle() function can be used to cycle through the children of a record. This is an old function and explicit use of the rec_fetch() function is the preferred method.

This function is implemented using the functions copy2child() and ftch2nd().

It uses the global variables firstitem and itemsdone to control its action. To use this function, set up the key fields in the parent record and set firstitem to TRUE.

Cycle will return FALSE when there are no more records to fetch (itemsdone is then set to TRUE.)

Return values

Returns TRUE if a child record has been found, otherwise FALSE

See also

reach()

Example

/*
   C Functions Reference Manual
   ----------------------------
   Filename: CYCLE.C
   Example: cycle
   Purpose: cycle through order header for a
       customer
*/

/*
#db salesord
#c
#tables customer,
        order_header;
#end
*/
#include "dp4.h"
#include "cycle.h"
   /* generate this file by running LIBMAKE */
 /* prototypes */
int get_customer(void);
void disp_order(void);
void process()
{
   map_datamap(1);
   map_datamap(2);
   while (get_customer())
     disp_order();
}

int get_customer()
/* get a customer from user */
{
   for (;;)
   {
     if (askf_n(2,1,&customer.customer_number))
       return FALSE;
     if (rec_fetch_main(EQUAL,&customer.l))
       return TRUE;
     else map_remark(4);
   }
}

void disp_order()
 /* display orders one at a time */
{
   firstitem = TRUE;
   while (cycle(&order_header.l,&customer.l,"")
      && abort_code != ESCAPE)
   {
      map_draw(3,RETAIN);
      show_l(3,1,order_header.order_number);
      show_n(3,2,order_header.order_value);
      show_d(3,3,&order_header.delivery_date);
      jump = FALSE;
      askf_special(3,4,0);
   }
}
>


Functions D

  • date_add_number()
  • date_compare()
  • date_ok()
  • date_subtract_date()
  • date_to_number()
  • db_checkpoint()
  • db_close()
  • db_commit()
  • db_decommit()
  • db_get_generation()
  • db_get_flag()
  • db_get_status()
  • db_open()
  • db_update()
  • df_make_name()
  • dp4_error_status()
  • dp4_halt()
  • dvc_check()
  • dvc_choose()
  • dvc_close()
  • dvc_get_inp_source()
  • dvc_get_status()
  • dvc_open()
  • dvc_set_inp_mask()
  • dvc_set_subdevice()


date_add_number()

Purpose | Add a number of days to a date
Old name | #define date_plus_number date_add_number
Syntax | datetype* date_add_number(date, number)
Parameters | datetype* date | Pointer to the date to use
 | long number | The number of days to add
Description | The date_add_number() function adds the number of days given in the parameter number to the parameter date specified and calculates the resulting date. The date pointed to by the parameter date is unaltered. This function is implemented as: number_to_date(date_to_number(date) + number);
Return values | Returns pointer to new date
Example | app_start_date = *date_add_number(app_start_date,7);


date_compare()

Purpose | Compares two dates (new version)
Syntax | int date_compare(datetype* date1, datetype* date2);
Parameters | datetype* date1 | One of the dates for comparison
 | datetype* date2 | The other date for comparison
Description | The date_compare() function compares two dates to test whether they are the same or if not, which one is the earlier and which the later. This function is more efficient than date_subtract_date().
Return values | Returns 1 if date1 is after date2, -1 if date2 is after date1 or 0 if they are the same


date_ok()

Purpose | Checks that a date is valid
Old name | #define valid_date date_ok()
Syntax | int date_ok(date)
Parameters | datetype* date | Pointer to date to check
Description | The date_ok() function checks to see if a given date is valid in the Gregorian calendar. It does not have to be within the range of dates acceptable to the other arithmetic functions that deal with dates.
Return values | Returns TRUE if date is valid, otherwise FALSE
See also | date_to_number(), number_to_date()
Example | if (!date_ok(&due_date));


date_subtract_date()

Purpose | Returns the number of days between two dates
Old name | #define date_minus_date date_subtract_date
Syntax | long date_subtract_date(date1, date2)
Parameters | datetype* date1 | Pointer to first date
 | datetype* date2 | Pointer to second date
Description | The date_subtract_date() function calculates the number of days by which date1 follows date2. This is negative if date1 is before date2, and zero if they are the same. It works on dates from 01 March 1600 to 31 December 9999 and uses the Gregorian calendar. It does not take account of changes from the Julian calendar which took place in English speaking countries in 1752.
Return values | Returns difference in days. (Remember that this is a long integer.)
See also | date_add_number(), date_to_number(), date_subtract_date(), number_to_date()
Example | date_subtract_date(&date1,&date2);


date_to_number()

Purpose

Converts date to number

Old name

#define date_2_number date_to_number

Syntax

long date_to_number(date)

Parameters

datetype* date

Pointer to date to convert

Description

The date_to_number() function converts a date to the number of days since 01 Mar 1980. It works on dates from 01 March 1600 to 31 December 9999 and uses the Gregorian calendar. It does not take account of changes in the Julian calendar which took place in English speaking countries in 1752 and in 1570 in Catholic countries.

This function is useful for avaluating the day of the week of any date. ((date_to_number(&date) % 7) +6) % 7) is 0 for Sunday through to 6 for Saturday.

Some software, especially on Unix, calculates dates and times as a number of seconds since 1 January 1970. This is day number -3712 so you can create a Unix time froma date using (date_to_number(&date)-3712)*3600*24.

Return values

Returns the number of days. (Remember that this is a long integer).


db_checkpoint()

Purpose

Writes and flushes all outstanding updates to the database

Old name

#define checkpoint() (db_checkpoint()==0)

Syntax (CCOWN)

int db_checkpoint(void);

Syntax (DP4DBAPI)

int dp4_db_checkpoint(DCONN * dconn);

Description

This function commits the current transaction like db_commit(), but in addition it guarantees that the transaction log file is flushed, and that therefore the transaction is recoverable. If the operation is successful, all records locked by the calling process are unlocked.

If the system crashes, the transactions up to the last successful checkpoint() are guaranteed to be recoverable. If the function db_commit() is used instead, the last few transactions may be lost,(but it is guaranteed that there will be no partial transactions).

Return values

Returns 0 if the commit succeeds or and error code indicating the reason for failure. The CCOWN API stores the return value in the global variable fail_code

See also

db_commit(), rec_post_with_flags(), db_decommit(), db_update()


db_close()

Purpose

Closes the currently open database

Old name

#define dbclose db_close

Syntax (CCOWN)

void db_close(void)

Syntax (DP4DBAPI)

BOOLEAN dp4_db_close(DCONN * dconn)

Description

Closes the currently open database (db_close()) or the specified database (dp4_db_close()). All databases are closed automatically when a program terminates, but calling db_close() ensures that the user is correctly prompted to remove the log disk if logging to removable media.

For the OWN API the current open database is the one determined by the global variable db_nr .

For C programs LIBMAKE generates a call to db_close() in the function finish() unless you specify the #noinit toggle.

For the DP4DBAPI version of the function the return value is TRUE if the function completes normally, and FALSE if the function encountered an error. Whatever the return value is, the DCONN handle is invalid upon return from dp4_db_close().

See also


db_commit()

Purpose | Commits updates to a database
Old name | #define commit() (db_commit()==0)
Syntax (CCOWN) | int db_commit(void)
Syntax (DP4DBAPI) | int dp4_db_commit(DCONN * dconn)
Description | The function db_commit() attempts to write the pending updates to the database, making any checks specified in previous calls to the functions rec_post_with_flags(), rec_verify() and rec_kill().
Return values | Returns 0 for success or an error code indicating the reason for the commit failure. The OWN version of the funcions stores the return value in the global variable fail_code
See also | db_checkpoint(), db_decommit(), rec_post_with_flags()


db_decommit()

Purpose | Rolls back all updates from last call to the functions db_commit() or db_decommit()
Old name | #define decommit db_decommit
Syntax (CCOWN) | void db_decommit(void);
Syntax (DP4DBAPI) | BOOLEAN dp4_db_decommit(DCONN *dconn);
Description | Causes all pending updates for the transaction to be cancelled. For the DP4DBAPI version of the function the return value is TRUE if the function completes without error.
See also |


db_get_flag()

Purpose

Obtains a database header entry from a DP4 database

Old name

#define get_db_flag db_get_flag

Syntax (CCOWN)

int db_get_flag(int flag_nr);

Syntax (DP4DBAPI)

int dp4_db_get_flag(DCONN * dconn,int flag_nr);

Parameters

int flag_nr

Value indicating which header flag is to be retrieved

This parameter used to be an offset from the nr_system field of the header record (struct DATABASE) stored at the beginning of the index database file. However as of release 4.622 the values are logical flags, and not all flag values corrsespond to the offset of the flag to be read.

Description

The db_get_flag() function obtains the entry for the specified flag in the database header record (struct DATABASE in btrees.h) .

This function is primarily for internal use. The permitted flag values are given in dbflags.h. Most useful values in the header can more easily be accessed by fetching the virtual db_status record for the database.

Return values

Returns the value read from the database header record

Example

/* This returns the value of nr_system */
db_get_flag(DBF_NR_SYSTEM)


db_get_generation()

Purpose

Obtains the latest generation of the active database

Old name

#define get_generation db_get_generation

Syntax (CCOWN)

int db_get_generation(void);

Syntax (DP4DBAPI)

int dp4_db_get_generation(DCONN * dconn);

Description

The db_get_generation() function obtains the generation number of the active database. It does not change the generation in which the database is currently opened. For CCOWN prograsm this is determined by the global variable db_generation. For DP4DBAPI programs it is determined by the generation specified in the call to dp4_db_open()

To open a database at the latest generation, call the function db_open() with a generation of 0, then either set the global variable db_generation to the value returned by the function db_get_generation(), or call db_open()/dp4_db_open() again, this time specifying the return value from this function. Please refer to the advice given in the information on db_generation.

Return values

Returns the latest generation number

See also

open_database()


db_get_status()

Purpose | Obtains current status of database. (You must #include "dbflags.h" to compare the status against the flags shown below.)
Old name | #define get_status db_get_status
Syntax (CCOWN) | int db_get_status(void);
Syntax (DP4DBAPI) | int dp4_db_get_status(DCONN * dconn);
Description | The function db_get_status() obtains the status of the active database. The return value is one of the following values given in the header file DBFLAGS.H.
 | Value | Meaning
 | DBS_NORMAL | Normal status (0)
 | DBS_BACKUP | The database must be backed up.
 | DBS_MAKELINK | MAKELINK needs to be run on the database
 | DBS_MKLNK_REORGDB | MAKELINK needs to be run on the database, followed by REORGDB
 | DBS_SETLINK | SETLINK needs to be run against the database. (This flag is now redundant)
 | DBS_STLNK_REORGDB | SETLINK needs to be run on the database, followed by REORGDB
 | DBS_REORGDB | REORGDB needs to be run on the database
Return values | Returns the status value


db_open()

Purpose

Opens a database

Old name

#define dbopen db_open

Syntax (CCOWN)

int db_open(const char *name, int generation, int flags,ADDITIONAL)

Syntax (DP4DBAPI)

DCONN * dp4_db_open(CONN * conn, const char *name,int generation,int flags, int reserved,dp4_swapper swapper,struct DP4_SWAP swap_table[])

Parameters

CONN * conn

Connection handle, as returned by an earlier call to dp4_srv_connect()

char* name

Null-terminated string specifying name of database to be opened

int generation

Generation in which the database is opened

int flags

Specifies the mode in which the database is opened

The value of the flags parameter determines the mode in which the database is opened. This is a combination of the following:

Value

Meaning

SHARED

Open the database in shared mode

READ_ONLY

Open in database in read only mode. Posting records to the database causes the program to be terminated

EXCLUSIVE

Open the database in exclusive mode. No other users are able to access the database. If the database is already in exclusive mode the program terminates

QUIT_ON_FAILURE

If a commit fails, a DP4 system error will be generated. Usually the program will terminate.

NO_LOGGING

Open the database with no logging (only effective with EXCLUSIVE)

NO_ROLLBACK

Open the database without rollback (only effective with EXCLUSIVE)

RETRIEVE_ENABLE

Allows the subsequent use of the map_retrieve function

FORCE_ENTRY

Opens the database even if it has been opened exclusively by another program. This value is reserved for internal use, and should not be used in applications.

PRIVATE_DB

Open the database using the private database facility. All updates (from calls to rec_post() or rec_kill() are effective immediately as far as the owning process is concerned. No other process can see the updates until the next call to db_update() or a similar function.

The database fetch functions with flags such as NEXT or PREV see the updated records even though the changes have not been committed.

A call to db_update(), db_commit() or db_checkpoint() is still required to make permanent updates to the database

BIG_TRANSACTIONS

Supplies a hint to the database manager that the program expects to perform very large transactions (in execess of several hundred updates without a commit). The database manager will allocate a larger than usual buffers for storing pending transactions for this program, to avoid performance degradation.

This flag should not be used unless absolutely necessary. If at all possible you should redesign your application to avoid the need for excessively large transactions.

MISSING_OK

db_open() normally generates DP4 system error 15 or 16 if one of the database files is mising. If thsi flag is specified and the database does not exist, db_open() returns -1

int reserved

Pass 0

This parameter is reserved for internal use.

dp4_swapper swapper

On big endian platforms this parameter must point to a function used for swapping data records between the native format and the DP4 internal format. On PC and other little endian platforms this pointer should be 0.

struct DP4_SWAP swap_table[]

On big endian platforms this parameter must point to the swap structure to be used for swapping the tables on this database. This is generated using the #swap directive in LIBMAKE. On PC and other little endian platforms this parameter should be 0.

Description

The db_open() function opens the database specified by the parameter name. All subsequent database processing is then for this database.

To override the database name in the program, use the command tail:
-DB <DBNAME>

LIBMAKE automatically generates a call to db_open() with the latest value for the generation parameter. The program DBCHECK also displays this value.

Certain LIBMAKE directives have the same effect as particular values of the flags parameter, as shown below:

LIBMAKE Directive | Equivalent Flag
#exclusive | EXCLUSIVE
#private | PRIVATE_DB
#no_logging | NO_LOGGING
#quit | QUIT_ON_FAILURE

For db_open() the command tail -db dbname can be used to override the database name passed to db_open(). (This normally only works for the first call to db_open())

You may have more than one database open at a time. To do this, save the value of the global variable db_nr after a call to db_open(), then reset the global variable db_nr back to the saved value after opening another database.

The SYSTEM database is initially open for users; the global variable db_nr has the value 0.

Return values

db_open() returns the value of the global variable db_nr if successful, otherwise -1 if the program does not terminate.

If dp4_db_open() succeeds it returns a handle of type DCONN * to be passed to other dp4_xxx() functions. Otherwise it returns 0. Error information can be found by calling dp4_error_status().

See also

db_close(), db_nr

Example

db_open("time2",0,SHARED+QUIT_ON_FAILURE);
timedbnr = db_nr;
db_open("sop2",0,SHARED+QUIT_ON_FAILURE);
sop2 = db_nr;
/* to process time2 database */
db_nr = timedbnr;
/* etc */
/* to process sop2 database */
db_nr = sop2dbnr;
/* to load a system mapset */
db_nr = 0;
map_load("SYSTEMMP");
/* etc */


db_update()

Purpose

Updates the database

Syntax (CCOWN)

int db_update(int flags)

Syntax (DP4DBAPI)

int dp4_db_update(DCONN * dconn,int flags

Parameters

int flags

Parameter specifying how database is to be updated

The parameter flags is a combination of the following values:

Value

Meaning

COMMIT

Equivalent to calling the function db_commit()

CHECKPOINT

Equivalent to calling the function db_checkpoint()

FLUSH_LOG

Ensures that the transaction can be recovered if there is a crash.

UNLOCK_IF_OK

Unlocks any records only if the update was successful

UNLOCK_ALWAYS

Always unlocks any records regardless of the success of the function

FAIL_IF_ERROR

If an error occurs, a system error will be generated explaining the reason for the failure, and the program will be terminated.

MSGE_IF_ERROR

If an error occurs, a system error message is displayed but the program is not halted

CHECK_ONLY

For internal use only

UPDATE_ONLY

For internal use only

Description

The db_update() function is used to update the database in the way specified by the parameter flags.

Return values

Returns 0 for success or a value indicating the reason for the failure. See the documentation of the global variable fail_code

The CCOWN version of the API stores the return value of the function in the global variable fail_code.

For each reason for failure there is a corresponding map with number (50 + error_code) in the mapset of the SYSTEM database, (and in the DP4 configuration file errmsg section). If the parameter flags contains either of the values MSGE_IF_ERROR or FAIL_IF_ERROR, this information is displayed. However this will depend on how the application is handling DP4 errors, so in some programs these flags will have no effect.

See also


df_make_name()

Purpose

constructs the file name from its component parts

Old name

#define _nmfl df_make_name

Syntax

char* df_make_name(prefix, name, suffix, suff_over_ride)

Parameters

char* prefix

Prefix to use in constructed file name

char* name

Name of file

char* suffix

Suffix to use in constructed file name

int suff_over_ride

Indicates whether suffix in file name should be overidden

Description

The df_make_name function constructs a file name from a prefix, a name and a suffix. The prefix indicates the disk and overrides that given in the name. The suffix contains 3 characters or less (except with UNIX). Typical suffixes used in DP4 are .PRN, .RLB, .DAT and .IND

Return values

Returns null-terminated pointer to the constructed name

See also

/*
 C Functions Reference Manual
 ----------------------------
 Filename: DF_MAKE_.C
 Example: df_make_name
 Purpose: program illustrates df_make_name by asking
          user to specify prefix, name and suffix
          strings, and generating the result
*/

/*
#db salesord
#c
#end
*/
#include "dp4.h"
#include "userdata.h"
#include "df_make_.h"
 /* generate this file by running LIBMAKE */

static char suffix[12];
static char prefix[4];
static char name[32];
static boolean suff_over_ride;
 /* prototypes */
void nmfl_inner(int,int*);
void zero_term(char*,int);
void demo_nmfl(void);
void process()
{
   trm_read_user();
   /* only required if using Microsoft C: a bug in
      the linker causes an unresolved external error
      in the DP4 library */
   map_draw(1,RETAIN);
   while (map_get_inputs(20,1,nmfl_inner,
          CLEAR|GI_GLOBALS) != 0);
}

static void zero_term(char* name,int max_len)
{
   register int i = 0;
   while (*name != ' ' && i < max_len)
   {
      i++; name++;
   }
   *name = '\0';
}

static void nmfl_inner(int m,int* f)
{
   /* get entries - forget about bad entries */
   switch (*f)
   {
      case 1:
        inpm_c(prefix);
        break;
      case 2:
        inpm_c(name);
        break;
      case 3:
        inpm_c(suffix);
        break;
      case 4:
        inpm_y(&suff_over_ride);
        break;
      case 5:
        /* null terminate all names used */
        zero_term(prefix,sizeof(prefix) - 1);
        zero_term(name,sizeof(name) - 1);
        zero_term(suffix,sizeof(suffix) - 1);
        showc(m,*f,df_make_name(prefix,name,suffix,
                 suff_over_ride));
        /* Fall through to omit */
      default:
        inpm_omit();
        break;
   }
}


dp4_error_status()

Purpose

Check whether the preceding call to a DP4 interface function encountered an error.

Syntax

int dp4_error_status(const CONN *conn,int *error_level, int *error_nr,int *error_p0,int *error_p1);

Parameters

CONN * conn

Handle to DP4 connection for which the error status is to be checked

int * error_level

Pointer to variable which receives the error level

int * error_level

Pointer to variable which receives the error number

int * p0
int * p1

Pointer to variables which receive the additional error parameters

Description

The function dp4_error_status() is used to check the error status of a DP4 connection. This function must be called immediately after a DP4 API has been called, and before any further DP4 APIs are called, otherwise at least part of the error information will be lost: the error_level information is updated after every call to a DP4DBAPI function that accesses the database manager, and the stored error level is set to 0 if such a call succeeds. (However, the error number and additional parameters are only updated when an error is encountered.)

The exit code is passed back to the operating system, so that programs calling the function dp4_error_status() can be used in batch files, and in other applications where the exit code is required.

Return values

Returns the same value as is placed in *error_level, in other words a non zero return value indicates that the last call to the DP4 database interface encountered an error.

See also

Error handling with DP4DBAPI


dp4_halt()

Purpose

Exits the program

Old name

#define halt() dp4_halt(1)

Syntax

void dp4_halt(exit_code)

Parameters

int exit_code

Exit code to pass back to the operating system

Description

The function dp4_halt() is used to exit a DP4 program. You should use this function instead of exit(), otherwise your program may not close its connection to DP4 correctly. dp4_halt() first calls app_close() and only actually exits if app_close() returns a non zero value

The exit code is passed back to the operating system, so that programs calling the function dp4_halt() can be used in batch files, and in other applications where the exit code is required.

See also

app_close()

Example

/*
 A serious error message might be implemented as
*/
void bad_error(int err)
{
   map_pause(err);
   dp4_halt(err);
}


dvc_check()

Purpose

Checks the status of the printer

Old name

#define check_printer dvc_check

Syntax

void dvc_check(void);

Description

The dvc_check() function checks the current status of the printer. You should call it after the printer is selected, after each call to the function prn_print().

The global variables print_error and pr_err_mode govern how this function responds to an error. When an error occurs following a call to prn_print(), the global variable print_error is set to a non-zero value and a subsequent call to the function dvc_check() handles the error. The action taken by dvc_check() depends on the value of the global variable pr_err_mode:

pr_err_mode
values

Action

0

The following error message is dislayed and the program aborts:

DP4 System error 129. Origin: Device driver
Error while writing to printer/device
Consult your system documentation for more details
Press any key to continue

1

The above message is displayed, but the user has the option to abort the program by pressing the <Esc> key or to continue by pressing any other key

2

No error message is displayed, it is up to the application to test the global variable print_error and display an appropriate message

3

This is for situations where a DDI is interfacing with the system and a special version of PROGRUN is being used. Errors flagged by the DDI cause the 'hot-key' functionality to be invoked to handle the error. Refer to the DP4 Device Integration Developers Manual for more details

If the global variable pr_err_mode is 0, the error message is displayed by the function sys_error(), otherwise it is displayed by the function sys_request(). The function sys_error() aborts the program, but sys_request only aborts the program if the user presses <Esc>.

You can write your own version of dvc_check() if you wish to translate the error numbers into more meaningful messages for your application.

See also

prn_print()


dvc_choose()

Purpose

Selects a printer

Old name

#define ask_output_device dvc_choose

Syntax

int dvc_choose(device, printer_nr, file_name, ADDITIONAL)

Parameters

int device_flags

Indicates where output is to send plus other options as detailed below

int printer_nr

Number of the DFSETUP POD device to be used. If you need to hard code a device you should use DFSETUP to generate a suitable include file.

char *file_name

File name used for output if FILE is selected for output

ADDITIONAL

Two optional variables, including the default suffix of the file name and also the fixed width

The device_flags parameter is a combination of the following values:

Flag Meaning

SCREEN

Output is sent to the screen. The data is displayed below any maps on the screen and is scrolled, or from release 4.621 and if the use_viewer setting is in effect, is displayed using the designated viewer program when dvc_close() is called. If a DFPRINT device is selected DFPRINT will default to Landscape format instead of Portrait format unless PRINTER is also specified. Normally DFPRINT works in interactive mode when SCREEN is specified. To print immediately in Landsape format, without user interaction you need to also specify NO_SELECT and NOCTL. (Note that this means printing will take place even though the PRINTER flag is not specified. If the DFPRINT device specifies that the output should be saved to a file, then in this situation the output will be saved to the appropriate type of file instead)

PRINTER

Output is sent to the printer determined by the parameter printer_nr. If the printer is in use, the output is saved in a file and is printed when the program is finished

FILE
DP4_FILE

Output is sent to the file named by the parameter file_name. This file defaults to the directory specified in the DP4 licence file. If the parameter file_name has no suffix, .PRN is added, or the extension specied as thr fourth parameter if FT_DEFAULT is also passed in device flags. The FILE pre-processor define makes it difficult to use ANSI C file output functions. You can prevent FILE from being defined by defining NO_ANSI_POLLUTE before including dp4capi.h. DP4_FILE is a synonym for FILE that is defined in the 4.620 header files.

The file is formatted using the characterstics of the device specified.

NOCTL

Used with DP4_FILE to prevent printer control characters from being sent to the file (they are still sent to the printer)

NO_APPEND

Forces the creation of a new print file by deleting any old file of the same name

FT_DEFAULT

If this flag is used, the ADDITIONAL (char*) parameter should also be used to give the default suffix for the output file. This is displayed in the Select output devices dialog box instead of the normal default "(.PRN)"

NONE_ALLOWED

This disables the validation that ensures at least one of SCREEN,PRINTER or FILE is chosen.

NO_SELECT

This stops the user from being asked any questions. However, if the printer is not successfully selected and the global variable pr_available is -1, this value has no effect unless the NO_RETRY flag is also used

FIXED_WIDTH

This causes all lines to be forced to the width given by a fifth parameter. If FT_DEFAULT is not used you need to specify a dummy fourth parameter.

NO_RETRY

If the output values have been obtained from the user and the global variable pr_available is -1, the user has to re-enter the values even if NO_SELECT is passed. If this flag is used, the function dvc_choose() returns FALSE and the global variable pr_available is set to -1

Description

The dvc_choose() function replaces the older function dvc_open() for selecting a printer.

Unless the flag NO_SELECT is used, the function displays the "Select output devices" dialog on the screen, with the initial default values are given by the parameters to dvc_choose(). The user can fill in the fields and select the logical printer from a pop up.

If an output file is chosen and the file already exists, a delete/append/cancel menu is offered. If the user chooses cancel or presses Esc at this point, the "Select output devices" dialog reappears (even if NO_SELECT was specified) unless dvc_choose() is called with the flag NO_RETRY, in which case the function returns 0 and the global variable pr_available is set to -1.

A program that calls dvc_choose() may be invoked with these command tails:

Tail | Effect
−PRINTER | This causes output to go to the printer as well as to any other selected output device
−DEVICE device_name | This sets the printer_number parameter to the corresponding device_name
−FILE file_name | This causes the output to go the file given by file_name as well as to any other selected output device. The specified file_name overrides the hard-coded file name
−SCREEN | This causes the output to go the screen as well as to any other selected output device.

The following global variables are set when dvc_choose() is called:

Global Variable

Set To

pr_page

1

pr_line

0

page_depth

The page depth of the printer as set up with DFSETUP

page_width

The page width of the printer as set up with DFSETUP

print_error

Error code returned when opening the printer. The global variable pr_err_mode affects how the program reacts to printer errors. See the function dvc_check()

pr_available

Set to 1 if the selected printer is available, or no printer was selected. If spooling is possible, the global variable pr_available contains 0, otherwise it contains -1

pr_started

Set to TRUE if the printer is opened successfully, and is reset to FALSE when the function dvc_close() is called

pr_filename

Contains the name of the output file if one is being used

pr_streams

Set to the updated device value. You should use this value and not the device value passed to the function to test which output devices are being used

pr_device

This holds the current printer number

Return values

Returns ESCAPE if the user has pressed Esc or 0 if the printer is not available but is being spooled. Returns 1 if the printer is available

See also

dvc_open()

Example

/*
 C Functions Reference Manual
 ----------------------------
 Filename: DVC_CHOO.C
 Example: dvc_choose
 Purpose: simple customer listing reoprt output to  devices.
*/
/*
#db salesord
#c
#libtype 1
#tables customer,
        index_cust_name;
#end
*/
#include "dp4.h"
#include "dvc_choo.h"
 /* generate this file by running LIBMAKE */
 /* prototypes */
void cust_report(void);
void process()
{
   map_draw(1,RETAIN);
   cust_report();
}
void cust_report()
{
   if (dvc_choose(PRINTER|SCREEN,0,"","") != ESCAPE)
   {
      int srch = FIRST;
      form(1);
      /* fetch and print all customers */
      while (rec_fetch(srch,&customer.l,_INDEX_CUST_NAME,0))
      {
         srch = NEXT;
         form(2);
         prnc(2,1,customer.name,DEFAULT);
         prnn(2,2,customer.customer_number,DEFAULT);
         prnc(2,3,customer.address[0],DEFAULT);
         prnn(2,4,customer.credit_limit,DEFAULT);
         prnn(2,5,customer.balance,DEFAULT);
         prn_print(FALSE);
      }
      dvc_close(0);
   }
}


dvc_close()

Purpose | Ends printing
Old name | #define end_print() dvc_close(1)
Syntax | void dvc_close(dvc_nr)
Parameters | int dvc_nr | The printer to be terminated
Description | The function dvc_close() releases the printer corresponding to the dvc_nr parameter and/or closes the output file. It calls the function prn_print() as follows: prn_print(TRUE); It also sets the global variable pr_started to FALSE.
See also |


dvc_get_inp_source()

Purpose | Obtains the source of input to a program
Old name | #define dvc_source dvc_get_inp_source
Syntax | int dvc_get_inp_source();
Description | The function dvc_get_inp_source() is used in conjunction with device drivers. It returns the source of the device from which the input came. This is an integer with the bit set corresponding to that set up in DFSETUP. The DP4 Device Drivers Developers Manual has more details on this function.
Return values | Returns the bitmask of the device of the input source
See also | dvc_set_inp_mask()


dvc_get_status()

Purpose

Obtains the status of the open device

Old name

#define dvc_status dvc_get_status

Syntax

int dvc_get_status(status)

Parameters

char* status

Pointer to an allocated area where the status can be placed

Description

The dvc_get_status() function is used in conjunction with device drivers. It describes the state of the currently open device, as determined by the last call to the function dvc_set_subdevice().

This function places a null-terminated string in the space pointed to by the status parameter. The meaning of this string is determined by the device driver protocol.

For example, a device driver may return an ASCII 0 if the current device is the cash drawer and it is closed and an ASCII 1 if it is open.

See the DP4 Device Integration Developers Manual for more information on this function.

Return values

Returns the length of the string inserted in the status area

See also


dvc_open()

Purpose

Selects a printer. (Use dvc_choose() or dvc_choose(NO_SELECT) in preference to this function.)

Old name

#define printer dvc_open

Syntax

boolean dvc_open(device, printer_nr, char*file_name, ADDITIONAL)

Parameters

int device

Determines where output is to be sent

int printer_number

Number of printer

char* file_name

Null-terminated string containing name of file (if used) to which output is sent

ADDITIONAL

Indicates fixed width if in use

The parameter device determines where to send output. It can take any combination of the following values:

Value

Meaning

SCREEN

Output is sent to the screen. For more information on the behaviour of this flag refer to the documentation of dvc_choose().

PRINTER

Output is sent to the printer determined by the parameter printer_number. If the printer is in use, the output is saved in a file and printed when the program finishes

FILE

Output is put in the file named by the parameter file_name. This file will default to the directory specified in the file DATAFIT.SYS. If the string in the parameter file_name is without a suffix, ".PRN" is added

NOCTL

Used with the value FILE to prevent the inclusion of printer control characters in the file

NO_APPEND

Forces the creation of the new print file by deleting the old file of the same name

SPOOL_ALWAYS

Causes the print output to be spooled even if the printer is available. This feature is only available under CDOS

OFFER_DELETE

If the output file already exists, a menu offering options to delete the file, append to it, or cancel printing is displayed. If cancel is selected or <Esc> pressed, the value FALSE is returned and the global variable pr_available is set to -1

Description

The dvc_open() function selects a device for subsequent printing. The printer_number is the number assigned to the printer when it was set up using the utility DFSETUP. A header file called DEVICES.H containing these numbers can be generated by selecting the option Generate Device Names in DFSETUP. This header file can then be included in C programs. Note that this may create illegal C macros which should be changed or deleted.

Normally, the value 0 is used as the parameter printer_number, because the printer can be specified as a command tail (see below).

In previous versions of the function dvc_open(), the user was offered a choice between retrying and aborting the program when the selected printer is not available. Now the user is offered a "retry or abort" message and if abort is chosen, the function returns FALSE and the global variable pr_available is set to -1.

The values held in the parameter device can be overridden with command tails:

Tail

Effect

-PRINTER

Causes output to go to the printer as well as to any other specified output device

-DEVICE <device_name>

Sets the parameter printer_number to the device corresponding to <device_name>

-FILE <file_name>

Sets the parameter <file_name>, causing output to go to this file as well as to any other specified output device

The following global variables are set when the function dvc_open() is called:

pr_page

1

pr_line

0

page_depth

The page depth of the printer as set up with DFSETUP

page_width

The page width of the printer as set up with DFSETUP

print_error

Error code returned when opening the printer. The global variable pr_err_mode affects how the program reacts to printer errors. See the function dvc_check()

pr_available

Set to 1 if the selected printer is available, or no printer was selected. If spooling is possible, the global variable pr_available contains 0, otherwise it contains -1

pr_started

Set to TRUE if the printer is opened successfully, and is reset to FALSE when the function dvc_close() is called

pr_filename

Contains the name of the output file if one is being used

pr_streams

Set to the devices chosen.

pr_device

This holds the current printer number

Return values

Returns TRUE if the printer is successfully selected, otherwise FALSE. If spooling is possible, FALSE is returned and the global variable pr_available is set to 0.

See also

form(), prn_x(), dvc_choose(), prn_print(), dvc_check()

Example

if (dvc_open(FILE+PRINTER,1,"c:output.doc") ||
      pr_available >= 0)
{..


dvc_set_inp_mask()

Purpose

Changes the input source to a program

Old name

#define dvc_redirect dvc_set_inp_mask

Syntax

void dvc_set_inp_mask(ipsource)

Parameters

int ipsource

Combination of sources from which to obtain input

Description

The function dvc_set_inp_mask() is used in conjunction with device drivers to change the source of input from devices other than the normal keyboard.

You can set up the input sources for a driver with the utility DFSETUP. For example, a device might have the following input devices set up:

Source Number

Device

1

Keyboard

2

MSR

3

Scanner

4

Scales

Each of the source numbers correspond to a bitmask of 2 to the power of the number. For example, to take input from the keyboard, scanner or the scales, set the ipsource parameter to 1 + 4 + 8.

See the DP4 Device Drivers Developers Manual for more information about this function.

See also

dvc_get_inp_source()


dvc_set_subdevice()

Purpose | Selects a subdevice
Old name | #define subdevice dvc_set_subdevice
Syntax | int dvc_set_subdevice(logical_device_nr)
Parameters | int logical_device_nr | Logical device number
Description | The dvc_set_subdevice() function selects the logical device with the specified internal number. The device should be a subdevice of the printer currently selected. To find the internal number required, use DFSETUP to generate the device names. The function dvc_check() is called after this function has tried to select the device.
Return values | The value of the global variable print_error


Functions E-F

• extract_close() | • field_force_size()
• extract_open() | • field_help()
• extract_read() | • field_highlight()
• extract_sort() | • field_map_length()
• extract_write() | • field_read()
• field_blank() | • field_skip()
• field_char_get() | • field_start()
• field_choose() | • form()
• field_colour() | • form_beside()
• field_cursor() | • form_over()
• field_eg_choose() | • form_test()
• field_end() | • freep()
• field_force_colour() | • freex()
• field_force_range() | • ftch2nd()


extract_close()

Purpose | Closes an extract file
Old name | #define close_extract extract_close
Syntax | void extract_close(void)
Description | The function extract_close() closes the extract file so that it is available for subsequent extracts.
See also | extract_open(), extract_write(), extract_sort(), extract_read()
Example | See the example in extract_open()


extract_open()

Purpose

Opens an extract file

Old name

#define open_extract extract_open

Syntax

void extract_open(filename, header)

Parameters

char* filename

Name of file to use in the extract

struct EXT_HEADER* header

Pointer to extract header structure giving extraction parameters

Description

The extract_open() function opens and prepares an extract file using the information in the extract header. The DP4 extract functions are used to sort a collection of records.

The parameter header defines a C structure named EXT_HEADER that is used to determine which fields are used, in order of precedence, in a subsequent sort.

The fields that make up the structure of an entry in the extract file are equivalent to a database record called EXTRACT. This is generated by LIBMAKE.

The struct EXT_HEADER is declared in the file dp4capi.h

To perform a sort on selected records from the database, follow the steps below:

1. Construct the header record and the corresponding extract record. Use LIBMAKE to do this automatically by including the #extract directive, as in:

#extract <field>, [<field>], … [<field>];

Each <field> corresponds to an entry in the extract file of the same name. By default, the fields are assumed to be database fields. A <field> may include a role, for example address.invoice

LIBMAKE determines the datatype, length and occurs value for each field from the database

Non-database fields can be included by adding the datatype, length and occurs values after the field name separated by colons, for example address:c:4:16. You can leave out some of these values as there are defaults, so value:n corresponds to a double-precision value. LIBMAKE issues an error message if you attempt to specify a database field with this notation

2. Define the sort required by using the LIBMAKE directive #sorton, as below:

#sorton <field>, [<field>], … [<field>];

This gives the list of fields in the extract file on which to sort, in order of precedence, starting with the highest

3. Call the function extract_open() to open an extract file. LIBMAKE normally generates this call in the function init

4. Write records to the extract file by assigning the required fields to the extract record and calling the function extract_write()

5. Call the function extract_sort() to sort the collection of records

6. If the sort is successful, read each of the records in turn by calling the function extract_read()

7. The actual sorting of records is done by the DP4 SORTEXR program which is called by the function extract_sort(). SORTEXR is called when the function trm_run_program() is called with the parameter batch set to FALSE and the copy_screen parameter set to TRUE. You may wish the copy_screen parameter to be FALSE, in which case you should write your own version of extract_sort() as shown in the example below

See also

extract_write(), extract_sort(), extract_read()

Example

/*
 C Functions Reference Manual
 ----------------------------
 Filename: EXTRCT_O.C
 Example: extract_open
 Purpose: simple report of order headers for a
          customer ordered by delivery data.
*/

/*
#db salesord
#c
#libtype 1
#tables customer,
        order_header;
#extract order_number,
         delivery_date,
         order_value,
         payment_value;
#sorton delivery_date;
#end
*/

/*
 Run 'libmake extrct_o.c' to generate the file
 extrct_o.h containing the definition of struct
 EXTRACT and initialisation of ext_header. We don't
 want to display any details of extract while the
 sort is being done so add to the init function:
 ext_header.suppress_output = TRUE;
*/

#include "dp4.h"
#include "open_ext.h"
 /* generate this file by running LIBMAKE */

 /* prototypes */
static void get_orders(void);
static int do_sort(struct EXT_HEADER*,char*);
void process()
{
   /* get a customer number */
   map_draw(1,RETAIN);
   map_draw(2,RETAIN);
   for (;;)
   {
      if (askf_n(2,1,&customer.customer_number))
        break;
        /* need to check valid customer */
      if (rec_fetch_main(EQUAL,&customer.l))
      {
         get_orders();
         break;
      }
      else map_remark(3);
   }
}

static void get_orders()
 /* get and sort details */
{
   /* extract_open("EXTRACT",&ext_header) has
    already been called by the init function */
   int count = 0;
   int srch = FIRST_BUT;
   /* fetch all orders of the customer */
   order_header.customer_number =
              customer.customer_number;
   while (ftch2nd(srch,&order_header.l,
               &customer.l,""))
   {
      srch = NEXT_BUT;
      count++;
      /* add to the extract file */
      extract.order_number = order_header.
                 order_number;
      extract.delivery_date = order_header.
                 delivery_date;
      extract.order_value = order_header.
                 order_value;
      extract.payment_value = order_header.
                 payment_value;
      extract_write(&extract.l);
   }

     /* check for no orders */
   if (!count)
   {
      map_pause(4);
      return;
   }
   if (!do_sort(&ext_header,"EXTRACT"))
   {
       /* something went wrong*/
      map_pause(7);
      return;
   }   /* fine - do report */
   if (dvc_choose(PRINTER|SCREEN,0,"","") != ESCAPE)
   {
      form(5);
      prnc(5,1,customer.name,DEFAULT);
      prnn(5,2,customer.customer_number,DEFAULT);
      while (!read_extract(&extract.l))
      {
         form(6);
         prnl(6,1,extract.order_number,DEFAULT);
         prnn(6,2,extract.order_value,DEFAULT);
         prnn(6,3,extract.payment_value,DEFAULT);
         prnd(6,4,&extract.delivery_date,DEFAULT);
         prn_print(FALSE);
      }
   }
}

static int do_sort(ext_header, filename)
  struct EXT_HEADER* ext_header;
  char* filename;
{
   return extract_sort();
   /* On MS-DOS you might decide to write your own version of extract_sort() as below,
      but it would probably be best just to modify the code in ccextrct.c */
#if 0
   char command[80];
   int fd;
   extract_close();
   strcpy(command,"sortextr ");
   strcpy(command+strlen(command),filename);
   trm_run_program(command,FALSE,FALSE);

   /* reopen file and check header */
   if ((fd = tf_open(filename,VF_RW|VF_SEQ|VF_RET))
              < 0) return (FALSE);
   tf_read(fd,ext_header,sizeof(struct EXT_HEADER));
   tf_close(fd);
   return ext_header->sorted;
#endif
}


extract_read()

Purpose

Reads a record from the extract file

Old name

#define read_extract extract_read

Syntax

int extract_read(data)

Parameters

short* data

Pointer to the L field of extract record to contain record that is read

Description

The function extract_read() reads a record from the extract file. The first call to this function resets the pointer to the start of the extract file.

Return values

Returns FALSE if a record has been read, otherwise TRUE

See also

extract_open(), extract_write(), extract_sort(), extract_close()

Example

if (!extract_read(&extract.l))
{..
/* See the example in extract_open */


extract_sort()

Purpose

Sorts an extract file

Old name

#define sort_extract extract_sort

Syntax

int extract_sort(void);

Description

The extract_sort() function sorts an extract file, using the DP4 program SORTEXTR.

The sorted field of the header record EXT_HEADER is set to TRUE by the SORTEXTR program and used to check that the sort was successfully run.

The extract file must have been previously created by a call to the function extract_open(). See this function for details of how to write your own sort program.

The progress of the sort program is normally displayed on the screen. To suppress this, set the suppress_output field of the header record EXT_HEADER to TRUE. If this is done, a 'please wait' message is displayed before calling the function extract_sort().

The function extract_sort() calls the function trm_run_program() with the following parameters:

Parameter

Description

name

"sortextr <extfile>", where <extfile> is the filename passed to the function extract_open()

batch

FALSE. Do not run as a batch program

copy_screen

TRUE. The screen as it is when sortextr completes is copied to this applications screen buffer. (This is only effective on MS-DOS,FlexOS, and OS/2), and may be undesirable.

If you want the third parameter to the function trm_run_program() to be FALSE, you must write your own version. This is shown in the example under extract_open().

Return values

Returns the sorted field of the EXT_HEADER header record

See also

extract_open(), extract_write(), extract_read(), extract_close()

Example

See the example in extract_open()

Remarks

The sort algorithm used is a variation on natural merge-sort:

  1. Records are added to an AVL balanced binary tree until the area allowed for in-memory sorting is full.

  2. Then the smallest record is removed from the in memory area and written to a file and a new record is added, and the process is repeated for each remaining record. Until and unless the new record is added becomes the smallest record it is known that the file and the in memory area are perfectly sorted.

  3. If a new record does become the smallest record all the remaining memory records are written to the file, so that a new in memory sort can be started. The size of each section in the file created is remembered for a later merge phase. At the original extract file is no longer needed as all the information is now in the partially sorted file.

  4. When the sort phase is complete, as many merge pahses as are required to reduce the number of sorted sub-sections of the file to 1 are performed. The merging uses a straight forward write to the file that is no longer required to ceate the merged file. The merge algortihm adds one record from each merge section to an AVL binary tree, and repeatedly removes the smallest node to the new sort file and adds a new node from the same merge section that the removed node came from.


extract_write()

Purpose | Writes a record to the extract file
Old name | #define write_extract extract_write
Syntax | void extract_write(data)
Parameters | short* data | Pointer to the L field of the extract record to write
Description | The functions extract_write() writes an extract record to the extract file. The definition of the extract record is normally generated by LIBMAKE. The L field of the extract record should be passed to the function extract_write() as the parameter data. See the function extract_open() for details of how to use the extract facility.
See also | extract_open(), extract_sort(), extract_close()
Example | See the example given in extract_open()


field_blank()

Purpose | Blanks out a field
Old name | #define blank field_blank
Syntax | void field_blank(mapnr, fieldnr)
Parameters | int mapnr | Number of map containing field
 | int fieldnr | Number of field to blank out
Description | The field_blank() function blanks out the contents of the specified field.


field_char_get()

Purpose

Returns input characters from a field

Syntax

void pascal field_start(int mapnr,int fieldner,
int flags)

Parameters

int mapnr

Number of map

int fieldnr

Number of field

int flags

Determines how function is used; see below

The parameter flags may take any of the following values:

Value

Meaning

insert_mode

This is used to toggle the cursor between overtype and insert mode shapes. Pass TRUE for the insert_mode caret and FALSE for the overtype caret

row, column

On return these may be filled in with the position of the caret in the field or with information indicating that another control has gained input focus. See Description below.

Description

The return value of the function field_char_get() is the character the user inputs, subject to the following exceptions:

  • Key presses of GR_REDIR (normally<Ctrl+B>) are not returned, but are used for the redirect program
  • If the user uses the mouse to move around within the field, this information is not returned to the program immediately. After each key press, however, row and column contain the character coordinates of the caret.
  • If the user clicks outside the control, the value GR_MDOWN, GR_DRAG or GR_MOUSE is returned to the program, depending on how the left mouse button was used. row and column now contain information about the control where the cursor is now located, as follows:
    • if row = -1, the mouse is outside all controls
    • if row >0, the mouse is being clicked on a control within the map whose number is contained in row

    • if row < 0 and > GR_FUNC, the mouse is being clicked on a map whose activating function key is contained in row. So clicking on a map having an activating function key or for which map_alter() has been used with MA_ACTIVATOR returns information of this sort.

    In the last two cases column contains the field number associated with the control

See also

field_start(), field_end()

Example

See the example given under field_start()


field_choose()

Purpose

Selects an option from menu

Old name

#define choose field_choose

Syntax

int field_choose(mapnr, data, flags)

Parameters

int mapnr

Number of map to select field from

int* data

Pointer to variable to hold selected field

int flags

Parameter specifying choice of key responses and other menu characteristics

The parameter data points to a variable which holds the initial field to be highlighted and the value of the final field selected.

The parameter flags can be any one of the following values:

Value

Meaning

HAS_PULL_DOWN

The menu has a pull-down menu attached

PULL_DOWN

The menu is a pull down menu. When it is specified and the user clicks outside the choice map, ESCAPE is returned

ABORT_FUNC_KEYS

This allows the use of the function keys to select an option

NR_KEYS

This allows the number keys to select an option

CURS_KEYS

This causes the <Ins>, <Del>, <Pg Up> and <Pg Dn> cursor keys to select the highlighted option and return with the global variable abort_code set to the appropriate value

NO_REARRANGE

This prevents fields from being brought to the top of a stack of overlaid maps. (This allows field_choose() to work usefully with attached maps).

The use of this flag was withdrawn from 4.603. Use the function map_glue() instead

NOT_MISSING_FIELD

This checks that the field chosen exists on the map

NO_WRAP

This makes the function field_choose() return with the value of the cursor key if the user tries to move outside the choice array

ABORT_MNEMONICS

This allows the use of a mnemonic map to get responses from the user. The mnemonic map must already be displayed on the screen and its number is passed as an additional variable. If the user clicks on the mnemonic map or presses <Enter>, 0 is returned. It is possible to distinguish between the values by examining the selected variable, as in:

  • When 0 is returned because <Enter> is pressed, selected is TRUE
  • When 0 is returned because of a click on the mnemonic map, selected is FALSE

Description

This function asks the user to select an option from a menu. The map must already be on the screen and have a number of its fields filled in with suitable text corresponding to the options available - this may be done with the functions show() or show_over() or simply by making the fields coincide with text on the map.

The highlighted field is displayed using the input colour of the corresponding field. The user changes the field by pressing the <Space> key or one of the cursor keys.

The function sets the global variable jump to FALSE so the user cannot skip past it. Pressing <Tab> selects the current highlighted field and sets the global variable jump to TRUE.

Pressing <Esc> selects the highlighted option and sets the global variable abort_code to ESCAPE, otherwise the global variable abort_code is set to zero.

If the global variable range is not zero, only options with numbers less than or equal to range can be reached.

The set of fields that the user can move the cursor to is defined by the map's row and column values, set up with MAPEDIT. If either of these values are zero, the function returns ESCAPE without stopping.

It is permissible for some of the fields to be missing - attempting to move the cursor to a missing field moves it to the next valid field instead. If a function key or number key is used to select an option, no check is made that a corresponding field exists on the map. This enables options to be selected by function keys only and not by moving the cursor. To prevent this, set the parameter flags to the value NOT_MISSING_FIELD.

Return values

Returns the value of the global variable abort_code

See also

map_choose()


field_colour()

Purpose

Permanently colours a field

Old name

#define colour_field field_colour

Syntax

void field_colour(mapnr, fieldnr, colour)

Parameters

int mapnr

Map containing field

int fieldnr

Field to highlight

int colour

Colour to use

The values and the interpretation of the colour parameter are the same as for the field_force_colour() function

Description

The field_colour() function permanently changes the colour of the contents of a field. This function is used in the internal construction of functions such as field_eg_choose() and map_bar_choose() and is unlikely to be useful except for building such specialised functions.

See also

field_force_colour()

Example

/* actionbar only needs these two calls to
      field_colour */
field_colour(mapnr,fieldnr,-C_OBSCURE);
field_colour(mapnr,fieldnr,-C_OPTION);


field_cursor()

Purpose

Offsets the logical cursor

Syntax

int field_cursor(row, column)

Parameters

int row

Number of rows by which to offset the cursor

int* column

Number of columns by which to offset the cursor

Description

The field_cursor() function is intended for use by developers wishing to create their own askf_x() or inpm_x() functions. It changes the logical cursor position by the specified numbers of rows and columns. This is relative to the top-left of the last field that was displayed with a show() function.

Example

field_start(mapnr, fieldnr, 0);
while (!finished)
{
   show(mapnr, fieldnr, 'C', data_size, INPUT_COLOUR);
   field_cursor(0, column);
   inchar = field_get_character(user.insert_mode);
   /* process inchar */
}
show(mapnr, fieldnr, data);
field_end(0);


field_eg_choose()

Purpose | Selects an option from a menu with greyed-out options, and displays extra information
Old name | #define eg_choose field_eg_choose
Syntax | int field_eg_choose(mapnr, option, flags, extra_show, ban_list, ADDITIONAL)
Parameters | int mapnr | Number of map containing menu to display
 | int* option | Pointer to variable to hold the chosen field
 | int flags | Parameter affecting display
 | void (*extra_show) (int, int, int) | Function to call when a field is highlighted
 | short* ban_list | Array of integers, zero-terminated, giving fields to grey out
 | ADDITIONAL | Extra parameter for mnemonic map if in use
 | The parameter flags can take any one of the following values:
 | Value | Meaning
 | HAS_PULL_DOWN | Menu has a pull-down menu attached
 | PULL_DOWN | Menu is a pull-down menu
 | ABORT_FUNC_KEYS | Allows the use of the function keys to select an option
 | NR_KEYS | Allows the number keys to select an option
 | CURS_KEYS | Causes the <Ins>, <Del>, <Pg Up> and <Pg Dn> cursor keys to select the highlighted option and return with the global variable abort_code set to the appropriate value
 | NOT_MISSING_FIELD | Checks that the chosen field exists on the map
 | NO_WRAP | This makes field_choose() return with the value of the cursor key if the user tries to move outside the choice array
 | ABORT_MNEMONICS | This allows the use of a mnemonic map to get responses from the user. The mnemonic map must already be displayed on the screen and its number is given in the ADDITIONAL parameter
Description | The field_eg_choose() function allows you to select an option from a menu, to display extra information and to grey out field options. Many of the menu functions are implemented using this function.
Return values | Returns the value of the global variable abort_code
See also | map_g_choose()


field_end()

Purpose

Removes the cursor from a field and reads or writes information to a redirect file when the REDn program is in use

Syntax

void pascal field_end(int flags)

Parameters

int flags

Determines how function is used; see below

The parameter flags may take any of the following values:

Value

Meaning

0

Field has been completed

non-zero (use 1)

Processing is temporarily interrupted prior to a call to field_start(1)

Description

This function, together with field_start() and field_char_get(), is used internally in the construction of DP4 entry routines and is available for developers wishing construct custom input functions which do not use the askf_x() or inpm_x() functions.

The operation of these functions is internal to the terminal manager. The field_end() function is responsible for removing the cursor from a field and for reading or writing information to a redirect file when the REDn program is in use.

See also

field_start(), field_char_get()

Example

See the example given for field_start()


field_force_colour()

Purpose | Changes the colours for the next screen function
Old name | #define force_colour field_force_colour
Syntax | void field_force_colour(in_colour, out_colour, in_beep, outtrm_beep)
Parameters | int in_colour | The colour to use for the input colour
 | int out_colour | The colour to use for the output colour
 | int in_beep | The beep duration for next input
 | int out_beep | The beep duration for next output
 | The parameters in_colour and out_colour determine the colour for input and output. If the colour value is greater than 0, the lower 8 bits determine the colour as follows:
Bit | Colour
bit 0 bit 1 bit 2 bit 3 bit 4 bit 5 bit 6 bit 7 | Blue Green Red Brightness Blue background Green background Red background Flashing

If the colour is less than zero the colour is forced to the default colour of the standard colour types.

The code values must be negated in calls to field_force_colour().

The codes are shown below:

Value

Meaning

C_BACKGROUND
C_WINDOW
C_MAP
C_OUTPUT
C_INPUT
C_ERROR
C_HELP
C_REMARK
C_WARNING
C_MNEMONIC
C_EMPHASIS
C_OPTION
C_OBSCURE
C_FKEY_WINDOW
C_FKEY_DATA
C_SHADOW
C_ACTIVE_TITLE
C_INACTIVE_TITLE

Default background colour
Default popup window text colour
Default map text colour
Default field output colour
Default field input colour
Default error message colour
Default help colour
Default remark colour
Default warning error colour
Colour of mnemonics & window emphasized text
Colour of emphasized text in maps
Colour of highlighted option within menu
De-emphasis colour for forbidden options
Function key area colour for popup windows
Function key area colour for data maps
Colour of shadowed parts of the screen
Colour of activated title bar
Colour of deactivated title bar

 | The parameters in_beep and out_beep determine the length of the beep duration for input and output functions. Use -1 for the default beep and 0 for no beep at all.
Description | The field_force_colour() function is used to change the default colour values in a map for the next screen function call only.
See also |


field_force_range()

Purpose

Changes validation for the next input

Old name

#define force_range field_force_range

Syntax

void field_force_range(validation);

Parameters

union validation* VALIDATION

Pointer to the validation record to be used

Description

The field_force_range() function changes the validation to take place at the next call to one of the askf_x() or prnx() functions.

The validation only checks that input is in the correct format. You must perform any other checks that are required.

See also


field_force_size()

Purpose

Changes the field attributes in next screen function call

Old name

#define force_scrn field_force_size

Syntax

void field_force_size(field_length, occurs,
precision, scalefactor, alphabet)

Parameters

int field_length

New field length to be used

int occurs

The number of occurrences the field should have

int precision

The precision of the field to be used

int scalefactor

The scale factor to be used

char* alphabet

Null-terminated string of valid input characters

The set of acceptable characters for the functions askf_c and askf_u is restricted to those in the parameter alphabet.

A value of NULL_SHORT for any of the field attributes causes the default value from the map to be used.

An empty string ("") for the parameter alphabet means that any characters are allowed.

Description

The function field_force_size() forces the next call to one of the show_x(), askf_x() or field_blank() functions to use the specified field attributes instead of taking them from the map.

Note this function affects only the next call and not subsequent ones.

Example

field_force_size(80,10,0,0," NS");


field_help()

Purpose

Shows help message of a field (new version)

Old name

#define help_message(mapnr,fieldnr) field_help(mapnr,fieldnr,0)

#define help_text field_help

Syntax

void field_help(mapnr, fieldnr, flags)

Parameters

int mapnr

Number of map containing field

int fieldnr

Number of field owning help message

int flags

Parameter specifying action to be taken

The value of the flags can be any combination of the following:

HT_CLEAR

This is used with HT_PAUSE. It clears the map after the user presses a key

HT_PAUSE

This is used to force the user to acknowledge the help message

Description

The field_help() function displays the help message of the field passed. Many of the DP4 utility programs now use this procedure to display their help messages below the main map.

Example

/* how a program can display its help message */
#include "dp4.h"
void process()
{
   map_load("MYPROG");
   trm_read_user();
   if (tail_get_option("HELP",FALSE))
   {
      map_datamap(100);
      /* programs help message is constructed as the
          help message of field 1 of map 100 */
      field_help(100,1,HT_PAUSE);
      return;
   }
   ...
}


field_highlight()

Purpose

Highlights a menu option

Old name

#define highlight_field field_highlight

Syntax

int field_highlight(mapnr, fieldnr)

Parameters

int mapnr

Number of map containing field

int fieldnr

Number of field in menu to highlight

Description

The field_highlight() function temporarily changes the colour of the selected field to that for a field selected in a menu. The next time the screen is updated, the highlighting disappears.

field_highlight() is used in the construction of menu functions like map_bar_choose() and field_choose(). Because it returns FALSE if the field does not exist, it is used to check that a valid option exists for the key pressed.

Return values

Returns FALSE if field does not exist, otherwise TRUE

See also

field_force_colour(), field_colour()

Example

askm_special() has a good example using field_highlight() to check for valid key input


field_map_length()

Purpose | Obtains the length of a field
Old name | #define _show_length field_map_length
Syntax | int field_map_length(mapnr, fieldnr);
Parameters | int mapnr | Number of map containing field
 | int fieldnr | Field whose length is required
Description | The field_map_length() function finds the length of the given field
See also | prn_length()


field_read()

Purpose

Enables data to be read from map fields

Syntax

int field_read(mapnr, fieldnr, data);

Parameters

int mapnr

Number of map containing field

int fieldnr

Field from which to read

char * data

Pointer to buffer in which to place data

Description

This function enables data of any type to be read from fields in a map and converted to text format.

The map from which data is to be returned must already be on screen, through a call to map_draw() or map_remark() and so on. However, if the map serves no other practical use, you can hide it by displaying it with the HIDDEN attribute.

The function copies the characters contained in the map image of the field to the data parameter. You must ensure that the data parameter points to a sufficiently large buffer. You can force a particular length to be returned by calling the field_force_size() function before field_read(), or you can discover the amount of data which will be returned by calling field_map_length().

The return value of field_read() is the amount of data copied to the buffer or 0 if an incorrect mapnr or fieldnr combination is specified.

Normally, you set this data by a prior call to a show_x() function, but you could, if you wanted to, use it to retrieve a string from a map.

In the simplest case, you can use field_read() to convert a single data item to text format like this:

char *date_to_char(datetype *date)
{
   static char x[12];
   /* Large enough for any date */
   map(99,HIDDEN);
   showd(99,1,date);
   field_read(99,1,x);
   map_clear(99);
   return x;
}

You convert an item of any type as follows:

char * data_to_char(void * data,int dtype,
  int length,int precision,int scale,int show_flags)
{
   static char x[80];
   /* Large enough for any data */
   map(99,HIDDEN);
   if (length 80)
     length = 80;
   force_scrn(length,1,precision,scale,"");
   show(99,1,data,dtype,length,show_flags);
   force_scrn(length,1,precision,scale,"");
   field_read(99,1,x);
   map_clear(99);
   return x;
}

You can also use field_read() to perform a more complex conversion by using overlapping fields, as in:

#map 99
_____________
|___________________________________________________________
#map 99
_____________
Time: |_______ Amount: |_____.__
Operator |_________________
#fields 2:,3:AMOUNT,4:;
timetype clock = {99,99,99,99};
/* Displays as system clock time /*
map(99,HIDDEN);
showt(99,2,&clock);
shown(99,3,amount);
showc(99,4,operator);
field_read(99,1,journal.text);

Because field 1 occupies the entire width of the map, journal.text contains, for example:

Time: 16:25:03 Amount: $123.54 Operator FRED

It is assumed here that the format flags for the amount field are set to include a unit which displays the dollar currency symbol.


field_skip()

Purpose

Moves to the next field

Old name

#define omit(mapnr,fieldnr)
field_skip(mapnr,fieldnr,0)
#define skip_field field_skip

Syntax

void field_skip(mapnr, DP4P fieldnr, direction)

Parameters

int mapnr

Number of map containing field

int DP4P fieldnr

Pointer to variable holding current field

int direction

Direction in which to move to next field

The parameter direction can take one of these values:

Value

Meaning

0

Moves in the "current" direction. The current direction will depend on how the user last left and field, and whether there have been any other calls to field_skip() since. Internally this function may wait for input, for example if the current field number corresponds to a button.

1

Acts as if the user had terminated normally

2

Acts as if the user had pressed the <Tab> key

LEFT

Acts as if the user presses the <←> key

RIGHT

Acts as if the user presses the <→> key

DOWN

Acts as if the user presses the <↓> key

UP

Acts as if the user presses the <↑> key

You can also use other direction values such as LEFT_LINE,RIGHT_LINE,BACKTAB, and SPACE_BAR

NULL_INT

If the fieldnr exists in the map, it is not changed. If it is invalid, it is set to the nearest valid field number. If the global variable range is not 0, the nearest valid field number less than or equal to the parameter fieldnr is taken. NULL_INT is used to ensure that fieldnr is valid in functions like field_choose(). To improve the behaviour of programs that do not have the MENUMAP flag specified on a map that is to be used as a menu, the map properties are changed as if the map were displayed with the MENUMAP flag and not with DATAMAP or REMARK. This value is only intended for use by field_choose() and similar functions.

Description

The field_skip() function is used in application programs which use scrnx_map() functions to avoid stopping for input in a given field. It increments or decrements the field number as required to move to the next field. The next field depends on how the parameter direction is set.

Return values

The return value is abort_code. This allows field_skip() to perform input if the field being omitted is a button field.

See also

inpm_omit(), field_skip()

Remarks

Calling this function with a non zero direction should be regarded as exceptional. About the only good reason for doing this is when you have just called an input function using one map, but want to set the next field number in another map as though the key used to terminate the input belonged to a field in that map. For example the BROWSER utility uses the following scheme for doing input in some situations (this code is schematic only):

void input_inner(int m,int *f)
{
  int fieldnr = 1;
  map_attach(m,*f,INPUT_MAP,DATAMAP|HIDDEN);
  scrn_map(INPUT_MAP,&f,get_data(*f),get_data_type(*f),get_data_size(*f),DEFAULT);
  map_glue(m,
  field_skip(m,*f,abort_code);
}

To the user it will appear as though input is taking place directly in the underlying map, and if he uses a cursor to leave the field the behaviour will be what he expects. The code shown here would not work properly if the user tried to move to another field with the mouse.


field_start()

Purpose

Initialises the cursor (if any) for a field

Syntax

void pascal field_start(mapnr, fieldnr, flags)

Parameters

int mapnr

Number of map containing field

int fieldnr

Field from which to read

int flags

Determines how function is used; see below

The parameter flags may take any of the following values:

Value

Meaning

EF_RESTART

Resume entering a field following a previous call to field_end(1). This is used by editor programs, for example to prevent lines in redirect files from becoming too long

EF_FULLEDIT

Pass if the input routine is one for which the caret may be moved freely around the edit control as in askf_c() . Pass this if you want to process the mouse.

EF_NOCARET

Pass if the input routine is one for which the cursir should be turned off (if the user disabled the cursor in menus)

EF_NOREDIR

Bypass redirect files and use keyboard

The other EF_ flags listed in dp4capi.h are for internal use by the terminal manager

Description

This function, together with field_end() and field_char_get(), is used internally in the construction of DP4 entry routines and is available for developers wishing construct custom input functions which do not use the askf_x() or inpm_x() functions.

The operation of these functions is internal to the terminal manager. The field_start() is responsible for initialising the cursor shape for the field. See the brief example program below.

See also

field_char_get(), field_end()

Example

field start(mapnr,fieldnr,EF_FULLEDIT);
  while (!finished)
  {
     show(mapnr,fieldnr,data,'C',data_size,
         INPUT_COLOUR);
     field_cursor(0,column);
     inchar = field_char_get(user.insert_mode,&row,
         &column);
     /* process inchar */
  }
  showc(mapnr,fieldnr,data);
  field_end(0);


form()

Purpose | Appends a map to the printer stream
Syntax | void form(mapnr)
Parameters | int mapnr | Number of map to append
Description | The function form() is used to append a map to the printer for subsequent printing. After form() is called, fields on the map can be filled in using the prn_x() functions. The function prn_print() is finally called to print the map.
See also |


form_beside()

Purpose | Places a new form alongside an existing one
Syntax | int form_beside(left_map, right_map)
Parameters | int left_map | Number of map to be placed on the left
 | int right_map | Number of map to be placed on the right
Description | The function form_beside() places a map alongside an existing map in the printer stream. The top left-hand corner of the right map is placed at the top right-hand corner of the left map. All subsequent forms until the next print are positioned under the right map. The global variable pr_line is set to the next line under the right map. The program terminates if the left map is not in the printer stream.
Return values | Returns the value of the global variable pr_line
See also |


form_over()

Purpose

Overwrites a map in the printer stream

Old name

#define formover form_over

Syntax

void form_over(old_map, new_map)

Parameters

int old_map

Number of map in printer stream

int new_map

New map to use

Description

The function form_over() overwrites the text of the old form with that of the new one. The program terminates with a system error message if the old map is not in the print buffer.

The pointer to the printer stream is changed so that subsequent calls to form are written below this new map. The value of the global variable pr_line is changed to reflect this.

The most common use of this function is to restore the starting position for subsequent forms. For example a program might print a tree structure using logic similar to the example below::

Return values

Returns the value of the global variable pr_line

See also

form(), dvc_choose()

Example

form(1);
form_beside(1,2);
form(3); /* comes out underneath form 2.
form_over(1,1);
form(3); /* comes out underneath form 1 */


form_test()

Purpose

Appends a map to the printer stream, filling in the map fields

Old name

#define lineup form_test

Syntax

int form_test(mapnr, c)

Parameters

int mapnr

Number of map to append

int c

Character used to fill in the fields

Description

The form_test() function appends a map to the printer stream and fills in all its fields with the character specified by the parameter c.

Return value

Returns the value of pr_line

See also

form(), form_over()

Example

form_test(2,'X');


freep()

Purpose | Releases managed heap memory allocated with mallocp()
Syntax | void freep(ANY * ptr);
Parameters | ANY *ptr | Pointer to memory area
Description | This function is similar to the ANSI C function free. It returns a block of memory, allocated with mallocp(), to the heap. Only memory allocated with mallocp() may be freed with freep().
Return values | None
See also | mallocp(), freex()
Example |


freex()

Purpose

Releases alllocate memory back to, or adds new memory to, the heap

Syntax

void freex(ANY *ptr,size_t size)

Parameters

ANY *ptr

Pointer to memory area size_t size

size_t size

Number of bytes to add to heap

Description

This function releases allocated memory back to the heap, or adds new memory to the heap (which is created if it did not already exist). The number of bytes specified by the size parameter is added to the heap. Any region of memory can be added to the heap with freex(), including for example, static variables that are no longer required.

From 4.620 freex() always assumes it is safe to round up any size passed to it to the nearest acceptable size (determined by calling mallocxable()) , on the assumption that this size was actually allocated by a previous call to mallocx(). If adding memory from elsewhere you must be careful to round down the size first, (by calling mallocxable() until it returns a size no geater than the actual size of the block you are adding to the heap). If you require memory to be properly aligned, you need to be careful to add only aligned memory to the heap.

When memory is freed, the new region is combined with contiguous free regions if there are any.

Return values

None

See also

freep(), mallocx()

Example

See cache_find() for an example of usage


ftch2nd()

Purpose

Fetches a record using a secondary index. This function is regarded as obsolete in 4.500

Syntax

boolean ftch2nd(flags, datarec, indexrec, role, ADDITIONAL)

Parameters

int flags

Indicates the search method used

short* datarec

Pointer to the L field of table record to be read

short* indexrec

Pointer to the L field of an index table record

int role

Null-terminated string indicating the role

char* ADDITIONAL

Optional fields depending on value of flags

The parameter flags can take any of the values as for the function rec_fetch_main(). The parameters indexrec and role determine which index is to be used in the search. If the flag contains the use of NEXT, the parameter ADDITIONAL is used to specify the depth required.

Description

The function ftch2nd() fetches a record from the database using a secondary index. Note that the key data should be assigned to the datarec record not the indexrec table.

Return values

The return value indicates whether the read was successful or not. The program terminates if the index or the role is invalid, or for any of the reasons that the function rec_fetch_main() terminates.

See also

rec_fetch_main(), rec_fetch()

Example

/*
 C Functions Reference Manual
 ----------------------------
 Filename: FTCH2ND.C
 Example: ftch2nd
 Purpose: illustrate ftch2nd by fetching all
          customer records.
*/
/*
#db salesord
#c
#libtype 1
#tables customer,
        index_cust_name;
#end
*/
#include "dp4.h"
#include "ftch2nd.h"
/* generate this file by running LIBMAKE */
void process()
{
   int srch = FIRST;
   map_datamap(1);
   while (ftch2nd(srch,&customer.l,
         &index_cust_name.l,"") && !abort_code)
   {
      map_datamap(2);
      show_c(2,1,customer.name);
      show_n(2,2,customer.customer_number);
      askf_special(2,3,0);
      srch = NEXT;
   }
}


Functions G-L

• heap_avail() | • inpm_e()
• heap_init() | • inpm_ge()
• inpm() | • inpm_omit()
• inpm_ban() | • inpm_x()
• inpm_bb() | • inpm_xnp()


heap_avail()

Purpose

Determines the size of the largest block in the heap or whether mallocx() will succeed

Syntax

size_t heap_avail(size_t size);

Parameters

size_t size

size of the heap block required

Description

This function has two modes of operation.

  • If the size is not 0, heap_avail() returns the size if a corresponding request to mallocx() can be satisfied or, if it cannot be satisfied, the size of the largest block of free memory (which may be larger).
  • If the size is 0, the size of the largest memory block is returned

Return values

The size of memory block asked for or the largest memory block


heap_init()

Purpose

Creates and initialises the heap used by DP4 functions.

Syntax

void heap_init(size_t size);

Parameters

size_t size

size of the heap required

Description

This function works very differently for 16 and 32 bit programs.

  • In 16 bit programs heap_init() creates a heap area by reducing the size of the stack by the amount specified or by the maximum possible if size is set to 0 bytes. The stack may be reduced to a maximum of 4KBytes, so this function is inappropriate if your program needs a larger stack. If heap_init() is used in a program, it is necessary to increase the size of the stack explicitly. For a DP4 program you can call:

    KENTCURS -MAXSTACK

    or

    KENTCURS -STACK <nnnn>

    Alternatively, you can specify a larger stack at link time.

    If enabled, Compiler stack checking is unaffected by this operation, i.e. it will cause a fault if your program uses more than the new 4K stack limit.

  • For 32 bit programs the function works by calling the underlying C implementation of malloc() (or possibly an OS function like HeapAlloc()). If 0 is passed as the size, the size specified in the global variable df_heap_size is used. You can declare and initialise this variable yourself. The default df_heap_size is quite small - a little below 64K bytes.

Return values

None

See also

freex()

This function should only be called once per program. Only the first call does anything. You can grow the DP4 heap by allocating memory some other way and adding to the heap with freex().


inpm()

Purpose | General combined data input function (new version)
Syntax | int inpm(data, dtype, length, flags)
Parameters | ANY* data | Pointer to data to display and input
 | int dtype | The data type of the data
 | int length | The internal length of the data
 | int flags | Parameter specifying the display format of input fields
 | The parameter flags may take any of the same values as the global variable scrn_flags when used with the askf_x() functions
Description | The inpm() function is the combined new input function that is the equivalent of the function scrn_map(). The inpm_x() functions are implemented using this function. See the inpm_x() functions for details.
Return values | Returns the value of the global variable abort_code unless it equals SPACE_BAR, when 0 is returned.
See also |


inpm_ban()

Purpose | Skips a field after displaying the underscore character
Old name | #define na_skip inpm_ban
Syntax | void inpm_ban(void);
Description | The inpm_ban() function is used in conjunction with the inpm() type functions. It blanks the current field given by the global variables input_map and input_field, and displays the dash character to indicate that the field is disabled.


inpm_bb()

Purpose

General bit input function

Syntax

int inpm_bb(flag, bit, ask)

Parameters

short* flag

Pointer to short integer containing bit to be edited

int bit

Bit in flag to be edited

int ask

Determines whether the function should stop for input

Description

The inpm_bb() function is a general input function for editing bit fields. If the value of (ask & 1) equals TRUE, it behaves like the function inpm_b(). Otherwise the function does not stop.

If (ask & 4) is not equal to TRUE, the bit can be changed.

If the value of (ask & 2) equals TRUE, it causes the bit to be set, otherwise it is unset. The function inpm_omit() is then called to move to the next field.

Return values

Returns the value of the global variable abort_code

See also

inpm_b()


inpm_e()

Purpose

Inputs a menu option

Syntax

int inpm_e(data, mapnr)

Parameters

short* data

Pointer to variable containing number of map to display over the current input field

int mapnr

Menu map to display

Description

The inpm_e() function allows the entry of a value from a menu while the cursor is still in the main data entry map

This function replaces the old function inpm_option which also required the number of fields in the menu. It assumes that data entry processing is being done and that the function map_use() has been called to set the global variables input_map and input_field.

This function displays the menu and if the parameter data is not zero, it shows the map given by map number (mapnr + data) over the field given by the global variable input_field.

If <Space> is pressed, a choose operation is performed on the map specified by the parameter mapnr, otherwise the number or mnemonic key pressed by the user defines the value of the input data.

If the user presses a function key or a number key which has not been set up, this is equivalent to pressing <Space>.

If an option is selected, the function inpm_e() shows the map with a map number (mapnr + <value of field chosen>) over the current input field.

The parameter data is set to the option chosen.

Return values

Returns ESCAPE if the user presses <Esc>, otherwise returns 0

See also

inpm_ge()

Example

/*
 C Functions Reference Manual
 ----------------------------
 Filename: INPM_E.C
 Example: inpm_e
 Purpose: A version of employee entry using inpm_e
          rather than askm_special
*/
/*
#db salesord
#c
#tables employee;
#end
*/
#include "dp4.h"
#include "inpm_e.h"
/* generate this file by running LIBMAKE */

/* prototypes */
void get_new_emp(void);
void init_new_emp(void);
void emp_inner(int,int*);

void process()
{
   map_datamap(1);
   trm_read_user();
   /* only required if using Microsoft C: a bug in
      the linker causes an unresolved external error
      in the DP4 library */
   get_new_emp();
}

void get_new_emp()
{
   map_datamap(2); /* display the datamap */
   init_new_emp(); /* initialise the new fields */
   if (!map_get_inputs(2, 1, emp_inner, GI_GLOBALS))
   rec_post_commit(&employee.l);
   /* Note: in 4.42x, the flag ASK_CONFIRM should be
      passed to this function. In 4.500 and onwards,
      Save/Cancel buttons should be added to the map
   */
}

void init_new_emp()
 /* initialise fields for a new customer */
{
   /* get last customer and add 1 */
   rec_fetch_main(LAST,&employee.l);
   employee.employee_number++;
   employee.gender[0] = 'M';
   memset(&employee.date_of_birth,0,
          sizeof(employee.date_of_birth));
   employee.grade[0] = 'W';
   WIPE(employee.name);
}

void emp_inner(m,f)
   int m; int* f;{ int field_nr = *f;
   switch (field_nr)
   {
      case 1:
        inpm_c(employee.name);
        break;
      case 2: /* show it only */
        show_h(m,2,employee.employee_number);
        inpm_omit();
        break;
      case 4:
        {
           int option = employee.gender[0] ==
                 'M' ? 1: 2;
           inpm_e(&option,6);
           if (!abort_code) employee.gender[0] =
                 (char)((option == 1) ? 'M': 'F');
        }
        break;
      case 5:
        inpm_d(&employee.date_of_birth);
        break;
      case 7: /* region code choose from a menu */
        {
           static char grades[4] =
                 {'D','M','C','W'};
           int option = 4;
           int i = 0;
           while (i < 3)
           {
              if (grades[i] == employee.grade[0])
                     option = i + 1;  i++;
           }
           inpm_e(&option,10);
           if (!abort_code)
             employee.grade[0] = grades[option - 1];
        }
        break;
      default:
        inpm_omit();
        break;
   }
}


inpm_ge()

Purpose | Inputs a menu option with greying out
Syntax | int inpm_ge(data, mapnr, banned_list)
Parameters | short* data | Pointer to variable containing map number to display over the current input field
 | int mapnr | Menu map to display
 | short* banned_list | Pointer to list of fields, terminated by the null character, of the menu map options which are to be greyed out
Description | The inpm_ge() function is like inpm_e() function but also supports greying-out of the menu options specified by the parameter banned_list.
Return values | Returns ESCAPE if user presses <Esc>, otherwise returns 0
See also | inpm_e()
Example | See the inpm_e() function for an example of how this class of function can be used in an application


inpm_omit()

Purpose

Skips processing of data entry (new version)

Old name

#define skip inpm_omit

Syntax

void inpm_omit(void)

Description

The inpm_omit() function is used with the inpm_x() functions to avoid processing certain fields. It is identical in use to the field_skip() function, but has the advantage of not taking any parameters.

You should always use the function inpm_omit() for the default case statement in data entry functions. Failure to do so is the main cause of programs hanging in such functions.

See also

inpm_x(), inpm(), field_skip()

Example

/* syntax - see inpm_x for full example */
switch (fieldnr)
{
   case 1:
     inpm_c(customer.name);
     break;
   default:
     inpm_omit();
     break;
}


inpm_x()

Purpose

data input functions for dialogs

Syntax

int inpm_b(short *flags, unsigned bit)
int inpm_c(char *data)
int inpm_d(datetype *data)
int inpm_h(short *data)
int inpm_i(int *data)
int inpm_l(long *data)
int inpm_n(double *data)
int inpm_s(char *data)
int inpm_t(timetype *data)
int inpm_u(char *data)
int inpm_y(boolean *data)

inpm_i()may be implmented as a macro. It is only required for variables you declare yourself as int.

Parameters

data

Pointer to variable holding data to display and input

short* flags

Pointer to flag containing the bit to be edited

unsigned bit

The bit in the flags parameter that the user is being asked to enter

Note that inpm_h(), and not inpm_i(), should be used for input of type I fields. A good C compiler will flag it as an error if you inadvertently use inpm_i() on a DP4 type I field. Unfortunately some compilers, including Microsoft C, only flag this as a warning, (at least for C programs), even though it causes a memory corruption.
You can force the Microsoft compiler to flag an error in this situation by putting #pragma warning(error:4133) at the top of your source modules, or in an include file, and you are recommended to do so. Compiling as C++ will also cause an error rather than a warning.

Description

The inpm_x() functions are the preferred functions for data entry, replacing the older scrnx_map() functions. inpm_x() functions are typically used with the map_get_inputs() function to create data entry functions.

The global variables input_map and input_field are used to control the current map and field numbers. These global variables have to be set up with a call to the function map_use(), before calling the inpm_x() functions. The map_get_inputs() function does this automatically for you. Using these global variables reduces code size (of both source and executables).

The inpm_x() functions are implemented by calls to the general inpm() function, using the global variable scrn_flags as the flags parameter, (inpm_d() used scrnd_flags).

To edit multi-occurs fields, the general inpm() function must usually be used,but you can use inpm_c(),inpm_s() and inpm_u() with multi-occurs fields.

If the global variable realpass is FALSE inpm_x() functions just display the data, increment the field number to the next field and return a non zero value. If realpass is TRUE they work as follows:

On entry the field is cleared (meaning that the data is set to the default on the database, or a zero or blank value if the field is not a database field) under the following conditions:

  • The global variable range is less than or equal to the field number indicated by the parameter fieldnr, and

  • The field is set as a clearfirst field on the map, and either:
    • The global variable clearfirst is 1 or 2
    • The stored value of the global variable clearfirst is 1 or 2 while moving to another field, as described in Field Navigation During Data Entry

Input is then solicited from the user unless jump is TRUE. inpm_x() returns when the user moves away from the field, (or the maximum number of character have been entered and the auto-enter property is set in scrn_flags or in the properties for the field).

On return from these functions, *input_field is set to the next field number to be processed. A field number of 0 means that the user has completed the dialog.

If the user moves to another field with the mouse or cursor keys input_field is adjusted in such a way as to ensure any fields that would be missed out if fieldnr was set directly to the target field are validated. This is explained in detail in Field Navigation During Data Entry.

If the user completes the field normally abort_code and the return value is set to 0,indicating the field should be validated.

If the user presses the <Esc> key, the global variable abort_code is set to the value ESCAPE (and *input_field is set to the first field in the map).

If the user leaves a field with a cursor movement key, or by clicking on another field with the mouse, or requests prompt, abort_code is set accordingly as listed below:

Value

User action

UP_LINE
DOWN_LINE
LEFT
RIGHT
LEFT_LINE
RIGHT_LINE
BACKTAB
SPACE_BAR
CLICK_LOCAL

User navigated with the <↑> key
User navigated with the <↓> key
User navigated with the <←> key
User navigated with the <→> key
User navigated with the <Home> key
User navigated with the <End> key
User navigated with the <Shift+Tabgt; keys
User requested prompt
User clicked on another field in the map

To use the prompt facility, add the value PROMPT_AVAILABLE to the global variable scrn_flags before calling inpm_x() functions. To determine whether prompt was requested, test that the return value is zero and that the global variable abort_code contains the value SPACE_BAR.

Return values

Returns a non zero value (usually the value of the global variable abort_code ) if the program should break (not validate the field), or zero if the data was entered normally and should be validated.

See also

map_use(), map_get_inputs(), inpm_xnp(), inpm(), inpm_omit(), scrnx_map()

Remarks

inpm_c() allows you to enter mix cased data, whereas inpm_u() converts all keystrokes to uppercase, inpm_s() like inpm_u() but converts spaces to underscore characters and only allows characters that are permitted in identifiers (and @). Usually each function is used with the corresponding DP4 datatype, but this is not manadatory.

inpm_b() is used to edit one bit in a short to be edited. For example, the call:

inpm_b(&device, PRINTER);

is used in the function dvc_choose() to toggle the PRINTER bit in the devive_flags parameter. inpm_b() allows a single I field to be used to store the equiavlent of 16 Y fields.

Example

See the map_get_inputs() example


inpm_xnp()

Purpose

Input function for fields without prompt

Syntax

int inpm_cnp(char *data)
int inpm_dnp(datetype *data)
int inpm_hnp(short *data)
int inpm_inp(int *data)
int inpm_lnp(long *data)
int inpm_nnp(double *data)
int inpm_snp(char *data)
int inpm_tnp(timetype *data)
int inpm_unp(char *data)

inpm_inp may be defined as a macro on some platforms.

Parameters

data Address of variable containing data to display and input

Description

The inpm_xnp() functions are the same as the inpm_x() functions except that they do not respond to the prompt facility. Pressing <Ctrl+P> has no effect when the global variable scrn_flags includes the value PROMPT_AVAILABLE.

In a screen of data entry where you wish some fields to respond to <Ctrl+P> but not others, you can use the inpm_xnp() functions to disable <Ctrl+P> for those fields that do not support it. By doing this, you do not have to keep changing the global variable scrn_flags.

These functions are implemented by calls to the general inpm() function using scrn_flags &~PROMPT_AVAILABLE as the flags parameter (or scrnd_flags in the case of date input).

Refer to the documentation of inpm_x() for further information on these functions.

Return values

Returns a non zero value (usually the value of the global variable abort_code) if the program should break (not validate the field), or zero if the data was entered normally and should be validated.


Functions M

• mallocp() | • map_datamap() | • map_old_position()
• mallocx() | • map_draw() | • map_old_window()
• mallocxable() | • map_draw_over() | • map_pause()
• map_activate() | • map_g_choose() | • map_predisplay()
• map_alter() | • map_get_depth() | • map_remark()
• map_askf_y() | • map_get_inputs() | • map_reveal()
• map_attach() | • map_get_width() | • map_set_scroll_end()
• map_bar_choose() | • map_glue() | • map_so()
• map_change() | • map_hide() | • map_switch_language()
• map_choose() | • map_items_to_scroll() | • map_use()
• map_clear_after() | • map_load() | • map_yes()
• map_clear_all() | • map_menu() | • mem_describe()
• map_clear_from() | • map_new_position() | • mem_find()
• map_clear_remarks() | • map_new_window() | • mem_post()


mallocp()

Purpose

Gets managed memory from the heap

Syntax

ANY * mallocp()(size_t size);

Parameters

size_t size

The amount of memory required

Description

This function is similar to the ANSI C malloc. It is a higher level function than mallocx() because extra memory is allocated to remember the size of the block requested. This means you do not need to keep track of the size of each allocated block. Also, if the heap was aligned to start with, the size is automatically rounded up to leave it aligned.

mallocp() calls mallocx() to allocate the memory and therefore uses the mallocx_handler variable for error handling. Note that memory allocated with mallocp() must only be freed with freep().

Return values

Returns a pointer to an area of memory of size_t size bytes. If the memory cannot be found, mallocp() returns 0.

See also


mallocx()

Purpose

Gets memory from the heap

Syntax

ANY PTR mallocx(size_t size);

Parameters

size_t size

The amount of memory required

Description

This is a low-level function which gets a pointer to an unused area in the heap of the size specified. There is no heap initially; you must create one by calling freex() or heap_init(). From 4.620 mallocx() always rounds up the number of bytes requested to the nearest acceptable size (by calling mallocxable()). This is usually a multiple of four or eight bytes. In previous versions it did not return aligned memory, unless you allocate an even number of bytes, or set mallocx_round to TRUE. Be careful of asking for less than:

sizeof(size_t) + sizeof(char *) bytes

(See freex() for further details).

Error handling can be provided by setting the variable mallocx_handler to point to an error handler. This error handler is called when mallocx() is about to return 0.

DP4 library functions do not assume that any kind of memory allocation succeeds automatically and they can, therefore, all be used inside a handler. The cache_xxxx, malloc, realloc and buffered versions of tf_xxxx file functions call mallocx() internally, so bear this in mind when allocating the heap size if your program uses these functions.

Return values

Returns a pointer to an area of memory of size bytes. If the memory cannot be found, mallocx() returns 0.

See also


mallocxable()

Purpose

Gets a size value suitable for a call to mallocx()

Syntax

size_t mallocxable(size_t size);

Parameters

size_t size

The size with which you want to call mallocx()

Description

This function tells you how much allocation to ask for to keep the heap aligned. If you wish to bypass calls to this function, be certain that the size passed to mallocx() is:

  • Larger than the permitted minimum size
  • A multiple of the size needed to satisfy compiler alignment restrictions

Return values

Returns a rounded up value of size suitable for a call to mallocx()

See also


map_activate()

Purpose

References earlier map copies

Old name

#define activate map_activate

Syntax

int map_activate(mapnr, fieldnr)

Parameters

int mapnr

Number of underlying map containing attached maps

int fieldnr

Number of field on map with attached map to activate

Description

The map_activate() function allows you to reference earlier copies of an attached map, still present on the screen. Normally, it is only the most recently attached copy of the map which is referenced by the askf_x(), scrnx_map(), inpm_x() and show_x() functions.

map_activate() causes any maps attached to the specified map and field to become the active copy of the map.

The function map_activate() does not affect the map referenced by the function map_change(), which always refers to the most recent 'non-dead' copy of a map.

This function is not commonly used and is likely to be withdrawn in the future.

Return values

The number of the map which was activated

Example

/*
 C Functions Reference Manual
 ----------------------------
 Filename: MAP_ACTI.C
 Example: map_activate
 Purpose: Simple example to show logic of
          map_activate. The program activates
          multiple copies of a map attached to the
          fields or another map, and  displays the
          number of the field in the map.
*/

/*
#db salesord
#c
#end
*/

#include "dp4.h"
#include "map_acti.h"
 /* generate this file by running LIBMAKE */
void process()
{
   int i;
   map_draw(1,RETAIN);
   /* attach map 2 to 10 fields in map 1 */
   for (i = 1; i < 11; i++)
     map_attach(1,i,2,DEFAULT);
     /* display numbers 1 to 10 in the copies
        of map 2 */
   for (i = 1; i < 11; i++)
     {  /* activate map 2 attached to i'th field */
        map_activate(1,i);
        show_h(2,1,i);
     }
     /* pause */
     askf_special(1,11,0);
}


map_alter()

Purpose | Changes the properties of a displayed map
Syntax | void map_alter(mapnr,subfunction,value);
Parameters | int mapnr | Map number of appropriate displayed map
 | int subfunction | The property to be altered
 | int value | The new value for the property
Description | The properties are described below
 | MA_ACTIVATOR | Specifies a new value for the activating function key (1 = <F1> and so on). MA_ACTIVATOR is most useful where you allocate each map its own unique activator. One convenient method is to keep a bitset for allocating numbers to maps as they are displayed. Although you can use low numbers corresponding to real function keys you may prefer to use numbers outside the range of normal function keys (values 0x100 to 0x7ff are available)
 | MA_COVERED | Brings the map to the top of the viewing order. Normally, maps are only brought so high in the viewing order that the current field is visible. For this property, value is not currently meaningful, so you should pass 0


map_askf_y()

Purpose

Obtains a confirmation from the user

Old name

#define ask map_askf_y

Syntax

int map_askf_y(mapnr, data, default_value)

Parameters

int mapnr

Number of map to display

boolean* data

Pointer to variable to hold result

int default_value

The initial Boolean value to be displayed

Description

The function map_askf_y() is used to request confirmation from the user. The map is displayed and the default value is displayed in the first field in the map. The map is cleared before this function returns.

In response to the map, the user either enters Yes or No, or presses any of the <Esc>, <Tab> or <Enter> keys, after which the function returns.

The parameter data points to a Boolean variable that is initially set to the parameter default_value.

The newer function map_yes() is now more commonly used than map_askf_y() because it only has two parameters (mapnr and default_value).

You can also use the function sys_del_confirm() to request confirmation from the user. Alternatively, you can use a menu method with the field_choose() or map_choose() functions.

Return values

Returns the value of the global variable abort_code. If the user presses <Esc>, the global variable abort_code is set to ESCAPE, otherwise it is set to zero and the parameter data is updated with the user's response.

Pressing <Tab> causes the global variable jump to be set to TRUE. If jump is TRUE when this function is called, it does not stop for user input.

See also

map_yes(), sys_del_confirm(), map_choose()

Example

/* Implementation of the map_yes library function */
int map_yes(int mapnr,int deflt)
{
   boolean answer;
   jump = FALSE;
   return map_askf_y(mapnr,&answer,deflt) ? FALSE :
       answer;
}


map_attach()

Purpose

Attaches one map to another map

Old name

#define attach_map map_attach

Syntax

void map_attach(mapnr, fieldnr, newmap, flags)

Parameters

int mapnr

The number of the map to which the new map is to be attached

int fieldnr

The field number to attach the new map

int newmap

The number of the map to be attached

int flags

Parameter specifying colour and other attributes

Permissible values for the parameter flags are:

Value

Meaning

DEFAULT

Take flags and default colours from the owning map. Use either this flag or a combination of the others

DATAMAP

Use if map is a data entry map. (DEFAULT works incorrectly if attached to a MENU)

RETAIN

The map stays until cleared by one of the map clearing functions. If the map is already on the screen attached to the same point the existing map is cleared

REMARK

Clears the map following the next input or next display of a free map which is not a remark map

ERRORMAP

Displays the map as an error map

MENUMAP

Sets cursor wrap on and displays the map in menu colours

HIDDEN

Initially displays the map as a hidden map. On its own this flag does not prevent the parent maps proprties from being inherited. You can use this flag if the attached map will be "glued".

IN_BOX

Displays the map in a box

NO_SCROLL_OFF

This retains any of the maps currently on the screen

MPA_FREE

This flag positions the map as an attached map, then allows it to become a free map. (It has the same value as CLEAR which is not implemented for map_attach().) This makes it easier to build up complex screens with a mixture of free and attached maps, because:

  • The map to which the map was attached can be cleared or hidden without the new map being cleared
  • The new map is avoided by subsequent free maps. But, you may encounter problems unless you draw other maps with NO_SCROLL_OFF

MPA_ON_TOP

When this flag is specified, the map is always kept on top of its attached map, no matter how the terminal manager rearranges the screen. You cannot use this flag with MPA_FREE. This is an alternative to the use of the map_glue() function, provided for the case when the attached map must be kept alive. If MPA_ON_TOP is not specified, it is possible for the underlying map to be put on top of maps to which it is attached. This flag is not implemented in version 4.6xx.

Description

This function attaches a map to an existing map on the screen. The program terminates if either the map given by the mapnr parameter is not on the screen, or the map to attach to does not exist.

The attached map is placed at the top of the viewing order.

Example

/*

 C Functions Reference Manual

 ----------------------------

 Filename: MAP_ATTA.C

 Example: map_attach

 Purpose: Program uses map_attach to illustrate

 fixed positioning of error maps.

*/

/*

#db salesord

#c

#end

*/

#include "dp4.h"

#include "map_atta.h"

 /* generate this file by running LIBMAKE */

 /* prototypes */

void error_mess(int);

void process()

{

 /* put up layout map - use hidden so it doesn't

    affect the position of subsequent map calls */

    map_draw(1,RETAIN | HIDDEN);

    map_datamap(2); /* header comment map */

    /* put up sample error message */

    error_mess(3);

}

void error_mess(int map_nr)

  /* display error message at a fixed position,

     ie, the position of field 1 in layout map */

{

   attach_map(1,1,map_nr,ERRORMAP);

   askf_special(map_nr,1,0);

   map_clear_from(map_nr);

}


map_bar_choose()

Purpose

Selects an option from an action-bar menu that has pull-down menus

Old name

#define actionbar(mapnr,option,banned_list)
map_bar_choose(mapnr,option,0,0,banned_list)

Syntax

int map_bar_choose(int mapnr,int DP4P option,int
flags, void (*draw_extra)(int mapnr),const short DP4P banned_list);

Parameters

int mapnr

Map number of the menu bar. This map must be on screen before making the call to map_bar_choose(). Although map_bar_choose() does not display the menu bar map initially, it does redraw the menu bar occasionally (using the map_draw_over() function with the flag NO_REARRANGE because of the need to colour menu bar fields

int *option

Pointer to an integer variable in which the selected option is returned. The initial value of this variable determines which option is highlighted to start with

int flags

Currently three flags are implemented:

HAS_PULL_DOWN

Enables a second level of pulldown menus

PULL_DOWN

Allows the user to escape from the whole menu as if it were a pulldown (by clicking outside it)

FUNC_KEYS

Enables function keys. These are not allowed to select options. Pressing a function key causes map_bar_choose() to return the value of the function key as its return value in abort_code

void (*draw_extra)()

If a non-zero value is passed for draw_extra, it is a pointer to a function called each time a map is drawn. This enables additional information to be displayed in the maps. The most likely use of this is to indicate 'check states' of particular options

short * banned_list

Pointer to a banned list of field options to grey out. This should point to an array of field numbers terminating in zero

Description

map_bar_choose() provides a simple mechanism to select an option from a menu bar with pulldown menus.

When your program runs, map_bar_choose() first does any necessary greying out, then highlights the initial option. The cursor keys, mnemonics and other usual means are available for the user to select options. Pulldown menus are displayed and greyed out as necessary. The selected option is returned in the integer variable option_nr.

The option number is returned by combining the field selected from the menu bar (the X co-ordinate) with the field selected from the pulldown menu (the Y co-ordinate) as (16*X)+Y. Assuming you have no more than nine options in the menu bar, or in each pulldown, it is easiest to code the actions selected using a switch statement with the cases coded in hex, as in:

if (!map_bar_choose(1,&option,0,0,banned_list))
  switch (option)
  {
     case 0x11:
       /* selects option 1 of pull down 1 */
     case 0x12:
       /* selects option 2 of pull down 1 */
     case 0x21:
       /* selects option 1 of pull down 2 */
  }

Consider these points:

  • Options for greying out are numbered in the same way
  • If option is greater than 16, the appropriate pulldown is displayed and the relevant option highlighted, if it is less than 16 the cursor starts on the corresponding field of the menu bar
  • map_bar_choose() clears any pulldown it draws but leaves the menu bar on screen

From v 4.613 it is possible to add a second level of pulldowns to menus. In other words, an option in a pulldown menu leads to its own pulldown menu. You must pass the flag HAS_PULL_DOWN to enable a second level of pulldowns.

Where there are two levels of pulldowns, the option number is calculated as (256*x) + (16*y) + z, where x is the number of the menu in the menu bar, y is the number of the option from that menu and z is the number of the option from the sub-menu.

Return values

map_bar_choose() returns 0 if the user makes a choice or ESCAPE if <Esc> is pressed. If an activating function key is pressed or the FUNC_KEYS flag is set, the return value is the function key number.

Designing a Menu Bar with Pulldown Menus

The principal effort in providing a menu bar lies with the map design. But this follows a fixed pattern and is quickly achieved using the MAPEDIT utility. The pulldown menu system used for the QABUTIL utility is currently map 230 from the PROGMAKE mapset on the system database and can be examined to see the general layout.

To create a pulldown menu system, use MAPEDIT to create the menu bar and add the top level fields.

To add a pulldown menu for option <n> you select the Pull Down option within MAPEDIT. When you do so, MAPEDIT automatically adds a field 100+<n> in the correct position on the map and opens a menu edit window for the creation of the pulldown menu. If the menu bar map number is <m>, the pulldown menu for option <n> is given the map number <m>+<n>. map_bar_choose() attaches the pulldown menu to the field numbered 100+<n>.

A maximum of 15 options is allowed in the menu bar as well as in each pulldown menu.

Adhere to these guidelines when designing menu bars that conform to the IBM SAA (text mode) convention:

  • Start the first menu bar option in column 3. Use two spaces to separate options. The field for each option begins one space before the text and ends one space after it. Options are not numbered
  • Locate field 100+<n> one line below, and two spaces to the left of, field <n> (field 100+<n> defines the pulldown menu's position)
  • The pulldown menus are normal menus with numbered options of the same width. The numbers are separated from their options either by two spaces or a full stop and a space. The menu appears in a box with one space between the edge of the box at either side and the fields

To conform with Windows the rules are different:

  • The first menu bar option begins in column 1. Use two spaces to separate options. The field for each option begins one space before the text and ends one space after it. Options are not numbered
  • Locate field 100+<n> one line below, and in the same column as, field <n>
  • The pulldown menus are normal menus without option numbers. Each option is the same width. The menu is in a box with one space between the edge of the box at either side and the fields

Example

/*
 C Functions Reference Manual
 ----------------------------
 Filename: MAP_BAR_.C
 Example: map_bar_choose
 Purpose: Simple example showing how to display an
 action ban using map_bar_choose.
*/

/*
#db salesord
#c
#end
*/
#include "dp4.h"
#include "map_bar_.h"
 /* generate this file by running LIBMAKE */
 /* prototypes */
int act_option(int*);
void init_ban(void);
short DP4P banned_list(void);
void grey_out(int, int);

void new_cust(void);
void rem_cust(void);
void cust_enq(void);
void ord_enq(void);
void new_ord(void);
void new_reg(void);
void edit_reg(void);
void del_reg(void);
void aval_mat(void);
void chk_lev(void);

void process()
{
   int option = 1; /* header map */
   map_draw(1,RETAIN); /* action bar menu map */
   init_ban();
   for(;;)
   {
      map_clear_after(1);
      map_draw(2, RETAIN|MENUMAP);
      /* if user presses Esc goto escape option */
      if (map_bar_choose(2,(int DP4P)&option,
                   PULL_DOWN,0,banned_list()))
        option = (option == 5) ? 0x51: 5;
      else if (act_option(&option)) break;
   }
}

int act_option(int* option)
  /* act on option from action bar */
{
   switch (*option)
   {
      /* customer menu */
      case 0x11:
        new_cust();
        break;
      case 0x12:
        rem_cust();
        break;
      case 0x13:
        cust_enq();
        break;
      case 0x18: /* Exit */
        *option = 1;
        break;
      /* order menu*/
      case 0x21:
        ord_enq();
        break;
      case 0x22:
        new_ord();
        break;
      case 0x28: /* Exit */
        *option = 2;
        break;
      /* region menu */
      case 0x31:
        new_reg();
        break;
      case 0x32:
        edit_reg();
        break;
      case 0x33:
        del_reg();
        break;
      case 0x38:
      /* Exit */
        *option = 3;
        break;
      /* material menu */
        case 0x41:
        aval_mat();
        break;
      case 0x42:
        chk_lev();
        break;
      case 0x48:
      /* Exit */
        *option = 4;
        break;
      /* exit menu */
      case 0x51:
        return TRUE;
      case 0x58:
      /* Exit */
        *option = 5;
        break;
      default:
        break;
   }
   return FALSE;
}

 /* banned options module */
static int ban[24];
static int ban_nr;

void init_ban()
{
   ban_nr = 0;
   ban[0] = 0;
}
short DP4P banned_list()
{
   return ban;
}

void grey_out(int option,int put_on)
  /* grey out */
{
   register int i = 0;

    /* check if already on list */
   while (ban[i] != 0)
   {
      if (ban[i] == option)
      {
         if (put_on)
           return;
         else
         {
            movmem(ban+i+1, ban+i, ban_nr-i);
            ban_nr--;
            return;
         }
      }
      i++;
   }
     /* put on end */
   if (put_on)
   {
      ban[i++] = option;
      ban_nr++;
      ban[i] = 0;
   }
}

 /* support functions - just display a map*/

void new_cust()
{
   map_pause(50);
}

void rem_cust()
{
   map_pause(51);
}

void cust_enq()
{
   map_pause(52);
}

void ord_enq()
{
   map_pause(53);
}

void new_ord()
{
   map_pause(54);
}

void new_reg()
{
   map_pause(55);
}

void edit_reg()
{
   map_pause(56);
}

void del_reg()
{
   map_pause(57);
}

void aval_mat()
{
   map_pause(58);
}

void chk_lev()
{
   map_pause(59);
}


map_change()

Purpose

Changes the type of a displayed map

Old name

#define mapchange map_change

Syntax

void map_change(mapnr, flags)

Parameters

int mapnr

The number of the map to be changed

int flags

The value of the attributes for the map to be changed

Description

This function changes the attribute of an existing map to that given by the parameter flags. It acts on the map and all maps attached to it. The new attributes are given by the parameter flags.

Typical values for the parameter flags are CLEAR, RETAIN, HIDDEN and DEAD. If the value CLEAR is used, the map is cleared but more recently displayed maps are not cleared.

The following attributes cannot be changed from their original settings and so are automatically preserved: ERROR_MAP, MENUMAP, IN_BOX.

The attribute INACTIVE can only be switched on or off for the specified map.

The attribute REMARK can be turned on but not off.

See also

map_draw(), map_attach()

Example

/* C
 Functions Reference Manual
 --------------------------
 Filename: MAP_CHAN.C
 Example: map_change
 Purpose: illustrate map_chan with a couple of
          maps.
*/

/*
#db salesord
#c
#libtype 1
#end
*/
#include "dp4.h"
#include "map_chan.h"

/* generate this file by running LIBMAKE */
void process()
{
   int map_nr;
   map_draw(1,RETAIN); /* put up map 1 */
   map_change(1,HIDDEN); /* hide it */
   map_draw(2,RETAIN); /* put up map 2 */
   map_change(2,HIDDEN); /* hide it */
   /* loop until escape, toggling map 1 and map 2 using map_change */
   for (map_nr = 1; !abort_code; map_nr %= 2,map_nr++)
   {
     map_change(map_nr,RETAIN); /* reshow the map */
     askf_special(map_nr,1,0); /* wait for a key */
     map_change(map_nr,HIDDEN); /* hide the map */
  }
}


map_choose()

Purpose

Chooses an option from a menu

Syntax

int map_choose(mapnr, option)

Parameters

int mapnr

Number of map to display

int option

Pointer to variable to hold current selection

Description

The map_choose() function allows the user to select an option from a map. It is similar to the function field_choose(), but displays and removes the specified map.

Numbers and function keys may be selected, but missing fields cannot be selected, unlike the field_choose().

The range of fields the user can cursor between is given by the row and column fields in the map, which are set using MAPEDIT. To display fields that you do not want the user to access, use field numbers outside this range. If either of the row or column values are zero, the function returns ESCAPE without stopping.

The more general function map_g_choose() can be used to grey-out options, preventing the user from selecting them.

The function map_choose() is implemented with a call to map_g_choose() using the global variable choose_any_option as the parameter banned_list.

Return values

Returns the value of the global variable abort_code

See also

map_g_choose(), field_choose()

Example

if (!map_choose(1,&option))
{
   /* Process option selected by the user */
   switch (option)
   {
      case 1:
        break;
      case 2:
        break;
      default:
        break;
   }
}


map_clear_after()

Purpose

Clears all the maps displayed after a given map

Old name

#define clearbelow map_clear_after

Syntax

void map_clear_after(mapnr)

Parameters

int mapnr

Number of map after which subsequent maps are cleared

Description

This removes all maps displayed after the specified map from the screen, but not the map itself. This is often used when some printing has been done and some display from maps remains on the screen.

See also

map_clear_from()

Example

/* typical printing example */
map_draw(1,RETAIN);
if (!ask_outdevice(PRINTER,0,"",) != ESCAPE)
{
   /* do some printing*/
   prn_print(FALSE);
}
map_clear_after(1);
/* remove remains of printing from screen */


map_clear_all()

Purpose | Clears the screen
Old name | #define clearscreen map_clear_all
Syntax | void map_clear_all(void)
Description | The function map_clear_all() clears and initialises the map functions so that the next map appears at the top of the screen.
Return values |


map_clear_from()

Purpose

Clears a map from the screen

Old name

#define clearmap map_clear_from

Syntax

void map_clear_from(mapnr)

Parameters

int mapnr

Number of map to be cleared

Description

The function map_clear_from() removes the specified map, and all maps drawn after it, from the screen. Clearing a map which is not on the screen has no effect.

See also

map_clear_after(), map_clear_all(), map_change()

Example

/* A simplified example of typical mistake in using
   of map_clear_from. This can be avoided by
   displaying and clearing maps locally. Functions
   which display maps should be responsible for
   clearing them. */
int get_name()
{
   askf_c(1,1,customer.name);
   map_clear_from(1);
}
map_draw(1,RETAIN);
   /* this should be in get_name */
map_draw(2,RETAIN);
   /* get_name clears map 1 and hence map 2 */
get_name();
   /* an error now occurs map 2 has been cleared */
askf_c(2,1,region.region_id);


map_clear_remarks()

Purpose | Clears all remark maps
Old name | #define clearremarks map_clear_remarks
Syntax | void map_clear_remarks(void)
Description | This function removes all maps displayed with the parameter flags set to the value REMARK. This function is called within the askf_x() and scrnx_map() functions just before they return.
See also | map_clear_after(), map_clear_from(), map_clear_all()
Example | map_clear_remarks();


map_datamap()

Purpose | Displays a map as a data map
Old name | #define datamap map_datamap
Syntax | void map_datamap(mapnr)
Parameters | int mapnr | Map to display
Description | The map_datamap() function displays a map as a data map using the global variable data_flags. This is the normal way of displaying a map on the screen for input. You can position the map on the screen by using the function map_new_position().
See also |


map_draw()

Purpose

Displays a map on the screen

Old name

#define map map_draw

Syntax

void map_draw(mapnr, flags)

Parameters

int mapnr

The number of the map to be displayed

int flags

Specifies position and colour of map display

The value of the parameter flags can be a combination of the following values:

Value

Meaning

REMARK

The map is cleared following next input, or next display of a map that contains a remark

DATAMAP

Display map as data map

ERRORMAP

The map is displayed as an error map

MENUMAP

Sets cursor wrap on and displays in menu colours. If possible, the map is displayed with an empty line between it and the map above. Adding RETAIN stops this

DEAD

Normally used with MAP_CHANGE. The map is displayed already 'dead', meaning that it is inaccessible to further operations

RETAIN

The map stays until cleared by one of the map clearing functions. Displays default colours of the map

SCROLL

When the screen is full, the map wraps around to its original position

IN_BOX

The map is displayed in a box. This is rarely used, as a box can be defined as a property of the map in MAPEDIT

HIDDEN

The map is initially displayed as a hidden map

CLEAR

All existing maps are cleared before displaying the existing map

INACTIVE

The map is displayed as inactive

NO_SCROLL_OFF

This makes sure that none of the maps currently on the screen are moved or removed

BOTTOM

The map is placed at the bottom of the screen. Under Windows, maps displayed with this flag are always positioned at the bottom of the window whether it is sized or scrolled

STEP_CURSOR

Obsolete

NO_STEP_CURSOR

Obsolete

NO_MEMFILE

The map is read from the database rather than the workfile. This is used by MAPEDIT

POP_UP_WINDOW

This positions the map as a popup map. Under Windows any map displayed with this flag is independently movable. Free maps acquire this flag automatically if displayed with MENUMAP, REMARK, SYSTEMMAP and ERRORMAP as long as RETAIN or BOTTOM are not specified. Attached maps only have this flag if it is passed explicitly

NO_REDIRECTION

Input redirection with the REDn program is disabled for this map

SYSTEMMAP

This indicates that the map comes from the system mapset contained in the SYSTEM database

Description

This function is the main function for displaying maps on the screen. The map is taken from the current mapset, loaded with the last call to the function map_load().

Although the database containing the mapset must be open, it need not be the currently open database described by the global variable db_nr.

If the map is not on the database, the program is terminated with an error message.

The position of the map on the screen is determined by an algorithm that takes account of the value of the parameter flag, and the user preferences defined in the file USERDATA.SYS.

For fixing the position of maps, see the function map_attach().

See also


map_draw_over()

Purpose

Replaces one map with another

Old name

#define mapover(old_map,new_map)
map_draw_over(old_map,new_map,DEFAULT)

Syntax

void map_draw_over(oldmap, newmap, NO_REARRANGE)

Parameters

int old_map

Number of map to be replaced

int new_map

Number of new map to display

int flags

Control the display characteristics

Description

This function replaces a displayed map with a new map. The text of the old map is overwritten and its fields are blanked out. If the new map is larger than the old map, it is truncated to fit in the space available. If it is smaller than the old map, some of the text of the old map may still be visible.

The only flag implemented at present is NO_REARRANGE. This keeps the map being replaced in its original position in the viewing order. If DEFAULT is passed, the replacement map is brought to the top of the viewing order. The NO_REARRANGE flag is useful if you wish to refresh a map which has other maps drawn on top.

The recommended use of this function is to call it with both parameters the same, for example:

map_draw_over(n, n, DEFAULT)

This is useful in pick lists.

See also

map_draw()


map_g_choose()

Purpose

Chooses an option from a menu with greying out

Old name

#define gm_choose map_g_choose

Syntax

int map_g_choose(mapnr, option, banned_list)

Parameters

int mapnr

Map number to choose from

int* option

Pointer to variable to hold chosen field

short* banned_list

Pointer to an array of integers, terminating with zero, containing the field numbers to grey out

The parameter banned_list should point to an array of short integers, terminating with a zero, which gives the list of fields to be greyed out.

Description

The map_g_choose() function is similar to map_choose(). It allows the user to choose an option from a menu and, in addition, options can be greyed out to make them inaccessible to the user.

If there are no fields to grey out, a pointer to a zero value should be passed. Never pass NULL as a value for the parameter banned_list.

If all the fields have been greyed out, ESCAPE is returned without any user interaction.

The initial value of the parameter option gives the field to be highlighted first of all. If it is greyed out, the next valid field will be used.

This function restores the original value of the global variable range on exiting. It is implemented using the field_eg_choose() function, with the parameter flags set to the value (NR_KEYS | NOT_MISSING_FIELD).

Return values

Returns the value of the global variable abort_code

See also

map_choose()

Example

/*
 C Functions Reference Manual
 ----------------------------
 Filename: MAP_G_CH.C
 Example: map_g_choose
 Purpose: Allows options to be selected from a menu
          using map_g_choose. The program also
          illustrates 'greying out' of menu options
          by supplying a list of banned options to
          map_g_choose.
*/

/*#db salesord
#c
#end
*/
#include "dp4.h"
#include "map_g_ch.h"
 /* generate this file by running LIBMAKE */
 /* prototypes */
static short DP4P banned_list(void);
static void call_menu_fn(int*);
static void customers(void);
static void new_prospects(void);

void process()
{
   int option = 1; /* header map */
   map_draw(1,RETAIN);
   while (!map_g_choose(2,&option,banned_list()))
   {
      call_menu_fn(&option);
   }
}

static void call_menu_fn(int* option)
/* call associated menu option function */
{
   switch (*option)
   {
      case 1:
        break;
      case 2:
        customers();
        break;
      case 3:
        break;
      case 4:
        new_prospects();
        break;
      case 5:
        break;
      default:
        break;
   }
}

static short DP4P banned_list()
   /* get banned fields for menu */
{
   static int ban[6];
   /* let's ban 1 3 and 5 */
   ban[0] = 1;
   ban[1] = 3;
   ban[2] = 5;
   /* must end with a zero */
   ban[3] = 0;
   return ban;
}

 /* support functions - just display a map*/
void customers()
{
   map_pause(50);
}

void new_prospects()
{
   map_pause(51);
}


map_get_depth()

Purpose

Returns the number of lines occupied by a map

Old name

#define mapsize map_get_depth

Syntax

int map_get_depth(mapnr)

Parameters

int mapnr

The number of the map whose size is required

Description

The function map_get_depth() returns the number of screen lines occupied by the specified map. If the map is not on the screen, it returns the number of lines in the map, which is useful in printing to determine whether there is sufficient room on the current page to display the map.

The return value in the two cases may be different if map_load() has been called since a map with the specified number was displayed, or the IN_BOX was used to put a box around a map. Therefore you should probably avoid calling map_get_depth() for a map to be printed if a map of the same number was displayed using map_draw() etc.

Return value

The number of lines occupied by the map

See also

map_get_width()

Example

nr_lines = map_get_depth(1);


map_get_inputs()

Purpose

Control function for data entry in maps

Old name

#define get_inputs map_get_inputs

Syntax

int map_get_inputs(mapnr,start_field, data_inner,flags)

Parameters

int mapnr

Number of map being used

int start_field

Initial field to begin data entry

void (*data_inner)(int, int *)

User-supplied data entry function

int flags

Specifies display attributes

The parameter start_field indicates where the field entry should start.

The data_inner function is called each time data is input. Here is a typical example:

void data_inner(int m,int* f)
{
   int current_field_nr;
   switch (curr_field_nr = *f)
   {
      case 1:
        if (inpm_c(&customer.acc_nr))
          break;
         /* do any validation if fails put *f = current_field_nr */
        break;
      case 2:
        if (inpm_c(&customer.short_name))
          break; /* do any validation */
      case 3:
        ...
      default:
        inpm_omit();
        break;
   }
}

The parameter m to the function data_inner is the same as the mapnr parameter passed to the function map_get_inputs(). The parameter f to data_inner points to the current map field for data entry. The parameter flags can be a combination of the following values:

Value

Meaning

NO_DISP

Indicates that the display map is already on the screen

ABORT_FUNC_KEYS

If this flag is set and the inner function returns a value greater than 0 in abort_code, map_get_inputs() returns without completing all the fields. It is intended for use with activating function keys. For example, if a program has a table describing the current state of a number of dialogs, each with a different activating function key. Pressing a function key starts or resumes a dialog which may be returned to in the same state in which it was left

GI_GLOBALS

When this flag is specified, the following variables are managed automatically:

  • jump Set to FALSE on entry to and exit from map_get_inputs()

  • range Set to the starting field number on entry to map_get_inputs() and to 0 on exit

  • clearfirst Set to 2 on entry to map_get_inputs() if PREDISPLAY is not specified and 0 otherwise and to 0 on exit

PREDISPLAY

All the fields are initially displayed before stopping for data entry. The global variable realpass is used to distinguish between a predisplay pass and a normal pass.

For a predisplay pass, the global variable jump is set to TRUE and the global variable realpass is set to FALSE.

inpm_x() functions all return RIGHT when realpass is FALSE so you will normally break before doing any validation. If you go into some validation code with realpass FALSE, and the validation fails and you reset the field number your program will hang.

GI_NO_ESC_CLEAR

Prevents the map from being cleared if the user escapes from rather than completing the dialog. Without this flag the map will always be cleared if the user escapes from the dialog, regardless of the other flags below.

RETAIN

The map is set to RETAIN on exit and not cleared

CLEAR

Clears the map on exit using map_change(,CLEAR)

HIDDEN

The map is hidden with map_change(,HIDDEN) when the dialog is completed normally. See map_change()

DEAD

The map is set to DEAD with map_change(,DEAD) when the dialog is completed normally. See map_change()

ASK_CONFIRM

If necessary puts up a confirmation menu after the user has entered the data, using sys_upd_confirm() (to which you should refer for an explanation of "if necessary"). You can replace this routine if desired. Depending on the value returned by sys_upd_confirm() map_getinputs() may return 0 for success, ESCAPE to cancel the dialog, or make another pass through the input fields.

Description

The map_get_inputs() function controls how the input is entered from several fields in a map.

This function is the preferred function for data entry and should be used with the inpm_x() functions. It simplifies considerably the development of data entry functions and reduces the code size of programs. Many of the checks that had to be done previously are automatically taken care of.

It is similar to the pick_record() function in that the user supplies a callback function which is called as necessary , in this case to actually get input from the user.

Note you do not need to call map_use() as this is automatically done for you. However if you make a nested call to map_get_inputs() from a data_inner() calllback function, you should call map_use() on return to correctly set the global variables input_map and input_field.

Return values

Returns ESCAPE if the user escapes from one of the data entry functions, or 0 when the dialog is completed.

See also

inpm_x(), inpm_omit(), map_predisplay()

Examples

The example below is a very basic one for the SALESORD database. A much better example, which is fully annotated, can be found at An Example Dialog in DP4

/*
#db salesord
#c
#tables customer, employee;
#end
*/

#include "dp4.h"
#include "map_get_.h"
/* generate this file by running LIBMAKE */
/* prototypes */
void get_new_cust(void);
void init_new_cust(void);
void cust_inner(int,int*);

void process()
{
   map_datamap(1);
   trm_read_user();
   get_new_cust();
}

void get_new_cust()
{
   map_datamap(2); /* initialise the new fields */
   init_new_cust();
   if (!map_get_inputs(2,1,cust_inner,
             DEFAULT|ASK_CONFIRM))
     rec_post_commit(&customer.l);
}

void init_new_cust()
{
 /* initialise fields for a new customer */

 /* get last customer and add 1 */
 rec_fetch_main(LAST,&customer.l);
 customer.customer_number++;
 customer.credit_customer = TRUE;
 customer.credit_limit = 1000.0;
 WIPE(customer.name);
 WIPE(customer.address);
 WIPE(customer.region_code);
}

void cust_inner(int m,int *f)
{
  int field_nr = *f;
  switch (field_nr)
  {
    case 1:
      inpm_c(customer.name);
      break;
    case 2: /* show it only */
      show_n(m,2,customer.customer_number);
      inpm_omit();
      break;
    case 3:
      inpm_c(customer.address[0]);
      break;
    case 4:
      inpm_y(&customer.credit_customer);
      break;
    case 5:
      if (customer.credit_customer)
        inpm_n(&customer.credit_limit);
      else
      {
        field_blank(m,field_nr);
        inpm_omit();
      }
      break;
     case 6:
       inpm_u(customer.region_code);
       /* check this is a valid region */
       break;
     default:
       inpm_omit();
       break;
  }
}


map_get_width()

Purpose

Finds the width of a map

Old name

#define mapwidth map_get_width

Syntax

int map_get_width(mapnr)

Parameters

int mapnr

The number of the map whose width is required

Description

This function gives the width of the specified map.

All maps have a default width of the screen in use when the map was posted, unless the default is explicitly overridden with MAPEDIT or MAPPOST.

The map must be on the screen when the function map_get_width() is called, else the program is terminated with an error message.

This function is rarely used.

Return values

Width of specified map

See also

map_get_depth()

Example

width = map_get_width(1);


map_glue()

Purpose

Moves the contents of one map onto another

Old name

#define glue_map map_glue

Syntax

void map_glue(mapnr, item_mapnr)

Parameters

int mapnr

Number of underlying map

int item_mapnr

Number of map to glue to mapnr

Description

The map_glue() function moves the contents of an attached map to the corresponding fields of the underlying map to which it is attached. The function then removes the attached map from the screen.

This has no immediate effect on the screen as seen by the user, but ensures that the terminal manager does not attempt to manipulate the attached map further.

This function is commonly used in the user-defined show() function that is passed as a parameter to the pick_record() function.

See also


map_hide()

Purpose

Hides a map that is visible on the screen

Old name

#define hidemap map_hide

Syntax

void map_hide(m)

Parameters

int m

Number of map to be hidden on screen

Description

The map_hide() function hides a map on the screen.

It can be re-displayed with a call to the functions map_reveal() or map_change(), for example:

map_change(m, RETAIN);

The function is implemented as:

map_change(m, HIDDEN);

Using the function map_hide() is preferred to using the function map_change() because it can reduce the code size if it is called a number of times.

Hiding maps takes up resources, and it may be better in an applications to clear the map and redisplay the map and its contents, as necessary, later.

See also


map_items_to_scroll()

Purpose | Returns the number of records to be brought in with the function pick_record()
Old name | #define items_to_scroll map_items_to_scroll
Syntax | int map_items_to_scroll(mapnr, direction)
Parameters | int mapnr | Number of display map
 | int direction | Direction corresponding to cursor input
Description | The map_items_to_scroll() function is used by the functions pick_record() and pick_list(). It is for private use only. See the code for the function pick_record() for more details on its use in pick lists.
Return values | Returns the number of records to be brought in according to the row and column arrangement of the map


map_load()

Purpose

Specifies a set of maps for subsequent use for screen or printer operations

Old name

#define loadmaps map_load

Syntax

void map_load(mapname)

Parameters

char* mapname

Name of the mapset to be loaded

Description

The map_load() function is used to select the mapset, from the current database, to be used for subsequent screen or printer operations. LIBMAKE typically generates a call to this function in the body of a function named init.

If you call map_load() before opening any application database, the mapset is taken from the SYSTEM database. This is because the global variable db_nr initially has the value 0.

If the command tail -MAP <mapname> is used, the specified mapname overrides the parameter mapname the first time map_load() is called.

The function map_load() merely sets a pointer, so there is no performance degradation however many times it is called to swap between mapsets.

You can still reference an existing map that has been displayed, even if another mapset has been loaded. If a map is displayed from a mapset, and the same map is already on the screen but comes from a different mapset, the first map is not removed.

The following code shows how maps from different mapsets may be used:

map_load("A");
map_draw(1,RETAIN);
map_load("B"); /* display map2 from B */
map_draw(2,RETAIN);
/* still ok to reference map 1 from A */
scrnh_map(1,&f,&i);
/* put up map 1 from B map 1 from A remains on the
screen */
map_draw(1,RETAIN);
map_clear_from(1);
/* clears map 1 of B, map 1 of A is still on
the screen */
map_clear_from(1);
/* clears map 1 of A */

Return values

See also

map_draw()

Example

db_open("salesord",0,SHARED); /* load mapset */
map_load("NAMES");
/* now it is valid to call the map functions */
map_draw(1,RETAIN);
/* ... */


map_menu()

Purpose | Displays a map as menu map
Old name | #define menu map_menu
Syntax | void map_menu(mapnr)
Parameters | int mapnr | Number of map to display
Description | The function map_menu() displays the map as a menu map using the global variable menu_flags to define the format. Use this function for giving your menus a consistent appearance. You can also position the map with the map_new_position() function.
See also |


map_new_position()

Purpose

Sets the position of maps used by the functions map_remark(), map_datamap() and map_menu()

Old name

#define map_position map_new_position

Syntax

void map_new_position(int mapnr, int fieldnr)

Parameters

int mapnr

Number of map and field to which maps drawn with map_remark(), map_datamap(), or map_menu() will be attached. If map number 0 is specified the maps will be drawn with map_draw() and not map_attach()

int fieldnr

Number of field in map in which to attach the maps

Description

The map_new_position() function allows a map to be used to determine the position of maps used in future calls to the functions map_datamap(), map_remark() and map_menu().

They are all attached to the field specified by the parameter fieldnr in the map specified by the parameter mapnr. The map specified must be on the screen when these functions are called.

Calls to map_new_position() can be stacked up to a level of 10. Stacking beyond this causes the program to terminate.

The function map_old_position() can then be called to remove the latest position values and restore the previous position.

For example: a program could use map_position() to force popup windows displayed with pick_record() to appear attached to the relevant input field. You could include a hook field with field number 100+field number for each input field with prompt. At the top of data_inner() you would include this call : map_new_position(m,*f+100); and at the bottom of data_inner() you would put map_old_position();

See also

map_old_position()


map_new_window()

Purpose

Saves the screen state

Old name

#define new_window map_new_window

Syntax

void map_new_window(void);

Description

The map_new_window() function saves the state of the screen. It is used in the %savescrn feature of QA Build.

The map_new_window() function causes all maps on the screen to become inaccessible. The limit to the scrolling area specified by the function map_set_scroll_end() is unset.

When the function map_old_window() is called, the screen is restored to its original state before the function map_new_window() was called.

The maps that are saved still count towards the limit of 50 maps which can be displayed at a time by the 4.5xx terminal manager. There is no limit to the number of maps in 4.6xx

See also

map_old_window()


map_old_position()

Purpose

Restores old map position values

Old name

#define old_map_position map_old_position

Syntax

void map_old_position()

Description

The map_old_position() function is used in conjunction with the map_new_position() function to set up a map and field to which all future maps, displayed using the map_datamap(), map_remark() or map_menu() functions, are attached.

You can call the function map_new_position() up to a level of 10 to change these values. map_old_position() is used to return to the previous level. If the function map_new_position() is not called previously this function has no effect and the map_remark(), map_datamap() and map_menu() functions display their maps in the normal way.

See also

map_new_position()


map_old_window()

Purpose

Restores the screen state

Old name

#define old_window map_old_window

Syntax

void map_old_window()

Description

The map_old_window() function restores the screen to how it was when the function map_new_window() was called. The old maps are re-activated, so they become accessible and any limit to scrolling is reset.

If you call this function without a previous matching call to the function map_new_window(), the behaviour is undefined.

See also

map_new_window()


map_pause()

Purpose | Displays a map and pauses
Syntax | int map_pause(mapnr)
Parameters | int mapnr | Number of map to display
Description | The function map_pause() displays a map, and waits for any key to be pressed using the function pause, then removes the map. It pauses at the first field in the map. If the map has no fields, the program terminates with an error message. If the global variable jump is TRUE, this function does not pause. If <Esc> is pressed, the global variable abort_code is set to ESCAPE, otherwise it is set to zero. If you wish to display data in some fields on the map to start with, use the function askf_special().
Return values | Returns the value of the global variable abort_code.
See also | askf_special()


map_predisplay()

Purpose

Pre-displays a set of fields on a map

Old name

#define predisplay map_predisplay

Syntax

void map_predisplay(mapnr, start_field, data_inner, flags)

Parameters

int mapnr

Number of display map being used

int start_field

Initial field in which to begin data entry

void (*data_inner)

User-supplied data entry function(int,*int)

int flags

Parameter specifying display attributes

The parameter flags can take the following values:

Value

Meaning

DEFAULT

Default action. Displays the map

NO_DISP

Use if the display map is on the screen

Description

The map_predisplay() function is used to display a number of fields in a map. Its format is the same as the map_get_inputs() function.

This function is useful in programs where sometimes you have to display the fields and sometimes the user has to enter the details. The same data_inner function can then be used for both map_predisplay() and map_get_inputs().

This function sets the global variable jump to TRUE, and the global variable realpass to FALSE.

Return values

While the global variable realpass is FALSE, the data_inner function must not reset the current field value. If it does, the program can hang. The data_inner function must not reset the global variable jump.

After calling this function, the global variable jump must be reset to FALSE.

The function map_predisplay() never clears the display map.

This function saves and restores the value of the input_field variable, used by the inpm() functions, making it possible to call the map_predisplay() function from inside a call to map_get_inputs(). This is useful for updating values on screen where a program provides appropriate default values.

See also

map_get_inputs()


map_remark()

Purpose | Displays a map as a remark map
Old name | #define remark map_remark
Syntax | void map_remark(mapnr)
Parameters | int mapnr | Number of map to be displayed as a remark map
Description | This function displays the map as a remark map.
 | The global variable rmk_flags is used to control the display attributes of the remark map. This global variable must contain the value REMARK, to which any of the following values may be added:
 | Value | Meaning
 | BOTTOM | Place remark map at bottom of screen. Under Windows, maps displayed with this flag are always positioned at the bottom of the window whether it is sized or scrolled.
 | NO_SCROLL_OFF | Do not scroll the remark map off the screen
 | RETAIN | Places remark map as if it were a data map
 | IN_BOX | Draw box around remark map
 | POP_UP_WINDOW | Place remark map as if it were a popup map. Under Windows, any map displayed with this flag is independently movable. Free maps acquire this flag automatically if displayed with map_remark() as long as neither RETAIN nor BOTTOM are specified. Attached maps only have this flag if it is passed explicitly
 | The remark map disappears when map is called, or when the next screen input function has finished. This function allows all the remark maps in a program to behave in a consistent way. For example, all the remark maps can be made to appear at the bottom of the screen by setting the global variable rmk_flags as follows: rmk_flags = REMARK | BOTTOM; You can also position where the map is placed with the map_new_position() function.
See also | map_draw(), map_new_position()
Example | map_remark(50);


map_reveal()

Purpose | Reveals a previously hidden map
Old name | #define revealmap map_reveal
Syntax | void map_reveal(m);
Parameters | int m | Number of map to reveal
Description | The map_reveal() function reveals a map which has previously been hidden with a call to the functions map_hide() or map_change(). This function is implemented as: map_change(m, RETAIN) This convenient function can reduce code size if it is called a number of times.
See also | map_change()


map_set_scroll_end()

Purpose

Confines scroll output

Old name

#define set_scroll_end map_set_scroll_end

Syntax

void map_set_scroll_end(line_nr)

Parameters

int line_nr

Number of lines to which scrolling is limited

Description

The map_set_scroll_end() function confines the display of scrolling maps to the specified number of lines of the screen.

The terminal manager may disregard this function if the map cannot be placed in the specified area without overlapping other maps, even after all possible scrolling has been done.

This function works well with simple 'teletype' displays.

You can use this function when you want to scroll maps between a header map and bottom map.

The function prn_print() also uses the scrolling limit set with this function.

The number of lines on the screen is determined by the structure element user.scrn_depth. The limit can be reset by the following calls:

map_set_scroll_end(user.scrn_depth);

or,

clear_screen();


map_so()

Purpose

Displays the specified string at the specified location

Syntax

void pascal map_so(int mapnr,int x,int y,
char DP4P data, int colour)

Parameters

int mapnr

The map to show the string

int x

Offset in 16ths of a standard character from the left side of the map

int y

Offset in 32nds of a standard character from the top of the map

char DP4P data

Pointer to the string which must be zero-terminated

int colour

Colour information, interpreted as follows:

Value

Meaning

0

The string inherits the map's colour scheme

-C_OBSCURE

The string inherits the map's colour scheme and is displayed in the disabled text colour

-C_OPTION

The string inherits the map's colour scheme and is displayed in the selected text colour

Other negative values

The string is diaplayed using the abs(colour) colour scheme on the database from which the map comes. Normally you would specify one of:

-C_REMARK, -C_WARNING or -C_ERROR

Positive values

The string is displayed using the appropriate indexes from the colour portion of the master palette, as for field_colour()

Description

The function map_so() displays the specified string in the specified map, at the specified offsets from the map's top left corner and in the specified colour scheme.

The displayed data becomes part of the map and is displayed until the map is cleared and refresh() is called.

Because of internal buffering performed by TRMW to improve performance the string will survive the sequence map_clear_from(mapnr) map_draw(mapnr,RETAIN).


map_switch_language()

Purpose

Switches to another language

Old name

#define change_language(language)
map_switch_language(language,0)

Syntax

void map_switch_language(language, flags)

Parameters

 int language

New language

int flags Parameter specifying display attributes

The parameter flags currently has no effect, so the value DEFAULT must always be passed.

Description

The map_switch_language() function overrides the language specified in the file USERDATA.SYS, or by log on so that all subsequent maps are taken from the new language set by this function.

The new language is used until the program calls either of the functions trm_set_preferences() or trm_unset_preferences() , or this function is called again or until it is terminated.

This function is intended to allow a program to display maps outside the normal language, where this is required for some reason. One possible use of the function is to create a set of maps for a program suitable for training purposes rather than for an experiernced user. It is not suited to a real change of language, because it does not update language related information such as month names, or the words for yes an no. To change languages properly you need to call the trm_set_preferences() functions to log on as a user using the desired language code. Dummy users can be created specifically for this purpose.

See also

trm_set_preferences(), trm_unset_preferences()


map_use()

Purpose

Sets up the missing inpm() fields

Old name

#define use_map map_use

Syntax

void map_use(mapnr, fieldnr)

Parameters

int mapnr

Number of map to set as default

int* fieldnr

Pointer to a variable to use as current field number

Description

The map_use() function is called by the inpm_x() functions to specify the map and field numbers from which to obtain input. It does this by setting the global variables input_map and input_field to the corresponding parameter values.

Normally, the field number passed to the function map_use() is the address of a local variable set to the initial data entry field. It is automatically changed with subsequent user input.

The function map_use() is usually called just once at the beginning of each data entry function.

If you use the map_get_inputs() function, the function map_use() is automatically called for you.

If you do not use this function before calling an inpm_x() function, the "map not on screen" error message is displayed.

See also

inpm_x(), inpm()

Example

See the example on inpm_x()


map_yes()

Purpose

Obtains Boolean response from user

Old name

#define yes map_yes

Syntax

int map_yes(mapnr, default_answer);

Parameters

int mapnr

Map number to use for confirmation

int default_answer

The default Boolean value to display

Description

The map_yes() function gets a Boolean response from the user by calling the map_askf_y() function. It is more convenient to use than the function map_askf_y(), because no parameter is required to hold the reply.

This function is normally used to get a confirmation from the user that the program may take some subsequent action, for example deleting records.

The global variable jump is set to FALSE, so the reply field cannot be by-passed by pressing <Tab>.

Return values

Returns TRUE if user enters YES, otherwise FALSE

See also


mem_describe()

Purpose

Initialises the memory workfile

Syntax

void mem_describe(type_nr, key_size)

Parameters

int type_nr

Table number of records to be used in work file

int key_size

Size of key of table. The key is assumed to be at the beginning of the record

Description

The DP4 memory work file function mem_describe() allows data records to be stored and retrieved from a work file. Each table used has its own work file, and this function must be called on each table to initialise the operation.

Another set of workfile functions are available which allow you to access records in a workfile file with an index. See the function wkf_open().

The parameter key_size used to differentiate between records in the workfile is assumed to be at the beginning of the record. You cannot use mem_describe() with database records where the key fields are spread throughout the record.

To use the memory workfile functions, follow the steps below:

1. Call the function mem_describe() to define the table and key size

2. Call the function mem_post() to post records to the workfile

3. Call the function mem_find() to fetch records with a specified key from the work file

You can define your own tables, but use type numbers greater than 4096. You cannot delete records from the workfile.

See also


mem_find()

Purpose | Fetches a record from the workfile
Syntax | int mem_find(dp4rec)
Parameters | short* dp4rec | Pointer to the record whose key fields indicate the record to fetch from the workfile
Description | The mem_find() function attempts to fetch a record from the corresponding workfile which has previously been posted to the workfile with a call to the corresponding mem_post() function. The owner of the record which posted the original record is given by the global variable df_process. The database labelled as containing the record is given by the global variable db_nr.
Return values | Returns TRUE if record is found, otherwise FALSE
See also |


mem_post()

Purpose | Posts a record to the workfile
Syntax | int mem_post(dp4rec);
Parameters | short* dp4rec | Pointer to the record to post to workfile
Description | The mem_post() function posts a record to the corresponding workfile which has previously been set up with a call to the corresponding mem_describe() function. The owner to label the record in the workfile with is held in the global variable df_process. The database labelled as containing the record is held in the global variable db_nr.
Return values | Returns TRUE if record posted, otherwise FALSE
See also |


Functions N-Q

• number_to_date() | • prn_append()
• open_database() | • prn_length()
• pattern_match() | • prn_over()
• pick_list() | • prn_print()
• pick_record() | • prn_x()
• prn() | • prnx()
• prn_abort() |


number_to_date()

Purpose

Converts a number to a date

Old name

#define number_2_date number_to_date

Syntax

datetype* number_to_date(number)

Parameters

long number

Number of days since 01 March 1980

Description

The number_to_date() function returns the date corresponding to the number of days given since 01 March 1980 which was a Saturday and counts as zero.

It works on dates from 01 March 1600 to 31 December 9999, and uses the Gregorian calendar. It does not take account of changes from the Julian calendar which took place in English speaking countries in 1752.

Return values

Returns pointer to calculated date

See also

date_to_number(), date_add_number()

Example

app_start_date = *number_to_date(20000);


open_database()

Purpose

Obtains a database name from the user

Syntax

void open_database(mapnr, fieldnr, flags, ADDITIONAL)

Parameters

int mapnr

Number of map containing fieldnr

int fieldnr

Number of field from which to get the name of the database

int flags

Defines how the database is opened

ADDITIONAL

Additional variable - not used

The parameter flags may take any of the same values as the function db_open()

Description

The open_database() function gets a database name from the user and opens that database at generation 0 with the required value for the parameter flags. To access the database at the latest generation, use db_generation = db_get_generation().

If the user presses <Esc> the program is halted.

You can use this function if the program can work with any database, as do most of the DP4 utility programs.

The command tail -DB <dbname> can be used to override the database name that is hard-coded in the program for the first call to open_database(). The function does not then stop for user input.

The global variable db_nr is set, and the global variable db_name contains the database name entered.

See also

db_open()


pattern_match()

Purpose

Wild card search for pattern in string

Syntax

int pattern_match(pattern, pat_length, data,
data_length)

Parameters

char *pattern

Pointer to characters for which to search

int pat_length

Length of pattern

char *data

Data in which to search for the pattern

int data_length

Length of the data

Description

The pattern_match() function does a wild card match for the parameter pattern in the parameter data. The matching is done using the following rules:

  • If the pattern length is zero, the match is always successful
  • A '*' character matches any group of characters
  • A '?' character matches any single character
  • Other characters must match exactly (the match is case sensitive)
  • If the pattern is longer than the data and the pattern matches up to the end of the data, the match only succeeds if all the remaining characters in the pattern are ' ' or '*'
  • If the data is longer than the pattern and the pattern matches up to the end of the pattern, the match only succeeds if all the remaining characters in the data are ' '

Return values

Returns TRUE if the pattern matches the data parameter

Example

Here are some example patterns:

"R*" matches data beginning with R (but not r)

"*R" matches data for which the last non-blank character is an R

"?R*" matches data whose second letter is an R

"A*b" matches data beginning with A and ending with b


pick_list()

Purpose

Picks an option from a fixed list of records

Old name

#define pick_optlist pick_list

Syntax

int pick_list(mapnr, extra_map, show_item,
item_count, choices, option_nr, flags,
ADDITIONAL)

Parameters

int mapnr

Number of display map

int extra_map

Number of extra map to display

void (show_item*)(int, int, int)

Pointer to function to call to display data

int item_count

Total number of items in list

int choices

Number of choices that can be displayed on the map

int* option_nr

Pointer to variable to hold the item number selected by the user

int flags

Parameter specifying display attributes and options

ADDITIONAL

Number of mnemonic map (required when parameter flags is set to ABORT_MNEMONICS)

The parameter flags can take the same values as for pick_record(), and the values have the same meaning except that: NO_DISP means the menu is on the screen, but the fields are redisplayed; NO_DISP+NO_READ means the menu is on the screen and the fields are correct.

Description

The function pick_list() is similar to the pick_record() function, but is used where the number of items in the list is known before the call is made.

This function differs from the function pick_record() in the following respects:

  • No user defined function for fetching records is required
  • No work area is required
  • No error map may be given. Only call the function when there are elements in the list

A user-defined function for displaying records is, however, required as with the function pick_record(). The display function is called by pick_list() to display each item in the appropriate way.

If the user selects a record the variable pointed to by the parameter option_nr is set to the selected field. Counting starts from 0. The initial value determined by the parameter option_nr indicates which field is highlighted when the map specified by the parameter choice is first displayed.

Return values

The return value indicates the key chosen, as follows:

Return Value

Key Pressed

0

If the <Enter> key is pressed

ESCAPE

If <Esc> is pressed

F<n>

Function key <n> is pressed (used when parameter flags includes the value ABORT_FUNC_KEYS)

PAGE UP

If there are no records

INSERT

If <Ins> is pressed, or <Enter> is pressed over the 'new record' message (used when parameter flags include the values ALLOW_NEW or ABORT_MNEMONICS)

DELETE

If <Del> is pressed (used when parameter flags includes the value ALLOW_DELETE)

The global variable abort_code is zero after a call to the function pick_list()

See also

pick_record()

Example

/*
 C Functions Reference Manual
 ----------------------------
 Filename: PICK_LIS.C
 Example: pick_list
 Purpose: Sample program on inserting order lines
          for an order
*/

/*
#db salesord
#tables customer,
        employee,
        order_header,
        order_line,
        index_cust_name,
        region;
#c
#end
*/

#include "dp4.h"
#include "pick_lis.h"
 /* generate this file by running LIBMAKE */
#define MAX_LINES  8
#define MAX_ORD_LINES 200
#define PICK_LINE_MAP 3

static struct ORD_LINE
{
   char mat_code[7];
   int quantity;
   double line_value;
}  ord_line[200 + 1];

static int nr_lines = 1;
/* holds number of order lines + 1 for new line */
static int opt_line;
/* holds current line selected */
void edit_order(void);
void disp_total(void);
void del_line(void);
int pick_line(void);
void edit_line(int);
void line_inner(int,int*);
void process()
{
   map_datamap(1);
   trm_read_user();
   /* only required if using Microsoft C: a bug in
      the linker causes an unresolved external error
      in the DP4 library */
   /* put up imaginary order and customer */
   map_datamap(2);
   order_header.order_number = 1234L;
   order_header.order_value = 0;
   WIPE(customer.name);
   memcpy(customer.name,"BROWN M",7);
   customer.customer_number = 1234567L;
   show_l(2,1,order_header.order_number);
   show_c(2,2,"BROWN M");
   show_n(2,3,order_header.order_value);
   menu_flags = MENUMAP | RETAIN;
   /* edit the order */
   edit_order();
}

void edit_order()
{
   /* put up mnemonic map */
   menu(7);
   for (;;)
   {
      switch (pick_line())
      {
         case ESCAPE:
         case 5:
           if (map_yes(8,FALSE))
             return;
           break;
         case 0:
         case 1: /* item selected */
           if (pk_chosen_field != nr_lines)
             {
                edit_line(FALSE);
             }
           break;
         case DELETE:
         case 2:
           if (pk_chosen_field != nr_lines)
           {
              if (map_yes(6,FALSE))
                del_line();
           }
           break;
         case INSERT:
         case 3:
           edit_line(TRUE);
           break;
         case 4: /* save option */
           return;
         default:
           break;
      }
      disp_total();
   }
}
void disp_total()
   /* redisplay order value */
{
   double total = 0.0;
   register i = 0;
   while (i < nr_lines - 1)
     total += ord_line[i++].line_value;
   show_n(2,3,order_header.order_value = total);
}

void show_line(m,field_nr,item)
  int m, field_nr,item;
{
   if (field_nr >= range)
   {
      show_over(m,field_nr,4);
   }
   else
   {
      map_attach(m,field_nr,5,DEFAULT);
      show_c(5,1,ord_line[item].mat_code);
      show_i(5,2,ord_line[item].quantity);
      show_n(5,3,ord_line[item].line_value);
      map_glue(m,5);
   }
}

void del_line() /* delete line from the list */
{
   movmem((char*)&ord_line[opt_line + 1],
     (char*)&ord_line[opt_line],
     (nr_lines - opt_line - 1) * sizeof(struct
     ORD_LINE));
   nr_lines--;
}
int pick_line() /* pickopt list */
{
   return pick_list(PICK_LINE_MAP,0,show_line,
     nr_lines,MAX_LINES, &opt_line,RETAIN|ALLOW_NEW|
     ALLOW_DELETE|NEW_NORMAL|ABORT_MNEMONICS,7);
} /* this should be in a separate module */

static int new_item;
void edit_line(new_line)
  int new_line;
{
   map_attach(PICK_LINE_MAP,_option,5,DEFAULT);
   new_item = new_line;
   if (new_line) opt_line = nr_lines - 1;
   if (!map_get_inputs(5,1,line_inner,NO_DISP|
     (new_line ? 0: PREDISPLAY)))
   {
      if (new_line)
        nr_lines++;
   }
}

void line_inner(m,f)
  int m; int* f;
{
   int field_nr;
   switch (field_nr = *f)
   {
      case 1: /* material code */
        if (new_item)
          inpm_u(ord_line[opt_line].mat_code);
        else /* don't allow editing of this field */
        {
           show_c(m,field_nr,ord_line
             [opt_line].mat_code);
           inpm_omit();
        }
        /* should check and offer pickfill */
        break;
      case 2: /* quantity */
        inpm_i(&ord_line[opt_line].quantity);
        /* should now work out correct line value
           there may be discounts etc. here just
           multiply by 12 for effect. */
        ord_line[opt_line].line_value = 12 *
          ord_line[opt_line].quantity;
        break;
      case 3: /* display only */
         show_n(m,field_nr,ord_line
          [opt_line].line_value);
          /* fall through to skip field */
      default:
        inpm_omit();
        break;
   }
}


pick_record()

Purpose

Displays a list of record details from which the user can choose

Old name

#define pick_fill pick_record

Syntax

int pick_record(mapnr, extra_map, error_map,
fetch_item, show_item, save_array, item_size,
choices, flags, ADDITIONAL);

Parameters

int mapnr

Number of map to display list of choices

int extra_map

Map used for title (or 0)

int error_map

Map used when there are no items to display

boolean (*fetch_item)(int)

Pointer to function which fetches items to display

void(*show_item)()

Pointer to function which displays item in the map

void* save_array

Pointer to the workspace area

int item_size

Size of one element of save_array

int choices

The number of items that can be displayed in the map

int flags

Parameter which specifies display attributes

ADDITIONAL

Optional variable for mnemonic map

The parameter flags can be a combination of the following values:

Value

Meaning

DEFAULT

The choice map is displayed and cleared after the user has either selected a record or pressed <Esc>

RETAIN

Stops the display map from being cleared when the record has been selected

NO_DISP

Indicates that the choice map is already on the screen when the pick_record() function is called. The user-defined show_item function is still called to fill in the map unless the value NO_READ is added to the parameter flags

NO_READ

This implies that the save_array has already been filled in, so no initial fetching is required

E_CHOOSE

Tells pick_record() to call the function field_eg_choose() rather than the field_choose() function. In this case, a third parameter is passed to the user-defined function show_item which is called with this parameter set to TRUE as each field receives the input focus. This can be used to display extra information associated with the current option. This feature is used for example by the pick_record() function in the DP4 MAPEDIT utility to display the maps in the menu of maps

ABORT_FUNC_KEYS

If a function key is pressed, the return value of the function pick_record() indicates the function key pressed. The record selected is the one highlighted when the function key was pressed

ABORT_MNEMONICS

This requires an extra parameter to be passed to the function pick_record() that gives the map number of a mnemonic map, which must be on the screen. The mnemonic keys can then be used to exit the pick_record() function. The return value is the corresponding field number of the map. If the parameter flags contains the value ALLOW_NEW, the mnemonic keys are ignored while the cursor is over the 'New record' message. This can be overridden by adding the value NEW_NORMAL to the parameter flags

If the user clicks on the mnemonic map or presses<Enter>, 0 is returned. It is possible to distinguish between the values by examining the selected variable, as in:

  • When 0 is returned because <Enter> is pressed, selected is TRUE
  • When 0 is returned because of a click on the mnemonic map, selected is FALSE

ALLOW_NEW

This reserves the last field in the display map for a 'NEW RECORD' message which is displayed at all times. Selecting this field or pressing the <Ins> key causes the function pick_record() to return the value INSERT. The user-defined function show_item must include the test:

if (fieldnr >= range)

to decide when the new record message is displayed.

Only the <Ins> and <Enter> keys can select the new record field, and not the function keys, even if ABORT_FUNC_KEYS is used

ALLOW_DELETE

If the <Del> key is pressed, the highlighted record is selected and the function pick_record() returns the value DELETE

NEW_NORMAL

This value is added to the parameter flags when the values ALLOW_NEW and ABORT_MNEMONICS are also added. This enables the pick_record() function to return after a mnemonic key has been pressed while the cursor is over the 'New record' message. You must then save the value of the field that contains this message and compare it with the value of the global variable pk_chosen_field

NO_PICK

Forces the function pick_record() to display only

VIEW_PICK

The user can view records, but cannot choose any

Description

The pick_record() function is a general function for displaying details from several records at once from which the user can select a particular record. It offers a menu-like display, similar to the field_choose() function, but the menu list may be of variable length.

An extra field is used to indicate whether there are more fields below, or above, which can be brought into view by using the cursor keys and the <Pg Up>, <Pg Dn>, <Home> and <End> keys.

To use the pick_record() function, you do the following:

1. Set up the work area for use by pick_record(). This is treated by pick_record() as an array of items describing each record and is pointed to by the parameter save_array. You decide how much information to store in the array, but there must be enough to identify each record uniquely. You might choose to:

  • Store the whole record
  • Store all the key and all the fields to be displayed for each record
  • Store the minimum information to identify the record. This would normally be the primary key or the part of it allowed to vary

Storing the whole record allows you to code the fetch routine without having to define new structures. If you only store the key and not all the fields to be displayed, your show() routine will have to refetch the record

The number of elements in the array is always at least one more than the number of records that can be displayed at once. Element zero is used for reading in records and determining whether there are more items than can be displayed at once. The other elements contain the information to be displayed at the corresponding field

If you wish you can use the same area of memory for all your calls to pick_record(). To do this, define a union containing all the save_arrays required by the calls to pick_record(). In this case, remember that either an "&" is needed before the name of the union or else the correct component of the union is passed (which is an array so the "&" can be omitted)

The structure of the record normally corresponds to the fields to be displayed from the records fetched from the database. Here is a typical record structure, SAVE_CUST, that selects three records from the CUSTOMER record:

struct SAVE_CUST
{
  int cust_nr;
  char cust_name[32];
  char address[4][32];
} save_cust[MAX_CUST + 1];

There is at most MAX_CUST records visible at one time

2. Write the user-defined function for fetching the records, which is passed as the parameter fetch_item. This function is declared as:

boolean fetch_item(int direction)

where direction indicates the direction in which to search the database

The fetch_item function is responsible for fetching records from the database and storing the details to be displayed in the work area pointed to by the parameter save_array. The work area itself is set up in step 1 above. Its name is not fixed; it is called fetch_item here for clarity. You only need to work with the 0th item in the work area

fetch_item is called by the pick_record() function as necessary. Its direction parameter may take one of the values FIRST, NEXT, LAST, PREV or EQUAL

If the direction parameter passed to the function fetch_item is not EQUAL, you fetch a record from the database using this flag (DEPTH or BUT may be added). The fields from the record are then copied into the 0th item of the work area

If the direction parameter passed to the function fetch_item is EQUAL, the information must be copied from the 0th item in the work area to a database record. This record is used in a call to one of the functions rec_fetch_main(), ftch2nd() or rec_fetch()

EQUAL is passed in two situations:

  • When the function pick_record() has reached the end of records in one direction and is going to the other end of the list
  • Just before the function pick_record() returns, so that the selected record is in memory

A typical fetch_item function is given in the Example section later

3. Write the user-defined function for displaying the records, which is passed as the parameter show_item. This function is declared as:

show_item(int mapnr, int fieldnr)
show_item(int mapnr, int fieldnr, int extra)

where the parameters are:

int mapnr

Same as parameter mapnr to function pick_record()

int fieldnr

The field in which to display the data, and also the index into the work area array containing the information

int extra

Required when the value E_CHOOSE is passed in the pick_record() function's parameter flags. Set to TRUE if extra information is displayed with the fields, or FALSE to display the fields as normal

The show_item function is used to display the record information in the choice list. Its name is not fixed, it is called show_item here for clarity

A typical show_item function contains the following:

map_attach(mapnr,CUST_LINE_MAP,DEFAULT);
show_h(CUST_LINE_MAP,1,save_cust[fieldnr].cust_nr);
  ...
map_glue(m,CUST_LINE_MAP); /* map_glue is needed to
  stop the information displayed from disappearing
  when the cursor is moved */

Two factors which influence the writing of this function are whether a new record message is to be displayed and whether extra information for each field is to be displayed

If you wish to display a 'NEW RECORD' message which the user can select to create a new record, the value ALLOW_NEW must be added to the pick_record() function's parameter flags

The function show_item must then check to see when to display the 'NEW RECORD' message rather than the record details. This is done by adding the following code to the beginning of the show_item function:

if (fieldnr >= range)
{
  show_over(m,fieldnr,NEW_RECORD_MAP);
}
else
...

The parameter extra is used when the value EG_CHOOSE is added to the pick_record() function's parameter flags. This is used to display additional information as the cursor moves to each item. For example, you may not have room to show a complete description for the records, so just display the description for the currently highlighted record at the bottom of the map

4. Write the call to the pick_record() function. Because the function pick_record() has a number of parameters it is normally called indirectly from another function, for example:

int pick_rec()
{
  return pick_record(CUST_BODY_MAP, 0,
       CUST_ERROR_MAP, fetch_item, show_item,
       save_item, sizeof(struct SAVE_CUST),
       MAX_CUST, ALLOW_NEW|ALLOW_DELETE);
}

5. Create the CUST_BODY_MAP map. This should have:

  • MAX_CUST fields for displaying the record details. These fields are highlighted as the user moves between them
  • A field 100 for displaying a message such as 'more records below/above'. This field should be 24 characters long

If you wish to use the same display map for different choice functions, you can pass a title map for the choice list in the parameter extra_map. This title map is then displayed in field 101 of the display map.

If you wish to display an error message when no records are fetched (except when the value ALLOW_NEW is included in the parameter flags) it should be passed in the parameter error_map.

The number of records which can be displayed should correspond to the number of records set up for the error message, and should be at least one less than the number created in the work area.

The pick_record() function uses the global variables pk_more_choices, pk_chosen_field, count and range.

The original value of the global variable range is restored when exiting from the function pick_record() so that it can be readily used in data entry functions.

The global variable pk_chosen_field indicates the field number chosen. It is commonly used to attach a map to the chosen record for some data entry processing by the user.

The global variable pk_more_choices indicates which of the 'more' messages in field 100 is to be displayed.

Return values | Return Value | Key Pressed
 | 0 | If the <Enter> key is pressed
 | ESCAPE | If <Esc> is pressed
 | F<n> | Function key <n> is pressed (used when parameter flags includes the value ABORT_FUNC_KEYS)
 | PAGE UP | If there are no records
 | INSERT | If <Ins> is pressed, or <Enter> is pressed over the 'new record' message (used when parameter flags include the values ALLOW_NEW or ABORT_MNEMONICS)
 | DELETE | If <Del> is pressed (used when parameter flags includes the value ALLOW_DELETE)

The global variable abort_code is always zero after a call to the function pick_record()

See also

field_choose(), show_more(), map_glue()

Example

/*
 C Functions Reference Manual
 ----------------------------
 Filename: PICK_REC.C
 Example: pick_record (also map_attach and map_glue)
 Purpose: program illustrates pick_record by
          allowing the user to pick a record from a
          scrolling list of customer records. Also
          illustrates map_attach and map_glue to
          display customer records into the main
          pick_record map.
*/

/*
#db salesord
#c
#tables customer,
        employee,
        order_header,
        order_line,
        index_cust_name,
        region;
#end
*/

#include "dp4.h"
#include "pick_rec.h"
 /* generate this file by running LIBMAKE */
#define CUST_BODY_MAP 200
#define CUST_ERROR_MAP 201
#define CUST_LINE_MAP 202
#define MAX_CUST 6 /* work area */
static struct SAVE_CUST
{
   double cust_acc_nr;
   char name[30];
   boolean cred_customer;
   double credit_limit;
   double balance;
   char region[3];
}
save_cust[MAX_CUST + 1];
static void show_cust(int,int,boolean);
static boolean fetch_cust(int);
static int pick_cust(void);
static int edit_cust(void);

void process()
{
   map_draw(1,RETAIN);
   for(;;)
   {
      if (edit_cust() == FALSE)
        break;
   }
}
 /* user defined functions */
static void show_cust(m,item,extra)
  int m,item;
  boolean extra;
  {
     if (item >= range)
       /* put up new customer record message */
       show_over(m,item,102);
     else /* display customer details for item
             element in save array */
     {
        map_attach(m,item,CUST_LINE_MAP,DEFAULT);
        show_n(CUST_LINE_MAP,1,save_cust[item].
              cust_acc_nr);
        show_c(CUST_LINE_MAP,2,save_cust[item].
              name);
        show_y(CUST_LINE_MAP,3,save_cust[item].
              cred_customer);
        show_n(CUST_LINE_MAP,4,save_cust[item].
              credit_limit);
        show_n(CUST_LINE_MAP,5,save_cust[item].
              balance);
        show_c(CUST_LINE_MAP,6,save_cust[item].
              region);
        map_glue(m,CUST_LINE_MAP);
     }
}

static boolean fetch_cust(int flag)
{
   if (flag == EQUAL)
     /* copy key fields from element 0 */
     customer.customer_number = save_cust[0].
           Cust_acc_nr;
     if (rec_fetch_main(flag,&customer.l))
     {
        save_cust[0].cust_acc_nr = customer.
             customer_number;
        MOVE(save_cust[0].name,customer.name);
        save_cust[0].cred_customer = customer.
             credit_customer;
        save_cust[0].credit_limit = customer.
             credit_limit;
        save_cust[0].balance = customer.balance;
        MOVE(save_cust[0].region,customer.
             region_code);
     }

     return (boolean)there;
}
/* wrap up the pick_fill function */
static int pick_cust()
{
   return pick_record(CUST_BODY_MAP,0,
         CUST_ERROR_MAP,fetch_cust,
         show_cust,save_cust,
         sizeof(struct SAVE_CUST), MAX_CUST,
         RETAIN|ALLOW_NEW|ALLOW_DELETE);
}
/* now use our pick_cust function - normally in another module */

static int edit_cust()
{
   int option, retval = TRUE;
   /* set true by default */
   switch (option = pick_cust())
   {
      case 0:
        map_pause(104);
        break;
      case INSERT:
        map_pause(102);
        break;
      case DELETE:
        map_pause(103);
        break;
      case ESCAPE:
        retval = FALSE;
        break;
   }
return retval;
}


prn()

Purpose

General printing in field

Syntax

void prn(mapnr, fieldnr, data, dtype, size, flags);

Parameters

int mapnr

Number of map containing field

int fieldnr

Number of field in which to print

ANY* data

Pointer to data containing data to print

int dtype

Data type of data to print

int size

Internal size of data to print

int flags

Parameter specifying format of output

The parameter flags may take the same values as the corresponding parameter to the function show()

Description

The prn() function is a general purpose function from which the prn_x() functions are constructed. It works in the same way as the show() function.

The map must have previously been appended to the printer stream with a call to one of the functions form(), form_over() or form_beside().

See also

show(), prn_x(), form(), form_over(), form_beside()

Example

prn(2,6,&orderline.quantity,'I',2,DEFAULT);


prn_abort()

Purpose

Aborts output to printer

Old name

#define abort_print prn_abort

Syntax

void print_abort(void);

Description

The prn_abort() function cancels all items that are currently in the printer stream. (To print them, use the function prn_print(). )

This enables a program to discard text that has been built in the printer stream instead of printing it. This may happen if the user is asked to confirm a print request, but decides not to obtain a printout.

See also


prn_append()

Purpose | Appends text to printer
Old name | #define _prn_append prn_append
Syntax | void prn_append(row, col, colour, text);
Parameters | int row | Row where to start to append
 | int col | Column where to start to append
 | int colour | Colour to use in printing text
 | char* text | Null terminated text to print
Description | The prn_append() function appends text to the printer work file. The text is printed subsequently when the next call to the function prn_print() is made. This function is recommended when low-level output is needed.
See also | prn_print()


prn_length()

Purpose

Obtains the length of a print field

Old name

#define _prn_length prn_length

Syntax

int prn_length(mapnr, fieldnr);

Parameters

int mapnr

Number of map containing the field

int fieldnr

Field number in map

Description

The function prn_length() gets the field length of the requested field in a map which has been sent to the printer with the function form().

This function may be used even if a new mapset has in the meantime been loaded with the function map_load().

Return values

Returns the length of the field

See also

field_map_length()


prn_over()

Purpose | Prints over an existing map
Old name | #define prnover prn_over
Syntax | void prn_over(old_map, fieldnr, new_map);
Parameters | int old_map | Number of map in stream
 | int fieldnr | Number of field in the old map over which to print
 | int new_map | Number of new map to print
Description | The text from the second map specified is placed in the form. Its position is determined by the position of the specified field. You cannot use the function prn() to print in fields on the new map.
See also | prn(), prn_x(), form()
Example | prn_over(2,7,90);


prn_print()

Purpose

Sends output to the printer

Old name

#define print_it prn_print

Syntax

void prn_print(new_page)

Parameters

int new_page

Indicates whether a form feed should be sent

Description

The function prn_print() sends the current contents of the printer stream to the printer. If the parameter new_page is TRUE, a form feed is also sent to the printer. If an error occurs during printing, the global variable print_error is set to a non-zero value.

The function dvc_check() is called when the printing has finished.

After a call to prn_print(), the printer stream is empty so new calls to form() have to be made.

The parameter new_page should be set to 1 (for TRUE) if a form feed is required, or 0 (FALSE) if not. No other values should be passed; this function may be enhanced in future to use other values in this parameter.

See also


prn_x()

Purpose

Functions that print data in map fields

Syntax

int prn_c(mapnr, fieldnr, data)
int prn_d(mapnr, fieldnr, data)
int prn_h(mapnr, fieldnr, data)
int prn_l(mapnr, fieldnr, data)
int prn_n(mapnr, fieldnr, data)
int prn_t(mapnr, fieldnr, data)
int prn_y(mapnr, fieldnr, data)

Parameters

int mapnr

Map containing field to print data

int fieldnr

Number of field to print data in

<type> data

Variable or pointer to variable in which to place data to be printed

The <type> of the parameter data depends on which of the prn_x() functions is being used, as shown below. Also shown is the corresponding DP4 datatype for each of the prn_x() functions:

Function | Type | DP4 datatype
prn_c | char * | C,M,S,U
prn_d | datetype * | D
prn_h | short | I,J,K
prn_l | long | L
prn_n | double | N
prn_t | timetype * | T
prn_y | BOOLEAN | Y

Description

These functions print the data in the specified field of the given map.

You are recommended to set up the output properties for data fields with MAPEDIT. This enables you to change the format without having to change the program.

You can also control the format in which the data is printed with the global variables show_flags and showd_flags. The global variable show_flags is used for all the prn_x() functions except prn_d which uses the global variable showd_flags. These global variables are initialised to the value DEFAULT.

The global variables show_flags and showd_flags may take any suitable combination of the following values:

Value

Meaning

DEFAULT

The data is formatted using the flags set in MAPEDIT. This is the preferred method of controlling print formats. (If no format flags are set in MAPEDIT, the user preferences are used).

NOT_DEFAULT

You can use this value when the format you want corresponds to the absence of any of the explicify format flags but you won'd want to use the users preferred default format.

SKIP_MISSING_FIELD

Allows the program to silently the ignore the system error which would normally be generated if the field to be printed into is missing from the map.

BLANK_IF_ZERO

The field is left blank if you print a value of zero

LEFT_JUSTIFY

This left justifies the data printed within a field

RIGHT_JUSTIFY

This right justifies the data printed within a field

FILL_WITH_ASTERISK

This places asterisks (*) in the portion of a field that is not used for printing data

NO_LEADING_ZEROS

This suppresses zeros from the front of numeric data

LEADING_ZEROS

This adds zeros to the front of numeric data

TRAILING_MINUS

This places a minus sign (-) at the end of negative numbers

IN_PARENTHESIS

This places brackets ( () ) around negative numbers

WITH_COMMAS

This places commas (,) between every 3 digits of numeric data

LONG_YEAR

This prints dates with the year in full, for example 1993 rather than 93

YEAR_FIRST

This prints dates with the year first

MONTH_FIRST

This prints dates with the month first

MONTH_NAME

This prints dates with the month in words. The words used are those that you set in the user preferences

NO_SEPARATOR

This prints dates without any separating character (such as /) between the day, month and year

SHOW_SECONDS

This includes seconds when times are printed

SHOW_HUNDREDTHS

This includes hundredths of seconds when times are printed

Numeric Data

When using numeric data, you must set the global variable show_flags to DEFAULT or any suitable combination of:

BLANK_IF_ZERO, LEFT_JUSTIFY, RIGHT_JUSTIFY, FILL_WITH_ASTERISK, LEADING_ZEROS, NO_LEADING_ZEROS, TRAILING_MINUS, IN_PARENTHESIS, WITH_COMMAS, NOT_DEFAULT, SKIP_MISSING_FIELD

Dates

When using dates, you must set the global variable showd_flagsto DEFAULT or any suitable combination of:

LONG_YEAR, YEAR_FIRST, MONTH_FIRST, MONTH_NAME, NO_SEPARATOR, NOT_DEFAULT, SKIP_MISSING_FIELD

Times

When using times, you must set the global variable show_flags to DEFAULT or any suitable combination of:

SHOW_SECONDS, SHOW_HUNDREDTHS, NO_SEPARATOR, NOT_DEFAULT, SKIP_MISSING_FIELD

To print multi-occurs fields you should use the function prn() directly unless character fields are being printed, in which case you can still use the function prn_c().

See also

prn(), show() .

Example

 prn_c(1,1,customer.name);
 prn_d(1,2,&order.date);
 prn_t(1,2,&order.time);
 prn_h(1,3,orderline.quantity);
 prn_l(1,3,longdata);
 prn_n(1,4,order.ordernr);
 prn_y(1,5,orderline > 100);


prnx()

Purpose

General printing of field functions. (These functions are regarded as obsolete; use the prn_x() functions in place of them.)

Syntax

void prnc(mapnr, fieldnr, data, flags)
void prnd(mapnr, fieldnr, data, flags)
void prnh(mapnr, fieldnr, data, flags)
void prnl(mapnr, fieldnr, data, flags)
void prnn(mapnr, fieldnr, data, flags)
void prnt(mapnr, fieldnr, data, flags)
void prny(mapnr, fieldnr, data, flags)

Parameters

int mapnr

Number of map in which to print

int fieldnr

Number of field in which to print

<type> data

Variable or pointer to variable holding data to be printed

int flags

Parameter specifying print format

The <type> of the parameter data depends on which of the prnx() functions is being used, as shown below. Also shown is the corresponding DP4 datatype for each of the prnx() functions:

Function | Type | DP4 datatype
prnc | char * | C,M,S,U
prnd | datetype * | D
prnh | short | I,J,K
prnl | long | L
prnn | double | N
prnt | timetype * | T
prny | BOOLEAN | Y

The parameter flags may take the same values as described for the function show()

Description

These functions display the data in the specified field of the given form. In character data, the number of characters displayed is whichever is the least of the map field width and the database field width, assuming the map field has been verified against the database.

See also

show(), prn()

Example

prnc(2,1,customer.name,DEFAULT);
prnd(2,2,&order.date,LONGYEAR);
prnt(2,2,&order.time,DEFAULT);
prnh(2,3,orderline.quantity,TRAILINGMINUS);
prnl(2,3,logdata,DEFAULT);
prnn(2,4,material.price,DEFAULT);
prny(2,5,job.complete,DEFAULT);


Functions R

• reach() | • rec_kill_ignore()
• reallocx() | • rec_parent_copy()
• rec_autoinc() | • rec_post()
• rec_child_copy() | • rec_post_commit()
• rec_fetch() | • rec_post_with_flags()
• rec_fetch_main() | • rec_retrieve()
• rec_kill() | • rec_verify()


reach()

Purpose

Obtains the parent of a record. This function is regarded as obsolete in 4.500

Syntax

int reach(par_rec, child_rec, role, mapnr)

Parameters

short* par_rec

Pointer to parent record

short* child_rec

Pointer to child record

char* role

Role to use

int mapnr

Number of remark map to display if parent not found

Description

The reach() function can be used to fetch the parent of a child record with the given role. This involves a call to the database manager to perform the field transcriptions. This is inefficient, particularly in networking applications. It is better to do the transfer explicitly in the program.

If the parent is not found and a valid map number is passed in the parameter mapnr, this map is displayed as a remark.

This function is implemented using the functions copy2parent() and rec_fetch_main().

If the global variable realpass is set to TRUE, this function always returns TRUE and sets the global variable there to TRUE.

Return values

Returns TRUE if the parent is successfully fetched, otherwise FALSE, unless the global variable realpass is FALSE when TRUE is always returned

See also


reallocx()

Purpose | Reallocates a heap block with a new size
Syntax | ANY PTR reallocx(ANY PTR ptr,size_t old_size,size_t new_size);
Parameters | ANY PTR ptr | Pointer to allocated heap area. Do not pass a pointer allocated through the function mallocp(); use only the function mallocx()
 | size_t old_size | Size of the old block
 | size_t new_size | Required size of the new block
Description | This function implements memory reallocation. The main limitation is that if the block has to be moved, the existing block is not freed until the new allocation has succeeded. So, the allocation may fail even if there is enough memory to satisfy the request. It behaves like this to preserve the existing block intact should the reallocation fail.
Return values | If reallocx() succeeds, the old block is freed and a pointer to a block of memory of size new_size is returned. If it fails, 0 is returned and the old block of memory is preserved.
See also | mallocx()


rec_autoinc()

Purpose

Gets the next increment number for an autoincrement table

Old name

#define autoinc rec_autoinc
Syntax (CCOWN)

void rec_autoinc(short *datarec);
Syntax (DP4DBAPI)

BOOLEAN dp4_rec_autoinc(DCONN * dconn,short * datarec);
Parameters

DCONN * dconn

Handle to database connnection

short * datarec

Pointer to the L field of the database record
Description

If the data record belongs to a table which has the autoincrementing property, the last key field of the record is filled in with an autoincrement value, otherwise an error is generated.

Note that rec_autoinc() cannot update the database until a subsequent call to db_commit() or db_checkpoint() is made. Two calls to rec_autoinc() between consecutive calls to db_commit() or db_checkpoint() give the same value.

The utility MAKEDB shows the current value for an autoincrement table.

For the DP4DBAPI version of the API the return value is TRUE if the function completes without errors.

See also

rec_post_with_flags(), db_commit(), db_decommit()


rec_child_copy()

Purpose

Transfer of key fields from a parent table to a child table

Old name

#define ncopy2child(parent,child,role)
rec_child_copy(child,parent,role)

Syntax (CCOWN)

int rec_child_copy(short * child_rec, short *parent_rec, int role)

Syntax (DP4DBAPI)

int dp4_rec_child_copy(DCONN * dconn,short * child, short *parent_rec, int role)

Parameters

DCONN * dconn

Handle to database connection

short* child_rec

Pointer to the L field of child record

short* parent_rec

Pointer to the L field of parent record

int role

Role of index

Description

The rec_child_copy() function copies the key fields of the given parent's primary index from the parent record to the child.

This involves a call to the database manager to perform the field transcriptions. This is inefficient, particularly in networking applications. It is better to do the transfer explicitly in the program.

However, the function has the advantage that it is still valid if the database parent's child index details are changed, and may be conveneient to use in an application that is designed to work with any DP4 database.

Return values

Returns TRUE if the fields being copied initially had different values in the parent and child records or FALSE if they were the same.

See also

rec_parent_copy()


rec_fetch()

Purpose

Reads a record from the database

Old name

#define fetch rec_fetch

Syntax (CCOWN)

int rec_fetch(int flags, short *datarec, int index, int role,ADDITIONAL)

Syntax (DP4DBAPI)

BOOLEAN dp4_rec_fetch(DCONN * dconn,short * datarec, int flags,int index,int index_role, int match_keys);

Parameters

DCONN * dconn

Handle to database connection

int flags

This parameter determines the search method used. This parameter is formed from a combination of at least one direction flag, and zero or more other flags, as explained in the following tables:

Direction Flags

Direction | Meaning
EQUAL | Searches for a record matching on all the keys. For a non unique secondary index, if there is more than one possible matching record EQUAL finds the first such record in the index sequence, unless you also specify PREV, in which case it will find the last search record
FIRST | Finds the first record in the index sequence. If a non zero value was specified for the match_keys parameter, and the DEPTH flag is effective, then the first record in the sequence with the specified number of matching keys is found.
LAST | Finds the last record in the index sequence. If a non zero value was specified for the match_keys parameter, and the DEPTH flag is effective,then the last record in the sequence with the specified number of matching keys is found
NEXT | Finds the record with the next higher key than the key initially in datarec. Note that for non unique secondary indexes the key returned may actually match the initial key. Internally the database manager distinguishes between identical keys using an entity known as the find list, which is explained later. There are some restrictions on when you can use a NEXT (or PREV) search on a secondary index.
PREV | Similar to NEXT, but goes backwards instead of forwards

In calls to rec_fetch() itself you will generally only pass one direction flag, though the combinations of EQUAL|NEXT and EQUAL|PREV are meaningful: they return a matching record if there is one, or the nearest record in the appropriate direction otherwise. When you call a block or buffered fetch function two other combinations are also meaningful: FIRST|NEXT and LAST|PREV. It should be noted that searching forwards through an index is slightly faster than search backwards, though both are efficient.

All the five basic direction flags apart from the EQUAL flag can be modified with the BUT and DEPTH flags, as explained in the next section.

FIRST,LAST and EQUAL fetches are called terminal fetches: presented with the same intiial record and other parameters the database manager will always return the same record. NEXT and PREV searches are different ("no terminal"), because the record returned by a call to a fetch function may be different according to what records have been fetched previously. The record to be found depends on the find list, which stores an extra hidden key component for every record on the database, known as its sequence number or row identifier. When doing a NEXT or PREV search the row identifier of the record previously fetched to that table/memory address combination is used as the extra key component.

When accessing records using the primary index, NEXT and PREV searches are effectively also terminal, because the database manager ignores the find list since there can be only one record with a given key. But for a secondary index the find list is not ignored unless NO_FINDLIST is specified. Where a secondary index is non-unique a fetch using a PREV or NEXT value on a record area that has been modified since it was last used for a fetch() will have indeterminate results: If you set the key fields for a record when using a secondary index, you should always perform one of the three "terminal" fetches before embarking on a sequence of NEXT or PREV fetches to read further data. Even if the secondary index is unique you may still encounter unexpected results if you do a NEXT or PREV search with a changed key: the database manager treats such a secondary index like any other secondary index, and uses the find list information - a fetch NEXT or PREV with a changed key might or might not find a matching record depending on the relative values of the row identifier of the matching record and the row identifier in the find list.

Direction Modifier flags

There are two main direction modifier flags: DEPTH and BUT. The DEPTH flag instructs the database manager to respect the match_keys (or depth) parameter: any records returned will have a key matching at least as many leading keys as were specified in the match_keys parameter, so that NEXT,PREV,FIRST, and LAST searches are all relevant to the subset of records determined by the contents of the datarec parameter and the match_keys parameter. In the CCOWN implementation of fetch functions you must specify the DEPTH flag explicitly if you want to pass a non zero match keys parameter. Conversely, in the DP4DBAPI implementation, since you have to pass match_keys anyway, the DEPTH flag is added automatically if you specify a non zero value for match_keys.

The BUT modifier instructs the database manager to match the number of keys specified in the index properties. For a secondary index this is always all the keys. For the primary index it is the number of keys in the prime parent if there is one, or all the keys otherwise. Since the number of keys the BUT value matches might change if the database structure is changed, most DP4 programmers prefer to specify an explicit match_keys parameter rather than relying on this flag.

For historical reasons, the DP4 C include files define all the various possible combinations of direction and modifier flags explicitly, so you can use FIRST_DEPTH, NEXT_BUT and so on, as well as FIRST|DEPTH NEXT|BUT. The two forms are identical in effect in every case.

In theory you can also specify NO_FINDLIST as a direction modifier. This causes the stored find list position to be ignored and for an "impossible" sequence number to be used, so that a NEXT or PREV record would always find a record with a different key from the one initially passed in. This might be useful if you were implenenting some kind of "select distinct" functionality, but is rarely if ever used in practice.

Other flags

The following values may also be added to the parameter flags:

NO_UPDATE | If the record has been updated but db_commit() has not been called, the original version of the record is fetched even if the PRIVATE_DB flag is in effect. Passing NO_UPDATE can improve performance considerably when the private database is in effect, if you are scanning through a table sequentially updating records as you go and do not want to commit until all the records have been updated.
NO_DATA | The record is looked up in the index and the keys for the record found are copied back into it. The record is not read from the data file and the other data fields are not filled in. This provides a fast way of reading the record if the application only requires the fields which are key fields for the index being used. For example the count mode option of DYNACHEK uses this flag, and it is also used by a number of deletion programs.
NO_READ | The record found is not read from the database although the return value and depth are set as appropriate. Note that this flag cannot be used with NEXT or PREV effectively, except in the unusual case where you just want to count, but not read, the number of records with a specific key.
LOCK | If a matching entry is found and is not locked by another user, it is marked as locked. For the CCOWN API the global variable locked may be tested to see whether the record was already locked by another user. In the DP4DBAPI you can test the flags field of the record header to see if the F_LOCKED bit is set (this also works in CCOWN).
UNLOCK | If the record is locked by this program, it is unlocked immediately instead of at the next call to the function db_checkpoint()
UPDATE_ONLY | The record is looked up only in the work file in which the application program keeps records that have been posted to the database but not yet committed. When this flag is used, it is permissible for the application program to use an index which has updating turned off.

short* datarec

Pointer to the L field of a database record to be fetched. LIBMAKE is used to declare suitable variables to be used as record areas for fetch() functions to work with.

int index

Index number of the index used. You can use the #index toggle of LIBMAKE to generate suitable constants for both the index and role parameters

int role

Role number of the index used

int match_keys

Number of keys to match when doing a FIRST,LAST,NEXT or PREV fetch(). For the CCOWN API this parameter is optional, and you must pass DEPTH as one of the flags for it to be respected. In DP4DBAPI you must pass this parameter, and should pass 0 if you do not want to limit the search to matching keys at all.

Description

rec_fetch() is the most general function for fetching database records one at a time, as it allows you to specify a search index unlike rec_fetch_main() which always uses the primary index. If you want to process several records table sequentially, for example in report programs, then you should use buffered fetch functionality to read the database.

Return values

Returns whether the read was successful or not

See also

rec_fetch_main(),bf_fetch()

Example

rec_fetch(EQUAL,&customer.l,0,0);

This is the same as:

rec_fetch_main(EQUAL,&customer.l);

and

rec_fetch(EQUAL,&customer.l,_REGIONS,0);

The Find List and Nested Searches

As explained above, when searching on a secondary index, the database manager uses an extra key component, to remember where in a sequence of identical keys the search has got to. You may sometimes need to do some kind of recursive search on a secondary index, to enumerate a complete bill of materials for instance.

You may wonder how the find list can keep track of the search position in this case. In order for the searches to proceed as expected you must do one of two things:

  • Allocate a new table structure for each level of the search. The find list is keyed on the memory address of the record area being used in the search. So if each level of the search uses a separate area there is no problem. This solution is convenient in C++ programs, provided the level of recursion is not likely to exceed the application stack or heap size (depending on exactly how the record area are allocated).
  • You can use one record area for all the levels. Before entering a new level of the search save the primary key of the current record. When you return from the recursice search, restore the primary key and fetch the record again using rec_fetch_main() with an EQUAL direction. This will restore the correct record, and reset the find list to the correct position in the index.


rec_fetch_main()

Purpose | Fetches a record using the main index
Old name | #define ftchprm rec_fetch_main
Syntax (CCOWN) | BOOLEAN rec_fetch_main(int flags, short *datarec, ADDITIONAL);
Syntax (DP4DBAPI) | BOOLEAN dp4_rec_fetch_main(DCONN * dconn,short * datarec,int flags,int match_keys);
Parameters | DCONN *dconn | Handle to database connection
 | short* datarec | Pointer to the L field of database record to be read
 | int flags | Determines type of search used. For an explanation of this parameter see rec_fetch()
 | int match_keys | Indicates number of leading key fields which must match the initial values in the record. In the DP4DBAPI implementation you must pass a value for this parameter, even if it will be ignored. In the CCOWN implementation this parameter is optional, and the value is ignored unless you specify the DEPTH value in flags.
Description | rec_fetch_main() fetches, if there is one, a record from the database that matches the keys specified in the record using the prime index to search for a match. The rules for matching are explained in the documentation of rec_fetch(). For rec_fetch_main() the global variable there is set to indicate whether the read was successful.
Return values | Returns TRUE if a record was successfully read from the database, otherwise FALSE. If the table that the record belongs to is not a valid database table or the the user does not have read access on the table being fetched, a system error will be generated.
See also | rec_fetch()


rec_kill()

Purpose

Deletes a record from a database

Old name

#define kill rec_kill

Syntax (CCOWN)

void rec_kill(short *datarec, int flags)

Syntax (DP4DBAPI)

BOOLEAN dp4_rec_kill(DCONN * dconn,short * datarec,int flags);

Parameters

DCONN * dconn

Handle to a database connection

short* datarec

Pointer to the L field of the database record to be deleted

int flags

Indicates what checks should be made

The parameter flags can take a combination of the following values:

Value

Meaning

ORPHAN_CHECK

The transaction will fail with fail_code ORPHAN_CHECK if any orphans would be left in the tables that have this table as a parent, if the index is marked for orphan checking. This test is also made for any children which would be deleted by the CHILD_DELETE option if the CHILD_DELETE flag is not specified. This flag is useful if you want to do things such as preventing a customer record being deleted while there are still outstanding orders for him.

CHILD_DELETE

All children in tables that have this table as a parent, and have the index marked for child deletion will be killed. This rule is applied recursively, so that the grandchildren and so on are also killed. This flag is useful for doing things like deleting an order record together with all its order lines.

IGNORE_CHILD or NO_CHECK

This should be passed if neither ORPHAN_CHECK nor CHILD_DELETE are desired. No orphan checking or child deletion is done. It is your responsibility to ensure database consistency is maintained by performing your own orphan checking and deleting child records that shold be deleted.

NO_UPD_CHILD

This flag can improve performance when the CHILD_DELETE flag is used in programs that opened the database with the PRIVATE_DB flag. In such programs rec_kill() will normally add all the child records that would be deleted because of the CHILD_DELETE flag to the post workfile as deleted. This is so that a later call to rec_fetch() for one of these records will know that the record is deleted. If you won't be calling rec_fetch() on the child records, or don't mind that they will be returned even though they will be deleted by the following commit() then passing this flag will speed up deletion considerably.

Additionally the value NO_FINDLIST can be added to the flag, as with the function rec_post_with_flags()

Description

The rec_kill() function deletes the record specified by the datarec parameter from the database. A call to either db_commit(), db_checkpoint() or db_update() is required to make the deletion permanent.

Until one of these functions is called, the record may still be read from the database by calls to rec_fetch() unless the database has been opened with the flag PRIVATE_DB. For dp4_rec_kill() the return value is TRUE if the function completes without errors. This does not indicate anything about the eventual success or failure of the transaction

See also

rec_post_with_flags(), db_commit(), db_checkpoint()

Example

rec_kill(&order,NO_CHECK);


rec_kill_ignore()

Purpose | Kills a record without any checking
Old name | #define rawkill rec_kill_ignore
Syntax | void rec_kill_ignore(dp4rec)
Parameters | short* dp4rec | Pointer to DP4 record to post
Description | The rec_post_with_flags() function deletes a record from the database without any checks. It is implemented as: rec_kill(dp4rec, IGNORE_CHILD);
See also |


rec_parent_copy()

Purpose

Transfer of key fields from a parent table to a child table

Old name

#define ncopy2parent(child,parent,role)
rec_parent_copy(parent,child,role)

Syntax (CCOWN)

int rec_parent_copy(short * parent,const short * child,int role);

Syntax (DP4DBAPI)

int dp4_rec_parent_copy(DCONN * dconn,short * parent,const short * child,int role);

Parameters

DCONN * dconn

Handle to database connection

short* parent

Pointer to the L field of parent record

short* child

Pointer to the L field of child record

int role

Role of index

Description

The rec_parent_copy() function is copies the primary key fields of the given parent table to the parent record from the corresponding foreign key fields in the child record.

This involves a call to the database manager to perform the field transcriptions. This is inefficient, particularly in networking applications. It is better to do the transfer explicitly in the program.

The function has the advantage that it will still be valid if the database parent's child index details are changed.

Return values

Returns TRUE if the fields being copied initially had different values in the parent and child records or FALSE if they were the same. An application might use this to decide whether or not to join the parent record - on the basis that if the key haas not changed the coorrect record is already in memory.

See also

rec_child_copy()


rec_post()

Purpose | Posts a record without any checking
Old name | #define rawpost rec_post
Syntax | void rec_post(dp4rec);
Parameters | short* dp4rec | Pointer to DP4 record to post
Description | The rec_post() function posts a record to the database without any checks. It is implemented as: rec_post_with_flags(dp4rec, NO_CHECK); This function is convenient to use because most records are posted with the parameter flags set to the value NO_CHECK, and any necessary integrity checks are normally done by the program.
See also |


rec_post_commit()

Purpose

Posts and commits a record

Old name

#define postcommit rec_post_commit

Syntax

void rec_post_commit(dp4rec)

Parameters

short* dp4rec

Pointer to DP4 record

Description

The rec_post_commit() function posts and then commits a record. The calling program fails if the commit operation fails.

This function is implemented as:

rec_post_with_flags(rec,NO_CHECK);
db_update(FAIL_IF_ERROR);

This is a convenient function, and can save space if it is called a number of times.

See also


rec_post_with_flags()

Purpose

Posts a record to a database

Old name

#define post rec_post_with_flags

Syntax (CCOWN)

void rec_post_with_flags(short * datarec, int flags)

Syntax (DP4DBAPI)

BOOLEAN dp4_rec_post(DCONN *dconn,short *datarec,int flags)

Parameters

DCONN * dconn

Handle to database connection

short* datarec

Pointer to the .l field of the table record to be posted

int flags

Indicates what checks should be made

The parameter flags can take any combination of the values below:

Value

Meaning

CHECK_PARENT

A subsequent commit fails on this record if the parent is missing in any of the indexes marked for parent checking

VERIFY_PARENT

A subsequent commit fails if the parent is either missing or has been updated since the start of the transaction in any of the indexes marked for parent verification. (This is equivalent to calling rec_verify() for each such parent)

CHECK_CHANGE

A subsequent commit fails if the record being posted is an amendment of an existing record, and any of the keys in secondary indexes marked for change-prevention have been changed

The following values can also be added to flags:

Value

Meaning

UPDATE_ONLY

Causes a commit failure if the record is not already on the database

POST_UNCHANGED

Causes the time stamp to be updated even if the record is unchanged

NO_AUTOINC

If the table is an autoinc table, the value of the autoincrementing key is not marked as used, so that the same value is returned again on a subsequent call to the function rec_autoinc()

NO_FINDLIST

No entry is made in the internal table containing the details of the last record found

The following value can be used if none of the above flags are required:

Value

Meaning

NO_CHECK

No parent checking is done

Description

The specified data record is posted to the database. If a record already exists which matches on the prime index, it is replaced; otherwise, a new record is inserted.

A call to either the db_commit() or db_checkpoint() function must be made to write the record to the database permanently. The calling application can subsequently read this record from the database before a call to either the db_commit() or db_checkpoint() function, using any of the fetch() functions if either the "flags" parameter to the fetch is set to the value EQUAL, or the private database mode was specified when the database was opened. The updated record will not be visible to fetch functions other than EQUAL fetches unless the private database facility is used. C programs using LIBMAKE generated code to open the database can enable the private database facility by using the LIBMAKE directive #private, otherwise pass PRIVATE_DB to the db_open() function.

Other processes are not able to read the updated record until either the db_commit() or db_checkpoint() function is called. If the record already exists they will see the prior state of the record.

The checks specified in the parameter flags are done when the records are written to the database with a call to either the db_commit() or db_checkpoint() function. The checks are not done by the post function itself.

Parent checking is usually done by the program while user is entering particular field values, so it is relatively unusual for any value other than NO_CHECK to be used with the parameter flags.

Unnecessary rpovessing is avoided in the following ways:

  • The data file is only altered if the record has changed (unless {POST_UNCHANGED) is specifed)
  • The index field is only altered if the keys of the record have changed

See also

db_commit(), db_checkpoint(), rec_kill(), db_decommit(), rec_verify(), rec_autoinc()

Example

rec_post_with_flags(&order_line.l,NO_CHECK);


rec_retrieve()

Purpose

Retrieves records after a commit failure

Old name

#define retrieve rec_retrieve

Syntax (CCOWN)

BOOLEAN rec_retrieve(short *datarec)

Syntax (DP4DBAPI)

BOOLEAN dp4_rec_retrieve(DCONN * dconn,short *datarec)

Parameters

DCONN * dconn

Handle to an open database connection

short* datarec

Pointer to the L field of a database record

Description

After an unsuccessful call to the function db_commit(), the contents of the workfile may be recovered by calling the function rec_retrieve(). This function can only be used if the RETRIEVE_ENABLE flag was passed to the db_open() function originally.

Each call to the function rec_retrieve() returns a record that was originally posted from the address of the data record specified. The return value is TRUE if such a record is found and FALSE if there are no more matching records in the workfile. In the CCOWN implementation the value returned by the function is stored in the global variable there.


rec_verify()

Purpose

To ensure a record is not updated by another user during the course of the current transaction and possible make addition checks, as for the function rec_post_with_flags()

Old name

#define verify rec_verify

Syntax (CCOWN)

void rec_verify(short *datarec, int flags)

Syntax (DP4DBAPI)

BOOLEAN dp4_rec_verify(DCONN * dconn,short * datarec,int flags);

Parameters

DCONN * dconn

Handle to database connection

short* datarec

Pointer to the L field of the database record to be checked

int flags

Specifies additional checks to be made

The parameter flags is the same as for the function rec_post_with_flags()

Description

This causes the same checking to be carried out on the specified record at commit time as would be done by the function rec_post_with_flags(), but the record is not posted to the database.

This function may be useful if you want to be sure that a record read, but not updated, during the course of a transaction has not changed when the record is committed. For example you might want to verify a record containing price information when creating a transaction line.

The DP4DBAPI version of this API returns TRUE if the function completes without errors. This does not indicate anything about the eventual success or failure of the transaction however.

See also

rec_post_with_flags(), db_commit()

Example

rec_verify(&order,NO_CHECK);


Functions S

  • scanftch()
  • scanreach()
  • scrn_map()
  • scrnx_map()
  • sf_read_sysdata()
  • show()
  • show_attach()
  • show_extra()
  • show_graphic()
  • show_more()
  • show_over()
  • show_resource()
  • show_special()
  • show_x()
  • srv_connect()
  • srv_disable()
  • srv_enable()
  • srv_end_process()
  • srv_init_process()
  • srv_load_srv()
  • srv_mem_describe()
  • srv_mem_find()
  • srv_mem_post()
  • str_lcase()
  • str_ucase()
  • sys_del_confirm()
  • sys_error()
  • sys_fail()
  • sys_message()
  • sys_request()
  • sys_upd_confirm()

For other functions beginning with sf_ see under the corresponding tf_ functions


scanftch()

Purpose

Offers an approximate match with scan. This function is regarded as obsolete

Syntax

int scanftch(dp4rec, index, role, mapnr)

Parameters

short* dp4rec

Pointer to data record

short* index

Pointer to index record on dp4rec

char* role

Role name of index to use

int mapnr

Number of map to display for user action

Description

The function scanftch() is similar to the scanreach() function and is also intended for less experienced programmers.

This function can be used when the user is entering the value of key fields and is not required to key exactly. It fetches a record with the given secondary key if one exists. If one does not exist, it offers a scan on the specified index.

The parameter mapnr specifies a map that is used to get the user response with a call to the function map_choose(), and should be of the form:

Press <Tab> for next, <Esc> to rekey, any other key to accept
To use the scanftch() function, follow the steps below:

1. Get the key fields from the user. Use a different copy of the main record, else other fields entered by the user will be overwritten

2. Call the function scanftch() as in the example skeleton below:

while (!scanftch(...) && jump == TRUE)
{
  /* show the current details record */
}
if (there) /* record has been accepted */
{
  ...
  /* copy key fields into main record */
}
else /* user has pressed escape re-enter fields */
  ...

You should only use the function scanftch() if there are records for it to scan through.

Return values

When scanftch() is first called and the record does not exist, the global variable jump is set to TRUE and FALSE is returned.

During subsequent calls while <Tab> is pressed it returns FALSE. If <Esc> is pressed it returns FALSE and resets the global variable jump to FALSE. For any other key it returns the value of the global variable there.

See also

scanreach()


scanreach()

Purpose

Offers a scan of available parent records. This function is regarded as obsolete

Syntax

int scanreach(par_rec, child_rec, role, mapnr)

Parameters

short* par_rec

Pointer to the L field of parent record

short* child_rec

Pointer to the L field of child record

char* role

Role of child - parent index to use

int mapnr

Number of message map to display if initial parent record is not found

Description

The scanreach() function checks to see if the parent record of the child record exists. If it does not, this function offers a scan of the parent record on its primary key.

The parameter mapnr specifies a map to display with the function map_choose(), and should be of the form:

Press <Tab> for next, <Esc> to rekey, any other key to accept.
This function is intended for less experienced programmers. The scan is only performed in the forward direction.

scanreach() is useful when the user is inserting records that must have parent records.

To use the scanreach() function, follow the steps below:

1. Get the parent's key fields from the user. These fields are entered into the child's fields

2. Call the function scanreach() as in the example skeleton below

while (!scanreach() && jump == TRUE)
{
  /* show the parent details record */
{
if (there) /* record has been accepted */
{
  ...
}
else /* user has pressed escape */
...

The function scanfetch automatically updates the child's fields as necessary.

Return values

When scanreach() is first called and the parent does not exist, the global variable jump is set to TRUE and FALSE is returned.

During subsequent calls, while <Tab> is pressed it returns FALSE. If <Esc> is pressed it returns FALSE and resets the global variable jump to FALSE. If any other key is pressed, it returns the value of the global variable there.

See also


scrn_map()

Purpose

General screen input function. This function is regarded as obsolete

Syntax

void scrn_map(mapnr, fieldnr, data, dtype, size,
flags)

Parameters

int mapnr

Number of map containing input field

int fieldnr

Number of field from which to get input

ANY* data

Pointer to data to contain value that the user input

int dtype

Data type of parameter data

int size

internal size of data

int flags

Parameter specifying display attributes

The parameter flags may take the same vales as the global variable scrn_flags in the askf_x() functions

Description

This is a generalised input function, used internally to construct the scrnx_map() functions. It is available to programmers who wish to construct their own input functions.

The data is assumed to be of the type specified by the parameter dtype, and of the size specified by the parameter size. The size is the internal storage size, not the display size.

Return values

Returns the value of the global variable abort_code

See also

scrnx_map()

Example

scrn_map(2,6,&orderline.quantity,'I',sizeof(int),
DEFAULT);


scrnx_map()

Purpose

Input functions with cursoring. These functions are regarded as obsolete

Syntax

int scrnc_map(int mapnr, int *fieldnr, char *data)
int scrnd_map(int mapnr, int *fieldnr, datetype *data)
int scrnh_map(int mapnr, int *fieldnr, short *data)
int scrni_map(int mapnr, int *fieldnr, int *data)
int scrnl_map(int mapnr, int *fieldnr, long *data)
int scrnn_map(int mapnr, int *fieldnr, double *data)
int scrns_map(int mapnr, int *fieldnr, char *data)
int scrnt_map(int mapnr, int *fieldnr, timetype *data)
int scrnu_map(int mapnr, int *fieldnr, char *data)
int scrny_map(int mapnr, int *fieldnr, boolean *data)

Parameters

int mapnr

Number of map to use

int* fieldnr

Pointer to field from which to input

data

Pointer to variable in which to place input data

Note that scrnh_map(), and not scrni_map(), should be used for input of type I fields. A good C compiler will flag it as an error if you inadvertently use scrni_map() on a DP4 type I field. Unfortunately some compilers, including Microsoft C, only flag this as a warning, (at least for C programs), even though it causes a memory corruption.
You can force the Microsoft compiler to flag an error in this situation by putting #pragma warning(error:4133) at the top of your source modules, or in an include file, and you are recommended to do so. Compiling as C++ will also cause an error rather than a warning.

Description

The scrnx_map() functions are used to input data from fields where the user is allowed to cursor among them in any order.

The format in which the data is displayed is controlled by the global variables scrn_flags and scrnd_flags, as in the askf_x() functions.

On entry to the function the field is cleared (meaning that the data is set to the default on the database, or a zero or blank value if the field is not a database field) under the following conditions:

  • The global variable range is less than or equal to the field number indicated by the parameter fieldnr, and

  • The field is set as a clearfirst field on the map, and either:
    • The global variable clearfirst is 1 or 2
    • The stored value of the global variable clearfirst is 1 or 2 while moving to another field as described in Field Navigation During Data Entry

On return from these functions, the value of the variable pointed to by the parameter fieldnr is changed to the next field number to be processed. A field number of 0 means that the user has completed the dialog.

If the user moves to another field with the mouse or cursor keys fieldnr is adjusted in such a way as to ensure any fields that would be missed out if fieldnr was set directly to the target field are validated. This is explained in detail in Field Navigation During Data Entry .

If the user presses the <Esc> key, the global variable abort_code is set to the value ESCAPE and the variable pointed to by the parameter fieldnr is set to the first field in the map.

If the user leaves a field with a cursor movement key, or by clicking on another field with the mouse, or requests prompt, abort_code is set accordingly as listed below:

Value

User action

UP_LINE
DOWN_LINE
LEFT
RIGHT
LEFT_LINE
RIGHT_LINE
BACKTAB
SPACE_BAR
CLICK_LOCAL

User navigated with the <↑> key
User navigated with the <↓> key
User navigated with the <←> key
User navigated with the <→> key
User navigated with the <Home> key
User navigated with the <End> key
User navigated with the <Shift+Tabgt; keys
User requested prompt
User clicked on another field in the map

If the global variable range has the value 1 initially, it is updated to hold the value of the highest field number processed so far.

It is recommended that the new inpm_x() functions are used rather than the scrnx_map() functions. The scrnx_map() functions are retained for compatibility with older programs.

Return values

 Returns a non zero value (usually the value of the global variable abort_code) if the program should break (not validate the field), or zero if the data was entered normally and should be validated.

Remarks

scrnc_map() allows you to enter mix cased data, whereas scrnu_map() converts all keystrokes to uppercase. scrns_map() is like scrnu_map() but converts spaces to underscore characters and only allows characters that are permitted in identifiers (and @). Usually each function is used with the corresponding DP4 datatype, but this is not manadatory.

See also

inpm_x(), field_skip()

Example

The following code includes a call to the function scrnn_map:

while (fieldnr)
{
   switch (fieldnr)
   {
      case 1:
        scrnn_map(mapnr,&fieldnr,&number);
      case 2:
        ...
      default:
        field_skip(mapnr,&fieldnr);
   }
}

This code can be changed to use the function inpm_n as follows:

map_use(mapnr,&fieldnr);
while (fieldnr)
{
   switch (fieldnr)
   {
      case 1:
        inpm_n(&number);
        break;
      case 2:
        ...
      default:
        inpm_omit(); /* inpm version of inpm_omit */
        break;
   }
}


sf_read_sysdata()

Purpose

Reads system data file (server's view). You must: #include "SYSDATA.H"

Old name

#define read_srv_sysdata sf_read_sysdata

Syntax (CCOWN)

void sf_read_sysdata(void);

Syntax (DP4DBAPI)

BOOLEAN dp4_sf_read_sysdata(CONN * conn,struct SYSDATA * sysdata,size_t size_sysdata);

Description

The sf_read_sysdata() function reads the contents of the server's DP4 licence file into the global variable srv_sysdata. The dp4_sf_read_sysdata() reads the same data into the specified buffer. You must pass the size of the buffer to be used for reading the file. This must be at least 512 bytes (sizeof(struct SYSDATA) is correct)

The licence file data reflects the server's view of the system, which may be different from the local DP4 licence file if the program is running on a network.

The global variable srv_sysdata is declared in the header file SYSDATA.H as:

extern struct SYSDATA sysdata

See also


show()

Purpose

General function for displaying data in a field

Syntax

void show(mapnr, fieldnr, data, dtype, size, flags)

Parameters

int mapnr

Number of map containing field

int fieldnr

Number of field to display data

ANY* data

Data to be displayed

int dtype

Data type of the data to be displayed

int size

internal size of data to be displayed

int flags

Parameter specifying display format

The parameter flags may take any combination of the following values

Value

Meaning

DEFAULT

The data will be formatted using the flags set in MAPEDIT. This is the preferred method of controlling display formats. If no format flags were set in MAPEDIT, the user preferences are used.

BLANK_IF_ZERO

The field is left blank if the data has a value of zero

LEFT_JUSTIFY

This left justifies the data displayed in the field

RIGHT_JUSTIFY

This right justifies the data displayed in the field

FILL_WITH_ASTERISK

This places asterisks (*) in the part of the field that is not displaying data

NO_LEADING_ZEROS

This suppresses zeros from the front of numeric data

LEADING_ZEROS

This adds zeros to the front of numeric data

TRAILING_MINUS

This places a negative sign(−) at the end of negative numbers

IN_PARENTHESIS

This places brackets ( ) around negative numbers

WITH_COMMAS

This places commas (,) between every three digits of numeric data

LONG_YEAR

This displays the year in dates in full, for example 1998 instead of 98

YEAR_FIRST

This displays dates with the year first

MONTH_FIRST

This displays dates with the month first

MONTH_NAME

This displays dates with the month in words. The words used are those set in the user preferences

NO_SEPARATOR

This displays dates without any separating character (such as /) between day, month and year

SHOW_SECONDS

This includes seconds when times are displayed

SHOW_HUNDREDTHS

This includes hundredths of seconds when times are displayed

Description

This function is a generalised version of the show_x() functions. It caters for all datatypes.

The data is assumed to be of the type specified by the parameter dtype and of the size specified by the parameter size. The size is the internal storage size, not the display size.

This function is generally used in advanced applications.

See also

show_x()

Example

/* to display one integer */
show(2,6,&orderline.quantity,sizeof(int),'I',DEFAULT);
/* to display a field of multiple occurs 4 */
show(2,6,&orderline.quantity,'I',4 * sizeof(int),DEFAULT);


show_attach()

Purpose

Shows the contents of one map over the field of another

Old name

#define show_attach_map show_attach

Syntax

void show_attach(mapnr, fieldnr, newmap, flags)

Parameters

int mapnr

Number of map on screen

int fieldnr

Number of field over which to display

int newmap

Number of new map to display

int flags

Parameter specifying display attributes (for example, colour)

The parameter flags may take any combination of the following values:

Value

Meaning

SKIP_MISSING_FIELD

Stops program from terminating if the field to attach to does not exist

SYSTEM_MAP

Indicates that the new map comes from the SYSTEM mapset on the SYSTEM database

USE_FIELD_LENGTH

This makes the new map contract or expand to the size given by the field to which the new map is attached

INPUT_COLOUR

The text is displayed using the input colour rather than the output colour

Description

The function show_attach() displays text from the second map specified on the screen. The position of the second map is determined by the position of the specified field.

The pick_record() function calls the function show_more() to display the 'More available' messages. The show_more() function is in turn implemented with the function show_attach().

See also

show_over()


show_extra()

Purpose

Function used by the pick_record() function to display extra information

Old name

#define showextra show_extra

Syntax

void show_extra(mapnr, extra_map);

Parameters

int mapnr

Number of display map being used

int extra_map

Map used in the show_over() function

Description

The function show_over() is called by the pick_record() function to allow the user to display extra information in the choice map.

This function is implemented as:

if (extra_map)
  show_over()(mapnr, 101, extra_map);

This function enables you to use the same choice map for several different kinds of choices.

See also


show_graphic()

Purpose | Displays objects in map fields without reference to a resource file
Old Name | #define show_object show_graphic
Syntax | void pascal show_graphic(int mapnr, int fieldnr, long object, int flags)
Parameters | int mapnr | Number of map to display object
 | int fieldnr | Number of field to display object
 | long object | Object handle
 | int flags | Specifies the position and source of the object to be displayed

The parameter flags may take the following values:

SHOW_ICON | Use this option if you wish to display an icon at the field position
SHOW_FOCUS | Use the focus position to display an icon, leaving the field available to display text

Description

The show_object function is generally similar to show_resource(), but handles the object do be displayed via a handle instead of a resource file reference.

It can be used, for example, to display an object that the application has itself created, such as a signature that has been scanned and converted to a bitmap using the Windows function CreateBitMap(). The handle returned from this call is passed as the object parameter and is in turn passed to the DP4 Terminal Manager.

Note that show_object does not destroy the object when the map is cleared.

See also

show_resource()


show_more()

Purpose

Function used by the pick_record() function used to display more messages

Old name

#define showmore show_more

Syntax

void show_more(mapnr, more)

Parameters

int mapnr

Number of the choice list map

int more

Indicates which map to display

The parameter more specifies which of the maps from the SYSTEM mapset of the SYSTEM database to display. It can take one of the following values:

Value

Meaning

0
1
2
3

Display map 180
Display map 181
Display map 182
Display map 183

Description

The show_more() function is used by the pick_record() and pick_list() functions to display the more messages.

It is implemented as:

show_attach(mapnr, 100, 180 + more,
SKIP_MISSING_FIELD | SYSTEMMAP);

The value SYSTEMMAP in the flags parameter to the function show_attach() indicates that the map to be used comes from the system mapset from the SYSTEM database. The value SKIP_MISSING_FIELD in the parameter flags prevents the program from crashing if field 100 is missing from the display map.

The maps used by the function show_more() are maps 180-183 of the SYSTEM mapset from the SYSTEM database. These are shown below.

Map 180:

* All items shown *

Map 181:

* More below *

Map 182:

* More above *

Map 183:

* More above and below *

The global variable pk_more_choices gives the current value of the parameter more.

See also


show_over()

Purpose | Shows the contents of one map over a field in another map
Old name | #define showover show_over
Syntax | void show_over(mapnr, fieldnr, newmap)
Parameters | int mapnr | Number of map on screen
 | int fieldnr | Number of field to display over
 | int newmap | Number of new map to display
Description | The text from the second map specified is displayed on the screen. Its position is determined by the position of the specified field. The text is truncated if it would otherwise flow outside the boundaries of the map.
Example | show_over(1,4,51);


show_resource()

Purpose

Displays objects dynamically from a resource file

Syntax

void pascal show_resource(int mapnr, int fieldnr,
const * res_name, int flags)

Parameters

int mapnr

Number of map to display image in

int fieldnr

Number of field to display image in

const char * res_name

Resource or file name

int flags

Specifies the position and source of the object to be displayed. You may specify either DEFAULT or a combination of the other flags. Please note that, at least from 4.619 on, format flags set in the control or map field used to display the image over-ride the flags passed by the program.

DEFAULT

Display a bitmap from a resource file

or a combination of the following and, optionally, SKIP_MISSING_FIELD:

SHOW_ICON

Use this option if you wish to display an icon. If this is not specified a bitmap is assumed.

SHOW_FOCUS

In 4.5xx Use the focus rectangle position of the field to display the graphic, leaving the field available to display text. In 4.6xx look for a "static" control instead of an edit control with a matching field number,

SHOW_APPN

Use this option to specify resources that are part of your program rather than DP4RES.DLL. This option is available in Windows only.

SHOW_FILE

Use this option to specify that the name is the name of a file containing a bitmap or icon, and not a resource. If no path is specified the current directory (normally the executables own directory, is asssumed). This option is available from 4.619.

Bitmaps and icons can be displayed using this function, as can filetypes supported by the Windows API OleLoadPicture(). These include JPEG and Windows Metafiles on most versions of Windows.

SHOW_STRETCH

Stretches or shrinks a bitmap to fit the control it is shown in. This option is available from 4.619.

Description

This function allows DP4 Windows applications to display icons and bitmaps in a DP4 maps.

The bitmaps or icons to be shown must be declared in a respurce file (unless SHOW_FILE is used). By default DP4 for Windows uses DP4RES.DLL, but in a C application you may use an alternative filename.

If, for example, you wish to use a bitmap named DOG.BMP, you could declare it as follows in your resource file:

DOG BITMAP DOG.BMP

In a QA Build application, set the field type of the field to display the resource to 'G' (graphic) and specify the resource name as the field's formula.

Example

show_resource(mapnr, fieldnr, "DOG", DEFAULT);


show_special()

Purpose

Provides for various special effects that can be used to change the appearance of controls in a map

Syntax

void pascal show_special(int mapnr, int fieldnr, int object, int flags)

Parameters

int mapnr

Number of map

int fieldnr

Number of field

int object

Identifies the object to be displayed:

  • SHOW_BLANK causes the text of the control to be removed - calling show_special() like this is equivalent to using field_blank()
  • SHOW_TICK shows a tick against a menu item without changing the text displayed in the control.
  • SHOW_STAR causes the text of a control (usually an edit control) to be prefixed with an asterisk. Several DP4 utilities use this functionality to allow multiple selections from a window of records.
  • SHOW_NA specifies that the control is disabled (from 4.603). It also causes the text of edit controls to be replaced with a dash.
  • SHOW_MORE is used to show a scrollbar in pick_record() and similar functions. The flags value specifies the state of the scrollbar.
  • SHOW_POPUP is used to show a secondary pulldown indicator (a triangle) after the text of the control.
  • SHOW_HIDE is used to completely hide a control in a map. DP4 does not unhide the control automatically even if it is used as an input field. This setting was introduced in release 4.623
  • SHOW_REVEAL is used to redisplay a control that has been hidden with the SHOW_HIDE option of show_special(). This setting is introduced in release 4.623

int flags

Apart from the SHOW_MORE case flags are used as for the regular show() function. The only option that is likely to be useful is SKIP_MISSING_FIELD<./P>

The parameter flags may take any combination of the same values as listed for the show() function

Description

Most of the time you will not call this function directly. It is used internally in the implementation of functions such as pick_record() and map_bar_choose(). However the SHOW_HIDE and SHOW_REVEAL functions may be generally useful, and some of the other options may be useful if you are implementing your own high level functions for menus or record selection from a list of choices.

See also

map_bar_choose(), pick_record()

Example

/*
 C Functions Reference Manual
 ----------------------------
 Filename: SHOW_SPE.C
 Example: show_special
 Purpose: Insert tick before active items in a
          pull-down menu
*/

/*
#c
#db wintext
#mapname test1
#end
*/

#include "dp4.h"
#include "winmenu.h"

#define ACTIONBAR     2
#define FILE_PULLDOWN 3

void process(NOPARAS);
void draw_extra(int mapnr);

void process()
{
   int option_nr = 0x11;
   short banned_list[8];

   map(2, MENUMAP);
   banned_list[0] = 0;
   map_bar_choose(ACTIONBAR, &option_nr,
           0, draw_extra, banned_list);
}

void draw_extra(mapnr)
{
   /* Place a tick against first item of first
      pull-down */
   if (mapnr == FILE_PULLDOWN)
     show_special(mapnr, 1, SHOW_TICK, DEFAULT);
}


show_x()

Purpose

Functions that display data in map fields on screen

Old name

#define showx show_x

Syntax

int show_c(mapnr, fieldnr, data)
int show_d(mapnr, fieldnr, data)
int show_h(mapnr, fieldnr, data)
int show_l(mapnr, fieldnr, data)
int show_n(mapnr, fieldnr, data)
int show_t(mapnr, fieldnr, data)
int show_y(mapnr, fieldnr, data)

Parameters

int mapnr

Map containing field to display

int fieldnr

Number of field to display data in

<type> data

Variable or pointer to variable in which to place data to be displayed

The <type> of the parameter data depends on which of the show_x() functions is being used, as shown below. Also shown is the corresponding DP4 datatype for each of the show_x() functions:

Function | Type | DP4 datatype
show_c | char * | C,M,S,U
show_d | datetype * | D
show_h | short | I,J,K
show_l | long | L
show_n | double | N
show_t | timetype * | T
show_y | BOOLEAN | Y

Description

These functions display the data in the specified field of the given map.

You are recommended to set up the output properties for data fields with MAPEDIT. This enables you to change the format without having to change the program.

You can also control the format in which the data is displayed with the global variables show_flags and showd_flags. The global variable show_flags is used for all the show_x() functions except show_d which uses the global variable showd_flags. These global variables are initialised to the value DEFAULT.

The global variables show_flags and showd_flags may take any suitable combination of the following values:

Value

Meaning

DEFAULT

The data is formatted using the flags set in MAPEDIT. This is the preferred method of controlling display formats. (If no format flags are set in MAPEDIT, the user preferences are used).

NOT_DEFAULT

You can use this value when the format you want corresponds to the absence of any of the explicify format flags but you won'd want to use the users preferred default format.

SKIP_MISSING_FIELD

Allows the program to silently the ignore the system error which would normally be generated if the field to be displayed into is missing from the map.

BLANK_IF_ZERO

The field is left blank if you display a value of zero

LEFT_JUSTIFY

This left justifies the data displayed within a field

RIGHT_JUSTIFY

This right justifies the data displayed within a field

FILL_WITH_ASTERISK

This places asterisks (*) in the portion of a field that is not used for displaying data

NO_LEADING_ZEROS

This suppresses zeros from the front of numeric data

LEADING_ZEROS

This adds zeros to the front of numeric data

TRAILING_MINUS

This places a minus sign (-) at the end of negative numbers

IN_PARENTHESIS

This places brackets ( () ) around negative numbers

WITH_COMMAS

This places commas (,) between every 3 digits of numeric data

LONG_YEAR

This displays dates with the year in full, for example 1993 rather than 93

YEAR_FIRST

This displays dates with the year first

MONTH_FIRST

This displays dates with the month first

MONTH_NAME

This displays dates with the month in words. The words used are those that you set in the user preferences

NO_SEPARATOR

This displays dates without any separating character (such as /) between the day, month and year

SHOW_SECONDS

This includes seconds when times are displayed

SHOW_HUNDREDTHS

This includes hundredths of seconds when times are displayed

Numeric Data

When using numeric data, you must set the global variable show_flags to DEFAULT or any suitable combination of:

BLANK_IF_ZERO, LEFT_JUSTIFY, RIGHT_JUSTIFY, FILL_WITH_ASTERISK, LEADING_ZEROS, NO_LEADING_ZEROS, TRAILING_MINUS, IN_PARENTHESIS, WITH_COMMAS, NOT_DEFAULT, SKIP_MISSING_FIELD

Dates

When using dates, you must set the global variable showd_flagsto DEFAULT or any suitable combination of:

LONG_YEAR, YEAR_FIRST, MONTH_FIRST, MONTH_NAME, NO_SEPARATOR, NOT_DEFAULT, SKIP_MISSING_FIELD

Times

When using times, you must set the global variable show_flags to DEFAULT or any suitable combination of:

SHOW_SECONDS, SHOW_HUNDREDTHS, NO_SEPARATOR, NOT_DEFAULT, SKIP_MISSING_FIELD

To display multi-occurs fields you should use the function show() directly unless character fields are being displayed, in which case you can still use the function show_c().

See also

show() , show_over(), field_blank() .

Example

 show_c(1,1,customer.name);
 show_d(1,2,&order.date);
 show_t(1,2,&order.time);
 show_h(1,3,orderline.quantity);
 show_l(1,3,longdata);
 show_n(1,4,order.ordernr);
 show_y(1,5,orderline > 100);


srv_connect(), dp4_srv_connect()

Purpose

Obtains a connection to the DP4 database manager on a named machine

Syntax (CCOWN)

int srv_connect(const char * server_name);

Syntax (DP4DBAPI)

int srv_connect(const char * server_name)
CONN * dp4_srv_connect(dp4_name_string server_name,int nr_system,int rw_access,dp4_error_callback error_handler,int channel,void * reserved);

Parameters

const char * server_name

Name of server to connect to. This parameter is ignored if DP4 is loaded in a configuration that does not support named connections. For this reason you may wish to access the @net_status table to check the connection type in the @resil_type field after calling srv_enable() to finish initialisation of the connection.

int nr_system

Number system to be used by applications. This should be N_C for C or C++ applications

int rw_access

Desired access level. Read access is in bits 0 to 7,write access in bits 8 to 15. 0 gives minimum access. -1 gives full access to any databases opened (255 for both read and write access).

dp4_error_callback error_handler

Pointer to function to be used to raise errors. See definition of dp4_error_callback to see how to define such a function or consult the source module ccsrvcal.c (in the src subdirectory of each DP4 version on the DP4 CD ROM). If the error callback function is not required then pass 0. In this case the calling application must call dp4_error_status() after each call a dp4 function, and take appropriate action if an error has occured. The source code of dp4dbase.cpp gives an example of how to do this.

int channel

Internal IPC channel to use. A C program or an ADC should pass the DP4 global app_ipc_channel for this. Other programs should pass 0.

void * reserved

Used by the "traditional" DP4 C library to pass a pointer to a structure containing pointers to all the DP4 global variables used by the database API. Pass 0.

Description

The srv_connect() functions establish a connection to a named server if DP4 is loaded in a configuration that supports named connections, or to the local database manager otherwise. (Named connections are supported on Win32, Linux, and Unix releases of DP4 when the TCP network requester is loaded and AUXDISTR is not used.)

Normally you would not call dp4_srv_connect()function directly. It will be wrapped by a higher level function. For C, the srv_connect() function is the wrapper.

After a sucessful call to dp4_srv_connect() you must call srv_enable() or srv_enable_ex() to perform additional initialisations for the connection.

The srv_connect() function will not return an error value if you attempt to connect to a named server when DP4 is not loaded in a configuration that supports named connections. If your program needs to detect this situation then you should fetch the network status record of the connection after it has opened successfully, and check that the connection is a network connection. See Creating Named Connections from C/C++ Code in the Guide to DP4 Networking for more information.

Return values

srv_connect() returns a positive value if successful, also assigned to df_process, or -1 otherwise. dp4_srv_connect() returns a connection handle if successful or NULL (0) otherwise.

See also

srv_enable(), srv_disable(), srv_end_process(), Error handling with DP4DBAPI


srv_disable(),srv_disable_ex(),dp4_srv_disable()

Purpose | Closes any databases and files opened by a DP4 connection
Old name | #define _srv_logoff srv_disable
Syntax (CCOWN) | void srv_disable(void) void srv_disable_ex(int file_only)
Syntax (DP4DBAPI) | int dp4_srv_disable(CONN * conn,int file_only)
Parameters | CONN * conn | Handle returned by preceding call to dp4_srv_connect()
 | int file_only | You must pass the same value that was passed to the preceding call to srv_enable_ex().
Description | The srv_disable() function performs various de-initialisations for a connection to DP4 that is to be closed. It is normally called immediately before a connection handle is freed with srv_end_process().
See also | srv_init_process(), srv_enable(), srv_end_process(),


srv_enable(),srv_enable_ex(),dp4_srv_enable()

Purpose

Opens the system database and performs other necessary initialisations for a new connection to DP4

Old name

#define _srvinit srv_enable

Syntax

void srv_enable(void)
void srv_enable_ex(int file_only)

Syntax (DP4DBAPI)

int dp4_srv_enable(CONN * conn,int file_only)

Parameters

CONN * conn

Handle returned by preceding call to dp4_srv_connect()

int file_only

If TRUE database functionality is disabled. You might set this if the application does not use the DP4 database interface, but just the DP4 file handling functions. For example SYSDB uses a file only connection so that it can update dp4.sys without encountering an error because of a missing system database or similar error.

Description

The srv_enable() function performs various initialisations for a new connection to DP4. It is normally called immediately after a connection handle has been obtained with srv_init_process().

For dp4_srv_enable() the return value is TRUE if the connection is succesfully initialised and FALSE otherwise. For srv_enable() and srv_enable_ex() there is no return value - a sys_fail() or sys_error() will have been generated to terminate the application if there is a problem with initialising the connection (for instance, because the system database is missing or corrupt).

See also

srv_connect(), srv_init_process(), srv_disable()


srv_end_process()

Purpose

Releases a connection handle previously obtained from the database manager

Old name

#define _srv_unproc srv_end_process

Syntax (CCOWN)

void srv_end_process(void)

Syntax (DP4DBAPI)

void dp4_srv_end_process(CONN *conn)

Parameters

CONN* conn

Connection handle previously obtained with dp4_srv_connect()

Description

The srv_end_process() function closes a connection to DP4. This function is normally called automatically from inside dp4_halt(). The srv_disable() function should be called before calling this function, to ensure all files and databases opened by the connection are properly closed down.

This function always succeeds - any errors that occur while it is being executed are ignored.

dp4_srv_disconnect() is defined as a synonym for dp4_srv_end_process(). You may prefer to use this name for connections created with dp4_srv_connect().

See also

srv_connect(), srv_init_process(), srv_disable()


srv_init_process()

Purpose

Obtains a connection handle from the database manager to be used in subsequent calls to DP4 functions.

Syntax

int srv_init_process(void)

Description

The srv_init_process() obtains a connection handle from the database manager. In C programs this function is usually called in the main() function implementation in the C library, and not called directly. The function is implemented by a call to tail_get_option() to check for the presence of the -server_name command tail, followed by an appropriate call to srv_connect().

srv_enable() must be called after srv_init_process() to enable the process to call database or file functions within the the database manager.

Return values

Returns a positive value, also assigned to df_process if a connection is successfully obtained, or -1 otherwise.

See also

srv_connect(), srv_enable(), srv_end_process()

Example

An application program may acquire multiple process numbers in order to be able to process two transactions independently. For example a program designed for an order entry system might wish to update orders, but also maintain statistics about the programs usage, and it would be desirable to be able to update the statistics tables in the middle of an order transaction without updating the order. This can be achieved as follows.

void update_statistics()
{
  int save_process = df_process;
  int save_db_nr = db_nr;
  if (srv_init_process() > 0)
  {
    srv_enable();
    db_open(dbname,db_generation,SHARED);
    /* perform updates -does not affect transaction in progress at call to
       srv_init_process */
    db_close();
    srv_disable();
    srv_end_process();
  }
  df_process = save_process;
  db_nr = save_db_nr;
}

The code could be improved by moving the code to open and close the second process to outside the update_statisitics() function and toggling the df_process and db_nr variables (in a similar way to the technique used for multiple dbs)


srv_load_srv()

srv_load_srv(),dp4_srv_load_srv()

Purpose

Ensures DP4 "service" is running. This function does not establish a connection to DP4

Syntax (CCOWN)

int srv_load_srv(void);

Syntax (DP4DBAPI)

int dp4_srv_load_srv(int argc,char **argv,dp4_name_string version_info);

Parameters

int argc

If desired you can pass a set of command line arguments to the DP4 loader. Typically you would simply pass on the arguments passed to your application, but you can pass a synthetic set if desired (though in this case you should bear in mind that DP4 will only be loaded in the specified configuration if it is not already running.

Specifies the number of arguments to pass the loader if DP4 is not running already.

char **argv

Pointer to an array of char * variables containing the command line arguments as null terminated strings. These arguments can be used to over-ride the default DP4 startup - specify one or more arguments normally used with the DP4 loaded (srvw32.exe,srvwce.exe or dbdaemon).

dp4_name_string version_info

const char * to the programs version information. This information will be passed to the dp4 error log if it is in use and will be displayed if debug_version=1 is set.

DP4 utilities are all linked with the verstamp.c source module and call vs_verstring() (prototyped in verstamp.h) to obtain the version string. verstamp.c is in the src subdirectory of each DP4 release on the DP4 CD ROM, and verstamp.h is in the include subdirectory. Windows executables should also contain a Windows version resource.

Description

srv_load_srv() checks that the DP4 service or daemon is running, and attempts to start it if not using the default configuration as specified in the DP4 configuration file (possibly over-ridden by command line arguments inherited from the calling application if these are known). On legacy versions of DP4 this function is responsible for providing the "auto-load" feature. However in Win32 DP4, and DP4 for Linux or Unix from 4.525 there is usually no need to call this function as lower level DP4 functions will ensure that DP4 is running when you make the first call to it. This function might still be useful if you have an application that wants to check that DP4 is running, but does not actually connect to it itself, or if you want the application to be able to start DP4 in a specific configuration based on command line arguments.

You can see an example of the use of command line inherited arguments in the install.cfg files on the DP4 CD ROM file for Win32 DP4 releases from 4.621 on.

Return values

Returns 1 if the DP4 service is already running (or can be started successfully), and 0 otherwise. This function may return 1 to programs built with a pre 4.620 DP4 C library even if the DP4 service cannot be started, in order to prevent them executing start-up code that was in the library at that time that is no longer appropriate.

See also

srv_connect(), srv_init_process()


srv_mem_describe()

This function was removed from the DP4 C library as of release 4.620. Old programs that used this function will continue to work, but must be altered not to use this function before they can be built using a 4.620 or later copy of the C library.

Purpose

Initialises the memory workfile. (This is a potentially dangerous function, because incorrect use can cause the database manager to malfunction, use mem_describe() instead.)

Old name

#define _amem_describe srv_mem_describe

Syntax

void srv_mem_describe(type_nr, key_size)

Parameters

int type_nr

Table number of records to be used in work file

int key_size

Size of key of table. The key is assumed to be at the beginning of the record

Description

The DP4 memory work file function srv_mem_describe() allows data records to be stored and retrieved from a work file. This function must be called on each table to initialise the operation.

The memory functions beginning with srv_ call the file handling support in the database manager, and the functions beginning with trm_ call the file handling support in the terminal manager. The srv_ version of the functions is used by a few DP4 utilities, such as auxdistr, which implement DP4 functionality. Previous editions of this page suggested the srv_ functions could be used in programs that did not use the DP4 terminal manager. A much better plan is either to implement your own functionality in this area or to use the trm functions in a program that uses only the file handling functionality of the terminal manager, and not the user interface functionality. The srv_ functions should be avoided in all circumstances.

Another set of workfile functions are available which allow you to access records in a workfile file with an index. See the function wkf_open().

The parameter key_size used to differentiate between records in the workfile is assumed to be at the beginning of the record. You cannot use srv_mem_describe() with database records where the key fields are spread throughout the record.

To use the srv_ memory workfile functions, follow the steps below:

  1. Call the function srv_mem_describe() to define the table and key size. You muse ensure that table numbers are always used with the same key size - if srv_mem_describe() is called with different key sizes on the same table number, even from different applications, the second keysize will be silently ignored, even if the first application has already terminated (unless there has been a time when all DP4 processes have ended in the mean-time).

  2. Call the function srv_mem_post() to post records to the workfile

  3. Call the function srv_mem_find() to fetch records with a specified key from the work file

You can define your own tables, but use type numbers greater than 4096. You cannot delete records from the workfile.

See also


srv_mem_find()

This function was removed from the DP4 C library as of release 4.620. Old programs that used this function will continue to work, but must be altered not to use this function before they can be built using a 4.620 or later copy of the C library.

Purpose | Fetches a record from the workfile. (This is a potentially dangerous function, use mem_find() instead.)
Old name | #define _amem_find srv_mem_find
Syntax | int srv_mem_find(dp4rec, owner, dbnr)
Parameters | short* dp4rec | Pointer to the record whose key fields indicate the record to fetch from the workfile
 | int owner | Owner of the record which posted the original record to the workfile. This is normally given by the global variable df_process
 | int dbnr | Database labelled as containing record. This is normally given in the global variable db_nr
Description | The srv_mem_find() function attempts to fetch a record from the corresponding workfile which has previously been posted to the workfile with a call to the corresponding srv_mem_post() function. The memory functions beginning with srv_ call the file handling support in the database manager, and the functions beginning with trm_ call the file handling support in the terminal manager. You must not mix up the server file functions with terminal file functions.
Return values | Returns TRUE if record is found, otherwise FALSE
See also |


srv_mem_post()

This function was removed from the DP4 C library as of release 4.620. Old programs that used this function will continue to work, but must be altered not to use this function before they can be built using a 4.620 or later copy of the C library.

Purpose

Posts a record to the workfile. (This is a potentially dangerous function, use mem_post instead.)

Old name

#define _amem_post srv_mem_post

Syntax

int srv_mem_post(short* dp4rec,int owner,int dbnr);

Parameters

short* dp4rec

Pointer to the record to post to workfile

int owner

Owner to label the record in the workfile with. Held in global variable df_process

int dbnr

Database labelled as containing record. Held in global variable db_nr

Description

The srv_mem_post() function posts a record to the corresponding workfile which has previously been set up with a call to the corresponding srv_mem_describe() function.

The memory functions beginning with srv_ call the file handling support in the database manager, and the functions beginning with trm_ call the file handling support in the terminal manager. You must not mix up the server file functions with terminal file functions.

Return values

Returns TRUE if record posted, otherwise FALSE

See also


str_lcase()

Purpose | Converts a string to lower case
Old name | #define _lcase str_lcase
Syntax | char* str_lcase(s)
Parameters | char* s | Null-terminated string to convert
Description | The str_lcase() function converts the null-terminated string specified by the parameter s to lower case. The converted string is also returned by this function. Only characters in the range 'A' to 'Z' are affected. This function must not be used for strings containing international characters or Kanji characters.
Return values | Returns the converted string
See also | str_ucase()


str_ucase()

Purpose | Converts a string to upper case
Old name | #define _ucase str_ucase
Syntax | char* str_ucase(s)
Parameters | char* s | Null-terminated string to convert
Description | The str_ucase function converts the null-terminated string specified by the parameter s to upper case. The converted string is also returned by this function. Only characters in the range 'a' to 'z' are affected.
Return values | Returns pointer to converted string
See also | str_lcase()


sys_del_confirm()

Purpose

Confirms an action

Old name

#define confirm sys_del_confirm

Syntax

int sys_del_confirm(void)

Description

The sys_del_confirm() function displays map 184 from the SYSTEM mapset on the SYSTEM database, as shown below:

Do you really want to do this ?

This function obtains a Y(es) or N(o) answer from the user. The initial value displayed is NO.

The global variable rmk_flags is used to specify the display format for the above map. sys_del_confirm() sets the global variable jump to FALSE so the user cannot skip this function.

This function is commonly used to get a confirmation from the user when the program is about to delete records.

Return values

Returns TRUE if user enters YES, otherwise FALSE.

See also


sys_error()

Purpose

Displays an error message and aborts

Old name

#define _sys_error sys_error

Syntax

void sys_error(msge_nr, ADDITIONAL)

Parameters

int msg_nr

Number of error map to display

ADDITIONAL

Two optional parameters:

  • error number to display
  • extra error parameter

Description

The sys_error() function is used by the library functions to display an error message and then abort the program. The error map displayed corresponds to the map number on the SYSTEM mapset of the SYSTEM database. You should use your own function to display critical error messages rather than this function.

This is one of the functions that you have to rewrite if you do not wish to load the terminal manager.

See also


sys_fail()

Purpose

Produces a fail message

Old name

#define _vfail sys_fail

Syntax

void sys_fail(error, secon, addit)

Parameters

int error

DP4 error number

int secon

Operating system error

int addit

Additional error

Description

The sys_fail() function produces an error message on the screen and then terminates the program. The message takes the form:

Fail error reported by DP4 code <error>
Operating system error code <secon>
Additional error code <addit>
Error <error> or error message corresponding to it.
Operating system error: <secon> or error message corresponding to it

Press any key to continue

The output is the same as the DP4 FAIL utility.

Example

/*
 C Functions Reference Manual
 ----------------------------
 Filename: SYS_FAIL.C
 Example: sys_fail
 Purpose: Simple version of FAIL
*/
#include "dp4.h"
extern int atoi(char*);
void process(){
  int n[3] = {0,0,0};
  char* p;
  int i;
  for (i = 0; i < 3;i++)
  {
     if ((p = tail_read(TAIL_NOUCASE)) == 0)
       break;
     n[i] = atoi(p);
  }
sys_fail(n[0],n[1],n[2]);


sys_message()

Purpose

Displays a system message

Old name

#define _sys_message sys_message

Syntax

void sys_message(msg_nr, ADDITIONAL)

Parameters

int msg_nr

Number of error map to display

ADDITIONAL

Two optional parameters:

  • error number to display
  • extra error parameter

Description

The sys_message() function is used by the library functions to display an error message. The error map displayed corresponds to the map number on the SYSTEM mapset of the SYSTEM database.

You should use your own function to display critical error messages rather than this function.

This function does not abort the program, unlike the function sys_error().

This is one of the functions that you have to rewrite if you do not wish to load the terminal manager.

See also


sys_request()

Purpose

Displays an error message and conditionally aborts

Old name

#define _sys_request sys_request

Syntax

void sys_request(msg_nr, ADDITIONAL)

Parameters

int msg_nr

Number of error map to display

ADDITIONAL

Two optional parameters:

  • error number to display
  • extra error parameter

Description

The sys_request() function is used by the library functions to display an error message followed by a pause. The program is aborted only if the user presses the <Esc> key. The error map displayed corresponds to the map number on the system mapset of the SYSTEM database.

You should use your own function to display critical error messages rather than this function.

This function does not abort the program, unlike the function sys_error().

It is one of the functions you must rewrite if you do not wish to load the terminal manager.

See also


sys_upd_confirm()

Purpose

Obtains confirmation for current changes

Old name

#define upd_confirm sys_upd_confirm

Syntax

int sys_upd_confirm(void);

Description

The sys_upd_confirm() function provides a standard method of confirming changes.

The function map_choose() is called to select an option from map 185 of the system mapset of the SYSTEM database:

1 Save changes
2 Rekey changes
3 Abandon changes

Return values

Returns the option chosen. Escaping from the menu selects option2 "Rekey changes".

This function is used by map_get_inputs() and QAB programs to confirm changes. You can replace this function with your own version if required.

This function is always automatically returns 1 "Save Changes" if you have a Windows or SAA userdata.sys file. The reasoning behind this is that a program will normally only arrive in the routine after the user has already pressed Enter or clicked an OK button to confirm changes. Without SAA Pressing Enter on the last field of a dialog usually prompts a call to sys_upd_confirm(), but with SAA or Windows pressing Tab on the last field of a dialog will either wrap around to the first field, or stick on the last field, but not attempt to exit the dialog.


Functions T-V

  • tail_found()
  • tail_get_option()
  • tail_read()
  • tf_check(), sf_check()
  • tf_close(), sf_close()
  • tf_default(), sf_default()
  • tf_delete(), sf_delete()
  • tf_disk_reset(), sf_disk_reset()
  • tf_flush(), sf_flush()
  • tf_get_date_time(), sf_get_date_time()
  • tf_get_file_timestamp(), sf_get_file_timestamp()
  • tf_get_position(), sf_get_position()
  • tf_get_size(), sf_get_size()
  • tf_open(), sf_open()
  • tf_read(), sf_read()
  • tf_read_record(), sf_read_record()
  • tf_read_sysdata()
  • tf_rename(), sf_rename()
  • tf_round_number()
  • tf_seek(), sf_seek()
  • tf_set_date_time(), sf_set_date_time()
  • tf_set_file_timestamp(), sf_set_file_timestamp()
  • tf_space(), sf_space()
  • tf_trunc_number()
  • tf_txt_close()
  • tf_txt_read()
  • tf_txt_seek_end()
  • tf_txt_write()
  • tf_write(), sf_write()
  • tf_write_record(), sf_write_record()
  • trm_beep()
  • trm_clr_ahead()
  • trm_co()
  • trm_control_subclass()
  • trm_ctrls()
  • trm_curs_off()
  • trm_curs_on()
  • trm_cursor()
  • trm_get_access()
  • trm_grabchr()
  • trm_lookchr()
  • trm_read_user()
  • trm_refresh()
  • trm_run_program()
  • trm_run_program_ex()
  • trm_screen_dump()
  • trm_set_preferences()
  • trm_set_progname()
  • trm_set_training_mode()
  • trm_so()
  • trm_unset_preferences()
  • trm_write_syslog()


tail_found()

Purpose

Checks for the presence of an option on the command line

Syntax

boolean tail_found(option)

Parameters

char * option

Zero-terminated name to look for on command line

Description

The tail_found() function returns TRUE if the string pointed to by the parameter option occurs in the argument list (preceded by the hyphen (-) character) and FALSE otherwise. If the command tail exists, it is removed from the command line, so subsequent calls to the function tail_found() with the same parameters will fail, unless multiple copies have been passed.

The parameter option is not case sensitive and must not contain the leading - character. To read command line options with arguments use tail_get_option().

tail_found(option) is equivalent to:
(int) tail_get_option(option,0)

Return values

Returns TRUE if the string is found, and FALSE otherwise

See also

tail_get_option()

Example

The following returns TRUE, on the first call only, if -HELP is supplied to the program on the command line:

tail_found("help")


tail_get_option()

Purpose

Reads DP4-style command tails with arguments

Old name

#define _tail_opt(option,get_value)
tail_get_option(option,get_value!=0)

Syntax

char* tail_get_option(option, flags)

Parameters

char* option

Zero-terminated name to extract from command line

int flags

Indicate how to treat the command tail if found

Description

The tail_get_option() function is used to read the command line passed to the program. It recognises DP4 command tails which begin with the minus (-) character. When the command tail has to pass an argument, specify a non-zero flag, for example:

dbname = tail_get_option("DB", 2);

This returns a pointer to the argument following -DB, in this case, the database name. Because flags is even, the argument is not converted to upper case. If flags is odd, the returned argument is converted to upper case. The predefined flags TAIL_UCASE and TAIL_NOUCASE can be used as well.

The parameter option is not case sensitive, and must not contain the negative sign.

(int) tail_get_option(option,0)

is equivalent to:

tail_found(option).

Whatever the flags, if the command tail is present, it is removed from the command line, so subsequent calls to the function tail_get_option() with the same parameters will fail, unless multiple copies have been passed.

Return values

Returns non-zero if command tail found

Example

/*
 C Functions Reference Manual
 ----------------------------
 Filename: TAIL_GET.C
 Example: tail_get_option and tail_read
 Purpose: Simple example illustrates reading a few
          tails*/

/*
#db salesord
#c
#end
*/
#include "dp4.h"
#include "tail_get.h"
 /* generate this file by running LIBMAKE */
char comm_cust[16];
char comm_reg[16];
void process()
{
   char* p;
   map_draw(1,RETAIN);
   /* check for help command tail*/
   if (tail_get_option("HELP",FALSE))
   {
      map_datamap(100);
      field_help(100,1,HT_PAUSE);
   }
   if ((p = tail_get_option("CUSTOMER",2)) != 0)
   {
      WIPE(comm_cust);
      memcpy(comm_cust,p,min(strlen(p),
             sizeof(comm_cust)));
      /* show customer passed */
      map_datamap(2);
      show_c(2,1,comm_cust);
      askf_special(2,2,0);
      map_clear_from(2);
   }
   if ((p = tail_get_option("REGION",TRUE)) != 0)
   {
      WIPE(comm_reg);
      memcpy(comm_reg,p,min(strlen(p),
             sizeof(comm_reg)));
      /* show region passed */
      map_datamap(3);
      show_c(3,1,comm_reg);
      askf_special(3,2,0);
      map_clear_from(3);
   }
      /* there shouldn't be any other tails */
   if ((p = tail_read(TAIL_NOUCASE)) != 0)
   {
      map_datamap(4);
      show_c(4,1,p);
      askf_special(4,2,0);
      return;
   }
}


tail_read()

Purpose

Reads the next command tail

Old name

#define _tail() tail_read(TAIL_UCASE)

Syntax

char * tail_read(flags)

Parameters

int flags

Indicate how to treat the command tails if found

Description

The tail_read() function reads the first command tail from the command line and returns a pointer to it. If flags is odd, the argument is converted to upper case. It is better to use the flags TAIL_UCASE or TAIL_NOUCASE as this is more meaningful.

As with the function tail_get_option(), this function removes the command tail from the command line. This function differs from the function tail_get_option() in that it simply returns the command line arguments in order.

The first command tail that the function tail_read() reads is the first one after the command name. The command name itself is not read.

Changed Behaviour From 4.620 there is a change to the behaviour of this function: in previous releases all arguments are returned, including any that begin with a - character. From 4.620, except on legacy platforms, the function ignores arguments that begin with a - character. This is because newer parts of DP4 use a non destructive read for parsing command line options, and the presence of such options would otherwise confuse DP4 utilities that use tail_read(), for example without this cahnge problems would arise when DP4 Configuration file options are specified on the command line to utilities such as MAPPOST or PROGDATE.

Return values

Returns a pointer to the command tail if it exists, otherwise zero

Example

See the example for tail_get_option()


tf_check(), sf_check()

Purpose | Checks that a file or directory exists
Old names | #define _vfcheck tf_check #define _afcheck sf_check
Syntax (CCOWN) | int tf_check(const char * file_name) int sf_check(const char *file_name)
Syntax (DP4DBAPI) | int dp4_sf_check(CONN *conn,dp4_name_string filename)
Parameters | const char* file_name | Null-terminated string containing file/directory name to check
Description | The tf_check() and sf_check() functions check whether the given name exists as a file or directory. You can include disk drives and directory names in the file name.
Return values | Returns 1 if file of this name exists, 2 if the name is actually a directory, or 0 otherwise


tf_close(), sf_close()

Purpose

Closes a file

Old names

#define _vclose tf_close

#define _aclose sf_close

Syntax (CCOWN)

tf_close(int file_handle)
sf_close(int file_handle)

Syntax (DP4DBAPI)

BOOLEAN dp4_sf_close(FCONN * fconn);

Parameters

int file_handle

File handle to close, obtained from an earlier call to tf_open() or sf_open()

FCONN * fconn

File handle to close,, obtained from an earlier call to dp4_sf_open()

Description

The functions tf_close() and sf_close() close a file handle previously returned from a call to the corresponding tf_open() or sf_open() function. All the internal buffers are flushed as necessary.

The sf_close() function calls the file handling support in the database manager, and the tf_close() function calls the file handling support in the terminal manager. You must not call an sf_ function with a handle obtained with tf_open(), or a tf_ function with a handle obtained with sf_open().

Failing to close a file after writing data to it is a common error and often leads to loss of data.

If the file was opened with the value VF_TMP contained in the parameter flags, the file is deleted.

For the DP4DBAPI version of the API the return value is TRUE if the function completes without any errors. Whether or not there is an error the file handle is invalid on return from this call.

See also

tf_open()


tf_default(), sf_default()

Purpose

Gives the default path name for DP4 system files

Old names

#define _vfdefault tf_default
#define _afdefault sf_default

Syntax (CCOWN)

char* tf_default(void)
char* sf_default(void)

Syntax (DP4DBAPI)

dp4_name_string dp4_sf_default(CONN * conn,char * path_buffer,size_t buffer_size);

Description

The tf_default() and sf_default() functions get the name of the directory used to read the DP4 licence file and return a pointer to a static buffer containing this name. In the DP4DBAPI version of this API you must pass a pointer to a buffer, which should be at least 256 bytes in size. Currently the database manager does not check that the buffer is actually larget enough to hold the value, and the buffer_size parameter is ignored.

Return values

Returns a constructed pointer to the disk and path name

See also

trm_read_user() tf_read_sysdata()

Example

/*
 C Functions Reference Manual
 ----------------------------
 Filename: TF_DEFAU.C
 Example: tf_default
 Purpose: Simple program to display the disk and
          path name of the where DATAFIT.SYS is
*/

/*
#db salesord
#c
#end
*/

#include "dp4.h"
#include "tf_defau.h"
  /* generate this file by running LIBMAKE */
void process()
{
   map_load("TF_DEFAU"); /* load the maps */
   map_draw(1,RETAIN);
    /* put up map which will display directory */
   show_c(1,1,tf_default());
    /* get and display */
   trm_refresh();
    /* make it visible and terminate */
}


tf_delete(), sf_delete()

Purpose | Deletes a file
Old names | #define _vdelete tf_delete #define _adelete sf_delete
Syntax (CCOWN) | int tf_delete(file_name) int sf_delete(file_name)
Syntax (DP4DBAPI) | BOOLEAN dp4_sf_delete(CONN * conn,dp4_name_string filename);
Parameters | char* file_name | Name of file to delete, which is null terminated
Description | The tf_delete() and sf_delete() functions delete the named file. Attempting to delete a missing file is not treated as an error.
Return values | Returns TRUE if the file was successfully deleted


tf_disk_reset(), sf_disk_reset()

Purpose

Disk reset function

Old names

#define _vdskrst tf_disk_reset

#define _adskrst sf_disk_reset

Syntax

int tf_disk_reset(file_name)
int sf_disk_reset(file_name)

Parameters

char* file_name

Null-terminated string containing file name on disk

Description

The tf_disk_reset() and sf_disk_reset() functions reset the disk on which file_name resides. They are used for floppy disk operations and must be called before asking the user to change the disk.

The sf_disk_reset() function calls the file handling support in the database manager, and the tf_disk_reset() function calls the file handling support in the terminal manager. You must not mix up the server file functions with terminal file functions.

Note that these functions are only required for the old CCPM operating system. Other operating systems reset the disk automatically.

Return values

Returns TRUE if it is safe for the disk to be changed, otherwise zero

These functions have now been withdrawn, and replaced with a macro that evaulates to TRUE


tf_flush(), sf_flush()

Purpose

Flushes buffers to a file

Old names

#define _vflush tf_flush

#define _aflush sf_flush

Syntax (CCOWN)

void tf_flush(int file_handle)
void sf_flush(int file_handle)

Syntax (DP4DBAPI)

BOOLEAN dp4_sf_flush(FCONN * fconn);

Parameters

int file_handle

File handle of file to flush, obtained by a successful call to tf_open() or sf_open()

FCONN * fconn

File handle of file to flush,obtained by a successful call to dp4_sf_open()

Description

The tf_flush() and sf_flush() functions flush data written to files, ensuring that it is all written from internal buffers to disk.

The sf_flush()amd dp4_sf_flush() functions call the file handling support in the database manager, and the tf_flush() function calls the file handling support in the terminal manager. You must not call an sf_ function with a handle obtained with tf_open(), or a tf_ function with a handle obtained with sf_open().

See also

tf_open()


tf_get_date_time(), sf_get_date_time()

Purpose

Obtains the current date and time

Old name

#define _get_date tf_get_date_time

Syntax (CCOWN)

void tf_get_date_time(datetype * date, timetype * time)
void sf_get_date_time(datetype * date, timetype * time)

Syntax (DP4DBAPI)

BOOLEAN dp4_sf_get_date_time(CONN *conn,datetype * date,timetype * time);

Parameters

datetype* date

Pointer to a variable to hold the current date

timetype* time

Pointer to a variable to hold the current time

Description

The tf_get_date_time() function gets the current date and time from the operating system. It is called by all DP4 C programs at startup to set the global variables app_start_time and app_start_date.

Example

See the example on tf_set_date_time()


tf_get_file_timestamp(), sf_get_file_timestamp()

Purpose

Obtains the last modification date and time for an open file

Syntax (CCOWN)

void tf_get_file_timestamp(int filenr, datetype *date, timetype *time)
void sf_get_file_timestamp(int filenr, datetype *date, timetype *time)

Syntax (DP4DBAPI)

BOOLEAN dp4_sf_get_file_timestamp(FCONN * fconn,datetype * date,timetype * time)

Parameters

int filenr

Handle to file, obtained by earlier call to tf_open() (or sf_open() for sf_ variant)

Parameters

FCONN * fconn

Handle to file, obtained by earlier call to dp4_sf_open()

datetype* date

Pointer to a variable to hold the last modification date of the file

timetype* time

Pointer to a variable to hold the last modification time of the file

Description

The tf_get_file_timestamp() function gets the last modification date and time of a file opened with tf_open(). For the DP4DBAPI version of the function the return value is TRUE if the function completed without error.


tf_get_position(), sf_get_position()

Purpose

Returns the current file pointer position

Old names

#define _vfposn tf_get_position

#define _afposn sf_get_position

Syntax (CCOWN)

file_pointer tf_get_position(int file_handle)
file_pointer sf_get_position(int file_handle)

Syntax (DP4DBAPI)

dp4_file_pointer dp4_sf_get_position(FCONN * fconn);
dp5_file_pointer dp4_sf64_get_position(FCONN * fconn);

Parameters

int file_handle

File handle of file opened with tf_open() or sf_open().

FCONN * fconn

File handle of file opened with dp4_sf_open().

Description

The tf_get_position() and sf_get_position() functions give the current file pointer position of the file with the given file handle.

sf_get_position() function calls the file handling support in the database manager, and the tf_get_position() function calls the file handling support in the terminal manager. You must not call an sf_ function with a handle obtained with tf_open(), or a tf_ function with a handle obtained with sf_open().

Return values

Returns the file position if successful, otherwise a negative number. If sf_get_poistion() or tf_get_position() fail they call sys_fail() function and do not normally return. The DP4DBAPI versions of the API return DP4_INVALID_PTR, or DP5_INVALID_PTR for the 64 bit version.

See also

tf_open()


tf_get_size(), sf_get_size()

Purpose

Obtains the size of a file

Old names

#define _vfsize tf_get_size

#define _afsize sf_get_size

Syntax (CCOWN)

file_pointer tf_get_size(int file_handle)
file_pointer sf_get_size(int file_handle)

Syntax (DP4DBAPI)

dp4_file_size dp4_sf_get_size(FCONN * fconn);
dp5_file_size dp4_sf64_get_size(FCONN * fconn);

Parameters

int file_handle

File handle returned by tf_open() or sf_open()

FCONN * fconn

File handle returned by dp4_sf_open()

Description

The tf_get_size() and sf_get_size() functions give the size of a file.

The sf_get_size() function calls the file handling support in the database manager, and the tf_get_size() function calls the file handling support in the terminal manager. You must not call an sf_ function with a handle obtained with tf_open(), or a tf_ function with a handle obtained with sf_open().

Return values

Returns size of file. The DP4DBAPI versions might fail if the file handle was invalid. In this case the return value is DP4_INVALID_SIZE. or DP5_INVALID_SIZE for the 64 bit version of the API, both defined to be -1.


tf_open(), sf_open()

Purpose

Opens a file

Old names

#define _vfopen tf_open
#define _afopen sf_open

Syntax (CCOWN)

int tf_open(file_name, mode)
int sf_open(const char *file_name, int mode)

Syntax (DP4DBAPI)

FCONN * dp4_sf_open(CONN * conn,dp4_name_string filename,int mode);

Parameters

CONN * conn

Connection handle to server on which file is to be opened

char* file_name

Null-terminated string

int mode

Mode in which to open the file

Description

The tf_open() and sf_open() functions are used to open a file for further processing. This must be done before reading from, or writing to, the file.

The sf_open() function calls the file handling support in the database manager and the tf_open() function calls the file handling support in the terminal manager. The function sf_open() can, therefore, be used for applications where the terminal manager is not loaded. You must not call an sf_ function with a handle obtained with tf_open(), or a tf_ function with a handle obtained with sf_open().

The file_name must be null terminated.

The parameter mode can take any suitable combination of the following values:

Value

Meaning

VF_RO

Opens the file in read-only mode. Writing to the file generates an error

VF_RW

Opens the file in read and write mode. This is the normal mode

VF_SEQ

Passing this flag if the file will predominantly be accessed sequentially may improve performance, but probably only if the file is read only

VF_BATCH

Provides better performance than VF_SEQ when the file is opened with VF_RW and will be written sequentially and is expected to be large.

VF_TMP

Opens a temporary file. It is deleted when closed

VF_SHR

Opens the file in shared mode. Other users may access the file

VF_CKS

Causes the file checksum to be maintained. This is for internal use only.

VF_RET

Makes the function tf_open() return a negative handle if the file cannot be opened rather than the default action. The default action is to terminate the program with an error message

VF_NEW

Opens a new file. If the file already exists, it is truncated

VF_NOBLK

For internal use only

VF_SYNC

This causes write operations to be done immediately instead of through the operating system buffers. DP4 will still buffer the file itself however. This flag is used internally.

VF_SORT

Only worth doing when writing, it causes the manager program to sort file buffers and to write 'near by' regions of files together. This may improve performance, depending on how your program works. (This flag is no longer implemented, due to the increased sophistication of file buffering inside DP4)

For tf_open/sf_open() if the file is not opened successfully, a fail error message is displayed unless VF_RET was passed.

Return values

sf_open()/tf_open() return an integer file handle if successful or a negative number if they fails dp4_sf_open() returns an FCONN * file handle if successful or NULL otherwise.

See also


tf_read(), sf_read()

Purpose

Reads a specified amount of data from a file

Old names

#define _vnread tf_read
#define _anread sf_read

Syntax (CCOWN)

unsigned tf_read(int file_handle, void * data, unsigned bytes)
unsigned sf_read(int file_handle, void * data, unsigned bytes)

Syntax (DP4DBAPI)

dp4_file_size dp4_sf_read(FCONN * fconn,void * buffer,dp4_file_size nr_bytes);

Parameters

int file_handle

File handle of file from which to read, obtained earlier with a successful call to tf_open() or sf_open()

Parameters

FCONN * fconn

File handle of file from which to read, obtained earlier with a successful call to dp4_sf_open()

ANY* data

Pointer to buffer to hold data that is read

unsigned bytes

Number of bytes to read from file

Description

The tf_read() and sf_read() functions read the specified number of bytes from a file into a buffer .The sf_read() function calls the file handling support in the database manager, and the tf_read() function calls the file handling support in the terminal manager. You must not call an sf_ function with a handle obtained with tf_open(), or a tf_ function with a handle obtained with sf_open().

Return values

Returns the number of bytes read. This may be less than the requested number when the end of file is reached.

For dp4_sf_read only. If an error occurs when dp4_sf_read() is called, then the return value will be the number of bytes that had been read when the error occurred: If you do not have an error callback installed, then you will need to cal dp4_error_status() to check whether an error occurred.

See also

tf_write_record()


tf_read_record(), sf_read_record()

Purpose

Reads a DP4 record from a file

Old names

#define _vread tf_read_record
#define _aread sf_read_record

Syntax (CCOWN)

void tf_read_record(int file_handle, short *datarec)
void sf_read_record(int file_handle, short * datarec);

Syntax (DP4DBAPI)

BOOLEAN dp4_sf_read_record(FCONN * fconn,short * datarec)

Parameters

int file_handle

File handle of file

FCONN * fconn

File handle of file

short * datarec

Pointer to a DP4 record to hold record read from file

Description

The tf_read_record() and sf_read_record() functions read a DP4 record from the current position in the file. You obtain the file handle with a successful call to tf_open() or sf_open().

The sf_read_record() function calls the file handling support in the database manager, and the tf_read_record() function calls the file handling support in the terminal manager. You must not call an sf_ function with a handle obtained with tf_open(), or a tf_ function with a handle obtained with sf_open().

A DP4 record is a record corresponding to a structure of the form below:

struct MY_RECORD
{
   short l;
   /* and normally but not necessary */
   short typenr;
   short generation;
   short flags;
   long timestamp;
   /* other fields */
   ...
}

The L field is used to determine the number of bytes to read from the file. The file pointer is automatically incremented.

datarec must be large enough to hold the largest expected record.

Do not mix calls to the function tf_read_record() with calls to the function tf_write(). This is because the function tf_read_record() allows for any byte ordering differences on non-INTEL machines, but the function tf_write() does no such translation.

This function is not intended for reading database files directly, but only for reading and writing extract files, or similar files you have created yourself.

See also

tf_read(), tf_write_record(), tf_open()

Example

/*
 C Functions Reference Manual
 ----------------------------
 Filename: TF_READ_.C
 Example: tf_read_record, and various other tf_XXXX
          functions
 Purpose: Simple import program which reads and
          posts records from a file. The example for
          tf_write_record generates a file which can
          be used here.
*/

/*
#db salesord
#c
#end
*/

#include "dp4.h"
#include "tf_read_.h"
 /* generate this file by running LIBMAKE */

 /* prototypes */
static int get_valid_file(char*);

 /* variables */
static struct DATAREC datarec;

 /* general DP4 record */
void process()
{
   char fname[13]; /* file name holder */
   map_draw(1,RETAIN); /* header map */
   WIPE(fname); /* blank file_name buffer */
   if (get_valid_file(fname) == TRUE)
   /* get name of existing file from user */
   {
      int fd;
      file_pointer size;
      fd = tf_open(fname, VF_RO|VF_SEQ);
      /* open file read-only, sequential */
      if (fd 0)
      {
         size = tf_get_size(fd);
         /* get size of user specified file */
         if (size 0)
         {
            int count;
            map_draw(2,RETAIN);
            /* HEADER_SIZE is a minimum record
               length */
            for (count = 0;((size -
             tf_get_position(fd))HEADER_SIZE);
             count++)
            {
               tf_read_record(fd,&datarec.l);
               rec_post_with_flags(&datarec.l,
                     NO_CHECK);
               show_h(2,1,count);
               trm_refresh();
               /* make count visible */
               if ((count % 20) == 0)  db_commit();
               /* commit every 20 records */
            }
            tf_close(fd); /* close the file */
         }
         else
           askf_special(3,1,0);
           /* tell user that file is empty */
      }
      else
        askf_special(4,1,0);
        /* tell user that open failed */
   }
}

/**/

static get_valid_file(fname)
  char* fname;
{
   int valid_fname = FALSE;
   /* not valid by default */
   map_draw(10,RETAIN);
   /* draw map to enter filename */
   while (valid_fname == FALSE)
   {
      if (!askf_c(10,1,fname))
      {
         register i; /* strip out spaces */
         for (i=0; i < 12 && fname[i] != ' '; i++);
         fname[i] = '\0'; /* terminate string */
         if (strlen(fname) > 0)
         {
            if (tf_check(fname) != 1)
            /* 1 means file exists */
            {
               map_draw(11,RETAIN);
               /* tell user file does not exist */
               show_c(11,1,fname);
               askf_special(11,2,0);
               /* get user to continue */
            }
            else
            {
               valid_fname = TRUE;
               /* found an existing file */
               break;
            }
         }
      }
      else break;
   }
   return valid_fname;
}


tf_read_sysdata()

Purpose

Reads the system data file (terminal manager's view). #include "SYSDATA.H".

Old name

#define readsysdata tf_read_sysdata

Syntax

void tf_read_sysdata(void);

Description

The tf_read_sysdata function reads the contents of the local DP4 licence file into the global variable sysdata. This reflects the terminal manager's view of the system, which may be different from the server's view if the program is running on a network.

The global variable sysdata is declared in the header file SYSDATA.H as:

extern struct SYSDATA sysdata

See also

trm_read_user(), sf_read_sysdata()

Example

/*
 C Functions Reference Manual
 ----------------------------
 Filename: READ_SYS.C
 Example: tf_read_sysdata
 Purpose: Program displays information kept in
          DATAFIT.SYS
*/

/*
#db salesord
#c
#end
*/

#include "dp4.h"
#include "sysdata.h"
#include "read_sys.h"
 /* generate this file by running LIBMAKE */
void process()
{
   tf_read_sysdata();
   map_draw(1,RETAIN);
   show_c(1,1,sysdata.drvdat);
   show_c(1,2,sysdata.drvind);
   show_c(1,3,sysdata.drvlog);
   show_c(1,4,sysdata.drvrlb);
   show_c(1,5,sysdata.drvbkp);
   show_c(1,6,sysdata.drvspl);
   show_c(1,7,sysdata.drvsys);
   show_c(1,8,sysdata.drvwrk);
   show_c(1,9,sysdata.drvdta);
   show_c(1,10,sysdata.drvprn);
   trm_refresh();
}


tf_rename(), sf_rename()

Purpose

Renames an existing file

Old names

#define _vrename tf_rename
#define _arename sf_rename

Syntax (CCOWN)

int tf_rename(const char *new_name, const char * old_name)

Syntax (DP4DBAPI)

int dp4_sf_rename(CONN * conn,const char * new_name, const char * old_name)

Parameters

char* new_name

New name for file (null terminated)

char* old_name

Existing name of file (null terminated)

Description

The tf_rename() and sf_rename() functions change the name of an existing file. Both the existing file and the new file must be on the same disk and file system. The renaming operation fails if the new file already exists as a file or as a directory.

Return values

Returns TRUE if the operation is successful, otherwise FALSE


tf_round_number()

Purpose

Rounds a double-precision number to the specified accuracy

Old name

#define round_number tf_round_number

Syntax

double tf_round_number(number, decimal_places)

Parameters

double number

Number to round

int decimal_places

Required accuracy, as a number of decimal places

Description

The tf_round_number() function rounds the given number to the specified number of decimal places.

If the value of the digit in the decimal place after that given by the parameter decimal_places is greater than or equal to 5, the previous digit is incremented by 1. For example, the call:

tf_round_number(3.149, 2);

returns 3.15. The call:

tf_round_number(3.999, 2)

returns 4.00.

Return values

Returns the rounded double-precision number

See also

tf_trunc_number()

Example

/*
 C Functions Reference Manual
 ----------------------------
 Filename: TF_ROUND.C
 Example: tf_round_number
 Purpose: illustrates the library function
  tf_round_number
*/

/*
#db salesord
#c
#end
*/
#include "dp4.h"
#include "tf_round.h"
/* generate this file by running LIBMAKE */
void process()
{
   double n = 0.0;
   map_draw(1,RETAIN);
   for (;;)
   {
      if (askf_n(1,1,&n))
      /* get number from user */
      break;  /* break if escape */
      show_n(1,2,tf_round_number(n,2));
      /* display round in field 2 */
      show_n(1,3,tf_trunc_number(n,2));
      /* display trunc in field 3 */
   }
}


tf_seek(), sf_seek()

Purpose

Moves the file pointer

Old names

#define _vfseek tf_seek
#define _afseek sf_seek

Syntax (CCOWN)

file_pointer tf_seek(int file_handle, file_pointer posn)
file_pointer sf_seek(int file_handle, file_pointer posn)

Syntax (DP4DBAPI)

dp4_file_pointer dp4_sf_seek(FCONN * fconn, dp4_file_pointer posn);
dp5_file_pointer dp4_sf64_seek(FCONN * fconn, dp5_file_pointer posn);

Parameters

int file_handle

File handle of file, obtained by a call to tf_open() or sf_open()

FCONN * fconn

File handle of file, obtained by a call to dp4_sf_open()

file_pointer posn

New position of file pointer

Description

The tf_seek() and sf_seek() functions move the current position of the file pointer to be used for reading or writing the file.

The sf_seek() function calls the file-handling support in the database manager, and the tf_seek() function calls the file-handling support in the terminal manager. You must not call an sf_ function with a handle obtained with tf_open(), or a tf_ function with a handle obtained with sf_open().

The new position is from the beginning of the file. There is no support for moving the position as an offset from the current position or end of file, though you can use the get_position() functions to discover your current position.

See also

sf_get_position()


tf_set_date_time(), sf_set_date_time()

Purpose

Sets the current date and time

Old name

#define _set_date tf_set_date_time

Syntax (DP4DBAPI)

void tf_set_date_time(const datetype *date,const timetype * time)

Syntax (DP4DBAPI)

BOOLEAN dp4_sf_set_date_time(CONN *conn, const datetype * date, const timetype * time);

Parameters

CONN * conn

Handle to a DP4DBAPI connection, obtained by an earlier call to dp4_srv_connect()

const datetype* date

Pointer to a variable to holding the new current date

const timetype* time

Pointer to a variable to holding the new current time

Description

The tf_set_date_time() and sf_set_date_time() functions (attempt to) change the operating system's current date and time to that specified in the parameters.

Note that these functions do not work in all environments. For example, they fail on Novell because there is no way to set the date and time in this operating system. For the DP4DABPI version of the API, the return value indicates that the function completed without encountering an error from DP4. This does not necessarily indicate that the time was successfully changed.

See also

tf_get_date_time()

Example

/*
 C Functions Reference Manual
 ----------------------------
 Filename: TF_SET_D.C
 Example: tf_set_date_time and tf_get_date_time
 Purpose: Simplified version of the DP4 program
          SETDATE
*/

/*
#db salesord
#c
#end
*/
#include "dp4.h"
#include "tf_set_d.h"
  /* generate this file by running LIBMAKE */

/* variables */
static datetype new_date; /* DP4 datetype record */
static timetype new_time; /* DP4 timetype record */

/* prototypes */
static void edit_date(void);
static void data_inner(int,int*);

void process()
{
   tf_get_date_time(&new_date,&new_time);
     edit_date();    /* and edit it */
}

/**/

static void edit_date()
{
   if (!map_get_inputs(1,1,data_inner,RETAIN|PREDISPLAY))
     tf_set_date_time(&new_date,&new_time);
}

/**/

static void data_inner(mapnr, f)
  int mapnr;
  int* f;
  {
     switch (*f)
     {
        case 1:
          inpm_d(&new_date);
          break;
        case 3:
          inpm_t(&new_time);
          break;
        default:
          inpm_omit();
          break;
   }
}


tf_set_file_timestamp(), sf_set_file_timestamp()

Purpose

Sets the last modification date and time of a file opened for writing

Syntax (CCOWN)

void tf_set_file_timestamp(int filenr, const datetype *date, const timetype *time);
void sf_set_file_timestamp(int filenr, const datetype *date, const timetype *time);

Syntax (DP4DBAPI)

BOOLEAN dp4_sf_set_file_timestamp(FCONN * fconn,const datetype * date,const timetype * time)

Parameters

int filenr

Handle to file, obtained by earlier call to tf_open() (or sf_open() for sf_ variant)

Parameters

FCONN * fconn

Handle to file, obtained by earlier call to dp4_sf_open()

const datetype* date

Pointer to a variable holding the derired last modification date

const timetype* time

Pointer to a variable holding the derired last modification time

Description

The tf_set_file_timestamp() and sf_set_file_timestamp() functions change the last modification date/time infomration for a file to the value specified in the parameters.

In order to successfully set the timestamp of a file you must close the file immediately after calling this function, or at least ensure that you do not write to the file again. If you write to the file again later on, the underlying operating system will change the last modification of the file.

See also

tf_get_file_timestamp()


tf_space(), sf_space()

Purpose

Finds free space on disk

Old names

#define _vdspace tf_space

#define _adspace sf_space

Syntax (CCOWN)

file_pointer tf_space(const char *file_name)
file_pointer sf_space(const char *file_name)

Syntax (DP4DBAPI)

dp4_file_size dp4_sf_space(CONN * conn,dp4_name_string filename);
dp5_file_size dp4_sf64_space(CONN * conn,dp4_name_string filename);

Parameters

const char* file_name

Name of file on disk

Description

The tf_space() and sf_space() functions return the free disk space in bytes, of the disk on which the file resides. The file need not exist.

The 32 bit version of thi API is unreliable, as most disks have more free space than can be returned in a 32 bit value. If 2 Gigabytes or more are available DP4 returns 2 Gigabytes-1 bytes. Use the 64 bit version of the API for a reliable value. Even so, this function is unreliable in some environments. For example, a drive may be compressed so that the amount of free space is unknown.

Return values

Returns the free disk space, or 0L if the drive is invalid. The value returned is likely to be innaccurate unless you use the 64 bit version of this function.


tf_trunc_number()

Purpose

Truncates a double-precision number after a specified number of decimal places

Old name

#define trunc_number tf_trunc_number

Syntax

double trunc_number(number, decimal_places)

Parameters

double number

Double-precision number to be truncated

int decimal_places

Number of decimal places to use for truncation

Description

The tf_trunc_number() function truncates a double-precision number to the number of decimal places given.

All the digits after the decimal place given are truncated to zero. For example, the call:

tf_trunc_number(3.149, 2);

returns 3.14.

Return values

Returns a truncated double-precision number

See also

tf_round_number()

Example

/*
 C Functions Reference Manual
 ----------------------------
 Filename: TF_TRUNC.C
 Example: tf_trunc_number
 Purpose: illustrates the library function
    tf_trunc_number
*/

/*
#db salesord
#c
#end
*/

#include "dp4.h"
#include "tf_trunc.h"
  /* generate this file by running LIBMAKE */
void process()
{
   double n = 0.0;
   map_draw(1,RETAIN);
   for (;;)
   {
      if (askf_n(1,1,&n))
      /* get number from user */
      break;  /* break if escape */
      show_n(1,2,tf_trunc_number(n,2));
      /* display trunc in field 2 */
      show_n(1,3,tf_round_number(n,2));
      /* display round in field 3 */
   }
}


tf_txt_close()

Purpose | Append <Ctrl+Z> to an ASCII file
Old name | #define _dfclose tf_txt_close
Syntax | void tf_txt_close(file_handle)
Parameters | int file_handle | File handle
Description | The tf_txt_close() function appends <Ctrl+Z> to an ASCII file if necessary (for the DOS and CCPM operating systems) then closes the file. The file handle should be at the end of the file before calling this function. Use this function when you have generated new files.
See also | tf_close()


tf_txt_read()

Purpose

Reads a line of text from an ASCII file

Old name

#define _dfrdln tf_txt_read

Syntax

char* tf_txt_read(file_handle)

Parameters

int file_handle

File handle of a file

The file_handle parameter is obtained by calling the function tf_open()

Description

The tf_txt_read() function reads a line of text from an ASCII file.

The maximum length of a line that tf_txt_read() can read is given by:

(2 * F_DATA_SIZE) - 1

where the symbolic constant F_DATA_SIZE is defined, in the header file DBFLAGS.H, as 256. You do not need to include this header file to use the function tf_txt_read().

The end-of-line characters are not returned.

Only use this function for ASCII text.

To determine the length of the line, use the C function strlen. Note that if the line contains binary zeros the wrong value is returned.

Return values

Returns a null-terminated pointer to the line read in, or 0 if at the end of the file

See also

tf_txt_write()


tf_txt_seek_end()

Purpose | Positions file pointer at the end of a file
Old name | #define _dfeofile tf_txt_seek_end
Syntax | void tf_txt_seek_end(file_handle)
Parameters | int file_handle | File handle of a file
 | The file_handle parameter must be obtained by calling the function tf_open()
Description | The tf_txt_seek_end() function moves the file pointer position to the end of the file so that text can be appended to it. It can be used with any operating system supported by DP4.


tf_txt_write()

Purpose | Writes a line of text to an ASCII file
Old name | #define _dfwtln tf_txt_write
Syntax | void tf_txt_write(file_handle, string)
Parameters | int file_handle | File handle of the file
 | char* string | Null-terminated character string to write to file
Description | The tf_txt_write() function writes the string to the current position of the file pointer in the file and appends the end-of-line characters depending on the operating system (DOS/Windows appends a carriage return and the line feed character, but UNIX only uses a line feed).
See also | tf_txt_read()


tf_write(), sf_write()

Purpose

Write data to a file

Old names

#define _vnwrite tf_write
#define _anwrite sf_write

Syntax (CCOWN)

void tf_write(int file_handle, const void *data, unsigned bytes)
void sf_write(int file_handle, const void * data, unsigned bytes)

Syntax (DP4DBAPI)

BOOLEAN dp4_sf_write(FCONN * fconn, void *data, dp4_file_size)

Parameters

int file_handle

File handle of file to which to write, obtained from an earlier call to tf_open() or sf_open()

FCONN * fconn

File handle of file to which to write, obtained from an earlier call to dp4_sf_open()

const void* data

Pointer to buffer to hold data that is written

unsigned bytes

Number of bytes to write to file

Description

The tf_write() and sf_write() functions write a number of bytes to the current position in the file. The file position is automatically incremented.

The sf_write() function calls the file handling support in the database manager, and the tf_write() function calls the file handling support in the terminal manager. You must not call an sf_ function with a handle obtained with tf_open(), or a tf_ function with a handle obtained with sf_open().

For the DP4DBAPI functions, the return value is TRUE if the function completes without errors.

See also

tf_write_record(), tf_read()


tf_write_record(), sf_write_record()

Purpose

Writes a DP4 record to a file

Old names

#define _vwrite tf_write_record
#define _awrite sf_write_record

Syntax (CCOWN)

void tf_write_record(file_handle, short *datarec)
void sf_write_record(int file_handle, short *datarec)

Syntax (DP4DBAPI)

BOOLEAN dp4_sf_write_record(FCONN * fconn,const short * datarec)

Parameters

int file_handle,

File handle of file

FCONN * fconn

File handle of file

short* datarec

Pointer to the data to write to the file. The first two bytes must specify the length of the record.

Description

The tf_write_record() and sf_write_record() functions write a DP4 record to the current position in a file. You obtain the file handle with a successful call to tf_open() or sf_open().

The sf_write_record() function calls the file handling support in the database manager, and the tf_write_record() function calls the file handling support in the terminal manager. You must not mix up the server file functions with terminal file functions.

A DP4 record is a record corresponding to a structure of the form below:

struct MY_RECORD
{
   short l; /* and normally but not necessary */
   short typenr;
   short generation;
   short flags;
   long timestamp;
   /* other fields */
   ...
}

The L field is used to determine the number of bytes to write to the file. The file pointer is automatically incremented.

See also

tf_read_record(), tf_open()

Example

/*
 C Functions Reference Manual
 ----------------------------
 Filename: TF_WRITE.C
 Example: tf_write_record
 Purpose: Program fetches all the customer records
          and  writes them to the file CUSTOMER.EXT.
*/

/*
#db salesord
#c
#tables customer;
#end
*/

#include "dp4.h"
#include "tf_write.h"
  /* generate this file by running LIBMAKE */
void process()
{
   int fd;
   int srch = FIRST;
   map_draw(1,RETAIN); /* header map */
   /* open CUSTOMER.EXT for read/write, as new, and
      write in sequence */
   fd = tf_open("CUSTOMER.EXT",VF_RW|VF_NEW|VF_SEQ);
   if (fd > 0)
   {
      map_draw(2,RETAIN); /* progress map */
      /* fetch, display and post the records to
         CUSTOMER.EXT */
      while (rec_fetch_main(srch,&customer.l))
      {
         srch = NEXT;
         show_c(2,1,customer.name);
         show_n(2,2,customer.customer_number);
         show_c(2,3,customer.address[0]);
         trm_refresh();
         /* make count visible */
         tf_write_record(fd,&customer.l);
      }
   }
   else
     askf_special(3,1,0);
     /* tell user that file does not exist */
}


trm_beep()

Purpose | Sounds a beep
Old name | #define _beep trm_beep
Syntax | void trm_beep(duration)
Parameters | int duration | The value 1 should always be passed
Description | The trm_beep() function sounds a beep of the requested duration. You are recommended to use MAPEDIT rather than trm_beep() to sound beeps.


trm_clr_ahead()

Purpose | Clears the type-ahead buffer
Old name | #define _clr_ahead trm_clr_ahead
Syntax | void trm_clr_ahead(void);
Description | The function trm_clr_ahead() clears the type-ahead buffer. This may be used to make sure important messages are displayed on the screen which might otherwise have been missed due to the user pressing <Enter> too many times.
Example | trm_clr_ahead();


trm_co()

Purpose | Sends a character to the console
Old name | #define _co trm_co
Syntax | void trm_co(ch)
Parameters | int ch | Character to write to the console
Description | The function trm_co() sends the specified character directly to the console. The terminal manager does not treat any displayed characters as part of the background, and they are, therefore, erased by subsequent calls to the function map_remark() and similar functions. The only control characters recognised are '\n', '\r' and '\b'. Do not use this function if you use calls to any of the map functions. Note that the output is immediately visible. Refreshing the screen has no effect on this function.
See also | trm_so()
Example | See the example for trm_so()


trm_control_subclass()

Purpose

Allows subclassing of all or individual control classes used by the 4.600 terminal manager

Syntax

long pascal trm_control_subclass(int type,long NewWndProc)

Parameters

int type

See Description below

long NewWndProc

See Description below

Description

In 4.500 the only window created by DP4 is the window returned by trm_get_hwnd(). In 4.600, however, every control in every map is a separate window. Consequently subclassing terminal manager windows has to be handled differently.

The function trm_control_subclass() allows subclassing of all or individual control classes used by the 4.600 terminal manager. The types of control are listed in MAPS.H, namely the defines from CT_EDIT to CT_VCR. Note that CT_ICON and CT_BITMAP use the same Windows class, as do all the classes CT_CHECKBOX to CT_VCR.

There are two ways to use this function:

  • If you pass a specific type, such as CT_EDIT, you only subclass controls with the corresponding window class. Because some control types share window classes, however, you may subclass several control types. For example, passing CT_BUTTON would also subclass checkboxes and radio buttons.

  • If you pass 0 as the type, all classes used by the terminal manager are subclassed with NewWndProc, which should be a pointer to a window function or 0. 0 removes subclassing previously installed by trm_control_subclass().

When subclassing a single control type, the return value is the previous window function for the class. This value can be used to pass on messages to the old window function through CallWindowProc().

When subclassing all types of control, the return value must be ignored. To pass on a call to the previous window function, use GetClassLong() as follows:

CallWindowProc(GetClassLong(hWnd,0),hWnd,wMsg,wParam,lParam);

See the Example program below.

Note that when trm_control_subclass() is used in this way, calls to it cannot be nested successfully. The return value from GetClassLong(0) is set when trm_control_subclass() is first called and is not changed by subsequent calls to the function.

Calling trm_control_subclass(0,NewWndProc) does not subclass the main window of the application. To subclass this window, proceed as follows:

hWnd = (HWND) trm_get_hwnd();

OldWndProc = GetWindowLong(hWnd,GWL_WNDPROC);

SetWindowLong(hWnd, GWL_WNDPROC,NewWndProc);

As a convenience to programmers GetClassLong(hWnd, 0) also returns the original Window function for the window, allowing one Window function to subclass all windows created by the terminal manager.

Please note the following two very important points concerning trm_subclass():

  • trm_control_subclass() is only effective for Windows created after the call to trm_control_subclass() is made. Terminal manager windows that already existed before the call are not subclassed.

  • Similarly, if trm_control_subclass(,0) is called to remove subclassing, the subclassing is only removed for Windows created after the call to trm_control_subclass(). The subclassing window function remains in effect for controls that remain on screen. If your subclass function resides in a DLL that may be unloaded (for example inside an OSDI), then your program will terminate with an unrecoverable error if the DLL is unloaded before the windows are destroyed. For this reason it is advisable to ensure that you clear the screen before removing subclassing, for example by calling map_clear_all() followed by trm_refresh().

Return values

See Description above

Example

#include <windows.h>
#include "dp4far.h"
#include "dp4capi.h"
#include "600.h"

long CALLBACK NewWndProc(HWND hWnd, int wMsg,int wParam, long lParam)
{
   printf("subclass %x %x %x %x\r\n",hWnd,sMsg,wParam,lParam);
   return CallWindowProc(GetClassLong(hWnd,0),hWnd,sMsg,wParam,lParam);
}

void process()
{
   char x[8];
   trm_control_subclass(0,(long) caddr(NewWndProc));
   map_load("PROGMAKE");
   map_datamap(505);
   askf_u(505,1,x);
}


trm_ctrls()

Purpose

Allow termination by pressing <Ctrl+S> followed by <Ctrl+C>

Old name

#define _ctrls trm_ctrls

Syntax

void trm_ctrls(void)

Description

The function trm_ctrls() halts the program if the user presses <Ctrl+S> followed by <Ctrl+C>. It must be called repeatedly to have the desired effect.

The functions which update the screen contain calls to the function trm_ctrls(). These functions are useful as a means of halting batch processing.


trm_curs_off()

Purpose | Turns off the cursor
Old name | #define _curs_off trm_curs_off
Syntax | void trm_curs_off(void)
Description | The function trm_curs_off() turns off the cursor. It has no effect on input functions or menu functions, except for trm_grabchr(). Use the utility DFSETUP to turn off the cursor in menus.
See also |


trm_curs_on()

Purpose | Turns on the cursor
Old name | #define _curs_on trm_curs_on
Syntax | void trm_curs_on(void)
Description | The function trm_curs_on() turns on the cursor. It has no effect on input functions or menu functions.
See also |


trm_cursor()

Purpose

Sets the cursor (position for low level output sent to the screen with trm_so()).

Old name

#define _cursor trm_cursor

Syntax

void trm_cursor(row, col)

Parameters

int row

Row to which cursor moves

int col

Column to which cursor moves

Description

The function trm_cursor() moves the cursor to the given position.

The values start from 0, so the call:

trm_cursor(0, 0);

moves the cursor to the top left-hand corner of the screen.

This function has no effect on the positioning of maps. It should not be used in mormal DP4 programs

Example

/*
 C Functions Reference Manual
 ----------------------------
 Filename: TRM_CURS.C
 Example: trm_cursor, trm_curs_on and trm_grabchr
 Purpose: Let's move the cursor to the top left-hand
          corner
*/

#include "dp4.h"
void process()
{
   map_clear_all();
   trm_refresh();
   trm_cursor(0,0);
   trm_curs_on();
   trm_grabchr();
}


trm_get_access()

Purpose

Obtains the user's access level

Old name

#define get_access trm_get_access

Syntax

int trm_get_access(void);

Description

The trm_get_access() function retrieves the user's access level as given in the file USERDATA.SYS. You do not need to include the file USERDATA.SYS to use this function.

The write access is returned in the high-order byte and the read access in the low-order byte.

If the function trm_read_user() has been called, you can directly read the read_access and write_access fields from the user record.

Return values

Returns the access level

See also

trm_read_user()

Example

/*
 C Functions Reference Manual
 ----------------------------
 Filename: TRM_GET_.C
 Example: trm_get_access
 Purpose: Simple program to display the access
          levels
*/

/*
#db salesord
#c
#end
*/

#include "dp4.h"
#include "trm_get_.h"
  /* generate this file by running LIBMAKE */
void process()
{
   int n;
   map_datamap(1);
   n = trm_get_access();
   map_datamap(2);
   show_h(2,1,(n > 8) & 0x00ff);
   /* display write access */
   show_h(2,2,n & 0x00ff);
   /* display read access */
}


trm_get_hwnd()

Purpose

Obtains the handle of the top level window used by the DP4 terminal manager

Syntax

long trm_get_hwnd(void);
long dp4_trm_get_hwnd(void);

Description

The trm_get_hwnd() function returns the handle of the top level Window used by the DP4 terminal manager on Windows or similar GUI based operating systems. The value can safely be cast to type HWND.

Return values

Returns the window handle, or 0 if there is no local top level window because the program is being run using the DP4 terminal server. The return value is undefined if the console terminal manager is being used to run the program

See also

trm_control_subclass()


trm_grabchr()

Purpose

Obtains a character from the keyboard

Old name

#define _grabchr trm_grabchr

Syntax

int trm_grabchr(void);

Description

The trm_grabchr() function waits until a key is pressed.

Return values

The positive ASCII value of the next key is returned if it is a display character, otherwise the negative dp4 value as defined in the file USERDATA.SYS is returned.

From v 4.603 onwards, if the user uses the mouse while trm_grabchr() is waiting for a character, one of the following values is returned:

GR_MDOWN, GR_DRAG, GR_MOUSE or GR_MDBL. Following this the program must make two further calls to this function to flush characters giving information about the mouse's location. These characters cannot be processed by user programs, which should use field_char_get() instead.

See also

askf_x(), inpm_x()

Example

trm_grabchr();


trm_lookchr()

Purpose

Inspects the input buffer

Old name

#define _lookchr trm_lookchr

Syntax

int trm_lookchr(take)

Parameters

int take

Indicates whether character should be removed from the buffer

Description

The trm_lookchr() function returns the next character in the input buffer. If the buffer is empty, GR_NOCHAR or NULL+INT is returned.

If the parameter take is TRUE, the character is removed from the input buffer. Otherwise it remains there and another call to trm_lookchr() returns the same character.

Return values

Returns next character in input buffer

See also

trm_ctrls()


trm_read_user()

Purpose

Reads the user preferences file. You must #include "USERDATA.H" to access the structure .

Old name

#define readuser trm_read_user

Syntax

void trm_read_user(void)
void dp4_trm_read_user(struct USER * user,size_t buffer_size)

Description

The trm_read_user() function reads the user preferences into a C structure area called user. This structure is declared in the header file USERDATA.H. From 4.620 you can read preferences into a variable of your choice by calling dp4_trm_read_user() instead.

Example

trm_read_user();
show_y(1,4,user.type_ahead);


trm_refresh()

Purpose

Refreshes the screen display

Old name

#define refresh trm_refresh

Syntax

void trm_refresh(void);

Description

The trm_refresh() function forces the screen output to be refreshed. The screen display is normally updated when input from the user is requested, because this is more efficient.

This function is commonly used in batch processing where no user input occurs for long periods of time, during which a progress report is being displayed on the screen.

Example

See the example given for tf_write_record() on updating the display during a batch process.


trm_run_program()

Purpose

Runs another program

Old name

#define _run_prog trm_run_program

Syntax

int trm_run_program(const char *command, int batch, int flags);

Parameters

char* name

Name of program to run

int batch

If the program cannot be executed directly and this parameter is TRUE the terminal manager uses the shell to execute the command. On Windows a value of 2 will cause the ShellExecute() function to be used to execute the command. In this case command wil normally just be a document name, and the program to be executed will be selected based on the registry information. This is useful where you want to invoke the user's preferred web browser to open a web page, for example.

int flags

This can contain a combination of the TR_ flags in dp4uiapi.h. Not all the flags are meaningful on all platforms. For example the TR_NOWAIT parameter is not supported on MS-DOS.

Description

The trm_run_program() function is used to run another program.

Return values

Returns TRUE if the named program is successfully called, otherwise FALSE. Exit code information for the called program is not available from trm_run_program(). See also trm_run_program_ex()

Example

/*
 C Functions Reference Manual
 ----------------------------
 Filename: TRM_RUN_.C
 Example: trm_run_program
 Purpose: program uses DOS's copy command to copy a
          file.
*/

#include "dp4.h"
#include "trm_run_.h"
 /* generate this file by running LIBMAKE */

/*
#db salesord
#c
#end
*/

 /* prototypes */
void name_inner(int,int*);
void do_copy(void);
 /* variables */
static char exis_file[65];
static char new_file[65];
void process()
{
   trm_read_user();
   /* only required if using Microsoft C: a bug in
      the linker causes an unresolved external error
      in the DP4 library */
   map_datamap(1);
   /* put up header map */
   map_datamap(2);
   /* put up input map */
   if (map_get_inputs(2,1,name_inner,NO_DISP|RETAIN)
           == 0)
     do_copy();
}

static void zero_term(char* name,int max_len)
{
   register int i = 0;
   while (*name != ' ' && *name != NULL_CHAR &&
        i < max_len)
   {
      i++;
      name++;
   }
   *name = '\0';
}

void do_copy()
{
   char command[156] = "copy ";
   strcat(command,exis_file);
   strcat(command, " ");
   strcat(command,new_file);
   /* move cursor */
   trm_cursor(15,1);
   trm_refresh();
   /* call copy command */
   if (!trm_run_program(command,TRUE,FALSE))
   {
      map_draw(3,SCROLL);
      trm_refresh();
   }
}

void name_inner(int m, int* f)
{
   int k = *f;
   switch (*f)
   {
      case 1: /* file to copy */
        if (inpm_c(exis_file))
          break;
        zero_term(exis_file,sizeof(exis_file) - 1);
        if (tf_check(exis_file) != 1)
        {
           map_remark(5);
           /* put up bad file name remark */
           *f = k;
        }
        break;
      case 2:
        if (inpm_c(new_file))
          break;
        zero_term(new_file,sizeof(new_file) - 1);
        /* go round again if all blanks */
        if (new_file[0] == '\0')
          *f = k;
        break;
      default:
        inpm_omit();
        break;
   }
}


trm_run_program()

trm_run_program_ex()

Purpose | Runs another program
Syntax | int trm_run_program_ex(const char *command, int batch, int flags,int * exit_code);
Parameters | char* name | Name of program to run
 | int batch | If the program cannot be executed directly and this parameter is TRUE the terminal manager uses the shell to execute the command. On Windows a value of 2 will cause the ShellExecute() function to be used to execute the command. In this case command wil normally just be a document name, and the program to be executed will be selected based on the registry information. This is useful where you want to invoke the user's preferred web browser to open a web page, for example.
 | int flags | This can contain a combination of the TR_ flags in dp4uiapi.h. Not all the flags are meaningful on all platforms. For example the TR_NOWAIT parameter is not supported on MS-DOS.
 | int *exit_code | On return contains the exit code of the program that was executed, if available, and -1 otherwise. The exit code is only available on WIN32 platforms, and for programs run with the TR_WAIT flag specified. The exit code is not available if the executed program was a 16 bit Windows program and the machine is running an NT based version of Windows.
Description | The trm_run_program_ex() function is used to run another program where the exit code of the program is required.
Return values | Returns TRUE if the named program is successfully executed, otherwise FALSE.


trm_screen_dump()

Purpose

Performs an ASCII screen dump

Syntax

trm_screen_dump()

Description

This function has a similar effect to pressing the <Print Scrn> key. The current screen textual image is apended to the SCREEN.PRN file. It is saved in an internal format suitable for display by the DP4ED utility.

Note that:

  • Icons, bitmaps and VCR buttons are ignored
  • Radio buttons are drawn as ( ) or (o)
  • Checkboxes are drawn as [ ] or [x]
  • Buttons are drawn as <>
  • Frames and lines are represented by +, - and | characters
  • Text within controls is always left-justified and may overflow the frame

The row and column position of each control is calculated by dividing the pixel position by the width or height of the system font. The result is rounded up or down. The size is calculated similarly, except that the size is always rounded up. One limitation of this function is that where extra spacing is allocated between lines of text - for example in menu maps or in maps containing framed edit controls - you may see a blank line. This is unavoidable if the relative alignment of controls from different maps is to be preserved. Where the row and column position of controls overlap, it is impossible to predict where the control will appear in the dump. This causes maps containing line controls to be dumped incorrectly.

For a WYSIWYG dump, use the Windows Print Screen facilities.


trm_set_preferences()

Purpose | Selects user preferences
Old name | #define set_preferences trm_set_preferences
Syntax | void trm_set_preferences(user_name)
Parameters | char user_name[8] | Name of user for whom to select preferences
Description | The trm_set_preferences() function selects the preferences of the specified user. It is called by the %logon function of the DP4 program and QA Build. It takes the following actions: 1. The corresponding menu user record from the SYSTEM database is read 2. An appropriate MENUDATA.SYS is created 3. The flag user.menu_running is set 4. The user name and access levels are filled in 5. The preferences are loaded so that the appropriate attributes (for example, colours) are used
See also | trm_unset_preferences()


trm_set_progname()

Purpose | Windows only, sets the current program
Syntax | void trm_set_progname(progname)
Parameters | char* progname | Program name to set as current program
Description | The trm_set_progname() function sets the specified program as the current program for the terminal manager (TRM<n>). This function is only relevant in Windows, where it causes the appropriate settings in the DP4 configuration file to be read.


trm_set_training_mode()

Purpose

Toggles the training mode operation

Old name

#define training_mode trm_set_training_mode

Syntax

void trm_set_training_mode(flag)

Parameters

int flag

Used to describe mode

Description

The trm_set_training_mode() function is used to switch on or off the flag which determines whether help messages should appear. The help key, normally <F10>, toggles between modes.

You can specify that all programs start in training mode with DFSETUP, or use this function in selected programs.

See also

field_help()

Example

trm_set_training_mode(TRUE);


trm_so()

Purpose

Sends a string to the console

Old name

#define _so trm_so

Syntax

trm_so(string)

Parameters

char* string

Null-terminated string to send to console

Description

The trm_so() function sends a string of characters to the screen. This function is normally only used where maps are not available, (for example it is used by sysdb and userdata), and is occassionally useful for producing diagnostics. You cannot send control characters,including new lines, to the screen with this function - you must use trm_co() instead. The effect produced by mixing calls to trm_so() and trm_co() with calls to higher level functions such as map_draw() and show() is somewhat dependent on the version of DP4 in use.

See also

trm_co(), field_read()

Example

/*
 C Functions Reference Manual
 ----------------------------
 Filename: TRM_SO.C
 Example: trm_so and trm_co
 Purpose: illustrates low level display and
          input functions trm_so and trm_co
*/

#include "dp4.h"
#define HELP_LINES 2
static char* help_mess[HELP_LINES] =
{
   "This message has been displayed with trm_so",
   "and the newline after each line it with trm_co"
};
void process()
{
   int i;
   for (i = 0; i HELP_LINES; i++)
   {
      trm_so(help_mess[i]);
      trm_co('\n');
   }
}


trm_unset_preferences()

Purpose | Cancels preferences of currently logged on user
Old name | #define unset_preferences trm_unset_preferences
Syntax | void trm_unset_preferences(void);
Description | The trm_unset_preferences() function cancels the preferences of the currently logged on user by deleting the file MENUDATA.SYS.
See also | trm_set_preferences()


trm_write_syslog()

Purpose | Writes a string to the system log
Old name | #define write_system_log trm_write_syslog
Syntax | void trm_write_syslog(string)
Parameters | char* string | Null-terminated string to write to system log
Description | The trm_write_syslog() function writes a string to the system log file SYSLOG.PRN, if logging has been enabled. It is prefixed by date, time and access levels


Functions W-Z

• wkf_fetch()
• wkf_open()
• wkf_post()


wkf_fetch()

Purpose

Fetches a record from the workfile

Old name

#define fetch_workfile(index,data) wkf_fetch(data,index)

Syntax

void wkf_fetch(dp4rec,index)

Parameters

short* dp4rec

Pointer to the L field of record to hold record

int index

Index of record in work file to be fetched

Description

The wkf_fetch() function reads a record from the workfile which has been previously posted using the function wkf_post(). The position of the records in the workfile is determined by the index, which starts from 0. This function reads the record by calling the functions tf_seek() and tf_read_record(). The read operation is fast with these functions.

Note that if a call to wkf_fetch() is not successful, an error message is generated with the function sys_error().

See also

wkf_open(), wkf_post()

Example

See example for wkf_open()


wkf_open()

Purpose

Opens a workfile

Old name

#define open_workfile wkf_open

Syntax

void wkf_open(dp4rec)

Parameters

short* dp4rec

Pointer to DP4 record indicating type and size of records to be used

Description

The wkf_open() function opens a workfile for use in storing and reading database records with the functions wkf_post() and wkf_fetch().

The parameter dp4rec points to a record indicating, in the TYPENR and L fields, the table number and size of each record to be stored in the file. The record is not stored in the file.

You can only use records from the same table in a workfile. The records posted must have a size less than or equal to the size of the record pointed to by the parameter dp4rec.

A maximum of 10 of these workfiles can be used at once, using the type number of the table to distinguish between them.

You store and access records in the file through an index which has no connection with the key fields of the record.

See also

wkf_post(), wkf_fetch(), mem_describe()

Example

/*
 C Functions Reference Manual
 ----------------------------
 Filename: WKF_OPEN.C
 Example: wkf_open
 Purpose: Program to fetch all the customer records
          and put them in a work file. Records are
          then fetched again from the workfile and
          displayed
*/

/*
#db salesord
#c
#tables customer;
#end
*/

#include "dp4.h"
#include "wkf_open.h"
  /* generate this file by running LIBMAKE */

void process()
{
   int nr_custs = 0;
   int srch = FIRST;
   tf_read_sysdata();
   /* only required if using Microsoft C: a bug in
      the linker causes an unresolved external error
      in the DP4 library */
   map_draw(1,RETAIN);
   wkf_open(&customer.l);
   /* post the records to the work file */
   while (rec_fetch_main(srch,&customer.l))
   {
      srch = NEXT;
      wkf_post(&customer.l,nr_custs++);
   }
   /* and fetch and display them */
   {
      int i;
      for (i = 0; i nr_custs; i++)
      {
         wkf_fetch(&customer.l,i);
         map_datamap(2);
         show_c(2,1,customer.name);
         show_n(2,2,customer.customer_number);
         show_c(2,3,customer.address[0]);
         if (askf_special(2,4,0))
           break;
      }
   }
}


wkf_post()

Purpose

Posts record to the workfile

Old name

#define post_workfile(index,data) wkf_post(data,index)

Syntax

void wkf_post(dp4rec, index)

Parameters

short* dp4rec

Pointer to the L field of the DP4 record to post to the workfile for its table

int index

Index giving order in file to write record

Description

The wkf_post() function posts a data record to the workfile previously set up with a call to the wkf_open() function.

The record's table must correspond to that passed to the call to wkf_open(). The index indicates where the record is to be written. The exact position is given by:

index * (*dp4rec)

where (*dp4rec) is the value in the L field. This means that the index starts from zero. This index is needed to retrieve the file with a subsequent call to the wkf_fetch() function.

Do not post records with an index until all indexes less than it have been used in previous calls to wkf_post().

See also

wkf_open(), wkf_fetch()

Example

See the example for wkf_open()


Error handling in DP4 Programs

The following notes apply in detail only to application programs created using the DP4 C OWN libraries, though some of the information may be of interest to users of the dp4dbapi.h interface and dp4dbase C++ encapsulation of it. Special considerations apply to ADC and NOTRM programs, which are noted from time to time.

Introduction

Whenever a program calls a function to perform some operation, potentially the function may not be able to complete the operation normally for some reason. In this situation it may do one of two things:

  • It may simply return some value to the caller which allows the caller to discover that something has gone wrong, expecting it to take appropriate remedial action.

  • It may raise some kind of exception which calls some special processing to remedy the problem,or which terminates the program to prevent a snowball effect where the error is propogated through the program with increasing severity.

The DP4 C library has traditionally favoured the second approach as it makes for simpler programming, and reduces the likelihood of a serious error condition's being inadvertently ignored. However this has left little scope for error handling beyond generating an error message and terminating the program, which in some situations is not acceptable.

How errors are raised in DP4

In the majority of cases DP4 functions raise errors by calling one of two error functions: sys_error(),and sys_fail(), which generate an error message and then call dp4_halt() to terminate the program.

Only functions which use the database manager or terminal manager generate calls to sys_error() or sys_fail(). Therefore none of the functions listed in dp4capi.h in the section devoted to functions that do not require SRVn or TRMn generate system or fail errors.

In the past a number of DP4 users tried to continue after errors by replacing the code of sys_error() and sys_fail() with functions which do not terminate, or (in more recent times) by writing an app_close() function which stops dp4_halt() from terminating the program.

Unfortunately this could lead to situations where programs have then failed catastrophically or gone into a tight loop, because DP4 library code was written with the expectation that sys_error() and sys_fail() would never return control to the caller.

In 4.523/4.6xx and later releases the sys_error(), sys_fail(), sys_message() and sys_request() functions all proceed via a new function, dp4_error(), which calls a user supplied function app_error() which can be used to intercept the error,allowing complete control over the generation and handling of error messages. Also library code does not rely on calls to DP4 error functions terminating the program.

The DP4 Quick Reference error documentation describes for each system and FAIL error, whether or not it is safe to try to recover from the error, and attempts to analyse on a function by function basis, (or error by error basis for errors which can be generated by a large number of functions), what modifications to the standard error handling are permitted. The next section discusses other types of error which can arise when DP4 functions are called and which are not handled by calls to sys_error() or sys_fail().

Errors not handled by calls to sys_error() and sys_fail()

  1. Parameter Errors

    In general DP4 functions do not make extensive checks on the validity of their parameters. Parameters are checked in situations where they could upset another program, so for example the length of records is checked on calls to the database manager.

    Passing an invalid pointer or a pointer to the wrong type of object is likely to cause some kind of protection violation. Under MS-DOS it is likely to cause disaster. (Meaning the kind of crash which can only be recovered from by physically switching machine off and on)

  2. Stack faults

    The DP4 C library functions do not use a substantial amount of stack and legacy versions are therefore generally built with compiler stack checking turned off. A stack overflow will therefore generally cause a protection fault or disaster under DOS.

  3. Heap Functions

    There are two types of error: memory exhaustion and heap corruption. Memory exhaustion is handled by calling the function pointed to by mallocx_handler() and/or returning 0.

    Heap corruption is not detected by the standard version of the DP4 heap functions. By compiling the module ccnheap1.c and cclru.c with DEBUG defined you can enable heap corruption to be detected. However this should only be needed at development time, as the heap can only be corrupted by programs containing bugs which cause memory corruption. Detection of heap corruption is quite expensive in terms of execution time.

  4. LRU functions

    The only type of error here is where the LRU chain becomes corrupt. Again this is only detected by linking in a version of cclru.c compiled with DEBUG defined.

  5. Errors from OSDI

    Errors from an OSDI are handled by calling the function dvc_check(). The standard version of this function generated sys_error(129). However typically this function is replaced by a custom version. In QAB the custom version typically uses hot key processing to handle such errors.

The sys_error(),sys_fail() and dp4_halt() error functions

Further details of exactly how and when calls to DP4 error functions are generated are contained in How calls to DP4 Error Functions are generated.

The following sections describes the implementation of the sys_error(),sys_fail() and dp4_halt() functions. The details of these functions depend considerably on the release of DP4 being used and also the type of program being written.

  1. Standard DP4 C programs
  2. NOTRM Programs
  3. ADCs


How Calls to DP4 Error Functions are Generated

Calls to the database manager and to the terminal manager are made using a parameter block (SRV_PARA or TRM_PARA in dp4ci.h). If an error is detected, on return the field para.error will be set to a non zero value and various other fields are set to give more information about the error. para.error is where the error level passed to dp4_halt() comes from.

  • If the error is a system error and SRVn was called para.error==3 and sys_error() is called.

  • If the error is a fail error and SRVN was called para.error==4 and sys_fail() is called.

  • If para.error==2 then dp4_error() or dp4_halt() are called depending on further information in the parameter block.
    para.error ==2 is generated from calls to TRM when the user indicates that he wished to terminate the program (dp4_halt() is called), or when a system or fail error has been processed by the terminal manager (dp4_error() is called) The parameters passed to dp4_error() give the details of the error that arose.

  • If para.error==1 then sys_message() is called. However this facility is never used by the current database manager or by TRMn

  • When calls to TRMn or SRVn succeed para.error==0.

  • If para.error has any other value then the call has failed catastrophically probably because SRVn or TRMn are not running or the channel to them is broken. In very old DP4 releases this situation depended on para.error being uninitialised. Post release 4.516 para.error is initialised to 5. If on return para.error is outside range 0 to 4 then if the call was into SRVn sys_fail(9) is generated. (In 4.515 and before a call to sys_fail() was generated using the error information supposedly returned from SRVn. This was obviously garbage and hence caused completely spurious fail messages to be generated when the channel to SRVn was broken.)

    If para.error is outside range 0 to 4 after a call to to TRMn then dp4_halt() is called immediately, with parameter 5, (there is no point trying to call sys_fail() as this function itself needs the terminal manager).

Attentive readers will have noted that the generation of calls to sys_error() and sys_fail() through para.error being 3 or 4 was described only in connection with SRVn. This is because traditionally TRM generates calls to sys_error() and sys_fail() internally and then returns with para.error 2, so that when control returns to the application an error message has already been produced.

With 4.6xx/4.523 you can optionally force the terminal manager to return error information in the same way as for calls to the database manager. This is achived by setting the high order bit of df_libtype as follows:

df_libtype |= 0x8000;

This is not recommended because in some situations it is difficult for TRMn to return the information needed for the call to the error function. Additionally TRMn always returns to the point at which errors were generated, and does not longjmp(), so treating them in the same way as SRVn might potentially introduce problems with errors never actually getting reported.


Error functions in standard DP4 C Programs

Error functions in Standard C programs pre 4.523/4.6xx

In the standard DP4 C library sys_error() and sys_fail() are functions which call the terminal manager to do their work. On return from the terminal manager para.error is 2 as described earlier, so dp4_halt() will be called with parameter 3 or 4.

dp4_halt() calls the user supplied functions app_close() and finish(). These may themselves generate calls to sys_error() or sys_fail() resulting in a recursive call to dp4_halt(). This situation is guarded against by dp4_halt() which does nothing if it is invoked again before the first invocation has completed.

The DP4 termination functions such as trm_end_trm() and srv_disable() are supposed never to generate any errors. Their doing so has been a frequent source of recursive errors in older releases of DP4. Now errors from these functions have been disabled in the library itself. Previously it was up to the interface programs (SRVn TRMn NTBn ADCm etc) to do the right thing.

Error functions in Standard C programs in 4.6xx and 4.523 or later

All the sys_() functions used to generate error messages and terminate a program abnormally make calls to the new dp4_error() function as follows:

void cdecl sys_error(int message_nr,...)
{
  dp4_error(DP4ERR_REPORT|DP4ERR_HALT,3,message_nr,(&message_nr)[1],(&message_nr)[2]);
}

int pascal sys_fail(error_nr,os_error,extra_error)
{
  dp4_error(DP4ERR_REPORT|DP4ERR_HALT,4,error_nr,os_error,extra_error);
  return -error_nr; /*Only gets here if program prevents termination*/
}

void cdecl sys_message(int message_nr,...)
{
  dp4_error(DP4ERR_REPORT,1,message_nr,(&message_nr)[1],(&message_nr)[2]);
}

void cdecl sys_request(int message_nr,...)
{
  dp4_error(DP4ERR_REPORT,2,message_nr,(&message_nr)[1],(&message_nr)[2]);
}

The code of the dp4_error() function is as follows:

void pascal dp4_error(int flags,int type,int message_nr,int param1,int param2)
{
  app_error(&flags,&type,&message_nr,&param1,&param2);
  if (flags & DP4ERR_REPORT && type >= 0 && type <= 4)
  {
    /* Uses private functions from terminal manager to produce the error message */
  }
  if (flags & DP4ERR_HALT)
    dp4_halt(type);
}

Note that before displaying an error message or terminating the program the programmer-supplied app_error() function is called. If a program wishes to modify the behaviour of dp4_error() it can do so by modifying the parameters in a custom app_error().

If the terminal manager causes a sys_error() or sys_fail() then unless the high order bit of libtype is set it will generate the error message itself immediately. In this case dp4_error() is called with the DPERR_REPORT flag unset. The error information relates to the error message which was produced.


Error functions in NOTRM Programs

The code for notrm.c varies according to the release of DP4 used.

4.523 and prior notrm.c

If a program is linked with notrm.c (supplied in source form) then replacement sys_error() and sys_fail() functions make calls to a routine called dp4_err(), which the program must supply. dp4_err() is passed 3 or 4 to indicate the error type, followed by the parameters passed to sys_error() or sys_fail().

On return a replacement dp4_halt() function is called, unless the code was compiled with BROKEN defined, in which case the DBMS is completely disabled and the routines return normally. (This is probably not a very good idea)

With notrm as supplied there is no call to an app_close() function from dp4_halt(), but you can add one if you require it.

For release 4.523 notrm.c should have been replaced with the 4.6xx version. Unfortunately this was overlooked and notrm.c was not updated. If you are using release 4.523, you can replace notrm.c with the version supplied for 4.6xx releases

4.6xx notrm.c

From 4.524/4.6xx the notrm.c code contains a replacement dp4_error() function which maintains compatibility both with pre-existing notrm.c programs and with programs previously written to use the terminal manager. sys_error() and sys_fail() do not need to be replaced.

dp4_error() will call app_error(), and then call the programmer supplied dp4_err() to display the error message if required.

On return from dp4_err() and if the DP4ERR_HALT flag is set the dp4_halt() function is called, unless the code was compiled with BROKEN defined, in which case the DBMS is completely disabled and the routines return normally.


Error functions in ADCs

Within an ADC sys_error(),sys_fail() and dp4_halt() are replaced by calls which set information in the parameter block and do a long jump to get out of the ADC and return to the calling program.

You therefore need to consider the following points:

  1. You should make sure that if your ADC calls DP4 C library functions which could cause a system error to be generated, that these calls are not made in places where jumping out of your current context could leave a mess (for example linked lists which are in the middle of being updated).

  2. If you write a multi-threaded ADC or have some other pathway into your ADC you run the risk of getting into a state where a sys_error() causes a long jump into a destroyed stack context.

DP4 Networking program and SRVn itself replace sys_error() in a similar way, and have had bugs of these types in the past.


Appendix 1: Renamed System Variables

This section is not an exhaustive list of all the system variables that existed in pre-4.500 versions. It lists only those system variables which have been renamed in 4.500. Each index entry comprises the pre-4.500 name followed by the 4.500 name. If you cannot locate a system variable in this index, it probably means that its name has remained the same in 4.500. If it has, you can locate it using the 4.500 index of system variables .

Old Name | 4.500 Name | Old Name | 4.500 Name
abort | abort_code | _dbnr | db_nr
dbname | db_name | _fieldnr | input_field
failure | fail_code | _generation | db_generation
libtype | df_libtype | _l_dvc_nr | pr_sub_device
linenr | pr_line | _map_dbnr | map_db_nr
mainpass | realpass | _map_name | map_name
now | app_start_time | _mapnr | input_map
op_device | pr_streams | _more | pk_more_choices
pagedepth | page_depth | _nr_system | app_nr_system
pagewidth | page_width | _option | pk_chosen_field
print_nr | pr_device | _os_version | os_number
today | app_start_date | _print_started | pr_started
_any_option | choose_any_option | _procnr | df_process
_count | pk_range | _rw_access | app_rw_access
_db_flags | db_open_flags | _tailc | app_tailc
_db_is_open | db_is_open | _tailv | app_tailv


Appendix 2: Renamed Functions

This section is not an exhaustive list of all the functions that existed in pre-4.500 versions. It lists only those functions which have been renamed in or since 4.500. Each index entry comprises the old name followed by the current name. If you cannot locate a function in this index, probably its name has remained unchanged. If so, you can locate it using the index of functions.

Old name | 4.500 name | Old name | 4.500 name
abort_print() | prn_abort() | secure_db() | db_update()
actionbar() | map_bar_choose() | select() | askm_special()
activate() | map_activate() | set_preferences() | trm_set_preferences()
ask() | map_askf_y() | set_scroll_end() | map_set_scroll_end()
ask_output_device() | dvc_choose() | show_attach_map() | show_attach()
attach_map() | map_attach() | showx() | show_x()
autoinc() | rec_autoinc() | showextra() | show_extra()
blank() | field_blank() | showmore() | show_more()
change_language() | map_switch_language() | showover() | show_over()
check_printer() | dvc_check() | show_object() | show_graphic()
checkpoint() | db_checkpoint() | skip() | inpm_omit()
choose() | field_choose() | skip_field() | field_skip()
clearbelow() | map_clear_after() | sort_extract() | extract_sort()
clearmap() | map_clear_from() | subdevice() | dvc_set_subdevice()
clearremarks() | map_clear_remarks() | switch_language() | map_switch_language()
clearscreen() | map_clear_all() | training_mode() | trm_set_training_mode()
close_extract() | extract_close() | trunc_number() | tf_trunc_number()
colour_field() | field_colour() | unset_preferences() | trm_unset_preferences()
commit() | db_commit() | upd_confirm() | sys_upd_confirm()
commit_or_fail() | db_update() | update_db() | db_update()
confirm() | sys_del_confirm() | use_map() | map_use()
datamap() | map_datamap() | valid_date() | date_ok()
date_minus_date() | date_subtract_date() | verify() | rec_verify()
date_plus_number() | date_add_number() | wait_field() | askf_special()
dbclose() | db_close() | wait_map() | askm_special()
dbopen() | db_open() | write_extract() | extract_write()
decommit() | db_decommit() | write_system_log() | trm_write_syslog()
dvc_redirect() | dvc_set_inp_mask() | yes() | map_yes()
dvc_source() | dvc_get_inp_source() | _beep() | trm_beep()
dvc_status() | dvc_get_status() | _clr_ahead() | trm_clr_ahead()
eg_choose() | field_eg_choose() | _co() | trm_co()
end_print() | dvc_close() | _ctrls() | trm_ctrls()
fetch() | rec_fetch() | _curs_off() | trm_curs_off()
fetch_workfile() | wkf_fetch() | _curs_on() | trm_curs_on()
force_colour() | field_force_colour() | _cursor() | trm_cursor()
force_range() | field_force_range() | _dfclose() | tf_txt_close()
force_scrn() | field_force_size() | _dfeofile() | tf_txt_seek_end()
formover() | form_over() | _dfrdln() | tf_txt_read()
ftchprm() | rec_fetch_main() | _dfwtln() | tf_txt_write()
get_access() | trm_get_access() | _get_date() | tf_get_date_time()
get_db_flag() | db_get_flag() | _grabchr() | trm_grabchr()
get_generation() | db_get_generation() | _halt() | dp4_halt()
get_inputs() | map_get_inputs() | _lcase() | str_lcase()
get_status() | db_get_status() | _lookchr() | trm_lookchr()
glue_map() | map_glue() | _nmfl() | df_make_name()
gm_choose() | map_g_choose() | _prn_append() | prn_append()
halt() | dp4_halt() | _prn_length() | prn_length()
help_message() | field_help() | _refresh() | trm_refresh()
help_text() | field_help() | _run_prog() | trm_run_program()
hidemap() | map_hide() | _set_date() | tf_set_date_time()
highlight_field() | field_highlight() | _show_length() | field_map_length()
kill() | rec_kill() | _so() | trm_so
items_to_scroll() | map_items_to_scroll() | _sys_error() | sys_error()
lineup() | form_test() | _sys_message() | sys_message()
loadmaps() | map_load() | _sys_request() | sys_request()
map() | map_draw() | _tail() | tail_read()
map_position() | map_new_position() | _tail_opt() | tail_get_option()
mapchange() | map_change() | _ucase() | str_ucase()
mapover() | map_draw_over() | _vfail() | sys_fail()
mapsize() | map_get_depth() | _aset_date() | sf_set_date_time()
mapwidth() | map_get_width() | _afdefault() | sf_default()
menu() | map_menu() | _afcheck() | sf_check()
na_skip() | inpm_ban() | _afopen() | sf_open()
ncopy2child() | rec_child_copy() | _adelete() | sf_delete()
ncopy2parent() | rec_parent_copy() | _afsize() | sf_get_size()
new_window() | map_new_window() | _afposn() | sf_get_position()
number_2_date() | number_to_date() | _afseek() | sf_seek()
old_map_position() | map_old_position() | _anread() | sf_read()
old_window() | map_old_window() | _aread() | sf_read_record()
omit() | field_skip() | _anwrite() | sf_write()
open_extract() | extract_open() | _awrite() | sf_write_record()
open_workfile() | wkf_open() | _aflush() | sf_flush()
pause() | askf_special() | _aclose() | sf_close()
pick_fill() | pick_record() | _adspace() | sf_space()
pick_optlist() | pick_list() | _arename() | sf_rename()
post() | rec_post_with_flags() | _adskrst() | sf_disk_reset()
post_workfile() | wkf_post() | _amem_describe() | srv_mem_describe()
postcommit() | rec_post_commit() | _amem_find() | srv_mem_find()
predisplay() | map_predisplay() | _amem_post() | srv_mem_post()
printer() | dvc_open() | _vfdefault() | tf_default()
print_it() | prn_print() | _vfcheck() | tf_check()
prnover() | prn_over() | _vfopen() | tf_open()
rawkill() | rec_kill()_ignore | _vdelete() | tf_delete()
rawpost() | rec_post() | _vfsize() | tf_get_size()
read_extract() | extract_read() | _vfposn() | tf_get_position()
read_srv_sysdata() | sf_read_sysdata() | _vfseek() | tf_seek()
readsysdata() | tf_read_sysdata() | _vnread() | tf_read()
readuser() | trm_read_user() | _vread() | tf_read_record()
refresh() | trm_refresh() | _vnwrite() | tf_write()
remark() | map_remark() | _vwrite() | tf_write_record()
retrieve() | rec_retrieve() | _vflush() | tf_flush()
revealmap() | map_reveal() | _vclose() | tf_close()
round_number() | tf_round_number() | _vdspace() | tf_space()
scrn_field() | askf() | _vrename() | tf_rename()
scrn_password() | askf_password() | _vdskrst() | tf_disk_reset()
scrnx_field() | askf_x() |  |


The SALESORD Database

The examples in this help file are based upon the dealings of a fictitious company, Acme Sales Order Plc, which sells a number of products throughout the United Kingdom. An example database called SALESORD is supplied with this help system for you to develop and run sample DP4 C programs. SALESORD stores information on Acme employees, customers, orders and supplier's materials.

SALESORD Entity Relationship Diagram

The working example programs in this manual are supplied, on the same disk as the SALESORD database, in source code (.C), header (.H) and executable (.EXE) file formats. All the source code files include a header comment, for instance:

/*
C Functions Reference Manual
----------------------------
Filename: MAP_GET_.C
Example: map_get_inputs
Purpose: Simple data entry for customers.
*/

This tells you:

  • The name of the file that contains the program. This is the function name truncated to eight characters with a ".C" extension. For example, MAP_GET.C is the file provided for the map_get_inputs() function
  • The name of the function being illustrated
  • The purpose of the function

The SALESORD database contains the mapsets for the examples. These follow the normal DP4 naming convention. That is, they have the same names as their programs. Each example includes a header file that defines the structures required for accessing and processing the SALESORD tables as well as the DP4 initialisation/termination functions. If necessary, the header files can be recreated by running the DP4 utility LIBMAKE against the source files.


Reading Updated Data

What happens if a program reads a record from the database that has been updated but for which the transaction has not yet been committed?

In the first place, unless the program itself updated the record, the change is completely invisible. No program can see another program's updates until they have been committed.

Whether a program sees its own updates depends on how the database is opened, the flags passed to the functions that performed the update, and the flags passed to the fetch() function.

Reading Updated Records when the Private Database is Enabled

If the database is opened with the PRIVATE_DB flags, in general the program will see the database as though its current transaction had just been successfully committed. For example if a program deleted an order header with the CHILD_DELETE flag, and then tried to fetch the order lines for the same order, they would appear not to exist.

Implementing the private database causes the database manager to have to do a lot of extra work. For typical transaction sizes this is not a problem, but if your program contains very large transactions (more than several hundred calls to update functions like rec_post() without a commit) the degradation can become severe. You can cure this to some extent by opening the database with the BIG_TRANSACTIONS flag. The extra work needed is more for calls to fetch() functions that do not use the EQUAL search method and the main index.Only use this flag when necessary as it will degrade performance of typically sized transactions slightly.

You can always see the real state of the database when a fetch() function is called. If you don't want to see your updates, pass the NO_UPDATE flag along with the other flags such as EQUAL or NEXT. Passing this flag can improve performance even if it would not actually affect the result of the fetch(). For example if a program is going through a table using a fetch(NEXT) type search, and posting updated versions of the records as it goes along, the calls to fetch(NEXT) will be slowed down significantly because the database manager has to check the post file (the file used to queue the pending transaction) in case an earlier call to rec_post() affects the result of the fetch(NEXT). Of course your changes probably don't affect the record to be fetched at all, but the database manager does not know this (you might after all be doing something bizarre such as posting the record back with a higher key value). Passing the NO_UPDATE flag to tell the database manager it does not need to check the post file will improve performance.

Another similar situation arises when rec_kill() is called with the CHILD_DELETE flag. In this case when the private database is enabled, the database manager has to discover all the records that will be deleted, and record them as being deleted in the post file, in case a later call to fetch() is made for one of the affected records. If you know your program will not do such a fetch you can specify the NO_UPD_CHILD flag and the rec_kill() will be much faster.

Reading Updates without the Private Database facility

If you do not specify the PRIVATE_DB flag when the database is opened, all calls to rec_kill() will function as though NO_UPD_CHILD was specified, so children that will be deleted will be visible. All calls to fetch() that do not use the EQUAL flag will behave as though NO_UPDATE were specified. If EQUAL is specified the fetch() will usually behave as it would with the private database, in particular it will do so if the main index is used. If a secondary index is specified with EQUAL, the result is undefined : the updated record may or may not be returned. Currently the updated record is returned unless the index is not marked for update. For example using the ORDER_HEADER index to access the ORDER_LINE table would NOT return the updated record, because the ORDER_HEADER index is not maintained, but is replaced by the main index. Using the main index directly the updated record would be returned!

Advice on Using the Private Database

Interactive programs should normally use the private database facility, to avoid the subtle bugs that may otherwise occur if a program unknowingly updates the same record twice in one transaction.

If possible batch update programs should avoid using the private database. If the private database is required, then where possible use the NO_UPD_CHILD flag on calls to rec_kill() that specify CHILD_DELETE and the NO_DATA flag on calls to fetch() on tables that may have been updated.

Try to design your programs so that they do not need excessively large transactions.


Using the Prompt Facility

You can offer the user the <Ctrl+P> facility by adding the flag PROMPT_AVAILABLE to scrn_flags or scrnd_flags. If the user presses the <Ctrl+P> keys together when an inpm_x function is attempting to obtain data from a screen, the function returns a value of 0, but the abort_code is set to the value SPACE_BAR.

Note
SPACE_BAR is a symbolic constant supplied by DP4.

This facility usually enables the user to obtain useful information, for example, a list of available data or options.


dp4capi.h

The main header file that all DP4 C programs must include to use the functions described in this manual. For many DP4 programs you only need include this file. dp4capi.h is a new file that contains prototypes for all the functions contained in the C libraries, apart from the interface between the interface libraries and the manager programs, which is prototyped in dp4ci.h and interfce.h.

Unlike the old datafit.h, dp4capi.h is not a monolithic file, but includes a number of other header files:

  • dp4cc.h is a small header file used to deal with differences between the various C compilers supported.
  • dp4port.h is a header file used to deal with differences in processor archictecture on the various machines where DP4 is supported.
  • dp4macro.h contains "useful" function type macros such as WIPE() and ZERO(). Although these may seem too trivial to bother with, in the past many lazy or sloppy programmers have fallen in the trap of passing hard coded values to the lenghh parameters of functions like memset(), with disastrous consequences when their database has been changed later on.
  • dp4types.h defines basic types used by DP4, such as the DP4 dates and times
  • dp4dbflg.h defines the symbolic constants such as EQUAL,CHILD_DELETE used by the DP4 database functions. If you are used to using the DP4DYN functions you may want to include the file dp4flags.h as well. This defines versions of these flags with names prefixed by DP4
  • dp4uiapi.h defines the functions and constants used by the DP4 terminal interface. These functions are wrapped to a greater or lesser extent in the DP4 C library. In 4.620 the wrapped functions are contained in dp4clibt.dll or its equivalent on other platforms.

The dp4capi.h prototypes are given with parameter names. This makes dp4capi.h a quick, online reference to functions. In particular, useful information about return values can be obtained:

  • If a function returns 0 for 'normal' completion and other values for exception conditions, it is prototyped as returning int
  • If a function returns 1 for success and 0 for failure, it is prototyped as BOOLEAN. Such functions only return 0 or 1

dp4capi.h includes the standard C file STRING.H. If you are writing operating-system independent programs, you should not need to use many ANSI C functions. On legacy platforms DP4 supports object code portability - single binaries that can be made to run on almost any 16 bit operating system. For source code portablity only it is acceptable to use any ANSI C function, though using some functions may restrict the ways in which your program can run to some extent (e.g. using printf() to display information will compromise language independence and stop your program working properly with the DP4 Terminal Server)


Field Navigation during Data Entry

The exact behaviour of the DP4 input functions as the user moves between fields in data entry maps is complex. (In menu maps the behaviour is straight forward - the field number is always immediately set to the user's intended field immediately.) The following explanation applies whether the inpm_x() or the older scrnx_map() functions are in use. First the big picture:

Whenever the user attempts to jump forwards in a dialog the intermediate fields are executed with jump TRUE. Calls to the inpm_x() functions just update the displayed values and return 0 indicating validation is needed, so if a field contains an invalid value, the program will trap the value, set jump FALSE and set the field number back to the field requiring correction, and data entry will continue normally from the problem field. It is impossible to reach a field without performing all the validations associated with the earlier fields.

Detailed Behaviour

If the user enters data in the normal way or the field was executed with jump TRUE the field number is set to the next input field, and abort_code to 0. If range is non zero then range is increased to the next field number if it is not already larger than this. (After the last field when field number is set to zero range is still increased as though there were another field. This is to avoid problems with repeated clearfirsting on the last input field)

Depending on the userdata.sys settings, and the context, pressing either the Enter or the Tab key may be considered to represent a request to complete the dialog. The variable jump is set to TRUE in this case to cause remaining entry fields to be skipped but validated. After the last field is processed field number is set to 0 to indicate the dialog has been completed. Clicking an OK button (a button mapped to the Enter key) is always treated as a request to complete the dialog, regardless of the effect that actually pressing Enter would have. If SAA or Windows behaviour is not selected Enter on the last field completes a dialog, even though it would not if there were another field. This is the reason for providing the ASK_CONFIRM flag in map_get_inputs(), and why it is not used when SAA or Windows behaviour is selected.

If the user uses the cursor left, cursor up or home keys to leave the field (or any input, including mouse, which means move to an earlier field) the field number is set to the requested field immediately.

If the user uses the cursor right,cursor down or end keys to leave the field (or any input which means move to a later field) the terminal manager discovers and remembers the field being aimed at, sets jump TRUE and returns a non zero value leaving the field number unchanged. Intervening fields are executed with jump TRUE. Assuming jump is not set FALSE explicitly by the program during the intervening processing, then after the field prior to the field being aimed at is executed jump is set FALSE once again. To the user it will appear that the cursor has jumped directly to the target field. If jump is set FALSE before the target is reached then the terminal manager forgets that a target was being aimed at and continues normally.

This scheme is complicated by the clearfirst variable. Setting clearfirst to 1 has two distinct effects:

  • It causes jump to be set FALSE at the start of a call to a field input function.

  • It causes the value in the field to be set to the default value for the field if the mapfield is a clearfirst field and range is zero or less than or equal to the current field number (indicating that this is the first time the user has reached this field)

The first of these effects would stop the method being described for navigating through entry fields working whenever clearfirst was set to TRUE. To get round this the terminal manager is forced to save and restore clearfirst, (it is saved at the time jump is set TRUE, and restored when it is set FALSE again: if the program sets jump FALSE, rather than the terminal manager it is restored at the next call to a field input function). However this first feature of clearfirst is in any case undesirable as it forces the user to enter a value into every field in the dialog. If clearfirst is required it should be set to 2: this has the same data defaulting effects of clearfirst = TRUE,but jump is unaffected (as for clearfirst = FALSE). Thus for clearfirst =2 the value of clearfirst is never changed by the terminal manager.

Buttons

The explanation above is slightly inaccurate in one respect: if the user navigates away from a field and the target field is a button field, the terminal manager immediately activates the button, but without returning control to the calling program first. Control is only returned to the program when the user navigates away from the button to an "ordinary" field, and then the navigation is handled as though the user had navigated from the original input field. If the button is pressed, and it represents a "Special Key" control is returned to the program as though the user had navigated away from the original input field with that key. For example clicking on a Cancel button, will cause inpm_x() to return ESCAPE.

Omitted Fields

Your program may need to disable or skip over some of the entry fields some of the time. This is done by falling the inpm_omit() or inpm_ban() functions. These functions are designed to cope with the situation that arises when a field that is navigated to turns out not to be used for data entry. In this case the terminal manager selects a new target field and navigates to that in the same way as has already been described. How the field is chosen depends on how the original navigation was done. For example if the user navigated with the right arrow key, then DP4 navigates to the next field on the right again.

The exact details of this can get extremely complex, and there is no need to know them. However you do need to be aware of one very important rule:

An input field must NEVER be disabled as a result of entering data into it. For example you should never have logic like this:

case 1:
  {
    static int first_time = TRUE;
    if (first_time)
    {
      inpm_x(&some_field);
      first_time = FALSE;
    }
    else
       inpm_omit();
}

For certain special keys, such as home and end, if there are several omitted fields, arriving at an enabled input field may take quite a few iterations, while the field number bounces around. In the worst case it may turn out that the key the user pressed cannot navigate to another field. In this case control should eventually return to the original field the user navigated away from. If that is now disabled too your program and the terminal manager will loop endlessly trying to find a field where data entry is possible. The program can usually be terminated with Ctrl+S Ctrl+C or Alt+F4 on Windows, but the user will not be happy.

libtype 5

The libtype 1 scheme explained above has one minor drawback. If the user actually enters data into a field, but then navigates away from the field in a way that would cause inpm_x() to return a non zero value, then the entered value may be lost in some circumstances. For example, DP4 utilities have a function inpm_number() which takes a short * parameter that represents the name of a field or table on the database. The initial number is translated to the name, and then inpm_s() is called. On return the entered value is converted back to a number. However entering a name and then pressing a key such as cursor down breaks, so the users name is not assigned to the short. Then the function gets called again with its old, and probably valid, initial value with jump TRUE, and input moves to the next field.

Although rather obscure, DP4 users did occasionally notice this problem. The problem was fixed, by inventing a new libtype: df_libtype 5. The only difference from df_libtype 1, is that internally when a field input function is going to return a non zero value, but with the field number still equal to the starting value, rather than returning the call is repeated, so that it will return 0 allowing validation to take place (but the original abort_code is now lost). Almost all DP4 utilities set df_libtype=5 now. However, if a program needs to know the original abort_code (for example if it uses one or more navigation keys in a non standard way, then using this df_libtype will cause problems.


Cursor Movement in Maps

In DP4 dialogs users can navigate between fields using the cursor keys. However the navigation is not based on the physical layout of the map. Cursor movement is interpreted using the numbers assigned to the fields. Each map is considered to be divided into rows and columns, and the field numbers are assumed to imply the row and colunn number. For instance, if your map has 2 rows and 3 columns, then fields 1,2 and 3 are on the first row, and fields 4,5, and 6 on the second. If the cursor is in field 2, and the user presses cursor down, DP4 will attempt to move to field 5.

It is not actually that difficult to number a map in rows and columns properly. You simply divide the map (or a sketch of it - it is probably a good plan to do this before you actually set up the map) roughly into rows and columns so that no more than one field appears in each box (you may need to use a little poetic licence if your fields don't line up neatly). It is OK if a particularly big field occupies several boxes in your grid. Number the fields according to the box in which they appear. In detail:

  1. When dividing the map into a grid you may ignore static controls that are headings for a following edit control. These two controls normally share a field number. You can also ignore all controls that will be given field number 0 (static controls that won't be associated with a field number, VCR controls, lines, and so on)

    You do not have to divide radio buttons that are in a group. These will all have the same field number. In any case, inside radio buttons navigation does not work in the same way as for other fields.

    If your map contains controls that need field numbers but are never used for input, for example hooks or output only edit controls, these should also be ignored when creating the grid. Assign them field numbers that are beyond the value rows*columns, so that the DP4 terminal manager knows they can be ignored when the user is navigating round a dialog. If you assign numbers in the range of the navigable controls your data entry routines will get called for these field numbers. This should be harmless (your code should drop through to the default case and omit the field), but it does impact performance slightly.

  2. In the map properties enter the value for 'Rows' as the number of rows in your grid. Enter the value for 'Cols' as the number of columns in the grid.

    Number the boxes in the grid, starting from 1, according to their position in the grid, from left to right, top to bottom. If there is a field in a box its field number should be the number of the box, If a field is in more than one box assign the lowest (top left) number. It does not matter if there are gaps in the field number sequence. Here is an example:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    This means the natural sequence or Tab Order of the fields is also left to right,top to bottom. If you want the Tab Order to go through columns then set the "Column major" property and number the fields top to bottom, right to left. The example above would now look like this:

    1

    5

    9

    2

    6

    10

    3

    7

    11

    4

    It is even possible to number fields so that you do something like the top half of the first column, then the top half of the second, then the second half of the first column, and then finally the bottom half of the second. The trick there is to mentally cut your map in half and to move the lower half so that the two half maps form two diagonally opposite quarters of a larger grid. You then number the enlarged grid in the same way as previously described. Many maps on the system database are numbered like this.

    If this all seems more trouble than its worth, you can simply number the fields sequentially, and say they are all in one column and set the number of rows to the last field number (or the other way round - it makes no difference). In this case the user will still be able to use the cursor keys to move round the map, but the left and up arrow keys will work the same, as will the down and right arrow keys.


Accessing an OSDI from DP4 C Program

An OSDI can be accessed using the C functions in the standard DP4 C library. The interface is functionally similar to QA Build. A small example program is supplied showing how the calls may be used to access the DP4 Till.

Where a POD is passed as a function parameter, you must pass the logical device number as allocated to you by the system (in DFSETUP). For convenience, you can use a #define with an equivalent POD name to that chosen in DFSETUP. You can achieve this conveniently by generating a header file from DFSETUP containing the device numbers defined as POD names, and include this file in your C source. To generate a device header file:

  1. Choose Printer and device set up... from the DFSETUP menu
  2. Choose Parameterised output device (POD) configuration...
  3. Choose Generate POD names

You will be prompted for a header file name. Here is an extract from an example device header file for the DP4 Till. Note that the device numbers here may differ from your definitions if you have set up a DP4 Till. DP4 uses sequential numbering for devices, and yours may differ, depending on existing device definitions on your system database. You can get control over the numbers assigned to PODS (and Device Groups) by running DFSETUP with the -number command tail.

#define SLIP_PRINTER  10082  /* Slip printer */
#define JOURNAL_PRINTER  10083 /* Journal printer*/
#define TILL_ID  10084 /* Till ID */
#define TILL_DRAWER  10085 /* Till drawer */
#define TILL_INIT  10086 /* Example till for OSDI doc */


Example C Application

The following is a simple example DP4 C application that uses some of the PODs from the DP4 Till. It initialises the DP4 Till, sends output to the journal printer, requests the status of the Till drawer and finally sets input to the scanner. Further information is given in the code comments:

This program is an example only - it would be very bad practice to use hard coded messages in a real program.

/* DP4 C LIBMAKE definitions :
#db time2
#c
#end
*/

#include "dp4.h"
#include "tillapp.h"
#include "devices.h" /* Device definition header file from DFSETUP */

/* Input device mask values */
#define INP_KEYBOARD 1
#define INP_SCANNER  2

/* Map numbers (map definitions not shown )*/
#define M_TEXT_REQUEST  6
#define M_TEXT_PRINT    7
#define M_DRAWER_STATUS 8
#define M_WARNING       10

/* prototypes */
void text_out(char *);
void request_user_text(char *);

/**/

void process()
{
  char ptext[80];
  char status_str[20];
  char pause_char;

  pr_err_mode = 2;   /* Ignore errors and continue */
  map_load("TILLAPP");

  /* Initialise device */
  dvc_choose(PRINTER+NO_SELECT, TILL_INIT, "",     "");

  /* Print string to journal */
  dvc_set_subdevice(JOURNAL_PRINTER);
  if (print_error)
  {
    map_draw(M_WARNING, ERRORMAP);
    show_c(M_WARNING, 1, "Error initialising journal    printer");
  }
  else
    text_out("Hallo journal");

  /* Check status of drawer */
  dvc_set_subdevice(TILL_DRAWER);
  dvc_get_status(status_str);
  /* Display status */
  map_draw(M_DRAWER_STATUS, DATAMAP);

  if (status_str[0] == '1')
    show_c(M_DRAWER_STATUS, 1, "Open");
  else if (status_str[0] == '0')
    show_c(M_DRAWER_STATUS, 1, "Closed");
  askf_c(M_DRAWER_STATUS, 2, &pause_char);
  map_clear(M_DRAWER_STATUS);

  /* Get some scanner input */
  dvc_set_inp_mask(INP_SCANNER+INP_KEYBOARD);
  request_user_text(ptext);
  text_out(ptext);
}

/**/

void request_user_text(char *text)
{
  /* Get text from user */
  map_draw(M_TEXT_REQUEST, DATAMAP);
  askf_c(M_TEXT_REQUEST, 1, text);
  map_clear(M_TEXT_REQUEST);
}

/**/

void text_out(char *text)
{
  /* Output text - used to output to current POD */
  form(M_TEXT_PRINT);
  prn_c(M_TEXT_PRINT, 1, text);
  prn_print(FALSE);
  if (print_error)
  {
    map_draw(M_WARNING, ERRORMAP);
    show_c(M_WARNING, 1, "Error printing");
  }
}